KONZEPT
Event-Sender
AUTOR
Holger@Wunderland
VORBEMERKUNG
Bevor man daran geht, Events zu benutzen oder gar zu erzeugen,
sollte man verstanden haben, um was es geht. Also bitte erst
events(WL) lesen und verstehen und dann hier nachgucken, wies geht.
Danke. :-)
BESCHREIBUNG
Wenn man einen Event senden will, sollte man erstmal wissen, wie
der Event heisst, also den Event-Typ kennen (event_types(WL)).
Danach sollte man pruefen, ob dieser Event nicht ohnehin schon
erzeugt wird (wie zum Beispiel ET_GO, wenn ein Lebewesen umher
laeuft). Ist dies nicht der Fall und/oder gibt es keinen Event,
der sich 'gebrauchen' laesst, kann man den Event selbst senden.
Die Event-Typen sind wohl meist vorgegeben und dokumentiert
(event_types(WL)). Zum Thema 'private Events' bitte die Manpage
events_fuer_alle(WL) lesen.
Einen Event senden ist simpel:
result=send_event(typ, data [, dest [, modus]] );
typ - (string) Ein Event-Typ. (event_types(WL))
data - (mapping) Die Event-Daten.
dest - (mixed) Objekt oder Array aus Objekten, an die der
Event gesendet werden soll. Globale Lauscher oder globale
Handler muessen nicht angegeben werden! (automatisch)
Alle Objekte im deep_inventory() von dest erhalten den
Event, wenn sie ihm lauschen.
modus - Ein Sende-Modus. In /sys/events.h sind diese definiert:
EM_SIMPLE - Standard Modus - maximale Performance
EM_COPY - einfache Kopie des Daten-Mappings
EM_DEEP_COPY - echte Kopie des Daten-Mappings
EM_FAKE - Simuliert den Event nur intern im Daemon
interessant i.V.m. EM_DEBUG, um alle Lauscher
auf einen Event zu finden
Der Modus kann kombiniert werden mit:
EM_COMPLEX - Mehr Daten in E_RECEIVERS
EM_DEBUG - maximale Infos in E_DEBUG_INFO
EM_STOP_CANCEL - 'spekulative Ausfuehrung', siehe unten
EM_STOP_HANDLE - fuer verspaetete Reaktionen
Um alle Lauscher eines Events zu finden, ohne den Event wirklich
auszufuehren, empfiehlt sich: modus=(EM_FAKE|EM_DEBUG). Dann steht
im Daten-Mapping in E_DEBUG_INFO eine Alist mit folgendes Werten:
({ Prios, Closures, Kosten }) (Kosten ist bei EM_FAKE nat. immer 0);
Die Modi EM_STOP_CANCEL und EM_STOP_HANDLE sind nur fuer die
extrem fortgeschrittene Ereignisprogrammiererin vorgesehen. Wo
welcher Stoppunkt greift kann man im Diagramm von events(WL) sehen.
Bitte wirklich nur benutzen wenn man Ereignisse im Schlaf beherrscht!
Besondere Vorsicht ist gegenueber Laufzeitfehlern walten zu lassen,
da diese nicht zu halbfertigen Ereignissen fuehren duerfen.
BEISPIEL
Will man in einem Raum einen GO-Event senden, wenn der Spieler
'keller' eingibt, sieht das wie folgt aus:
mapping result;
object zielraum; // Der Zielraum als Objekt
result = send_event(ET_GO,
([ E_MOVE_OBJ : this_player(),
E_MOVE_DEST : zielraum,
E_MOVE_MSGS :
({"Du steigst in den Keller.",
"&Name steigt in den Keller.",
"&Name kommt herab."})
]), ({ this_object(), zielraum }) );
Danach wird der Event wie ein normaler GO-Event behandelt, wie ihn
die MUDlib selbst auch ausloest. result[E_HANDLED] ist auf ME_OK
oder ME_SILENT gesetzt, wenn alles geklappt hat.
SPEKULATIV
Es besteht die Moeglichkeit Ereignisse spekulativ auszufuehren. Dies
wird fuer einige Bequemlichkeits-Funktionen der Mudlib verwendet und
sollte nur sehr erfahrenen Event-Programmierern vorbehalten bleiben.
Folgendes Szenario: Wir haben eine Liste von moeglichen Events,
wissen aber nicht, welches von diesen zum Erfolg fuehren wird.
Wir wollen nun einen nach dem anderen ausprobieren. Wenn er klappt
ist gut und das Verhalten soll normal sein. Wenn er nicht klappt
soll nichts (!) geschehen und der naechste Event wird probiert.
Weiterhin soll - wenn alle Ereignisse fehlschlugen - eines der
Ereignisse ausgesucht werden koennen und dann 'normal' fehlschlagen,
also eine Meldung an den Spieler ausgeben und aehnliches.
Hierzu gibt es den Modus EM_STOP_CANCEL. Das Ereignis wird dann
direkt bei einem cancel_event() eingefrohren. Es werden also keine
Objekte der Prioritaeten EPRIO_C_HANDLE oder EPRIO_C_REACT aufgerufen.
Man kann das Ereignis jedoch auch wieder 'auftauen' und weiterlaufen
lassen. Hierzu muss man einfach EVENTD->continue_event(e) aufrufen.
Als Parameter e ist dabei der Rueckgabewert von send_event() zu
verwenden, welches bei dem fraglichen Ereignis benutzt wurde.
Beispiel:
Wir haben ein Array aus Objekten und wollen fuer jedes Objekt (in
der Reihenfolgen von vorne nach hinten - Reihenfolge ist ja aus
Spielersicht da manchmal wichtig) ein Ereignis erzeugen, bis mal
eins klappt. Wenn keins klappt wollen wir das allererste
fehlschlagen lassen.
object* obs;
mapping ret, first_ret;
for (i = 0; i < sizeof(obs); ++i) {
ret = send_event(ET_XXX, ([]), dest, EM_SIMPLE|EM_STOP_CANCEL);
// Erstes Event speichern falls wir da mal weitermachen muessen
if (!first_ret) first_ret = ret;
}
if (ret[E_HANDLED]) return; // naja, ein Ereignis klappte
// Hier kommen wir nur an wenn kein einziges Ereignis klappte.
// Jetzt fuehren wir das erste (fehlgeschlagene) Ereignis weiter,
// so dass dort EPRIO_C_HANDLE usw zum Zuge kommt
ret = EVENTD->continue_event(first_ret);
SIEHE AUCH
events(WL), event_types(WL), event_prioriaeten(WL),
event_listeners(WL), events_fuer_alle(WL), send_event(S)
|