Controller ---------- Die Controller sind in UNItopia ein maechtiges Werkzeug, mit dem praktisch jeder Gott frueher oder spaeter einmal zu tun bekommt. Man kann damit bestimmte Aktionen kontrollieren und beeinflussen. Allgemein geht es um Kommunikation zwischen beliebigen Objekten. Das Prinzip erlaeutert am Beispiel einer Tuerglocke --------------------------------------------------- Eine Glocke kann an eine beliebige Tuer gehaengt werden. Wenn die Tuer von jemandem geoeffnet oder geschlossen wird, stoesst sie an die Glocke, die dann bimmelt. Normalerweise hat eine Tuer aber keine Glocke. Und die Glocke hat keine Ahnung davon, ob die Tuer jetzt gerade geoeffnet wurde oder geschlossen. Also kann sie auch nicht bimmeln. Damit das Problem geloest werden kann, muessen Tuer und Glocke miteinander kommunizieren. Sie fuehren kurze Unterhaltungen, die man sich etwa wie folgt vorstellen kann. Wenn die Glocke an die Tuer gehaengt wird: Glocke: "Du, Tuer..." Tuer: "Hmmmm?" Glocke: "Wenn du auf- oder zugemacht wirst, sag mir bitte Bescheid." Tuer: "Okay, mach ich." Wenn die Tuer dann geoeffnet wird, findet eine neue Unterhaltung statt: Tuer: "Du, Glocke..." Glocke: "Ja, was gibts?" Tuer: "Ich wurde gerade aufgemacht. Ich sollte dir doch Bescheid sagen..." Glocke: "Ah, ja, danke. Dann muss ich jetzt bimmeln." Die Glocke bimmelt. Wenn die Tuer dann wieder geschlossen wird, passiert das gleiche: Tuer: "Hey, Glocke!" Glocke: "Jo?" Tuer: "Ich wurde gerade wieder zugemacht. Nur zur Info." Glocke: "Ahso. Ja. Bimmeln. Genau." Die Glocke bimmelt. Wenn die Glocke wieder von der Tuer abgehaengt wird: Glocke: "Tuerchen!" Tuer: "Ja, Gloeckchen?" Glocke: "Ich mach jetzt Schluss mit dir." Tuer: "Gut, dann geb ich dir aber auch nicht mehr Bescheid." Und wenn die Tuer jetzt geoeffnet oder geschlossen wird, sagt sie der Glocke nichts mehr davon, also bimmelt sie auch nicht. Bis sie irgendwann eine neue Tuer gefunden hat, an die sie gehaengt wird, und die Unterhaltung wieder von vorne losgeht... Die Tuerglocke aus etwas technischerer Sicht -------------------------------------------- Tatsaechlich haben sich Tuer und Glocke natuerlich nicht so unterhalten, dass ein Spieler das mithoeren koennte. Die Wahrheit ist viel trockener und technischer: Tuer und Glocke rufen in sich gegenseitig Funktionen auf. Man sagt, die Glocke meldet bei der Tuer einen Controller an. Die Anmeldung laeuft ueber die Funktion add_controller(). Ein Controller ist zum Beispiel das Geoeffnet- oder Geschlossen-Werden der Tuer. Die Glocke meldet sich fuer diese Controller an, wenn sie an die Tuer gehaengt wird: tuer->add_controller("notify_open_door", glocke); tuer->add_controller("notify_close_door", glocke); Wenn 'tuer' nun geoeffnet oder geschlossen wird, ruft sie in 'glocke' die Funktion "notify_open_door" bzw "notify_close_door" auf. glocke->notify_open_door(...) glocke->notify_close_door(...) Die Glocke selbst hat so eine Funktion, in der sie dann eben bimmelt: void notify_open_door(...) { send_message(MT_NOISE, MA_MOVE, "Die Glocke an der geoeffneten Tuer" "bimmelt.\n"); } void notify_close_door(...) { send_message(MT_NOISE, MA_MOVE, "Die Glocke an der geschlossenen Tuer" "bimmelt.\n"); } Wenn die Glocke dann wieder von der Tuer abgehaengt wird, meldet sie die zuvor bei der Tuer angemeldeten Controller wieder ab. Die Abmeldung laeuft ueber die Funktion delete_controller(). tuer->delete_controller("notify_open_door", glocke); tuer->delete_controller("notify_close_door", glocke); Fertig ist der ganze Zauber. Verschiedene Controller-Arten ----------------------------- Es gibt mehrere verschiedene Arten von Controllern: - notify: Man moechte mitbekommen, dass etwas passiert (ist). - forbidden: Man moechte verhindern, dass etwas passieren wird. - allowed: Man moechte zulassen, dass etwas passieren wird. - concerned: Man moechte etwas selbst in die Hand nehmen. - modify: Man moechte etwas veraendern. Am haeufigsten genutzt werden "notify" und "forbidden"-Controller. Das obige Beispiel mit der Tuerglocke arbeitet mit "notify". Wenn die Tuer geoeffnet oder geschlossen wurde, moechte die Glocke das mitbekommen, damit sie bimmeln kann. Es sind nicht immer alle Controller-Arten zu einem bestimmten Ereignis verfuegbar. So gibt es zum Beispiel kein allowed_open_door oder modify_open_door. Welche Controller es gibt, kann man zum Beispiel mit dem dek-Befehl der Enzyclopedia UNItopia herausfinden. Saemtliche Controller-Funktionen sind dokumentiert (oder sollten es sein). Jeder Controller ist verschieden. Wo wird er aufgerufen, und mit welchem Parameter? So erfaehrt man in notify_open_door nicht nur, dass eine Tuer geoeffnet wurde, sondern auch von wem und in welchem Raum. Diese Details erfaehrt man nur ueber eine entsprechende Dokumentation. notify (ankuendigen, benachrichtigen, melden, kundtun) ------------------------------------------------------ Ein notify-Controller wird meist aufgerufen, direkt nachdem etwas passiert ist. Alle Objekte, die sich fuer den betreffenden Controller angemeldet haben, werden der Reihe nach benachrichtigt. Eine notify-Funktion hat keinen Rueckgabewert. Der Aufrufer teilt nur mit: dies und das ist passiert. Was die angemeldeten Objekte mit dieser Information anstellen, ist dem Aufrufer egal. Beispiel: Tuerglocke, siehe oben. forbidden (verboten, untersagt, unzulaessig) -------------------------------------------- Ein forbidden-Controller wird aufgerufen, wenn etwas passieren soll, aber noch verhindert werden kann. Die Objekte, die sich fuer den betreffenden Controller angemeldet haben, werden der Reihe nach gefragt, ob sie etwas verhindern wollen oder nicht. Sobald ein Objekt das Ereignis verhindert, werden alle uebrigen angemeldeten Objekte ignoriert - das Ereignis wurde dann schliesslich schon verhindert. Eine forbidden-Funktion hat einen Rueckgabewert. Ist der Wert == 0, wird das Ereignis nicht verhindert, bei allen anderen Rueckgabewerten dagegen schon. In den meisten Faellen liefert man 1 zurueck und muss selbst eine Meldung ausgeben, warum das Ereignis verhindert wurde. Beispiel: Brett, mit dem man eine Tuer verbarrikadieren kann. Das Brett meldet sich bei der Tuer fuer "forbidden_open_door" an. In dieser Funktion verhindert das Brett, dass die Tuer geoeffnet wird und gibt eine entsprechende Meldung aus. allowed (genehmigt, gestattet, zulaessig) ----------------------------------------- Ein allowed-Controller ist das Gegenstueck zu forbidden. Eine Aktion, die normalerweise nicht moeglich ist, kann hiermit ausnahmsweise erlaubt werden. Die Objekte, die sich fuer den Controller angemeldet haben, werden der Reihe nach gefragt, ob die Aktion ausnahmsweise zugelassen werden soll. Wenn ein Objekt mit dem Kopf nickt, darf die Aktion ausgefuehrt werden. Ist kein Objekt angemeldet, ist die Aktion nicht moeglich. Eine allowed-Funktion hat einen Rueckgabewert. Ein Wert == 0 bedeutet, dass das Objekt nicht mit dem Kopf nickt, also die Aktion nicht zulaesst. Ein Wert != 0 bedeutet, dass dieses Objekt mit der Aktion einverstanden ist. Sobald ein Objekt 1 liefert, werden die weiteren angemeldeten Objekte ignoriert. Die Aktion ist dann bereits erlaubt. Beispiel: Gegenstaende kann man normalerweise nicht knuddeln, weil sie nicht lebendig sind. Mit "allowed_seele" kann man aber eine Ausnahme machen. concerned (zustaendig) ---------------------- Oft werden bestimmte Aktionen durchgefuehrt, die man aber eigentlich ganz anders haben wollte. Der concerned-Controller gibt die Moeglichkeit dazu, ein bestimmtes Ereignis selbst ausfuehren zu koennen. Die angemeldeten Objekte werden der Reihe nach gefragt, wie sehr sie sich fuer eine bestimmte Aktion zustaendig fuehlen. Eine concerned-Funktion hat einen Rueckgabewert. Ein Wert == 0 bedeutet, dass das Objekt sich nicht zustaendig fuehlt. Ansonsten gilt, je groesser die zurueckgelieferte Zahl, desto wichtiger ist es, dass dieses Objekt die Aktion selbst ausfuehrt. Das Objekt mit der groessten Zahl erhaelt den Zuschlag. In dem Objekt, das den Zuschlag erhalten hat, wird dann eine weitere Funktion aufgerufen. Welche, ist der Dokumentation des jeweiligen Controllers zu entnehmen, meist ist es ein do_irgendwas(). Beispiel: Legt man eine Waffe auf das Meer, geht sie normalerweise unter (removed). Nun kann man an manchen Stellen aber tauchen, und man moechte, dass der schwere Gegenstand nicht removed wird, sondern im Unterwasserraum landet. Mit "concerned_sink" kann man einen Gegenstand selbst untergehen lassen, und anstatt ihn zu removen, ihn in den Unterwasserraum bewegen. modify (aendern, abwandeln) --------------------------- Ein modify-Controller erlaubt es, ein bestimmtes Ereignis zu aendern. Im Gegensatz zu concerned, wo man die Aktion komplett selbst bestimmt, sind hier meist nur leichte Modifikationen moeglich. Die angemeldeten Objekte werden der Reihe nach gebeten, ihre Aenderungen vorzunehmen. Eine modify-Funktion hat keinen Rueckgabewert. Es ist dem Objekt, das den modify-Controller aufruft, also egal, wer was modifiziert hat. Wie die Modifikation vorzunehmen ist, muss der Dokumentation des jeweiligen Controllers entnommen werden. Beispiel: Ein Spieler bekommt eine Waescheklammer auf die Nase und kann dann nicht mehr richtig sprechen. Die Waescheklammer kann sich fuer den Controller modify_comm anmelden, und den Text, den der Spieler so von sich gibt, vernuscheln. Eigene Controller programmieren ------------------------------- Bisher wurde nur behandelt, wie man sich fuer bestehende Controller anmeldet. Damit Controller aber ueberhaupt funktionieren, muessen sie erst einmal aufgerufen werden. Die Tuerglocke wuerde zum Beispiel nicht funktionieren, wenn die Standard-Tuer nicht die Controller notify_open_door und notify_close_door beim Oeffnen und Schliessen der Tuer aufrufen wuerde. Der Aufruf wird von den Funktionen notify(), forbidden(), allowed(), concerned() und modify() uebernommen. Diese Funktionen schauen, welche Objekte sich fuer den gewuenschten Controller angemeldet haben und rufen in diesen die eigentliche Funktion aus und werten ggf. den Rueckgabewert aus. Der Aufruf des notify_open_door-Controllers in der Tuer koennte beispielsweise etwa so aussehen: tuer->notify("open_door", spieler, tuer, raum); Damit wird in allen Objekten, die sich per tuer->add_controller(...) bei der Tuer fuer "notify_open_door" angemeldet haben, die Funktion notify_open_door(spieler, tuer, raum) aufgerufen. Die Tuer koennte diesen Event genausogut auch anderen Objekten melden, zum Beispiel: spieler->notify("open_door", spieler, tuer, raum); raum->notify("open_door", spieler, tuer, raum); Dieser Aufruf landet dann dementsprechend bei den Objekten, die sich beim Spieler bzw. beim Raum per add_controller(...) fuer "notify_open_door" angemeldet haben. In welchen Objekten ein Controller aufgerufen wird, ist der jeweiligen Dokumentation zu entnehmen. Meist wird der Aufruf in mehr als einem Objekt gemacht und dies leider nur recht umstaendlich erklaert. Es gibt auch noch zwei Hilfsfunktionen fuer den Aufruf eines Controllers in mehreren Objekten: do_notifies() und do_forbiddens(). Hierzu sollte man die jeweilige Dokumentation studieren. Anmerkungen zum Schluss ----------------------- Am besten lernt man den Umgang mit Controllern, indem man einfach mal etwas rumprobiert. Meldet euch mal fuer den einen oder anderen Controller an, lasst euch Debugmeldungen ausgeben, schaut ob er aufgerufen wird und was passiert, wenn ihr eine bestimmte Aktion mit forbidden verhindert und so weiter... Dokumentation zu Controllern ist leider oft recht kompliziert geschrieben. Stellt in diesem Fall einfach Fragen dazu auf dem Kanal. Meist ist jemand da, der diesen Controller schon einmal benutzt hat und weiterhelfen kann. Komplizierte Formulierungen koennen oft auch durch einfachere ersetzt werden.