SDK Sample Program 2

From ActiveWiki
Jump to: navigation, search

A simple DJ bot

This program implements a simple "DJ Bot". It enters the world and does a property query in order to locate the speaker object, which hopefully has already been placed in the proper location Once located, the bot changes the music every five minutes by changing the "create sound" action on the object.

Note that this program will not function exactly as is because it requires the privilege password of the owner of the speaker object in "beta" world. To get it to function in your world, simply change the name of the world that the bot enters. You may also want to change the name of the speaker object, and the cell that the speaker object is expected to be in.

There are many interesting enhancements that could be made to this simple program. Just a few ideas include:

  • Have the bot change the MIDI immediately when the previous one completes, instead of at the fixed interval of once every five minutes.
  • Have the bot take live requests.
  • Have the bot handle the case where the speaker object is missing at startup.
  • Have the bot handle the case where someone else moves or deletes the speaker object while the bot is running.
#include "Aw.h"
#include "Reason.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FIVE_MINUTES    (5 * 60 * 1000)
#define SPEAKER_OBJECT  "midispk.rwx"
#define WORLD           "beta"

void handle_cell_begin (void);
void handle_cell_object (void);
void handle_query (int rc);
void change_midi (void);

int owner;
int sequence[3][3];
int speaker_x;
int speaker_y;
int speaker_z;
int speaker_yaw;
int speaker_number;
int cell_x;
int cell_z;
int current_midi;
char *midis[] = {"class1.mid";, "class2.mid", "class3.mid", "class4.mid", "class5.mid",
                 "class6.mid", "class7.mid", "class8.mid", "class9.mid", "class10.mid", NULL};

int main (int argc, char *argv[])
{
  int rc;

  /* check command line */
  if (argc < 3)
  {
    printf ("Usage: %s number password\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 cell update event handlers */
  aw_event_set (AW_EVENT_CELL_BEGIN, handle_cell_begin);
  aw_event_set (AW_EVENT_CELL_OBJECT, handle_cell_object);

  /* install query callback */
  aw_callback_set (AW_CALLBACK_QUERY, handle_query);

  /* create bot instance */
  rc = aw_create (0, 0, 0);
  if (rc != RC_SUCCESS)
  {
    printf ("Unable to create bot instance (reason %d)\n", rc);
    return 1;
  }

  /* log bot into the universe */
  owner = atoi (argv[1]);
  aw_int_set (AW_LOGIN_OWNER, owner);
  aw_string_set (AW_LOGIN_PRIVILEGE_PASSWORD, argv[2]);
  aw_string_set (AW_LOGIN_APPLICATION, "SDK Sample Application #2");
  aw_string_set (AW_LOGIN_NAME, "Invisible DJ");
  rc = aw_login ();
  if (rc != RC_SUCCESS)
  {
    printf ("Unable to login (reason %d)\n", rc);
    return 1;
  }

  /* log bot into the world */
  rc = aw_enter (WORLD)
  if (rc != RC_SUCCESS)
    printf ("Unable to enter world (reason %d)\n", rc);
    return 1;
  }

  /* note that we don't announce our presence by calling aw_state_change (), so this
     bot will not physically appear in the world */

  /* issue first property query for the ground zero area so that we can locate
     the SPEAKER_OBJECT */
  printf ("Looking for speaker...\n");
  rc = aw_query (0, 0, sequence);
  if (rc != RC_SUCCESS)
  {
    printf ("Unable to query property (reason %d)\n", rc);
    return 1;
  }

  /* wait for query to complete and then change music every five minutes */
  while (!(rc = aw_wait (FIVE_MINUTES)))
    if (speaker_number)
      change_midi ();

  /* close everything down */
  aw_term ();
  return 0;

}

void change_midi (void)
{
  int rc;
  char action[100];

  aw_int_set (AW_OBJECT_OLD_NUMBER, speaker_number);
  aw_int_set (AW_OBJECT_OLD_X, speaker_x);
  aw_int_set (AW_OBJECT_OLD_Z, speaker_z);
  aw_int_set (AW_OBJECT_X, speaker_x);
  aw_int_set (AW_OBJECT_Y, speaker_y);
  aw_int_set (AW_OBJECT_Z, speaker_z);
  aw_int_set (AW_OBJECT_YAW, speaker_yaw);
  aw_int_set (AW_OBJECT_TILT, 0);
  aw_int_set (AW_OBJECT_ROLL, 0);
  aw_int_set (AW_OBJECT_OWNER, owner);
  aw_string_set (AW_OBJECT_MODEL, SPEAKER_OBJECT);
  aw_string_set (AW_OBJECT_DESCRIPTION, "SDK Sample Application #2 MIDI Speaker");
  
  sprintf (action, "create sound %s", midis[current_midi++]);
  
  if (!midis[current_midi])
    current_midi = 0;
  
  aw_string_set (AW_OBJECT_ACTION, action);
  rc = aw_object_change ();
  if (rc != RC_SUCCESS)
    printf ("Unable to change speaker (reason %d)\n", rc);
  else
  {
    printf ("Speaker changed.\n");
    speaker_number = aw_int (AW_OBJECT_NUMBER);
  }
}

void handle_cell_begin (void)
{
  int sector_x;
  int sector_z;

  /*
     This is called to indicate we are receiving the contents of a new cell. All 
     we need to do here is update the sequence number array for the next call to 
     aw_query ().
  */
  cell_x = aw_int (AW_CELL_X);
  cell_z = aw_int ([AW_CELL_Z]]);
  sector_x = aw_sector_from_cell (cell_x);
  sector_z = aw_sector_from_cell (cell_z);
  
  /* sanity check: make sure sector coordinates are within range */
  if (sector_x < -1 || sector_x > 1 || sector_z < -1 || sector_z > 1)
    return;
  
  sequence[sector_z + 1][sector_x + 1] = aw_int (AW_CELL_SEQUENCE);
}

void handle_cell_object (void)
{
  /* This is called once for each object in each cell that we are querying.  We
     check to see if the object is the SPEAKER_OBJECT we are looking for, by
     checking the name of the object and the cell it is located in. */
  if (cell_x == 1 && cell_z == 1 && 
    !strcmp aw_string (AW_OBJECT_MODEL), SPEAKER_OBJECT))
  {
    speaker_x = aw_int (AW_OBJECT_X);
    speaker_y = aw_int (AW_OBJECT_Y);
    speaker_z = aw_int (AW_OBJECT_Z);
    speaker_yaw = aw_int (AW_OBJECT_YAW);
    speaker_number = aw_int (AW_OBJECT_NUMBER);
    
    printf ("Speaker located!\n");
    
    change_midi ();
  }
}

void handle_query (int rc)
{
  /*
     This is called when the server has stopped sending property data. If we haven't
     found the speaker yet, then we check if the query is complete, and if not, 
     issue a new query with the updated sequence numbers to continue the update.
  */
  if (!speaker_number)
    if aw_bool (AW_QUERY_COMPLETE))
      aw_query (0, 0, sequence);
    else
      /* the query is complete but we didn't find the speaker! :( */
      printf ("Couldn't find speaker object.\n");
}