blob: 8025260d6e78c12a8efd2ef8737804a181865a91 [file] [log] [blame]
RM - HOWTO
**********
Vorlaeufiges Inhaltsverzeichnis:
1) Allgemeines
- Fehlerteufel
- Logtool
- Kommentierung von Aenderungen
- eigene Anschluesse
2) Abnahme von Code/Gebieten
- Balance/Genehmigungen
- Konzeptionelle Eignung
- formale Pruefung
- Gemeinschaftsarbeiten
3) Verlegung von Dateien
4) Seherhaeuser
- Sonderwuensche/Unsichtbarkeit
- anderer Befehl zum Betreten
- Verweise auf Beispielcode
5) Debuggen von Code
6) besondere Funktionen/Funktionaelitaeten
- catch_tell() / ReceiveMsg()
- move()
- Attack() / Defend()
- call_out()
- remove() / destruct()
- for()-Schleifen
- write_file()
- Verwalten von charakterbezogenen Daten
1) Allgemeines
==============
o Um stets einen Ueberblick ueber die in Deiner Region (hoffentlich selten)
auftretenden Bugs und Warnungen zu behalten, solltest Du einen
Fehlerteufel (/obj/tools/fehlerteufel.c) besitzen, bedienen koennen und
regelmaessig dessen Listen durchsehen. Deinen Regionsmitarbeitern solltest
Du, allein schon, um zusaetzliche Arbeit fuer Dich selbst zu reduzieren,
dieses Werkzeug ebenfalls an die Hand geben und sie auffordern, ihren
Kram moeglichst umfassend selbst zu reparieren.
Neuen Magiern wird dieses Tool uebrigens automatisch von Merlin in die
Hand gedrueckt, so dass sie sich vom ersten Tag an daran gewoehnen koennen.
o Es ist empfehlenswert, das Logtool zu besitzen (/obj/tools/logtool.c) und
in diesem fuer jedes Repfile eines Regionsmitarbeiters einen Eintrag vor-
zusehen. Warum das? Es ist bereits vorgekommen, dass Spieler ernsthaft
argumentiert haben, sie duerften einen von ihnen entdeckten Bug ausnutzen,
weil sie ihn ja gemeldet haetten (im Repfile!), er aber nicht repariert
worden sei (was nicht verwundert, da er im Repfile in der Mehrzahl der
Faelle weit unterhalb des Wahrnehmungshorizonts der allermeisten
Regionsmagier schwebt).
o Wenn Du in fremdem Code Aenderungen vornehmen musst, die mehr beruehren
als nur ein paar Tippfehler in Details oder Infos, dann kommentiere den
defekten Code aus und fuege den reparierten mit Kommentar ein. Es reicht
an dieser Stelle aus, Deinen Namen und das Datum zu vermerken. In den
Header der Datei solltest Du unbedingt ein Changelog einfuegen, in dem
das Datum der Aenderung, der Name des Ausfuehrenden (Deiner) sowie die
vorgenommene Aenderung mit Begruendung bzw. u.U. Bug-ID aus dem Fehler-
teufel einzutragen ist. Wir haben im MG leider keinen wirkungsvollen
Debugger und Bugtracker-Mechanismus, so dass wir zur Erhaltung einer
gewissen Code-Hygiene Bugfixes mit Hilfe solcher Eintragungen dokumentieren
und rueckverfolgen muessen. Insbesondere ist ein solcher Eintrag wichtig,
um im Falle von Folge-Bugs die Ursache schneller zu finden.
o Willst Du in Deiner eigenen Region ein Gebiet anschliessen, solltest Du
diesen vor Anschluss entweder vom Co-RM oder von einem RM einer anderen
Region lesen lassen. Auch wenn Du ein guter Programmierer bist, findet ein
zweites Paar Augen oft Dinge, an die man nicht gedacht hat.
2) Code wird zur Abnahme vorgelegt
==================================
o Bei Waffen und Ruestungen unbedingt auf Einhaltung der Balance-Vorgaben
achten.
o Sofern ein Objekt eine Balance-Genehmigung besitzt, muss die BTOP-Nummer,
die die Balance als eindeutige ID fuer jeden Antrag vergibt, im Header
des betreffenden Objektes erkennbar eingetragen sein.
o Vor Anschluss sollte man Ruecksprache mit verschiedenen Instanzen halten,
um sicherzustellen, dass der Magier alle wesentlichen Punkte
beruecksichtigt hat:
- liegen alle Balance-Genehmigungen vor?
- sind die FP eingetragen?
- sind die ZTs eingetragen und getestet?
- sind Sonder-EK-Stupse genehmigt und eingetragen?
o Fuer neue Gebiete ist eine grundsaetzliche Pruefung des Konzepts auf
Regionstauglichkeit sinnvoll, damit nicht alteingesessene Institutionen
oder Orte ploetzlich zur zweiten Wahl abgewertet werden - beispielsweise
einen grossen, neuen Hauptort in der Ebene anzuschliessen, der Port Vain
voraussehbar den Rang ablaufen wuerde.
o Alle NPCs sollten vor Anschluss einmal gekillt werden, um sie auf
grundsaetzliche Kampf-Funktionsfaehigkeit zu pruefen.
o Haben NPCs Namen, sollte ueberlegt werden, diese Namen ggf. zu banishen.
(hilfe banish).
o Es existiert ein Shell-Skript, mit dessen Hilfe man offensichtliche
Formfehler in einem kompletten Verzeichnis ermitteln kann (Umlaute im
Code, zu lange Zeilen etc.), wobei bezueglich der Formalien auch auf
den Regionsmitarbeiter-Leitfaden fuer neue Projekte verwiesen werden
soll. Das Skript hierzu ist auf Anfrage bei Zesstra erhaeltlich. Der
Regionsmagier hat hierbei die Entscheidungsfreiheit, die Berichte dieses
Skripts dem Programmierer des neuen Gebiets als Anhaltspunkte zur
Verfuegung zu stellen, oder die Abarbeitung der gemeldeten Punkte als
Voraussetzung vor dem Anschluss vorzuschreiben, aber auch jede Abstufung
dazwischen ist OK. :-)
Zusaetzlicher Tip: Das Skript differenziert zwischen eigentlich nicht
akzeptablen Konstrukten und zumindest fragwuerdigen, d.h. man kann diese
Unterscheidung an den Verantwortlichen mit entsprechenden Forderungen
weitergeben.
Das Skript ist unter ftp://mg.mud.de/Software/src_check/ erhaeltlich.
o Sollte ein zur Abnahme anstehendes Projekt eine (grundsaetzlich
selbstverstaendlich zulaessige) Gemeinschaftsarbeit sein, sollte man
als Regionsmagier darauf bestehen, eine sauber getrennte Codebasis
in unterschiedlichen Verzeichnissen vorgelegt zu bekommen, oder aber
eine Auflistung, welcher Beitrag von welchem Magier stammt.
Wenn diese Auflistung sich bis auf Funktionsebene erstrecken sollte
(z.B. "DoWield() ist von X, Details von Y, DefendFunc von Z"), ist
das unschoen und an sich abzulehnen.
3) Verlegung von Dateien
========================
o Sollte ein Objekt aus einer anderen Region bzw. allgemein aus einem
anderen Verzeichnis (z.B. /players) in Deine Region verlegt werden
muessen, sind VOR dem Umhaengen folgende Punkte zu beachten:
-- Forscherpunkte muessen umgetragen werden (EM ansprechen),
-- Erstkill-Stufenpunkte muessen umgetragen werden (EM ansprechen),
-- Zaubertraenke umtragen lassen (EM ansprechen),
-- Ruestungen in Padreics Ruestungsskill umtragen lassen (EM ansprechen),
-- Waffen in der Liste legendaerer Waffen der Kaempfergilde umtragen
(GM Kaempfer oder EM ansprechen),
-- evtl. im Gebiete vorhandene Seherhaeuser umziehen,
-- evtl. im Gebiet vorhandene Kraeuterskill-Kraeuter beruecksichtigen,
-- evtl. Briefempfaenger der Postquest beruecksichtigen,
-- ueber die Mudlib greppen lassen, um eventuell in anderen Regionen
verwendete Referenzen auf die alten Pfade des umziehenden Codes
zu finden und dort umzustellen. Hierbei sind in Ausnahmefaellen von
fiesem Code auch in Spielersavefiles gespeicherte Pfade zu
beruecksichtigen (*ARGL*).
4) Seherhaeuser
===============
o Die Erlaubnis zum Bau eines Seherhauses in einem Gebiet haengt einzig und
allein von dem verantwortlichen Magier ab. Sollte dieser laenger nicht
erreichbar sein (auch nicht ueber externe Mail!), liegt die Entscheidung
beim Regionsmagier, der aber in jedem Fall die Eignung des Bauplatzes
in der Umgebung bewerten muss.
o Fuer Sonderwuensche bezueglich Unsichtbarkeit von Seherhaeusern oder
besonderer Kommandos zum Betreten des Hauses sei auf die Datei
/doc/beispiele/misc/seherhaus.c verwiesen, wo die Vorgehensweise
erlaeutert wird. Ein Beispiel einer sehr umfangreichen Implementierung
findet sich in /d/ebene/room/hp_str8a.c.
o Bei geaenderten Befehlen zum Betreten muss beachtet werden, dass bei einer
Standardimplementierung die Erlaube-Liste umgangen wird, d.h. ohne
besondere Vorkehrungen u.U. JEDER das Haus ungehindert betreten kann.
Es ist hingegen moeglich, die Erlaube-Liste abzufragen und entsprechend
zu behandeln, ein Beispiel hierfuer ist in /d/ebene/room/hp_str8a.c
nachzulesen (derzeit jedoch auf Spielerwunsch deaktiviert).
5) Debuggen von Code
====================
o Nach dem Reparieren eines Objektes ist es meist erforderlich, das
betreffende Objekt neu zu laden. Falls es sich dabei um Objekte handelt,
die z.B. in einem Raum mittels AddItem() hinzugefuegt wurden (wie etwa
ein NPC), dann ist es am besten, die Datei mit dem Befehl
<upd -ad datei.c> zu aktualisieren und somit saemtliche Clones zu
zerstoeren. Wenn man mittels <upd -ar datei.c> die bestehenden Clones
ersetzen wuerde, wuerden diese aus der Item-Liste des clonenden Raumes
ausgetragen, so dass dieser Raum dann im reset() neue Items erzeugt und
diese in der Folge doppelt existieren wuerden.
6) besondere Funktionen
=======================
Es kommt haeufig vor, dass Funktionen ueberschrieben werden muessen, und das
ist auch normalerweise vollkommen normal und nicht beanstandenswert. Jedoch
sollte man bei bestimmten Funktionen einiges Augenmerk auf die korrekte
Ausfuehrung richten. Einige Beispiele sind nachfolgend aufgefuehrt:
o catch_tell() / ReceiveMsg()
Die Reaktion von Objekten, insbesondere NPCs, auf eingehende Textmeldungen
laesst sich nutzen, um schoene und stimmungsvolle Gebiete mit dynamisch
reagierenden Bewohnern zu schaffen. Es laesst sich auf der dunklen Seite
der Macht hingegen auch verwenden, um ueble Konstrukte zu realisieren,
fuer die es seit Ewigkeiten geeignete Implementierungen gibt, und die
demzufolge niemals durch eine Endabnahme durch einen RM durchschluepfen
duerfen. Ein paar reale NPC-Beispiele aus der Praxis:
Schnipsel 1)
if (sscanf(str, " %s greift den Priester %s",name1, dummy) == 2)
{
pl = find_player(lower_case(name1));
if (!pl || !living(pl))
return 1;
else
Kill(pl);
}
Zweck: Simulation von AddDefender() und InformDefend()/DefendOther()
Kommentar: Absolutes No-Go! Mit Echo-Faehigkeiten von Spielern (Gilde oder
anderweitig) ist hiermit ein indirekter Playerkill moeglich.
Inakzeptable Implementierung.
Schnipsel 2)
if (sscanf(str, "%s sagt: %s\n", wer,was))
{
if (lower_case(was)=="ja" )
this_player()->move(zielraum, M_TPORT);
}
Zweck: Ausloesen eines Kommandos, ohne "sage ..." als Befehl
ueberschreiben zu muessen.
Kommentar: Ausnutzbar als Remote-Fluchtteleport, indem man die Meldung
mittels teile-mit an den NPC sendet:
"Robert teilt Dir mit: sagt: ja", was ungeprueft ein move()
zur Folge hat. Offensichtlich ebenso ungeeignet wie das
vorige Beispiel.
Schnipsel 3)
if (sscanf(lower_case(str),"%s sagt: %sversteck%s",s1,s2,s3))
tell_room(environment(),sprintf("Der Fisch sagt: Das ist aber keine "
"grosse Hilfe, %s.\n",capitalize(s1)),({TO}));
sieht erstmal harmlos aus, fuehrt aber mit der Anweisung
SetChats(8, ({ "Der Fisch sagt: Wo kann ich mich nur verstecken?"}) );
dazu, dass der NPC dauernd mit sich selber schwatzt. Kein kritischer Bug
im eigentlichen Sinn, aber auf jeden Fall der Atmosphaere im Gebiet
sehr abtraeglich.
o move()
Ueberschreiben von move() ist eine extrem heikle Angelegenheit, bei der
beim kleinsten Fehler massive Probleme resultieren koennen, jedoch meist
nicht offensichtlich ist, woher das resultiert. Als allgemeine Empfehlung
sollte gelten, dass move() NIE ueberschrieben wird, und wenn, dann muss
ausfuehrlich und aufmerksam geprueft werden, was da passiert, und ob der
gewuenschte Effekt nicht anders sauberer erreicht werden kann.
Als zusaetzlicher Hinweis sei auf NotifyMove() und PreventMove() verwiesen,
mit deren Hilfe sich die allermeisten Faelle erschlagen lassen, in denen
Magier faelschlicherweise glauben, move() ueberschreiben zu muessen.
o Defend()/Attack()
hier ist ein beliebter Fehler einfach der, dass man am Ende der Funktion
::Defend() bzw. ::Attack() ruft, aber im Codeblock vorher das Objekt
durch eigenen Tod oder anderes schon zerstoert wurde. Dann geht das schief.
Einfach mal hinschauen - es ist aber kein wirklich gravierender Fehler,
da sowas im Kampf meist ziemlich schnell auffaellt.
o call_out()
Hierzu zwei Hinweise: zum einen fuehrt call_out("x",0) seit der Umstellung
auf LDmud als Driver nicht mehr implizit zu einem call_out("x",1), wie es
zuvor war, sondern tatsaechlich zu einem fast sofortigen Funktionsaufruf der
Funktion x() - mit allen Konsequenzen, inklusive eines "too long eval"-
Bugs. Wer eine echte Verzoegerung braucht, muss mindestens call_out("x",1)
verwenden.
Zum anderen wurde mit LDmud die Granularitaet des reset()-Aufrufes auf
Heartbeat-Genauigkeit (2 s) verfeinert, so dass man bequem laengere
Verzoegerungen auf die Verwendung von reset() umstellen kann.
o Zerstoeren von Objekten mit remove() oder destruct()
Man sollte einen sehr kritischen Blick auf Konstrukte werfen, die
nach einem remove() noch weiteren Code ausfuehren (Ausnahme: echte efuns
und Compiler-Konstrukte wie return).
Wenn man Objekte zerstoeren will oder muss, sollte man immer zuerst
remove() verwenden. Destruct muss dem absoluten Ausnahmefall vorbehalten
bleiben. Man sollte im Hinterkopf behalten, dass Objekte gute Gruende haben
koennen, sich einem remove() zu verweigern.
o for()-Schleifen
Eigentlich keine Funktion, aber an dieser Stelle doch passend:
for()-Schleifen sollten generell durch foreach()-Konstruktionen ersetzt
werden, da dies signifikant und messbar schneller ist. Die naechst-
schnellere Variante waere etwa folgendes, sofern die Reihenfolge der
Abarbeitung unerheblich ist (i ist integer, arr ist ein mixed-Array):
for ( i=sizeof(arr); i--; ) {
tu_etwas_mit(arr[i]);
}
o write_file()
Benutzung dieser Funktion nur in begruendeten Ausnahmefaellen abnehmen, da
keinerlei Begrenzung der Dateigroesse existiert. Insbesondere bei Logfiles
entstehen hierdurch im Laufe der Zeit Monsterdateien, die nur Plattenplatz
verbrauchen, aber kaum zu ueberschauen sind. Statt write_file() wird in
den allermeisten Faellen log_file() mit der Standardgroesse von 50 kB fuer
Logfile und eine ggf. vorhandene Altversion (*.OLD) ausreichend sein.
o Verwalten von charakterbezogenen Daten
Als Beispiel seien hier Statusdaten fuer den Ablauf von Quests genannt,
oder Highscores in irgendwelchen Toplisten. Fuer die Umsetzung dieser
Datenerfassung werden typischerweise zwei Techniken eingesetzt. Zum
einen kann ein Masterobjekt erstellt werden, das die Daten sammelt und
mittels save_object() dauerhaft speichert. Zum anderen kann man auch
Daten in einer Property im Spieler ablegen und diese je nach Anwendungs-
fall auf SAVE setzen.
Bei der ersten Variante wird man ueblicherweise die UID oder UUID des
Spielers als Indizierungsmerkmal verwenden. Der erste Fallstrick ist nun,
dass die UID bei Spielern (nicht Sehern) ggf. nach einer Selbstloeschung
neu vergeben werden kann, so dass die Daten inkonsistent werden bzw. der
Spieler scheinbar schon in Eurem Master eingetragen ist, obwohl es sich
eigentlich um jemand ganz anderes handelt.
Als Abhilfemassnahme bietet sich folgendes an:
a) UUID verwenden, da diese fuer jeden Spieler eindeutig ist.
b) Hinweis: find_player() kann auch mit UUIDs umgehen und das zugehoerige
Spielerobjekt ermitteln.
c) Man sollte ggf. auf das Mudlib-Event EVT_LIB_PLAYER_DELETION lauschen,
um Spieler auszutragen, die sich loeschen, wodurch die zugehoerigen
Daten obsolet werden.
Bei der zweiten Variante muss man verschiedene Dinge beruecksichtigen:
a) nicht endlos viele Properties machen, sondern pro Thema (z.B. Quest)
einfach eine.
b) nur die Properties speichern, bei denen das wirklich noetig ist.
c) Speichert man Properties und die Aufgabe wird erledigt, d.h. der
Inhalt der Property obsolet, muss die Property wieder geloescht werden.