Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/p/daemon/eventd.c b/p/daemon/eventd.c
new file mode 100644
index 0000000..b81d38b
--- /dev/null
+++ b/p/daemon/eventd.c
@@ -0,0 +1,334 @@
+// MorgenGrauen MUDlib
+//
+// /p/daemon/eventd.c -- Event Dispatcher
+//
+// $Id$
+#pragma strict_types,save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#include <wizlevels.h>
+#include <defines.h>
+#include <events.h>
+
+// Fuer Statistiken
+#define STATISTICS
+
+#define HOME(x) (__PATH__(0)+x)
+#define STORE HOME("save/eventd")
+
+
+#define LOG(x) log_file("EVENTS", sprintf(\
+ "[%s] %s\n",dtime(time()),x))
+//#define LOGEVENT(x,y,z) log_file("EVENTS", sprintf(\
+// "[%s] Event %s triggered by %O (Args: %.40O)\n",dtime(time()),x,y,z))
+#define LOGEVENT(x,y,z)
+//#define LOGREGISTER(w,x,y,z) log_file("EVENTLISTENER",sprintf(\
+// "[%s] %O (Fun: %.25s) registered for %s by %O\n",dtime(time()),w,x,y,z))
+#define LOGREGISTER(w,x,y,z)
+//#define LOGUNREGISTER(x,y,z) log_file("EVENTLISTENER",sprintf(\
+// "[%s] %O was unregistered from %s by %O\n",dtime(time()),x,y,z))
+#define LOGUNREGISTER(x,y,z)
+//#define LOGEVENTFINISH(x,y) log_file("EVENTS", sprintf(\
+// "[%s] Event %s (triggered by %O) finished\n",dtime(time()),x,y))
+#define LOGEVENTFINISH(x,y)
+
+#ifndef DEBUG
+#define DEBUG(x) if (find_player("zesstra"))\
+ tell_object(find_player("zesstra"),\
+ "EDBG: "+x+"\n")
+#endif
+
+#define TICK_RESERVE 300000
+#define TICKSPERCALLBACK 30000
+
+// Indizes fuer Callback-Infos (events, active)
+#define CB_FUN 0
+#define CB_CLOSURE 1
+#define CB_OBJECT 2
+// Indizes fuer Pending
+#define P_EID 0
+#define P_TRIGOB 1
+#define P_TRIGOBNAME 2
+#define P_ARGS 3
+#define P_TIME 4
+
+// Indizes fuer Active
+#define A_EID 0
+#define A_TRIGOB 1
+#define A_TRIGOBNAME 2
+#define A_ARGS 3
+#define A_LISTENERS 4
+#define A_TIME 5
+
+/* alle events, die er kennt.
+ Datenstruktur events:
+ ([id:([obname:fun;closure;object, ... ]),
+ id2: ([....]),
+ ]) */
+mapping events=([]);
+/* abzuarbeitende Events, Datenstruktur:
+ ({ ({id, trigob, trigobname, args}),
+ ({id2, trigob, trigobname, args}), ... }) */
+nosave mixed pending=({});
+/* Der gerade aktive Event, der wird gerade abgearbeitet.
+ Datenstruktur active:
+ ({ id:trigob;trigobname;args;([obname:fun;closure;object, ...]), trigtime})
+*/
+nosave mixed active=({});
+
+int lastboot; // Zeitstempel des letzten Reboots
+// Flag, wenn gesetzt, zerstoert sich der Eventd, wenn keine Events
+// abzuarbeiten sind.
+nosave int updateme;
+
+// einfache Statistik, gespeichert wird ein Histogramm. KEys sind die
+// Zeitdifferenzen zwschen Eintragen und Erledigen des Events, Values die
+// Haeufigkeiten.
+nosave mapping stats=([]);
+
+
+protected void process_events();
+protected void save_me();
+varargs int remove(int silent);
+
+// ist der Event-Typ "eid" schon bekannt, d.h. gib es min. 1 Listener?
+// Liefert Anzahl der Listener zurueck.
+int CheckEventID(string eid) {
+ if (!stringp(eid) || !member(events,eid))
+ return 0;
+ return(sizeof(events[eid]));
+}
+
+// entscheidet, ob ein Objekt fuer einen Event eingetragen werden darf. Zum
+// Ueberschreiben in geerbten Dispatchern, in diesem Scheduler sind alle
+// Events oeffentlich. Bekommt die Event-ID, das einzutragende Objekt und das
+// eintragende Objekt uebergeben.
+// 1 fuer Erlaubnis, 0 sonst.
+protected int allowed(string eid, object ob, object po) {
+ return(1);
+}
+
+// entscheidet, ob ein Objekt einen bestimmten Event triggern darf. Zum
+// Ueberschreiben in geerbten Dispatchern, in diesem Scheduler sind alle
+// Events oeffentlich. Bekommt die Event-ID und das triggernde Objekt
+// uebergeben.
+// 1 fuer Erlaubnis, 0 sonst.
+protected int allowedtrigger(string eid, object trigger) {
+ return(1);
+}
+
+// registriert sich fuer einen Event. Wenn es den bisher nicht gibt, wird er
+// implizit erzeugt. Wenn das Objekt ob schon angemeldet war, wird der
+// bisherige Eintrag ueberschrieben.
+// 1 bei Erfolg, <=0 bei Misserfolg
+int RegisterEvent(string eid, string fun, object ob) {
+ object po;
+ if (!stringp(eid) || !stringp(fun) ||
+ !objectp(ob) || !objectp(po=previous_object()))
+ return -1;
+ if (!allowed(eid, ob, po)) return -2;
+ closure cl=symbol_function(fun,ob);
+ if (!closurep(cl))
+ return -3;
+ if (!mappingp(events[eid]))
+ events[eid]=m_allocate(1,3); // 3 Werte pro Key
+ events[eid]+=([object_name(ob):fun;cl;ob]);
+// if (find_call_out(#'save_me)==-1)
+// call_out(#'save_me,15);
+ LOGREGISTER(ob,fun,eid,po);
+ return 1;
+}
+
+// entfernt das Objekt als Listener aus dem Event eid
+// Mehr oder weniger optional, wenn ein event verarbeitet wird und das Objekt
+// ist nicht mehr auffindbar, wird es ebenfalls geloescht. Bei selten
+// getriggerten Events muellt es aber bis dahin den Speicher voll.
+// +1 fuer Erfolg, <= 0 fuer Misserfolg
+int UnregisterEvent(string eid, object ob) {
+ object po;
+ if (!stringp(eid) || !objectp(ob) ||
+ !objectp(po=previous_object()) || !mappingp(events[eid]))
+ return -1;
+ string oname=object_name(ob);
+ if (!member(events[eid],oname))
+ return -2;
+ m_delete(events[eid],oname);
+ if (!sizeof(events[eid]))
+ m_delete(events,eid);
+ // aus aktivem Event austragen, falls es drin sein sollte.
+ if (sizeof(active) && active[A_EID] == eid)
+ {
+ m_delete(active[A_LISTENERS], object_name(ob));
+ }
+// if (find_call_out(#'save_me)==-1)
+// call_out(#'save_me,15);
+ LOGUNREGISTER(ob, eid, po);
+ return 1;
+}
+
+// Loest einen Event aus.
+// 1 fuer Erfolg, <= 0 fuer Misserfolg
+varargs int TriggerEvent(string eid, mixed args) {
+ object trigger;
+ if (!stringp(eid) ||
+ !objectp(trigger=previous_object()))
+ return -1;
+ if (!allowedtrigger(eid, trigger)) return -2;
+ if (!member(events,eid)) return -3;
+ if (sizeof(pending) > __MAX_ARRAY_SIZE__/5)
+ return -4;
+ pending+=({ ({eid,trigger,object_name(trigger), args, time()}) });
+ if (find_call_out(#'process_events) == -1)
+ call_out(#'process_events,0);
+ LOGEVENT(eid,trigger,args);
+ //DEBUG(sprintf("%O",pending));
+ return 1;
+}
+
+protected void process_events() {
+ // erstmal wieder nen call_out eintragen.
+ call_out(#'process_events, 1);
+
+ // solange ueber active und pending laufen, bis keine Ticks mehr da sind,
+ // bzw. in der Schleife abgebrochen wird, weil keine Events mehr da sind.
+ while(get_eval_cost() > TICK_RESERVE) {
+ // HB abschalten, wenn nix zu tun ist.
+ if (!sizeof(active)) {
+ if (!sizeof(pending)) {
+ remove_call_out(#'process_events);
+ break;
+ }
+ // scheint noch min. ein Eintrag in pending zu sein, nach active kopieren,
+ // plus die Callback-Infos aus events
+ active=({pending[0][P_EID],
+ pending[0][P_TRIGOB],pending[0][P_TRIGOBNAME],
+ pending[0][P_ARGS],
+ deep_copy(events[pending[0][P_EID]]),
+ pending[0][P_TIME] });
+
+ if (sizeof(pending)>1)
+ pending=pending[1..]; // und aus pending erstmal loeschen. ;-)
+ else
+ pending=({});
+ //DEBUG(sprintf("Pending: %O",pending));
+ //DEBUG(sprintf("Active: %O",active));
+ }
+ // jetzte den aktiven Event abarbeiten.
+ // Infos aus active holen...
+ string eid=active[A_EID];
+ object trigob=active[A_TRIGOB];
+ string trigobname=active[A_TRIGOBNAME];
+ mixed args=active[A_ARGS];
+ mapping listeners=active[A_LISTENERS];
+ // und ueber alle Listener iterieren
+ foreach(string obname, string fun, closure cl, object listener:
+ listeners) {
+ // erst pruefen, ob noch genug Ticks da sind. wenn nicht, gehts im
+ // naechsten Zyklus weiter.
+ if (get_eval_cost() < TICK_RESERVE) {
+ return;
+ }
+ // wenn Closure und/oder zugehoeriges Objekt nicht existieren, versuchen
+ // wir erstmal, es wiederzufinden. ;-)
+ if (!objectp(query_closure_object(cl))) {
+ if (objectp(listener=find_object(obname)) &&
+ closurep(cl=symbol_function(fun,listener))) {
+ //geklappt, auch in events wieder ergaenzen
+ events[eid][obname,CB_CLOSURE]=cl;
+ events[eid][obname,CB_OBJECT]=listener;
+ }
+ else {
+ // Objekt nicht gefunden oder Closure nicht erzeugbar, austragen
+ m_delete(listeners,obname);
+ // und aus events austragen.
+ m_delete(events[eid],obname);
+ // und naechster Durchgang
+ continue;
+ }
+ }
+ // Objekt noch da, Closure wird als ausfuehrbar betrachtet.
+ catch(limited(#'funcall,({TICKSPERCALLBACK}),cl,eid,trigob,args);publish);
+ // fertig mit diesem Objekt.
+ m_delete(listeners,obname);
+ }
+ // Statistik? Differenzen zwische Erledigungszeit und Eintragszeit bilden
+ // die Keys, die Values werden einfach hochgezaehlt.
+#ifdef STATISTICS
+ stats[time()-active[A_TIME]]++;
+#endif // STATISTICS
+ // ok, fertig mit active.
+ active=({});
+ //DEBUG(sprintf("Fertig: %O %O",eid, trigobname));
+ LOGEVENTFINISH(eid,trigobname);
+ } // while(get_eval_cost() > TICK_RESERVE)
+
+ // Soll dies Ding neugeladen werden? Wenn ja, Selbstzerstoerung, wenn keine
+ // Events mehr da sind.
+ if (updateme && !sizeof(active) && !sizeof(pending)) {
+ DEBUG(sprintf("Update requested\n"));
+ remove(1);
+ }
+}
+
+protected void create() {
+ seteuid(getuid(ME));
+ restore_object(STORE);
+ if (lastboot != __BOOT_TIME__) {
+ // Oh. Reboot war... Alle Events wegschmeissen (es koennten zwar die
+ // Eventanmeldungen von BPs ueberleben, aber auch die sollen sich lieber
+ // im create() anmelden.)
+ events=([]);
+ lastboot=__BOOT_TIME__;;
+ }
+ LOG("Event-Dispatcher loaded");
+}
+
+
+protected void save_me() {
+ save_object(STORE);
+}
+
+varargs int remove(int silent) {
+ save_me();
+ DEBUG(sprintf("remove() called by %O - destructing\n", previous_object()));
+ LOG(sprintf("remove called by %O - destructing",previous_object()));
+ destruct(ME);
+ return 1;
+}
+
+public void reset() {
+ if (updateme && !sizeof(active) && !sizeof(pending)) {
+ DEBUG(sprintf("Update requested\n"));
+ remove(1);
+ }
+}
+
+
+// fuer Debugzwecke. Interface und Verhalten kann sich jederzeit ohne
+// Vorankuendigung aendern.
+mapping QueryEvents() {
+ return(deep_copy(events));
+}
+
+mixed QueryPending() {
+ return(deep_copy(pending));
+}
+
+mixed QueryActive() {
+ return(deep_copy(active));
+}
+
+mapping QueryStats() {
+ return(copy(stats));
+}
+
+int UpdateMe(int flag) {
+ updateme=flag;
+ if (find_call_out(#'process_events)==-1)
+ reset();
+ return flag;
+}
+