[CODE] Event Queue (LONG)

From: si12632 (si12632@ci.uminho.pt)
Date: 07/10/96


Hi,
  I've been a member of this list for quite a while, and learned a lot
from listening to every question and answer in it, so now i think it is
time i start contributing.

  My first contribution, wich follows in the end of this messagei, is an
event queue. For those who do not know, it's a list of things that are
scheduled to happen in a certain moment in the future. 

  Before you start flaming, let me just tell you that i intended to
upload it to the ftp site, but i don't have ftp access for uploads. :(
If the person who is managing the site, could put it there, or maybe the 
person from the code snippets page (forgot your names, sorry), could put
it somewhere on the net.

  As far as i know, the code is free of bugs, and has worked perfectly in
my mud for a long time, with no trouble at all.

  Additionally, i've written a shop olc add-in for Sammy's OLC, and changed
the guild's code to work similar to shops. If there's any interest, i'll 
post it on the list or e-mail it to the persons in question.
 
  Well, enough talk, on with the code:

The event queue is composed by two files:
   events.c and events.h

---------  event.c ----------


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

#include "structs.h"
#include "events.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "db.h"

int in_event_handler=0;
struct event_info *pending_events = NULL;
struct event_info *prev = NULL;

void run_events() 
{
  struct event_info *temp, *prox;

  in_event_handler=1;

  prev=NULL;
  for(temp=pending_events;temp;temp=prox) {
    prox=temp->next;
    temp->ticks_to_go--;
    if (temp->ticks_to_go==0) {
      
      /* run the event */
      if (!temp->func)
        log("SYSERR: Attempting to run a NULL event. Ignoring");
      else
        (temp->func)(temp->causer,temp->victim,temp->info);

      /* remove the event from the list. */
      if (!prev)
        pending_events=prox;
      else
        prev->next=prox;
      free(temp);
    } else if (temp->ticks_to_go==-1) {
      if (!prev)
        pending_events=prox;
      else
        prev->next=prox;
      free(temp);
    } else  
      prev=temp;
  }; 

  in_event_handler=0;
};

void add_event(int delay,EVENT(*func),void *causer,void *victim,void *info)
{
  struct event_info *new;

  CREATE(new,struct event_info,1);

  new->ticks_to_go=delay;
  new->func=func;
  new->causer=causer;
  new->victim=victim;
  new->info=info;

  new->next=pending_events;
  pending_events=new;
  if (in_event_handler && !prev)
    prev=pending_events;
};

void clean_events(void *pointer)
{
  struct event_info *temp,*prox;
  struct event_info *previous;

  if (in_event_handler)
    log("SYSERR: Trying to remove events inside the handler. Attempting to continue.");
  previous=NULL;
  for(temp=pending_events;temp;temp=prox) {
    prox=temp->next;
    if (temp->causer==pointer || temp->victim==pointer ||
        (void *)(temp->func)==pointer)
      temp->ticks_to_go=0;
  };
};


/* *********************
*  Pre-defined events.(Just examples) *
********************* */
/*
extern struct room_data *world;
EVENT(teleport_event)
{
  int destin=0;
  void death_cry(struct char_data *ch);

  /* Is the character still in the same room? */
  if (GET_LEVEL(VICTIM_CH)<LVL_GOD && VICTIM_CH->in_room==info) {

    /* Tell the other players in the room about the teleport. */
    act(world[VICTIM_CH->in_room].teleport->message_you,TRUE,VICTIM_CH,NULL,
        VICTIM_CH,TO_VICT);
    act(world[VICTIM_CH->in_room].teleport->message_oth,FALSE,VICTIM_CH,NULL,
        VICTIM_CH,TO_NOTVICT);

    /* Perform the teleport. */
    if ((destin=real_room(world[info].teleport->to_room))!=-1)
    {
      char_from_room(VICTIM_CH);
      char_to_room(VICTIM_CH,destin);
      look_at_room(VICTIM_CH,1);
      if (IS_SET(ROOM_FLAGS(VICTIM_CH->in_room),ROOM_DEATH) && 
                                              GET_LEVEL(VICTIM_CH)<LVL_IMMORT)
      {
        log_death_trap(VICTIM_CH);
        death_cry(VICTIM_CH);
        extract_char(VICTIM_CH);
      }
    } else
      log("SYSERR: Trying to teleport to unexisting room. Ignoring.");
  };
};
*/

-------------  events.h ---------------------

/*  The macros provide the type casting useful when writing event drivers. */
#define VICTIM_CH  ((struct char_data *)victim)
#define CAUSER_CH  ((struct char_data *)causer)
#define VICTIM_OBJ ((struct obj_data *)victim)
#define CAUSER_OBJ ((struct obj_data *)causer)

void add_event(int delay,EVENT(*func),void *causer,void *victim,void *info);
void run_events();
void clean_events(void *pointer);


Plus there are some changes to other files:

--------- structs.h ------------

Just add the following at the bottom:

/****  Event-driven engine structs  ******************************/

#define PULSE_EVENT     (1 RL_SEC)

#define EVENT(name) void (name)(void *causer, void *victim, long info)

struct event_info {
  int ticks_to_go;
  EVENT(*func);
  void *causer,*victim;
  void *info;
  struct event_info *next;
};

----------- handler.c

In function extract_char(), just before the REMOVE_FROM_LIST:

  /* remove any pending event for/from this character. */
  clean_events(ch);

------------ comm.c

In function heart_beat(), just after the call to perform_violence()

  if (!(pulse % PULSE_EVENT))
    run_events();


That's all, I think. If it doesn't work mail me and i'll see what's missing. 
I also send my code for the teleport event, you can use it if you
want, but you'll have to change a lot more stuff (db.c and handler.c, structs.h
i think).

extern struct room_data *world;
EVENT(teleport_event)
{
  int destin=0;
  void death_cry(struct char_data *ch);

  /* Is the character still in the same room? */
  if (GET_LEVEL(VICTIM_CH)<LVL_GOD && VICTIM_CH->in_room==info) {

    /* Tell the other players in the room about the teleport. */
    act(world[VICTIM_CH->in_room].teleport->message_you,TRUE,VICTIM_CH,NULL,
        VICTIM_CH,TO_VICT);
    act(world[VICTIM_CH->in_room].teleport->message_oth,FALSE,VICTIM_CH,NULL,
        VICTIM_CH,TO_NOTVICT);

    /* Perform the teleport. */
    if ((destin=real_room(world[info].teleport->to_room))!=-1)
    {
      char_from_room(VICTIM_CH);
      char_to_room(VICTIM_CH,destin);
      look_at_room(VICTIM_CH,1);
      if (IS_SET(ROOM_FLAGS(VICTIM_CH->in_room),ROOM_DEATH) &&
                                              GET_LEVEL(VICTIM_CH)<LVL_IMMORT)
      {
        log_death_trap(VICTIM_CH);
        death_cry(VICTIM_CH);
        extract_char(VICTIM_CH);
      }
    } else
      log("SYSERR: Trying to teleport to unexisting room. Ignoring.");
  };
};

Finally, if you have mob progs, here's another example of use of events:

------------  mobcmd.c  ------

In the end of the file:

EVENT(delayed_command)
{
  command_interpreter(CAUSER_CH,(char *)info);
  free((char *)info);
}

ACMD(do_mpdelayed)
{
  char arg1[20];
  int delay;

  arg1[0]='\0';

  if (!IS_NPC(ch) || IS_AFFECTED(ch, AFF_CHARM))
    {
      send_to_char("Huh?\n\r", ch);
      return;
    }

  half_chop(argument,arg1,buf);
  delay=atoi(arg1);

  if (!delay)
    bug("Mpdelayed - the delay is invalid. - vnum #%d",0)
  else {
    sprintf(buf1,"Ok. Executing '%s' in %d seconds.\r\n",buf,delay);
    send_to_char(buf1,ch);
    add_event(delay,delayed_command,ch,NULL,(int)strdup(buf));
  };
}



                   I hope you find it usefull,
                                Luis Carvalho

      +------------------------------+---------------------------------+   
   (`o|  I'll never understand why   | Luis Pedro Passos de Carvalho   |o')
   ()=| important letters get lost   | E-mail: si12632@ci.uminho.pt    |=()
   || | and junk mail never does...  | The views expressed are my own. | ||
      +------------------------------+---------------------------------+   
  main(g,v)int**v;{for(;*++v;)for(g=open(*v,0);2<g*read(g,v,1);write(0,v,1));}



This archive was generated by hypermail 2b30 : 12/07/00 PST