blob: b81d38b9bdd9dcb96822d1973674b4d43b20a207 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// /p/daemon/eventd.c -- Event Dispatcher
4//
5// $Id$
6#pragma strict_types,save_types
7#pragma no_clone
8#pragma no_shadow
9#pragma pedantic
10#pragma range_check
11
12#include <wizlevels.h>
13#include <defines.h>
14#include <events.h>
15
16// Fuer Statistiken
17#define STATISTICS
18
19#define HOME(x) (__PATH__(0)+x)
20#define STORE HOME("save/eventd")
21
22
23#define LOG(x) log_file("EVENTS", sprintf(\
24 "[%s] %s\n",dtime(time()),x))
25//#define LOGEVENT(x,y,z) log_file("EVENTS", sprintf(\
26// "[%s] Event %s triggered by %O (Args: %.40O)\n",dtime(time()),x,y,z))
27#define LOGEVENT(x,y,z)
28//#define LOGREGISTER(w,x,y,z) log_file("EVENTLISTENER",sprintf(\
29// "[%s] %O (Fun: %.25s) registered for %s by %O\n",dtime(time()),w,x,y,z))
30#define LOGREGISTER(w,x,y,z)
31//#define LOGUNREGISTER(x,y,z) log_file("EVENTLISTENER",sprintf(\
32// "[%s] %O was unregistered from %s by %O\n",dtime(time()),x,y,z))
33#define LOGUNREGISTER(x,y,z)
34//#define LOGEVENTFINISH(x,y) log_file("EVENTS", sprintf(\
35// "[%s] Event %s (triggered by %O) finished\n",dtime(time()),x,y))
36#define LOGEVENTFINISH(x,y)
37
38#ifndef DEBUG
39#define DEBUG(x) if (find_player("zesstra"))\
40 tell_object(find_player("zesstra"),\
41 "EDBG: "+x+"\n")
42#endif
43
44#define TICK_RESERVE 300000
45#define TICKSPERCALLBACK 30000
46
47// Indizes fuer Callback-Infos (events, active)
48#define CB_FUN 0
49#define CB_CLOSURE 1
50#define CB_OBJECT 2
51// Indizes fuer Pending
52#define P_EID 0
53#define P_TRIGOB 1
54#define P_TRIGOBNAME 2
55#define P_ARGS 3
56#define P_TIME 4
57
58// Indizes fuer Active
59#define A_EID 0
60#define A_TRIGOB 1
61#define A_TRIGOBNAME 2
62#define A_ARGS 3
63#define A_LISTENERS 4
64#define A_TIME 5
65
66/* alle events, die er kennt.
67 Datenstruktur events:
68 ([id:([obname:fun;closure;object, ... ]),
69 id2: ([....]),
70 ]) */
71mapping events=([]);
72/* abzuarbeitende Events, Datenstruktur:
73 ({ ({id, trigob, trigobname, args}),
74 ({id2, trigob, trigobname, args}), ... }) */
75nosave mixed pending=({});
76/* Der gerade aktive Event, der wird gerade abgearbeitet.
77 Datenstruktur active:
78 ({ id:trigob;trigobname;args;([obname:fun;closure;object, ...]), trigtime})
79*/
80nosave mixed active=({});
81
82int lastboot; // Zeitstempel des letzten Reboots
83// Flag, wenn gesetzt, zerstoert sich der Eventd, wenn keine Events
84// abzuarbeiten sind.
85nosave int updateme;
86
87// einfache Statistik, gespeichert wird ein Histogramm. KEys sind die
88// Zeitdifferenzen zwschen Eintragen und Erledigen des Events, Values die
89// Haeufigkeiten.
90nosave mapping stats=([]);
91
92
93protected void process_events();
94protected void save_me();
95varargs int remove(int silent);
96
97// ist der Event-Typ "eid" schon bekannt, d.h. gib es min. 1 Listener?
98// Liefert Anzahl der Listener zurueck.
99int CheckEventID(string eid) {
100 if (!stringp(eid) || !member(events,eid))
101 return 0;
102 return(sizeof(events[eid]));
103}
104
105// entscheidet, ob ein Objekt fuer einen Event eingetragen werden darf. Zum
106// Ueberschreiben in geerbten Dispatchern, in diesem Scheduler sind alle
107// Events oeffentlich. Bekommt die Event-ID, das einzutragende Objekt und das
108// eintragende Objekt uebergeben.
109// 1 fuer Erlaubnis, 0 sonst.
110protected int allowed(string eid, object ob, object po) {
111 return(1);
112}
113
114// entscheidet, ob ein Objekt einen bestimmten Event triggern darf. Zum
115// Ueberschreiben in geerbten Dispatchern, in diesem Scheduler sind alle
116// Events oeffentlich. Bekommt die Event-ID und das triggernde Objekt
117// uebergeben.
118// 1 fuer Erlaubnis, 0 sonst.
119protected int allowedtrigger(string eid, object trigger) {
120 return(1);
121}
122
123// registriert sich fuer einen Event. Wenn es den bisher nicht gibt, wird er
124// implizit erzeugt. Wenn das Objekt ob schon angemeldet war, wird der
125// bisherige Eintrag ueberschrieben.
126// 1 bei Erfolg, <=0 bei Misserfolg
127int RegisterEvent(string eid, string fun, object ob) {
128 object po;
129 if (!stringp(eid) || !stringp(fun) ||
130 !objectp(ob) || !objectp(po=previous_object()))
131 return -1;
132 if (!allowed(eid, ob, po)) return -2;
133 closure cl=symbol_function(fun,ob);
134 if (!closurep(cl))
135 return -3;
136 if (!mappingp(events[eid]))
137 events[eid]=m_allocate(1,3); // 3 Werte pro Key
138 events[eid]+=([object_name(ob):fun;cl;ob]);
139// if (find_call_out(#'save_me)==-1)
140// call_out(#'save_me,15);
141 LOGREGISTER(ob,fun,eid,po);
142 return 1;
143}
144
145// entfernt das Objekt als Listener aus dem Event eid
146// Mehr oder weniger optional, wenn ein event verarbeitet wird und das Objekt
147// ist nicht mehr auffindbar, wird es ebenfalls geloescht. Bei selten
148// getriggerten Events muellt es aber bis dahin den Speicher voll.
149// +1 fuer Erfolg, <= 0 fuer Misserfolg
150int UnregisterEvent(string eid, object ob) {
151 object po;
152 if (!stringp(eid) || !objectp(ob) ||
153 !objectp(po=previous_object()) || !mappingp(events[eid]))
154 return -1;
155 string oname=object_name(ob);
156 if (!member(events[eid],oname))
157 return -2;
158 m_delete(events[eid],oname);
159 if (!sizeof(events[eid]))
160 m_delete(events,eid);
161 // aus aktivem Event austragen, falls es drin sein sollte.
162 if (sizeof(active) && active[A_EID] == eid)
163 {
164 m_delete(active[A_LISTENERS], object_name(ob));
165 }
166// if (find_call_out(#'save_me)==-1)
167// call_out(#'save_me,15);
168 LOGUNREGISTER(ob, eid, po);
169 return 1;
170}
171
172// Loest einen Event aus.
173// 1 fuer Erfolg, <= 0 fuer Misserfolg
174varargs int TriggerEvent(string eid, mixed args) {
175 object trigger;
176 if (!stringp(eid) ||
177 !objectp(trigger=previous_object()))
178 return -1;
179 if (!allowedtrigger(eid, trigger)) return -2;
180 if (!member(events,eid)) return -3;
181 if (sizeof(pending) > __MAX_ARRAY_SIZE__/5)
182 return -4;
183 pending+=({ ({eid,trigger,object_name(trigger), args, time()}) });
184 if (find_call_out(#'process_events) == -1)
185 call_out(#'process_events,0);
186 LOGEVENT(eid,trigger,args);
187 //DEBUG(sprintf("%O",pending));
188 return 1;
189}
190
191protected void process_events() {
192 // erstmal wieder nen call_out eintragen.
193 call_out(#'process_events, 1);
194
195 // solange ueber active und pending laufen, bis keine Ticks mehr da sind,
196 // bzw. in der Schleife abgebrochen wird, weil keine Events mehr da sind.
197 while(get_eval_cost() > TICK_RESERVE) {
198 // HB abschalten, wenn nix zu tun ist.
199 if (!sizeof(active)) {
200 if (!sizeof(pending)) {
201 remove_call_out(#'process_events);
202 break;
203 }
204 // scheint noch min. ein Eintrag in pending zu sein, nach active kopieren,
205 // plus die Callback-Infos aus events
206 active=({pending[0][P_EID],
207 pending[0][P_TRIGOB],pending[0][P_TRIGOBNAME],
208 pending[0][P_ARGS],
209 deep_copy(events[pending[0][P_EID]]),
210 pending[0][P_TIME] });
211
212 if (sizeof(pending)>1)
213 pending=pending[1..]; // und aus pending erstmal loeschen. ;-)
214 else
215 pending=({});
216 //DEBUG(sprintf("Pending: %O",pending));
217 //DEBUG(sprintf("Active: %O",active));
218 }
219 // jetzte den aktiven Event abarbeiten.
220 // Infos aus active holen...
221 string eid=active[A_EID];
222 object trigob=active[A_TRIGOB];
223 string trigobname=active[A_TRIGOBNAME];
224 mixed args=active[A_ARGS];
225 mapping listeners=active[A_LISTENERS];
226 // und ueber alle Listener iterieren
227 foreach(string obname, string fun, closure cl, object listener:
228 listeners) {
229 // erst pruefen, ob noch genug Ticks da sind. wenn nicht, gehts im
230 // naechsten Zyklus weiter.
231 if (get_eval_cost() < TICK_RESERVE) {
232 return;
233 }
234 // wenn Closure und/oder zugehoeriges Objekt nicht existieren, versuchen
235 // wir erstmal, es wiederzufinden. ;-)
236 if (!objectp(query_closure_object(cl))) {
237 if (objectp(listener=find_object(obname)) &&
238 closurep(cl=symbol_function(fun,listener))) {
239 //geklappt, auch in events wieder ergaenzen
240 events[eid][obname,CB_CLOSURE]=cl;
241 events[eid][obname,CB_OBJECT]=listener;
242 }
243 else {
244 // Objekt nicht gefunden oder Closure nicht erzeugbar, austragen
245 m_delete(listeners,obname);
246 // und aus events austragen.
247 m_delete(events[eid],obname);
248 // und naechster Durchgang
249 continue;
250 }
251 }
252 // Objekt noch da, Closure wird als ausfuehrbar betrachtet.
253 catch(limited(#'funcall,({TICKSPERCALLBACK}),cl,eid,trigob,args);publish);
254 // fertig mit diesem Objekt.
255 m_delete(listeners,obname);
256 }
257 // Statistik? Differenzen zwische Erledigungszeit und Eintragszeit bilden
258 // die Keys, die Values werden einfach hochgezaehlt.
259#ifdef STATISTICS
260 stats[time()-active[A_TIME]]++;
261#endif // STATISTICS
262 // ok, fertig mit active.
263 active=({});
264 //DEBUG(sprintf("Fertig: %O %O",eid, trigobname));
265 LOGEVENTFINISH(eid,trigobname);
266 } // while(get_eval_cost() > TICK_RESERVE)
267
268 // Soll dies Ding neugeladen werden? Wenn ja, Selbstzerstoerung, wenn keine
269 // Events mehr da sind.
270 if (updateme && !sizeof(active) && !sizeof(pending)) {
271 DEBUG(sprintf("Update requested\n"));
272 remove(1);
273 }
274}
275
276protected void create() {
277 seteuid(getuid(ME));
278 restore_object(STORE);
279 if (lastboot != __BOOT_TIME__) {
280 // Oh. Reboot war... Alle Events wegschmeissen (es koennten zwar die
281 // Eventanmeldungen von BPs ueberleben, aber auch die sollen sich lieber
282 // im create() anmelden.)
283 events=([]);
284 lastboot=__BOOT_TIME__;;
285 }
286 LOG("Event-Dispatcher loaded");
287}
288
289
290protected void save_me() {
291 save_object(STORE);
292}
293
294varargs int remove(int silent) {
295 save_me();
296 DEBUG(sprintf("remove() called by %O - destructing\n", previous_object()));
297 LOG(sprintf("remove called by %O - destructing",previous_object()));
298 destruct(ME);
299 return 1;
300}
301
302public void reset() {
303 if (updateme && !sizeof(active) && !sizeof(pending)) {
304 DEBUG(sprintf("Update requested\n"));
305 remove(1);
306 }
307}
308
309
310// fuer Debugzwecke. Interface und Verhalten kann sich jederzeit ohne
311// Vorankuendigung aendern.
312mapping QueryEvents() {
313 return(deep_copy(events));
314}
315
316mixed QueryPending() {
317 return(deep_copy(pending));
318}
319
320mixed QueryActive() {
321 return(deep_copy(active));
322}
323
324mapping QueryStats() {
325 return(copy(stats));
326}
327
328int UpdateMe(int flag) {
329 updateme=flag;
330 if (find_call_out(#'process_events)==-1)
331 reset();
332 return flag;
333}
334