MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | RM - HOWTO |
| 2 | ********** |
| 3 | |
| 4 | Vorlaeufiges Inhaltsverzeichnis: |
| 5 | |
| 6 | 1) Allgemeines |
| 7 | - Fehlerteufel |
| 8 | - Logtool |
| 9 | - Kommentierung von Aenderungen |
| 10 | - eigene Anschluesse |
| 11 | 2) Abnahme von Code/Gebieten |
| 12 | - Balance/Genehmigungen |
| 13 | - Konzeptionelle Eignung |
| 14 | - formale Pruefung |
| 15 | - Gemeinschaftsarbeiten |
| 16 | 3) Verlegung von Dateien |
| 17 | 4) Seherhaeuser |
| 18 | - Sonderwuensche/Unsichtbarkeit |
| 19 | - anderer Befehl zum Betreten |
| 20 | - Verweise auf Beispielcode |
| 21 | 5) Debuggen von Code |
| 22 | 6) besondere Funktionen/Funktionaelitaeten |
| 23 | - catch_tell() / ReceiveMsg() |
| 24 | - move() |
| 25 | - Attack() / Defend() |
| 26 | - call_out() |
| 27 | - remove() / destruct() |
| 28 | - for()-Schleifen |
| 29 | - write_file() |
| 30 | - Verwalten von charakterbezogenen Daten |
| 31 | |
| 32 | 1) Allgemeines |
| 33 | ============== |
| 34 | |
| 35 | o Um stets einen Ueberblick ueber die in Deiner Region (hoffentlich selten) |
| 36 | auftretenden Bugs und Warnungen zu behalten, solltest Du einen |
| 37 | Fehlerteufel (/obj/tools/fehlerteufel.c) besitzen, bedienen koennen und |
| 38 | regelmaessig dessen Listen durchsehen. Deinen Regionsmitarbeitern solltest |
| 39 | Du, allein schon, um zusaetzliche Arbeit fuer Dich selbst zu reduzieren, |
| 40 | dieses Werkzeug ebenfalls an die Hand geben und sie auffordern, ihren |
| 41 | Kram moeglichst umfassend selbst zu reparieren. |
| 42 | Neuen Magiern wird dieses Tool uebrigens automatisch von Merlin in die |
| 43 | Hand gedrueckt, so dass sie sich vom ersten Tag an daran gewoehnen koennen. |
| 44 | |
| 45 | o Es ist empfehlenswert, das Logtool zu besitzen (/obj/tools/logtool.c) und |
| 46 | in diesem fuer jedes Repfile eines Regionsmitarbeiters einen Eintrag vor- |
| 47 | zusehen. Warum das? Es ist bereits vorgekommen, dass Spieler ernsthaft |
| 48 | argumentiert haben, sie duerften einen von ihnen entdeckten Bug ausnutzen, |
| 49 | weil sie ihn ja gemeldet haetten (im Repfile!), er aber nicht repariert |
| 50 | worden sei (was nicht verwundert, da er im Repfile in der Mehrzahl der |
| 51 | Faelle weit unterhalb des Wahrnehmungshorizonts der allermeisten |
| 52 | Regionsmagier schwebt). |
| 53 | |
| 54 | o Wenn Du in fremdem Code Aenderungen vornehmen musst, die mehr beruehren |
| 55 | als nur ein paar Tippfehler in Details oder Infos, dann kommentiere den |
| 56 | defekten Code aus und fuege den reparierten mit Kommentar ein. Es reicht |
| 57 | an dieser Stelle aus, Deinen Namen und das Datum zu vermerken. In den |
| 58 | Header der Datei solltest Du unbedingt ein Changelog einfuegen, in dem |
| 59 | das Datum der Aenderung, der Name des Ausfuehrenden (Deiner) sowie die |
| 60 | vorgenommene Aenderung mit Begruendung bzw. u.U. Bug-ID aus dem Fehler- |
| 61 | teufel einzutragen ist. Wir haben im MG leider keinen wirkungsvollen |
| 62 | Debugger und Bugtracker-Mechanismus, so dass wir zur Erhaltung einer |
| 63 | gewissen Code-Hygiene Bugfixes mit Hilfe solcher Eintragungen dokumentieren |
| 64 | und rueckverfolgen muessen. Insbesondere ist ein solcher Eintrag wichtig, |
| 65 | um im Falle von Folge-Bugs die Ursache schneller zu finden. |
| 66 | |
| 67 | o Willst Du in Deiner eigenen Region ein Gebiet anschliessen, solltest Du |
| 68 | diesen vor Anschluss entweder vom Co-RM oder von einem RM einer anderen |
| 69 | Region lesen lassen. Auch wenn Du ein guter Programmierer bist, findet ein |
| 70 | zweites Paar Augen oft Dinge, an die man nicht gedacht hat. |
| 71 | |
| 72 | |
| 73 | 2) Code wird zur Abnahme vorgelegt |
| 74 | ================================== |
| 75 | |
| 76 | o Bei Waffen und Ruestungen unbedingt auf Einhaltung der Balance-Vorgaben |
| 77 | achten. |
| 78 | |
| 79 | o Sofern ein Objekt eine Balance-Genehmigung besitzt, muss die BTOP-Nummer, |
| 80 | die die Balance als eindeutige ID fuer jeden Antrag vergibt, im Header |
| 81 | des betreffenden Objektes erkennbar eingetragen sein. |
| 82 | |
| 83 | o Vor Anschluss sollte man Ruecksprache mit verschiedenen Instanzen halten, |
| 84 | um sicherzustellen, dass der Magier alle wesentlichen Punkte |
| 85 | beruecksichtigt hat: |
| 86 | - liegen alle Balance-Genehmigungen vor? |
| 87 | - sind die FP eingetragen? |
| 88 | - sind die ZTs eingetragen und getestet? |
| 89 | - sind Sonder-EK-Stupse genehmigt und eingetragen? |
| 90 | |
| 91 | o Fuer neue Gebiete ist eine grundsaetzliche Pruefung des Konzepts auf |
| 92 | Regionstauglichkeit sinnvoll, damit nicht alteingesessene Institutionen |
| 93 | oder Orte ploetzlich zur zweiten Wahl abgewertet werden - beispielsweise |
| 94 | einen grossen, neuen Hauptort in der Ebene anzuschliessen, der Port Vain |
| 95 | voraussehbar den Rang ablaufen wuerde. |
| 96 | |
| 97 | o Alle NPCs sollten vor Anschluss einmal gekillt werden, um sie auf |
| 98 | grundsaetzliche Kampf-Funktionsfaehigkeit zu pruefen. |
| 99 | |
| 100 | o Haben NPCs Namen, sollte ueberlegt werden, diese Namen ggf. zu banishen. |
| 101 | (hilfe banish). |
| 102 | |
| 103 | o Es existiert ein Shell-Skript, mit dessen Hilfe man offensichtliche |
| 104 | Formfehler in einem kompletten Verzeichnis ermitteln kann (Umlaute im |
| 105 | Code, zu lange Zeilen etc.), wobei bezueglich der Formalien auch auf |
| 106 | den Regionsmitarbeiter-Leitfaden fuer neue Projekte verwiesen werden |
| 107 | soll. Das Skript hierzu ist auf Anfrage bei Zesstra erhaeltlich. Der |
| 108 | Regionsmagier hat hierbei die Entscheidungsfreiheit, die Berichte dieses |
| 109 | Skripts dem Programmierer des neuen Gebiets als Anhaltspunkte zur |
| 110 | Verfuegung zu stellen, oder die Abarbeitung der gemeldeten Punkte als |
| 111 | Voraussetzung vor dem Anschluss vorzuschreiben, aber auch jede Abstufung |
| 112 | dazwischen ist OK. :-) |
| 113 | Zusaetzlicher Tip: Das Skript differenziert zwischen eigentlich nicht |
| 114 | akzeptablen Konstrukten und zumindest fragwuerdigen, d.h. man kann diese |
| 115 | Unterscheidung an den Verantwortlichen mit entsprechenden Forderungen |
| 116 | weitergeben. |
| 117 | Das Skript ist unter ftp://mg.mud.de/Software/src_check/ erhaeltlich. |
| 118 | |
| 119 | o Sollte ein zur Abnahme anstehendes Projekt eine (grundsaetzlich |
| 120 | selbstverstaendlich zulaessige) Gemeinschaftsarbeit sein, sollte man |
| 121 | als Regionsmagier darauf bestehen, eine sauber getrennte Codebasis |
| 122 | in unterschiedlichen Verzeichnissen vorgelegt zu bekommen, oder aber |
| 123 | eine Auflistung, welcher Beitrag von welchem Magier stammt. |
| 124 | Wenn diese Auflistung sich bis auf Funktionsebene erstrecken sollte |
| 125 | (z.B. "DoWield() ist von X, Details von Y, DefendFunc von Z"), ist |
| 126 | das unschoen und an sich abzulehnen. |
| 127 | |
| 128 | |
| 129 | 3) Verlegung von Dateien |
| 130 | ======================== |
| 131 | |
| 132 | o Sollte ein Objekt aus einer anderen Region bzw. allgemein aus einem |
| 133 | anderen Verzeichnis (z.B. /players) in Deine Region verlegt werden |
| 134 | muessen, sind VOR dem Umhaengen folgende Punkte zu beachten: |
| 135 | -- Forscherpunkte muessen umgetragen werden (EM ansprechen), |
| 136 | -- Erstkill-Stufenpunkte muessen umgetragen werden (EM ansprechen), |
| 137 | -- Zaubertraenke umtragen lassen (EM ansprechen), |
| 138 | -- in Padreics Ruestungsskill umtragen lassen (EM ansprechen), |
| 139 | -- evtl. im Gebiete vorhandene Seherhaeuser umziehen, |
| 140 | -- evtl. im Gebiet vorhandene Kraeuterskill-Kraeuter beruecksichtigen, |
| 141 | -- evtl. Briefempfaenger der Postquest beruecksichtigen, |
| 142 | -- ueber die Mudlib greppen lassen, um eventuell in anderen Regionen |
| 143 | verwendete Referenzen auf die alten Pfade des umziehenden Codes |
| 144 | zu finden und dort umzustellen. Hierbei sind in Ausnahmefaellen von |
| 145 | fiesem Code auch in Spielersavefiles gespeicherte Pfade zu |
| 146 | beruecksichtigen (*ARGL*). |
| 147 | |
| 148 | |
| 149 | 4) Seherhaeuser |
| 150 | =============== |
| 151 | |
| 152 | o Die Erlaubnis zum Bau eines Seherhauses in einem Gebiet haengt einzig und |
| 153 | allein von dem verantwortlichen Magier ab. Sollte dieser laenger nicht |
| 154 | erreichbar sein (auch nicht ueber externe Mail!), liegt die Entscheidung |
| 155 | beim Regionsmagier, der aber in jedem Fall die Eignung des Bauplatzes |
| 156 | in der Umgebung bewerten muss. |
| 157 | |
| 158 | o Fuer Sonderwuensche bezueglich Unsichtbarkeit von Seherhaeusern oder |
| 159 | besonderer Kommandos zum Betreten des Hauses sei auf die Datei |
| 160 | /doc/beispiele/misc/seherhaus.c verwiesen, wo die Vorgehensweise |
| 161 | erlaeutert wird. Ein Beispiel einer sehr umfangreichen Implementierung |
| 162 | findet sich in /d/ebene/room/hp_str8a.c. |
| 163 | |
| 164 | o Bei geaenderten Befehlen zum Betreten muss beachtet werden, dass bei einer |
| 165 | Standardimplementierung die Erlaube-Liste umgangen wird, d.h. ohne |
| 166 | besondere Vorkehrungen u.U. JEDER das Haus ungehindert betreten kann. |
| 167 | Es ist hingegen moeglich, die Erlaube-Liste abzufragen und entsprechend |
| 168 | zu behandeln, ein Beispiel hierfuer ist in /d/ebene/room/hp_str8a.c |
| 169 | nachzulesen (derzeit jedoch auf Spielerwunsch deaktiviert). |
| 170 | |
| 171 | |
| 172 | 5) Debuggen von Code |
| 173 | ==================== |
| 174 | |
| 175 | o Nach dem Reparieren eines Objektes ist es meist erforderlich, das |
| 176 | betreffende Objekt neu zu laden. Falls es sich dabei um Objekte handelt, |
| 177 | die z.B. in einem Raum mittels AddItem() hinzugefuegt wurden (wie etwa |
| 178 | ein NPC), dann ist es am besten, die Datei mit dem Befehl |
| 179 | <upd -ad datei.c> zu aktualisieren und somit saemtliche Clones zu |
| 180 | zerstoeren. Wenn man mittels <upd -ar datei.c> die bestehenden Clones |
| 181 | ersetzen wuerde, wuerden diese aus der Item-Liste des clonenden Raumes |
| 182 | ausgetragen, so dass dieser Raum dann im reset() neue Items erzeugt und |
| 183 | diese in der Folge doppelt existieren wuerden. |
| 184 | |
| 185 | |
| 186 | 6) besondere Funktionen |
| 187 | ======================= |
| 188 | |
| 189 | Es kommt haeufig vor, dass Funktionen ueberschrieben werden muessen, und das |
| 190 | ist auch normalerweise vollkommen normal und nicht beanstandenswert. Jedoch |
| 191 | sollte man bei bestimmten Funktionen einiges Augenmerk auf die korrekte |
| 192 | Ausfuehrung richten. Einige Beispiele sind nachfolgend aufgefuehrt: |
| 193 | |
| 194 | o catch_tell() / ReceiveMsg() |
| 195 | Die Reaktion von Objekten, insbesondere NPCs, auf eingehende Textmeldungen |
| 196 | laesst sich nutzen, um schoene und stimmungsvolle Gebiete mit dynamisch |
| 197 | reagierenden Bewohnern zu schaffen. Es laesst sich auf der dunklen Seite |
| 198 | der Macht hingegen auch verwenden, um ueble Konstrukte zu realisieren, |
| 199 | fuer die es seit Ewigkeiten geeignete Implementierungen gibt, und die |
| 200 | demzufolge niemals durch eine Endabnahme durch einen RM durchschluepfen |
| 201 | duerfen. Ein paar reale NPC-Beispiele aus der Praxis: |
| 202 | |
| 203 | Schnipsel 1) |
| 204 | |
| 205 | if (sscanf(str, " %s greift den Priester %s",name1, dummy) == 2) |
| 206 | { |
| 207 | pl = find_player(lower_case(name1)); |
| 208 | if (!pl || !living(pl)) |
| 209 | return 1; |
| 210 | else |
| 211 | Kill(pl); |
| 212 | } |
| 213 | Zweck: Simulation von AddDefender() und InformDefend()/DefendOther() |
| 214 | Kommentar: Absolutes No-Go! Mit Echo-Faehigkeiten von Spielern (Gilde oder |
| 215 | anderweitig) ist hiermit ein indirekter Playerkill moeglich. |
| 216 | Inakzeptable Implementierung. |
| 217 | |
| 218 | |
| 219 | Schnipsel 2) |
| 220 | |
| 221 | if (sscanf(str, "%s sagt: %s\n", wer,was)) |
| 222 | { |
| 223 | if (lower_case(was)=="ja" ) |
| 224 | this_player()->move(zielraum, M_TPORT); |
| 225 | } |
| 226 | Zweck: Ausloesen eines Kommandos, ohne "sage ..." als Befehl |
| 227 | ueberschreiben zu muessen. |
| 228 | Kommentar: Ausnutzbar als Remote-Fluchtteleport, indem man die Meldung |
| 229 | mittels teile-mit an den NPC sendet: |
| 230 | "Robert teilt Dir mit: sagt: ja", was ungeprueft ein move() |
| 231 | zur Folge hat. Offensichtlich ebenso ungeeignet wie das |
| 232 | vorige Beispiel. |
| 233 | |
| 234 | |
| 235 | Schnipsel 3) |
| 236 | |
| 237 | if (sscanf(lower_case(str),"%s sagt: %sversteck%s",s1,s2,s3)) |
| 238 | tell_room(environment(),sprintf("Der Fisch sagt: Das ist aber keine " |
| 239 | "grosse Hilfe, %s.\n",capitalize(s1)),({TO})); |
| 240 | |
| 241 | sieht erstmal harmlos aus, fuehrt aber mit der Anweisung |
| 242 | |
| 243 | SetChats(8, ({ "Der Fisch sagt: Wo kann ich mich nur verstecken?"}) ); |
| 244 | |
| 245 | dazu, dass der NPC dauernd mit sich selber schwatzt. Kein kritischer Bug |
| 246 | im eigentlichen Sinn, aber auf jeden Fall der Atmosphaere im Gebiet |
| 247 | sehr abtraeglich. |
| 248 | |
| 249 | o move() |
| 250 | Ueberschreiben von move() ist eine extrem heikle Angelegenheit, bei der |
| 251 | beim kleinsten Fehler massive Probleme resultieren koennen, jedoch meist |
| 252 | nicht offensichtlich ist, woher das resultiert. Als allgemeine Empfehlung |
| 253 | sollte gelten, dass move() NIE ueberschrieben wird, und wenn, dann muss |
| 254 | ausfuehrlich und aufmerksam geprueft werden, was da passiert, und ob der |
| 255 | gewuenschte Effekt nicht anders sauberer erreicht werden kann. |
| 256 | Als zusaetzlicher Hinweis sei auf NotifyMove() und PreventMove() verwiesen, |
| 257 | mit deren Hilfe sich die allermeisten Faelle erschlagen lassen, in denen |
| 258 | Magier faelschlicherweise glauben, move() ueberschreiben zu muessen. |
| 259 | |
| 260 | o Defend()/Attack() |
| 261 | hier ist ein beliebter Fehler einfach der, dass man am Ende der Funktion |
| 262 | ::Defend() bzw. ::Attack() ruft, aber im Codeblock vorher das Objekt |
| 263 | durch eigenen Tod oder anderes schon zerstoert wurde. Dann geht das schief. |
| 264 | Einfach mal hinschauen - es ist aber kein wirklich gravierender Fehler, |
| 265 | da sowas im Kampf meist ziemlich schnell auffaellt. |
| 266 | |
| 267 | o call_out() |
| 268 | Hierzu zwei Hinweise: zum einen fuehrt call_out("x",0) seit der Umstellung |
| 269 | auf LDmud als Driver nicht mehr implizit zu einem call_out("x",1), wie es |
| 270 | zuvor war, sondern tatsaechlich zu einem fast sofortigen Funktionsaufruf der |
| 271 | Funktion x() - mit allen Konsequenzen, inklusive eines "too long eval"- |
| 272 | Bugs. Wer eine echte Verzoegerung braucht, muss mindestens call_out("x",1) |
| 273 | verwenden. |
| 274 | Zum anderen wurde mit LDmud die Granularitaet des reset()-Aufrufes auf |
| 275 | Heartbeat-Genauigkeit (2 s) verfeinert, so dass man bequem laengere |
| 276 | Verzoegerungen auf die Verwendung von reset() umstellen kann. |
| 277 | |
| 278 | o Zerstoeren von Objekten mit remove() oder destruct() |
| 279 | Man sollte einen sehr kritischen Blick auf Konstrukte werfen, die |
| 280 | nach einem remove() noch weiteren Code ausfuehren (Ausnahme: echte efuns |
| 281 | und Compiler-Konstrukte wie return). |
| 282 | Wenn man Objekte zerstoeren will oder muss, sollte man immer zuerst |
| 283 | remove() verwenden. Destruct muss dem absoluten Ausnahmefall vorbehalten |
| 284 | bleiben. Man sollte im Hinterkopf behalten, dass Objekte gute Gruende haben |
| 285 | koennen, sich einem remove() zu verweigern. |
| 286 | |
| 287 | o for()-Schleifen |
| 288 | Eigentlich keine Funktion, aber an dieser Stelle doch passend: |
| 289 | for()-Schleifen sollten generell durch foreach()-Konstruktionen ersetzt |
| 290 | werden, da dies signifikant und messbar schneller ist. Die naechst- |
| 291 | schnellere Variante waere etwa folgendes, sofern die Reihenfolge der |
| 292 | Abarbeitung unerheblich ist (i ist integer, arr ist ein mixed-Array): |
| 293 | |
| 294 | for ( i=sizeof(arr); i--; ) { |
| 295 | tu_etwas_mit(arr[i]); |
| 296 | } |
| 297 | |
| 298 | o write_file() |
| 299 | Benutzung dieser Funktion nur in begruendeten Ausnahmefaellen abnehmen, da |
| 300 | keinerlei Begrenzung der Dateigroesse existiert. Insbesondere bei Logfiles |
| 301 | entstehen hierdurch im Laufe der Zeit Monsterdateien, die nur Plattenplatz |
| 302 | verbrauchen, aber kaum zu ueberschauen sind. Statt write_file() wird in |
| 303 | den allermeisten Faellen log_file() mit der Standardgroesse von 50 kB fuer |
| 304 | Logfile und eine ggf. vorhandene Altversion (*.OLD) ausreichend sein. |
| 305 | |
| 306 | o Verwalten von charakterbezogenen Daten |
| 307 | Als Beispiel seien hier Statusdaten fuer den Ablauf von Quests genannt, |
| 308 | oder Highscores in irgendwelchen Toplisten. Fuer die Umsetzung dieser |
| 309 | Datenerfassung werden typischerweise zwei Techniken eingesetzt. Zum |
| 310 | einen kann ein Masterobjekt erstellt werden, das die Daten sammelt und |
| 311 | mittels save_object() dauerhaft speichert. Zum anderen kann man auch |
| 312 | Daten in einer Property im Spieler ablegen und diese je nach Anwendungs- |
| 313 | fall auf SAVE setzen. |
| 314 | |
| 315 | Bei der ersten Variante wird man ueblicherweise die UID oder UUID des |
| 316 | Spielers als Indizierungsmerkmal verwenden. Der erste Fallstrick ist nun, |
| 317 | dass die UID bei Spielern (nicht Sehern) ggf. nach einer Selbstloeschung |
| 318 | neu vergeben werden kann, so dass die Daten inkonsistent werden bzw. der |
| 319 | Spieler scheinbar schon in Eurem Master eingetragen ist, obwohl es sich |
| 320 | eigentlich um jemand ganz anderes handelt. |
| 321 | Als Abhilfemassnahme bietet sich folgendes an: |
| 322 | a) UUID verwenden, da diese fuer jeden Spieler eindeutig ist. |
| 323 | b) Hinweis: find_player() kann auch mit UUIDs umgehen und das zugehoerige |
| 324 | Spielerobjekt ermitteln. |
| 325 | c) Man sollte ggf. auf das Mudlib-Event EVT_LIB_PLAYER_DELETION lauschen, |
| 326 | um Spieler auszutragen, die sich loeschen, wodurch die zugehoerigen |
| 327 | Daten obsolet werden. |
| 328 | |
| 329 | Bei der zweiten Variante muss man verschiedene Dinge beruecksichtigen: |
| 330 | a) nicht endlos viele Properties machen, sondern pro Thema (z.B. Quest) |
| 331 | einfach eine. |
| 332 | b) nur die Properties speichern, bei denen das wirklich noetig ist. |
| 333 | c) Speichert man Properties und die Aufgabe wird erledigt, d.h. der |
| 334 | Inhalt der Property obsolet, muss die Property wieder geloescht werden. |