-
Notifications
You must be signed in to change notification settings - Fork 23
/
dg_event.c
executable file
·356 lines (301 loc) · 9.62 KB
/
dg_event.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/**
* @file dg_event.c LuminariMUD
* This file contains a simplified event system to allow trigedit
* to use the "wait" command, causing a delay in the middle of a script.
* This system could easily be expanded by coders who wish to implement
* an event driven mud.
*
* Part of the core tbaMUD source code distribution, which is a derivative
* of, and continuation of, CircleMUD.
*
* This source code, which was not part of the CircleMUD legacy code,
* was created by the following people:
* $Author: Mark A. Heilpern/egreen/Welcor $
* $Date: 2004/10/11 12:07:00$
* $Revision: 1.0.14 $
*/
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "db.h"
#include "dg_event.h"
#include "constants.h"
#include "comm.h" /* For access to the game pulse */
#include "mud_event.h"
/***************************************************************************
* Begin mud specific event queue functions
**************************************************************************/
/* file scope variables */
/** The mud specific queue of events. */
static struct dg_queue *event_q = NULL;
/** Initializes the main event queue event_q.
* @post The main event queue, event_q, has been created and initialized.
*/
void event_init(void)
{
event_q = queue_init();
}
/** Creates a new event 'object' that is then enqueued to the global event_q.
* @post If the newly created event is valid, it is always added to event_q.
* @param func The function to be called when this event fires. This function
* will be passed event_obj when it fires. The function must match the form
* described by EVENTFUNC.
* @param event_obj An optional 'something' to be passed to func when this
* event fires. It is func's job to cast event_obj. If event_obj is not needed,
* pass in NULL.
* @param when Number of pulses between firing(s) of this event.
* @retval event * Returns a pointer to the newly created event.
* */
struct event *event_create(EVENTFUNC(*func), void *event_obj, long when)
{
struct event *new_event = NULL;
if (when < 1) /* make sure its in the future */
when = 1;
CREATE(new_event, struct event, 1);
new_event->func = func;
new_event->event_obj = event_obj;
new_event->q_el = queue_enq(event_q, new_event, when + pulse);
new_event->isMudEvent = FALSE;
return new_event;
}
/** Removes an event from event_q and frees the event.
* @param event Pointer to the event to be dequeued and removed.
*/
void event_cancel(struct event *event)
{
if (!event)
{
log("SYSERR: Attempted to cancel a NULL event");
return;
}
if (!event->q_el)
{
log("SYSERR: Attempted to cancel a non-NULL unqueued event, freeing anyway");
}
else
queue_deq(event_q, event->q_el);
if (event->event_obj)
cleanup_event_obj(event);
free(event);
}
/* The memory freeing routine tied into the mud event system */
void cleanup_event_obj(struct event *event)
{
struct mud_event_data *mud_event = NULL;
if (event->isMudEvent)
{
mud_event = (struct mud_event_data *)event->event_obj;
free_mud_event(mud_event);
}
else
free(event->event_obj);
}
/** Process any events whose time has come. Should be called from, and at, every
* pulse of heartbeat. Re-enqueues multi-use events.
*/
void event_process(void)
{
struct event *the_event = NULL;
long new_time = 0;
while ((long)pulse >= queue_key(event_q))
{
if (!(the_event = (struct event *)queue_head(event_q)))
{
log("SYSERR: Attempt to get a NULL event");
return;
}
/* Set the_event->q_el to NULL so that any functions called beneath
* event_process can tell if they're being called beneath the actual
* event function. */
the_event->q_el = NULL;
/* call event func, reenqueue event if retval > 0 */
if ((new_time = (the_event->func)(the_event->event_obj)) > 0)
the_event->q_el = queue_enq(event_q, the_event, new_time + pulse);
else
{
if (the_event->isMudEvent && the_event->event_obj != NULL)
free_mud_event((struct mud_event_data *)the_event->event_obj);
/* It is assumed that the_event will already have freed ->event_obj. */
free(the_event);
}
}
}
/** Returns the time remaining before the event as how many pulses from now.
* @param event Check this event for it's scheduled activation time.
* @retval long Number of pulses before this event will fire. */
long event_time(struct event *event)
{
long when = 0;
when = queue_elmt_key(event->q_el);
return (when - pulse);
}
/** Frees all events from event_q. */
void event_free_all(void)
{
queue_free(event_q);
}
/** Boolean function to tell whether an event is queued or not. Does this by
* checking if event->q_el points to anything but null.
* @retval int 1 if the event has been queued, 0 if the event has not been
* queued. */
int event_is_queued(struct event *event)
{
if (!event)
return 0;
if (event->q_el)
return 1;
else
return 0;
}
/***************************************************************************
* End mud specific event queue functions
**************************************************************************/
/***************************************************************************
* Begin generic (abstract) priority queue functions
**************************************************************************/
/** Create a new, empty, priority queue and return it.
* @retval dg_queue * Pointer to the newly created queue structure. */
struct dg_queue *queue_init(void)
{
struct dg_queue *q = NULL;
CREATE(q, struct dg_queue, 1);
return q;
}
/** Add some 'data' to a priority queue.
* @pre The paremeter q must have been previously created by queue_init.
* @post A new q_element is created to hold the data parameter.
* @param q The existing dg_queue to add an element to.
* @param data The data to be associated with, and theoretically used, when
* the element comes up in q. data is wrapped in a new q_element.
* @param key Indicates where this event should be located in the queue, and
* when the element should be activated.
* @retval q_element * Pointer to the created q_element that contains
* the data. */
struct q_element *queue_enq(struct dg_queue *q, void *data, long key)
{
struct q_element *qe = NULL, *i = NULL;
int bucket = 0;
CREATE(qe, struct q_element, 1);
qe->data = data;
qe->key = key;
bucket = key % NUM_EVENT_QUEUES; /* which queue does this go in */
if (!q->head[bucket])
{ /* queue is empty */
q->head[bucket] = qe;
q->tail[bucket] = qe;
}
else
{
for (i = q->tail[bucket]; i; i = i->prev)
{
if (i->key < key)
{ /* found insertion point */
if (i == q->tail[bucket])
q->tail[bucket] = qe;
else
{
qe->next = i->next;
i->next->prev = qe;
}
qe->prev = i;
i->next = qe;
break;
}
}
if (i == NULL)
{ /* insertion point is front of list */
qe->next = q->head[bucket];
q->head[bucket] = qe;
qe->next->prev = qe;
}
}
return qe;
}
/** Remove queue element qe from the priority queue q.
* @pre qe->data has been dealt with in some way.
* @post qe has been freed.
* @param q Pointer to the queue containing qe.
* @param qe Pointer to the q_element to remove from q.
*/
void queue_deq(struct dg_queue *q, struct q_element *qe)
{
int i = 0;
assert(qe);
i = qe->key % NUM_EVENT_QUEUES;
if (qe->prev == NULL)
q->head[i] = qe->next;
else
qe->prev->next = qe->next;
if (qe->next == NULL)
q->tail[i] = qe->prev;
else
qe->next->prev = qe->prev;
free(qe);
}
/** Removes and returns the data of the first element of the priority queue q.
* @pre pulse must be defined. This is a multi-headed queue, the current
* head is determined by the current pulse.
* @post the q->head is dequeued.
* @param q The queue to return the head of.
* @retval void * NULL if there is not a currently available head, pointer
* to any data object associated with the queue element. */
void *queue_head(struct dg_queue *q)
{
void *dg_data = NULL;
int i = 0;
i = pulse % NUM_EVENT_QUEUES;
if (!q->head[i])
return NULL;
dg_data = q->head[i]->data;
queue_deq(q, q->head[i]);
return dg_data;
}
/** Returns the key of the head element of the priority queue.
* @pre pulse must be defined. This is a multi-headed queue, the current
* head is determined by the current pulse.
* @param q Queue to check for.
* @retval long Return the key element of the head q_element. If no head
* q_element is available, return LONG_MAX. */
long queue_key(struct dg_queue *q)
{
int i = 0;
i = pulse % NUM_EVENT_QUEUES;
if (q->head[i])
return q->head[i]->key;
else
return LONG_MAX;
}
/** Returns the key of queue element qe.
* @param qe Pointer to the keyed q_element.
* @retval long Key of qe. */
long queue_elmt_key(struct q_element *qe)
{
return qe->key;
}
/** Free q and all contents.
* @pre Function requires definition of struct event.
* @post All items associeated qith q, including non-abstract data, are freed.
* @param q The priority queue to free.
*/
void queue_free(struct dg_queue *q)
{
int i = 0;
struct q_element *qe = NULL, *next_qe = NULL;
struct event *event = NULL;
for (i = 0; i < NUM_EVENT_QUEUES; i++)
{
for (qe = q->head[i]; qe; qe = next_qe)
{
next_qe = qe->next;
if ((event = (struct event *)qe->data) != NULL)
{
if (event->event_obj)
cleanup_event_obj(event);
free(event);
}
free(qe);
}
}
free(q);
}