Greetings,

I have implemented a 'wait' mobprogram command that delays execution of a mob
program for a certain amount of time, much like the 'mpsleep' snippet that is
out there.  I am doing this with the event system I have created for events such
as these.  (Pun intended)  Here's what an event looks like.
struct event
{
   struct event * next;
   union
   {
    ...
    struct prog_event_args  prog;
      /* etc.... */
   } event_args;

   sh_int delay;
   char *comments;
   void (*fire)(struct event  *);
   void (*destroy)(struct event *);
   bool valid;
};

However, I'm coming across a small problem that I'm not sure how to resolve, and
wondered if you might have an idea in how to straighten this out.

An event is created with an XXX_event function; in this instance, in
program_flow: prog_event(ch, (void *)mob, MOB, prog, count+1, timer);

...and is placed into the 'events' list until it is ready to fire.  It is fired,
which is a function specific to the event's properties, and then it
self-destroys, another function.  However, when I create a new event within the
previous event's fire function, the linked list gets out of whack.  I'll lay out
some code that'll help explain my problem.

Like mobile_update and the other update routines, events are updated as well:
void event_update(void)
{
   EVENT *ev = NULL, *ev_next = NULL, *ev_last = NULL;

   for(ev = events; ev; ev = ev_next)
   {
      ev_next = ev->next;

      if(--ev->delay <= 0)  //Expired, kill it
      {
          --> ev->fire(ev); <---

          if(ev_last)       //We're past the first element of the list.
             ev_last->next = ev_next;
          else              //First element.
             events = ev_next;

          --> ev->destroy(ev); <---
      }
      else                  //Not ready yet; start from here.
         ev_last = ev;
   }
}

Pretty straightforward stuff, the items between --> <-- are the focus.
Now, firing a program that is done waiting with ev->fire(ev) (function trimmed
for brevity):

void fire_prog_event(EVENT * ev)
{
    extern void program_flow( sh_int, char *, CHAR_DATA *, OBJ_DATA *,
ROOM_INDEX_DATA *,
                              CHAR_DATA *, const void *, const void *, int );

...
            if (ev->event_args.prog.mob && ev->event_args.prog.ch &&
                ev->event_args.prog.mob->in_room ==
ev->event_args.prog.ch->in_room)
            {
                program_flow(ev->event_args.prog.prog_code->vnum,
                             ev->event_args.prog.prog_code->code,
                             ev->event_args.prog.mob,
                             NULL,
                             NULL,
                             ev->event_args.prog.ch,
                             NULL, NULL, ev->event_args.prog.line);
            }
    }
}

So if the event's program is still valid--i.e. the mob and ch still exist and
are in the same room yet--then it'll continue execution with a call to
program_flow.  This means if there is another wait in the mobprog code, this
call to program_flow will create a new event to wait again.  As you can see,
this is all done inside the ev->fire(ev), meaning the linked list is now
different for the rest of the code in the event_update()'s for loop.

What do you think? I'm thinking I may have to put firing events onto their own
linked list so as not to disrupt the list of the ones still hatching.  However,
is there a much more elegant solution?




Reply via email to