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?