First of all let me say this is a system that takes a little knowledge with C to install. It's not a high level solution. It's low level. It's meant to implement timers, queues, and need-to-act lists. It helps to replace the standard polling systems that destroy CPU efficiency in applications. A lot of the events I have implemented below can be deleted out if you don't see yourself wanting to use them. This is just some ideas and some different types of events I personally use. It's sorta open ended to what you can use this for. To start an event you simple do this: { EVENT_DATA *pEvent = new_event(EVENT_CORPSE_CLEANUP); pEvent->target[0] = (void *) corpse; *(int *)pEvent->target[1] = 4*60*5; // Exactly 5 minutes } The above code registers a new event for the type I used of corpse cleanup. The code would be placed at the point a corpse is created after a death. It sets the one and only target defined as the corpse in question. And then I set it to exactly 5 minutes before the corpse decays. In the event system the counter is evaluated 4 times a second. (This depends on your pulse per second) So 4 times 60 times 5 is exactly 5 minutes. Next to actually evaluate the event you place this in your update_handler that runs 4 times a second. You can place this in a generic function and switch based on your event, but here is the actual code that would run. // Runs through all of our events for corpse cleanup that need to be acted on. for (e = event_list[EVENT_CORPSE_CLEANUP];e;e = e_next) { int *timer; OBJ_DATA *target; e_next = e->next; timer = (int *)e->target[1]; target = (OBJ_DATA *)e->target[0]; // No target, so free the event. It's no longer valid. if (target == 0){ free_event(e); continue; } if (*timer > 0) // Our timer has not expired yet. --(*timer); // Decrease the time left. else { //decay_item(target); // I've commented this out but I use this for decaying any item. // You could use your function to destroy an object here. free_event(e); // Free our event cause we are done with it. The event has fired. } } That's the only example I'm going to give, but in the code you'll see all of the different ideas for how the event can be used. It can have multiple, abstract targets. You just need to cast those void pointers and set them correctly. I use it to replace just about every polled function in my mud and it has improved my preformance dramatically. Oh, and the above function can be made into a generic function to work with multiple events. Just pass the event type to it. Well, one more example. // // Add them to the event handler for combat. *new_event(EVENT_VIOLENCE)->target = ch; // With just one line you can add them to a queue if there is no timer. // Anyone in this queue for example, is currently in combat. That removes having to go through everyone who is in combat. // Just anyone in the EVENT_VIOLENCE queue. It's also evaluated the same way the first example is except you would evaluate it in your violence_update. /***********************************************************************************/ //First events.h // You'd include this file anywhere you are using events. Specifically where the primary update from above comes from. // Just remove/add events you feel you need. Check out events.c for how to actually create a new type of event. enum { EVENT_AGGRESS = 0, // for aggression EVENT_VIOLENCE, // for combat EVENT_REGEN, // for character regeneration // Timer garbage! EVENT_SPELL, // for spells EVENT_DEATH, EVENT_GENERAL, EVENT_LOGOUT, EVENT_REVERSE, EVENT_GAME, EVENT_FIREWORK, EVENT_SHIP, EVENT_CORPSE_CLEANUP, MAX_EVENT }; typedef struct event_data EVENT_DATA; struct event_data { EVENT_DATA *next; // for traversing the main list. EVENT_DATA *prev; short type; // Type of event. Aggress..combat..more. // Multiple targets. void **target; // could be anything but most likely a character, object, or room. }; extern EVENT_DATA *event_list[MAX_EVENT]; EVENT_DATA *new_event(int type); void free_event(EVENT_DATA *pEvent); // ----------------------------- // EVERYTHING BELOW HERE IS PART OF // events.c /* Runter's low level event system */ #include #include #include #include "events.h" // event_list is an array of lists of different types of events. // All events are NOT thrown together. They are separated based on type // for preformance sake. EVENT_DATA *event_list[MAX_EVENT]; // This is a list of events that are currently free for recycle. // This is faster than a new malloc call. EVENT_DATA *free_event_list; // These are talleys of how many of each type of event is currently allocated or has been dealloced. // Used somewhat to debug for memory leaks. int event_alloc[MAX_EVENT]; int event_dealloc[MAX_EVENT]; // Allocates a new event data and initializes. EVENT_DATA *init_ev(void) { EVENT_DATA *pEvent; if (free_event_list) { pEvent = free_event_list; free_event_list = pEvent->next; } else pEvent = (EVENT_DATA*)malloc(sizeof(EVENT_DATA)); pEvent->next = 0; pEvent->target = 0; pEvent->type = 0; pEvent->prev = 0; return pEvent; } // function for creating a new event based on a number type. EVENT_DATA *new_event(int type) { EVENT_DATA *pEvent = init_ev(); // This just sets our type so it knows which it is. pEvent->type = type; // Depending on the type our void pointer may have more arguments. switch(type) { default: // Bad event, we need some type of logging here. Depends on your codebase. //logf("new_event: bad type\r\n"); free (pEvent); return 0; // EVENTS THAT NEED A SINGLE TARGET OF ANY KIND. // EXAMPLE: Aggressive mobs that need to be polled. // EXAMPLE: CHARACTERS IN COMBAT THAT NEED TO BE POLLED. // EXAMPLE: PLAYERS OR MOBILES HURT THAT NEED TO REGEN. case EVENT_AGGRESS: // An aggressive mob that needs to be acted on. case EVENT_REGEN: // The target is hurt and needs to regen. case EVENT_VIOLENCE: // The target is in combat. pEvent->target = (void **)malloc(sizeof(void *)); pEvent->target[0] = 0; break; // EVENTS THAT NEED EXACTLY 2 TARGETS. // EXAMPLE: ANYTHING THAT NEEDS A TIMER AS WELL AS A TARGET. // EXAMPLE: AN OBJ ON THE GROUND HAS X SECONDS BEFORE IT DECAYS. case EVENT_CORPSE_CLEANUP: // For cleaning up corpses or other objects that decay. case EVENT_LOGOUT: // Used if you want a timer in place that logs a character out. case EVENT_DEATH: // Used if you want a character to have to wait before respawning after death. pEvent->target = (void **)malloc(sizeof(void *) * 2); pEvent->target[0] = 0; // CHAR_DATA or DESCRIPTOR_DATA pEvent->target[1] = (int *)malloc(sizeof(int)); // int timer break; // I used this for having a timer for my spells. A pre-cast timer before spells go off. case EVENT_FIREWORK: case EVENT_SPELL: pEvent->target = (void **)malloc(sizeof(void *) * 3); pEvent->target[0] = 0; // character doing the casting. pEvent->target[1] = (int *)malloc(sizeof(int)); // An int timer. pEvent->target[2] = 0; // The string they passed with the spell. Could be the target of some kind. break; // I used this for a specific event when spell effects were resisted. They would initially land and // reverse themselves after the timer was up. Often just a few seconds. case EVENT_REVERSE: pEvent->target = (void**)malloc(sizeof(void *) * 3); pEvent->target[0] = 0; // CHAR_DATA, target effected by the spell pEvent->target[1] = (int *)malloc(sizeof(int)); // timer until the reverse pEvent->target[2] = (int *)malloc(sizeof(int)); // sn of affect to reverse break; // I used this for my minigames update handler. How long it takes to deal a card, etc. case EVENT_GAME: pEvent->target = (void**)malloc(sizeof(void *) *2); pEvent->target[0] = 0; // CHAR_DATA; pEvent->target[1] = (int *)malloc(sizeof(int)); // timer *(int *)pEvent->target[1] = (20); // I set the timer here. break; // For my ships code. This was the event for a ship to turn to a different direction after the command was given. case EVENT_SHIP: pEvent->target = (void**)malloc(sizeof(void *) * 3); pEvent->target[0] = 0; // AREA_DATA, the boat; pEvent->target[1] = (int *)malloc(sizeof(int)); // timer. pEvent->target[2] = 0; // SCHEDULE_COMMAND, command to execute. break; } // double liked for quicker extraction from list. pEvent->next = event_list[type]; pEvent->prev = 0; if (event_list[type]) event_list[type]->prev = pEvent; event_list[type] = pEvent; ++event_alloc[type]; return pEvent; } // remove and free a node from its list. void free_event(EVENT_DATA *pEvent) { if (pEvent->prev) { pEvent->prev->next = pEvent->next; } else { event_list[pEvent->type] = pEvent->next; } if (pEvent->next) pEvent->next->prev = pEvent->prev; pEvent->prev = 0; pEvent->next = 0; event_dealloc[pEvent->type]++; switch(pEvent->type) { // just pointers. default: free(pEvent->target); break; // just timers and pointer. case EVENT_REVERSE: case EVENT_SPELL: case EVENT_FIREWORK: free(pEvent->target[2]); case EVENT_GAME: case EVENT_CORPSE_CLEANUP: case EVENT_LOGOUT: case EVENT_DEATH: free(pEvent->target[1]); free(pEvent->target); break; } pEvent->next = free_event_list; free_event_list = pEvent; } // This function returns debug information in the form of a static c-string. // The purpose of this function is to be used in a command function to evaluate your event system. char* debug_information() { int i = 0; static char buf[500]; // Maximum of 500 characters. Will never be more than that. char buf2[256]; //temp buffer buf[0] = 0; for(;i < MAX_EVENT;++i) { sprintf(buf2, "Allocated: %d Deallocated: %d\r\n", event_alloc[i], event_dealloc[i]); strcat(buf, buf2); } return buf; }