Aw address C example 1

From ActiveWiki
Jump to navigation Jump to search

Lookup and announce the IP address of any user who says "What is my address?".

#include "Aw.h"
#include <stdio.h>
#include <stdlib.h>

void handle_chat (void);

int main(int argc, char *argv[])
{
  int rc;
  
  /* Check command line */
  if (argc < 4)
  {
    printf ("Usage: %s number password world\n", argv[0]);
    return 1;
  }   
  
  /* Initialize Active Worlds API */
  rc = aw_init (AW_BUILD);
  if (rc != RC_SUCCESS)
  {
    printf ("Unable to initialize API (reason %d)\n", rc);
    return 1;
  }
  
  /* Install handler for chat event */
  aw_event_set (AW_EVENT_CHAT, handle_chat);
  
  /* Create bot instance */
  rc = aw_create (NULL, 0, NULL);
  if (rc != RC_SUCCESS)
  {
    printf ("Unable to create bot instance (reason %d)\n", rc);
    return 1;
  }
  
  /* Log bot into the universe */
  aw_int_set (AW_LOGIN_OWNER, atoi (argv[1]));
  aw_string_set (AW_LOGIN_PRIVILEGE_PASSWORD, argv[2]);
  aw_string_set (AW_LOGIN_APPLICATION, "aw_address sample application #1");
  aw_string_set (AW_LOGIN_NAME, "MyBot");
  rc = aw_login ();
  if (rc != RC_SUCCESS)
  {
    printf ("Unable to login (reason %d)\n", rc);
    return 1;
  }
  
  /* Enter bot into the world */
  rc = aw_enter (argv[3]);
  if (rc != RC_SUCCESS)
  {
    printf ("Unable to enter world (reason %d)\n", rc);
    return 1;
  }
  
  /* Announce position in the world */
  aw_int_set (AW_MY_X, 1000); /* 1W */
  aw_int_set (AW_MY_Z, 1000); /* 1N */
  aw_int_set (AW_MY_YAW, 2250); /* Face towards GZ */
  rc = aw_state_change ();
  if (rc != RC_SUCCESS)
  {
    printf ("Unable to change state (reason %d)\n", rc);
    return 1;
  }
  
  /* Main event loop */
  while (aw_wait (-1) == RC_SUCCESS);
  
  /* Close everything down */
  aw_destroy ();
  aw_term ();
  
  return 0;
}

void handle_chat (void)
{
  int  rc;
  char name[256];
  char msg[256];
  
  /* Because AW_AVATAR_NAME could change while aw_address is blocking */   
  strcpy(name, aw_string (AW_AVATAR_NAME));
 
  /* Use stricmp or strcasecmp (*nix) for case-insensitive string comparison */
  if (strcmp (aw_string (AW_CHAT_MESSAGE), "What is my address?") == 0)
  {
    rc = aw_address (aw_int (AW_CHAT_SESSION));
    if (rc != RC_SUCCESS)
    {
      sprintf (msg, "%s, I cannot determine your IP address (reason %d)", name, rc);
    }
    else
    {
      int           address;
      unsigned char *p = (unsigned char*)&address;
      
      address = aw_int (AW_AVATAR_ADDRESS);
      
      /* The address is in network byte order which means that the most significant byte comes first in memory */
      sprintf (msg, "%s, your IP address is %u.%u.%u.%u", name, p[0], p[1], p[2], p[3]);
    }
    aw_say (msg);
  }
}

Consider the following scenario where two users A and B are visible to the bot.

  1. User A requests for its IP address to be announced.
  2. The SDK receives the AW_EVENT_CHAT event for user A, and the bot calls aw_address in the event handler.
  3. User B requests for its IP address to be announced before the call to aw_address completes (or even before the AW_EVENT_CHAT event is processed).
  4. The SDK receives the AW_EVENT_CHAT event for user B, and the bot calls aw_address in the event handler.

The bot is now waiting for the second call to aw_address, for user B, to complete.

It will however receive the IP address of user A first since it was requested first. This will be indicated by AW_AVATAR_SESSION which contains the session number of user A.

This leads to the following.

  1. The second call to aw_address for user B completes but the IP address of user A is announced.
  2. The first call to aw_address for user A completes but the IP address of user B is announced.

This is very unlikely to happen for the AW_EVENT_CHAT event as you need two users to make the request at the same time (or between two calls to aw_wait, or two blocking SDK API calls).

But, if you do an aw_address call when processing AW_EVENT_AVATAR_ADD events then it becomes very likely to happen. If more than one avatar is visible to the bot when it calls aw_state_change for the first time then it will need to process a quick succession of AW_EVENT_AVATAR_ADD events. One for each avatar. And it is likely that any blocking calls within the event handler won't have time to complete before the event handler is called again for the next avatar.

The best way to solve this problem is to register AW_CALLBACK_ADDRESS. The IP address would then be announced by the callback handler. And the session number passed to the callback handler in AW_AVATAR_SESSION has to be associated with a name.