MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | Das neue Hooksystem |
| 2 | (Implementierung von Muadib, ueberarbeitet von Zesstra) |
| 3 | |
| 4 | EINLEITUNG |
| 5 | ========== |
| 6 | Das neue Hooksystem baut nicht mehr auf der Eintragung eines Hooks in einer |
| 7 | Property auf. Dadurch wird es moeglich, dass nicht nur ein Objekt sich als |
| 8 | Hook eintraegt. |
| 9 | |
| 10 | Es gibt verschiedenen Arten von Hooks, die man eintragen kann: |
| 11 | * Listener (H_LISTENER) |
| 12 | diese Hooks werden ueber ein Ereignis nur informiert, |
| 13 | koennen aber nicht eingreifen oder aendern. |
| 14 | max. Anzahl: 5 |
| 15 | * Data-Modifier (H_DATA_MODIFICATOR) |
| 16 | diese Hooks duerfen die Daten eines Ereignisses aendern. |
| 17 | max. Anzahl: 3 |
| 18 | * Hook-Modifier (H_HOOK_MODIFICATOR) |
| 19 | diese Hooks duerfen die Daten des Ereignisses aendern und |
| 20 | zusaetzlich das Ereignis auch abbrechen. |
| 21 | max. Anzahl: 2 |
| 22 | * Surveyor (H_HOOK_SURVEYOR) |
| 23 | diese Hooks duerfen alles oben beschriebene. Zusaetzlich werden |
| 24 | sie aber auch gefragt, wenn andere Objekte einen Hook eintragen |
| 25 | wollen, einen Hook abbrechen wollen oder Daten aendern wollen. |
| 26 | Oder anders: Surveyorhooks entscheiden, was andere duerfen. |
| 27 | Kein normales Objekte sollte diese Art von Hook eintragen. Der RM |
| 28 | muss die Verwendung eines Surveyors genehmigen. |
| 29 | max. Anzahl: 1 |
| 30 | |
| 31 | Ausserdem lassen sich Hooks noch mit unterschiedlicher Prioritaet eintragen, |
| 32 | welche dann in der entsprechenden Reihenfolge abgearbeitet werden. Wenn ein |
| 33 | neuer Hook eingetragen wird, aber eigentlich die max. Anzahl schon erreicht |
| 34 | wird, wird der Konsument mit der niedrigsten Prioritaet geloescht. In diesem |
| 35 | Fall wird die superseededHook() im verdraengten Konsumenten gerufen. |
| 36 | |
| 37 | Um das neue Hook-System zu realisieren, gibt es zwei wesentliche Klassen. |
| 38 | Die Objekte, die die Eintragung von Hooks erlauben, erben hierzu von |
| 39 | hook_provider. Objekte, die einen Surveyor-Hook eintragen wollen, sollten von |
| 40 | der Klasse hook_surveyor erben. Objekte mit normalen Hooks brauchen nichts zu |
| 41 | erben. |
| 42 | |
| 43 | |
| 44 | Welche Hooks gibt es zur Zeit in der Basis-Mudlib? |
| 45 | ================================================= |
| 46 | * H_HOOK_MOVE |
| 47 | Bei Bewegung eines Objektes ausgeloest. Kann Bewegung beeinflussen oder |
| 48 | abbrechen. |
| 49 | * H_HOOK_DIE |
| 50 | Beim Tod eines Lebewesens ausgeloest. Kann den Tod abbrechen oder |
| 51 | <poisondeath> abaendern. |
| 52 | * H_HOOK_DEFEND |
| 53 | Im Defend() eines Lebenwesens ausgeloest. Kann das Defend() abbrechen und |
| 54 | Daten des Defend() aendern. |
| 55 | * H_HOOK_ATTACK |
| 56 | Im Attack() eines Lebenwesens ausgeloest. Kann das Attack() abbrechen. |
| 57 | * H_HOOK_HP |
| 58 | Bei Veraenderung der HP (LP) eines Lebewesens gerufen. (Zur Zeit keine |
| 59 | Datenveraenderung moeglich) |
| 60 | * H_HOOK_SP |
| 61 | Bei Veraenderung der SP (KP) eines Lebewesens gerufen. (Zur Zeit keine |
| 62 | Datenveraenderung moeglich) |
| 63 | * H_HOOK_ATTACK_MOD |
| 64 | Wird im Attack() ausgeloest, nachdem die fuer den Angriff wichtigen Daten |
| 65 | ermittelt und berechnet wurden. Diese koennen dann vom Hookonsumenten |
| 66 | nochmal geaendert werden. Auch ein Abbruch des Attack() ist hier noch |
| 67 | moeglich. |
| 68 | * H_HOOK_ALCOHOL |
| 69 | Bei Veraenderung von P_ALCOHOL im Lebewesens gerufen. |
| 70 | * H_HOOK_FOOD |
| 71 | Bei Veraenderung von P_FOOD im Lebewesens gerufen. |
| 72 | * H_HOOK_DRINK |
| 73 | Bei Veraenderung von P_DRINK im Lebewesens gerufen. |
| 74 | * H_HOOK_POISON |
| 75 | Bei Veraenderung von P_POISON im Lebewesens gerufen. |
| 76 | * H_HOOK_CONSUME |
| 77 | Beim Konsumieren von Speisen und Getraenken in Kneipen im Lebewesens |
| 78 | gerufen. |
| 79 | * H_HOOK_TEAMROWCHANGE |
| 80 | Bei Teamreihenwechsel vom Lebewesen ausgeloest. |
| 81 | * H_HOOK_INSERT |
| 82 | Wird von Spielerobjekten ausgeloest, wenn ein Objekt ins Spielerinventar |
| 83 | bewegt wird. (Keine Datenveraenderung moeglich) |
| 84 | * H_HOOK_EXIT_USE |
| 85 | Wird von einem Raum ausgeloest, wenn ein Lebewesen einen Ausgang benutzt. |
| 86 | Abbruch und Aenderung der Daten des Ausgangs moeglich. |
| 87 | |
| 88 | HOOK-KONSUMENTEN |
| 89 | ================ |
| 90 | Der Hook-Provider ruft bei Ausloesen des Hooks in allen Konsumenten eine |
| 91 | bestimmte Methode auf. Wenn bei der Registrierung eines Objekts keine Closure |
| 92 | angeben wurde, die in diesem Fall gerufen werden, wird standardmaessig die |
| 93 | lfun HookCallback() gerufen (gibt man eine Closure an, bekommt sie die |
| 94 | gleichen Argumente und es werden die beschriebenen Rueckgabewerte erwartet): |
| 95 | * mixed HookCallback(object hookSource, int hookid, mixed hookData) |
| 96 | Diese Methode wird in jedem Hook-Konsumenten eines Hook-Providers |
| 97 | aufgerufen, solange die Verarbeitung nicht vorher abgebrochen wurde. |
| 98 | Die Reihenfolge der Abarbeitung wird nach Liste (Surveyor, |
| 99 | Hook-Modifikator, Data-Modifikator, Listener) und dort nach Prioritaet |
| 100 | durchgefuehrt. |
| 101 | Ein Surveyor-Hook kann verhindern, dass Hooks bestimmte Aenderungen |
| 102 | durchfuehren. |
| 103 | |
| 104 | Rueckgabewert ist ein Array, das die folgenden Werte beinhaltet. |
| 105 | |
| 106 | H_RETCODE Gibt an, welcher Hook-Typ verwendet wurde. |
| 107 | H_NO_MOD => Nichts wurde veraendert. |
| 108 | H_ALTERED => Daten wurden veraendert. |
| 109 | H_CANCELLED => Hook-Kette soll abgebrochen werden. |
| 110 | => Ausserdem soll die Hook-ausloesende Stelle |
| 111 | abgebrochen werden. Z.B. wird das Defend() |
| 112 | abgebrochen, wenn ein H_HOOK_DEFEND |
| 113 | mit cancelled beantwortet wird. |
| 114 | H_RETDATA Gibt die (evtl. geaenderten) Daten an. |
| 115 | mixed-Objekt, das wie der Parameter hookData aufgebaut ist. |
| 116 | |
| 117 | Ein Objekt darf sich mehrfach fuer den gleichen Hook registrieren. Allerdings |
| 118 | ist fuer jede Registrierung eine andere Closure noetig. |
| 119 | |
| 120 | * void superseededHook(int hookid, object hookprovider) |
| 121 | Wird gerufen, wenn der Konsument von einem anderen mit hoeherer Prioritaet |
| 122 | verdraengt wurde. |
| 123 | |
| 124 | |
| 125 | HOOK-PROVIDER |
| 126 | ============= |
| 127 | Der Hook-Provider bietet eine Menge von Methoden an, die eine Konfiguration |
| 128 | ermoeglichen und die Eintragung von Hook-Konsumenten erlauben. Im |
| 129 | Normalfall sollte er geerbt und nicht modifiziert werden (ausser natuerlich, |
| 130 | die vom Objekte bereitgestellten Hooks einzutragen). |
| 131 | |
| 132 | * int* HListHooks(); |
| 133 | Diese Methode liefert eine Liste von Hooktypen, fuer die das Objekt |
| 134 | Eintragungen annimmt. Hier koennte beispielsweise eine Liste mit den |
| 135 | Eintraegen fuer Attack-, Defend- und Move-Hooks stehen. |
| 136 | |
| 137 | |
| 138 | * protected void offerHook(int hookid, int offerstate); |
| 139 | Diese Methode dient dazu, einen bestimmten Hook (z.B. H_HOOK_MOVE) |
| 140 | anzubieten. Nur Hooks, die hiermit angeboten wurden, stehen zur |
| 141 | Registrierung zur Verfuegung. |
| 142 | 'offerstate': 0 (nicht verfuegbar), 1 (verfuegbar/angeboten) |
| 143 | |
| 144 | |
| 145 | * int HRegisterToHook(int hookid, mixed consumer, int hookprio, |
| 146 | int consumertype, int timeInSeconds); |
| 147 | Registriert ein Objekt oder eine Closure als Hook-Konsument. |
| 148 | Parameter: |
| 149 | 'hookid' gibt den Hook-Typ an, z.B. den Defend-Hook. |
| 150 | Man kann sich nur fuer Hooktypen eintragen, die die Methode |
| 151 | HListHooks() angeboten hat. |
| 152 | 'consumer' Wenn ein Objekt, wird das Objekt eingetragen und spaeter |
| 153 | HookCallback() gerufen. |
| 154 | Wenn eine Closure, wird das Objekt der Closure eingetragen |
| 155 | und spaeter diese Closure gerufen. |
| 156 | 'hookprio' Gibt die Prioritaet an, mit der der Hook laufen soll. |
| 157 | Diese Angabe bestimmt die Reihenfolge, in der die Hooks |
| 158 | in der Liste der Hooks eingetragen werden. Die Prioritaet |
| 159 | ist H_HOOK_LIBPRIO(x), H_HOOK_GUILDPRIO(x) oder |
| 160 | H_HOOK_OTHERPRIO(x). x darf 0, 1 oder 2 sein (je niedriger, |
| 161 | desto hoeher die Prioritaet). |
| 162 | 'consumertype' Gibt an, um welche Art von Hook es sich handelt. |
| 163 | Es gibt vier festgelegten Typen, die fuer alle Hooks |
| 164 | existieren koennen. Die Methode HConsumerTypeIsAllowed() |
| 165 | gibt Aufschluss darueber, welche Hook-Typen existieren. |
| 166 | Die Hook-Typen sind in hook.h definiert. |
| 167 | 'timeInSeconds' gibt die Laufzeit des Hooks an. Falls 0 eingetragen wird, |
| 168 | laeuft der Hook ewig. |
| 169 | Rueckgabewerte: |
| 170 | 1 - Registrierung erfolgreich |
| 171 | <=0 - Registrierung fehlgeschlagen: |
| 172 | -1 : Hook unbekannt |
| 173 | -2 : consumer ist keine closure und es konnte kein Callback auf |
| 174 | HookCallback im consumer erstellt werden. |
| 175 | -3 : consumer ist bereits registriert |
| 176 | -4 : consumertyp ist nicht erlaubt |
| 177 | -5 : hookprio ist nicht erlaubt |
| 178 | -6 : Surveyor hat Registrierung nicht erlaubt |
| 179 | -7 : zuviele Hooks registriert / kein Hookeintrag frei |
| 180 | |
| 181 | * int HUnregisterFromHook(int hookid, mixed consumer); |
| 182 | Hebt die Registrierung von <consumer> fuer einen bestimmten Hook-Typ wieder |
| 183 | auf. |
| 184 | Parameter: |
| 185 | 'hookid' Die Kennung des Hook-Typs, z.B. die Kennung des Attack-Hooks. |
| 186 | 'consumer' Das Objekt oder die Closure, die/das nicht mehr registriert sein |
| 187 | soll. Bei einer Closure wird genau diese ausgetragen. Bei der |
| 188 | Angabe eines Objekts wird versucht, die Closure auf |
| 189 | HookCallback() in diesem Objekt auszutragen. |
| 190 | Rueckgabewerte: |
| 191 | 0 - 'consumer' nicht als Konsument gefunden |
| 192 | 1 - Austragen erfolgreich |
| 193 | |
| 194 | * int HConsumerTypeIsAllowed(int type, object consumer); |
| 195 | Diese Methode liefert 1 zurueck, wenn ein bestimmter Konsumenten-Typ |
| 196 | (fuer diesen Konsumenten) erlaubt wird. |
| 197 | Die Standardmethode liefert immer 1 (true) zurueck. Erbende Objekte |
| 198 | koennen diese Methode ueberschreiben, wenn sie nicht alle Hooktypen |
| 199 | anbieten. |
| 200 | |
| 201 | |
| 202 | * int HPriorityIsAllowed(int prio, object consumer); |
| 203 | Diese Methode gibt an, ob eine bestimmte Prioritaet (fuer den angegebenen |
| 204 | Konsumenten) erlaubt ist. Die Standardmethode liefert immer 1 (true) |
| 205 | zurueck. Erbende Objekte koennen diese Methode ueberschreiben, wenn |
| 206 | sie die verfuegbaren Hook-Prioritaeten einschraenken wollen. |
| 207 | |
| 208 | |
| 209 | * int HIsHookConsumer(int hookid, mixed consumer); |
| 210 | Ist <consumer> ein Objekt, liefert die Methode die Anzahl, wie oft dieses |
| 211 | Objekt (mit verschiedenen Closures) fuer den Hook <hookid> eingetragen ist. |
| 212 | Ist <consumer> eine Closure, liefert diese Methode 1, wenn diese |
| 213 | Closure fuer den Hook <hookid> eingetragen ist. |
| 214 | |
| 215 | |
| 216 | * protected mapping HCopyHookMapping(); |
| 217 | Diese Methode liefert eine Kopie des Hook-Mappings. |
| 218 | ACHTUNG: diese Daten sollten das Objekt NIEMALS verlassen. (Ausser fuer |
| 219 | Debugzwecke) |
| 220 | |
| 221 | |
| 222 | |
| 223 | HOOK-SURVEYOR |
| 224 | ============= |
| 225 | Objekte mit Surveyorhooks muessen eine Menge von Methoden definieren, die |
| 226 | der Hookprovider aufruft: |
| 227 | |
| 228 | * status HookRegistrationCallback( |
| 229 | object registringObject, |
| 230 | int hookid, |
| 231 | object hookSource, |
| 232 | int registringObjectsPriority, |
| 233 | int registringObjectsType) |
| 234 | Diese Methode wird vom Hook-Provider aufgerufen, wenn der Hook-Konsument |
| 235 | als Surveyor eingetragen ist und ein weiterer Hook eingetragen werden soll. |
| 236 | Gibt diese Methode 0 zurueck, dann verbietet der Konsument, dass der |
| 237 | andere Konsument als Hook eingetragen wird. |
| 238 | |
| 239 | * int HookCancelAllowanceCallback( |
| 240 | object cancellingObject, |
| 241 | int hookid, |
| 242 | object hookSource, |
| 243 | int cancellingObjectsPriority, |
| 244 | mixed hookData) |
| 245 | Diese Methode wird aufgerufen, um herauszufinden, ob ein bestimmter |
| 246 | anderer Hook die Ausfuehrung der Hook-Kette unterbrechen darf. |
| 247 | Nur Hooks im Bereich H_HOOK_MODIFICATOR werden der Methode uebergeben. |
| 248 | |
| 249 | * int HookModificationAllowanceCallback( |
| 250 | object modifyingObject, |
| 251 | int hookid, |
| 252 | object hookSource, |
| 253 | int modifyingObjectsPriority, |
| 254 | mixed hookData) |
| 255 | Diese Methode wird aufgerufen, um herauszufinden, ob ein bestimmter |
| 256 | anderer Hook die Daten des Hooks veraendern darf oder nicht. |
| 257 | Es werden die Hooks in den Bereichen H_HOOK_MODIFICATOR und |
| 258 | H_DATA_MODIFICATOR (in dieser Reihenfolge) aufgerufen. |
| 259 | |
| 260 | |
| 261 | WAS KOSTET DAS? |
| 262 | Das Ausloesen eines Hooks per HookFlow() kostet 111 Ticks und ca. 7 us, wenn |
| 263 | es gar keinen gibt, der drauf lauscht (sozusagen Fixkosten). |
| 264 | Pro H_LISTENER kommen dann 31 Ticks und ca. 2 us dazu. |
| 265 | |
| 266 | Gibts einen Surveyor-Hook (der wird dann gefragt, ob andere Objekte die |
| 267 | Daten des Hooks aendern oder die Hookverarbeitung abbrechen duerfen): |
| 268 | Fixkosten: 155 Ticks, 11 us. |
| 269 | Plus pro Data-Modifier: |
| 270 | 106 Ticks, 5.6 us |
| 271 | Plus pro Hook-Modifier, der aber nur Daten aendert: |
| 272 | 112 Ticks, 6.4 us |
| 273 | Und ein Hook-Modifier, der den Hook abbricht: |
| 274 | 76 Ticks, 4 us |
| 275 | |
| 276 | (Macht der Surveyor natuerlich irgendwas anderes als 'return 1;', wirds |
| 277 | natuerlich entsprechend teurer.) |
| 278 | |