Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/secure/potionmaster.c b/secure/potionmaster.c
new file mode 100644
index 0000000..78a0565
--- /dev/null
+++ b/secure/potionmaster.c
@@ -0,0 +1,515 @@
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma rtt_checks
+#pragma pedantic
+#pragma warn_deprecated
+
+#include "/secure/config.h"
+#include "/secure/wizlevels.h"
+
+#define MAX_ROOMS_PER_LIST 10
+
+// die div. Speicherorte und Pfade
+#define SAVEFILE "/secure/ARCH/potions"
+#define TIPS(x) "/secure/ARCH/ZT/"+x
+#define ORAKEL "/room/orakel"
+#define POTIONTOOL "/obj/tools/ptool"
+
+// Fuer die Dump-Funktion
+#define POTIONDUMP "/secure/ARCH/POTIONS.dump"
+#define DUMP(str) write_file(POTIONDUMP, str)
+
+// Modifikationen loggen. "event" ist eins der Events aus der Liste:
+// ADD_POTION, ACTIVATE, DEACTIVATE, MODIFY_PATH, MODIFY_LISTNO
+#define LOGFILE_MOD "/log/ARCH/POTIONS_MOD.log"
+#define MODLOG(event,num,data) write_file(LOGFILE_MOD, \
+ sprintf("%17s %-14s %-3d sEUID %s %s\n", \
+ strftime("%Y-%b-%d %R"), event, num, secure_euid(), data) )
+
+// Indizierungs-Konstanten fuer das potions-Mapping
+#define POT_ROOMNAME 0
+#define POT_LISTNO 1
+
+// Konstanten fuer div. Rueckgabewerte. Keine 0, da das eine gueltige ZT-ID
+// sein kann und abfragende Objekte verwirren koennte.
+#define POT_IS_ACTIVE 1
+#define POT_ACCESS_DENIED -1
+#define POT_WRONG_DATATYPE -2
+#define POT_NO_SUCH_ROOM -3
+#define POT_ALREADY_REGISTERED -4
+#define POT_INVALID_POTION -5
+#define POT_NO_SUCH_FILE -6
+#define POT_INVALID_LIST_NUMBER -7
+#define POT_ALREADY_ACTIVE -8
+#define POT_ALREADY_INACTIVE -9
+#define POT_NOT_INACTIVE -10
+#define POT_NOT_ACTIVE -11
+
+// Zaehler fuer den als naechsten anzulegenden ZT
+private int nextroom;
+
+// Liste aller ZTs einschl. inaktive, ([ int num : string room; int list ])
+private mapping potions = ([]);
+
+// Liste der inaktiven ZTs, ({ int num })
+private int *inactive = ({});
+
+// Cache mit den einzelnen Listen, ([ int list : int *potionlist ])
+private nosave mapping lists;
+
+// reverse_table Lookup Cache, ([string room: int number])
+private nosave mapping reverse_table = ([]);
+
+// Cache fuer die bereits von der Platte eingelesenen ZTs, um Plattenzugriffe
+// insbesondere beim Erzeugen der Tipliste durch das Orakel zu reduzieren.
+private nosave mapping tipmap = ([]);
+
+int ActivateRoom(string room);
+
+private int secure() {
+ return (!process_call() && ARCH_SECURITY);
+}
+
+mixed QueryPotionData(int num) {
+ if ( !secure() )
+ return POT_ACCESS_DENIED;
+ return ([num : potions[num,0]; potions[num,1] ]);
+}
+
+int *QueryInactivePotions() {
+ return copy(inactive);
+}
+
+private void RebuildCache() {
+ // Cache invalidieren; vor-initialisiert zur Beschleunigung des Rebuilds.
+ lists = ([0:({}),1:({}),2:({}),3:({}),4:({}),5:({}),6:({}),7:({})]);
+ foreach (int num, string room, int list : potions) {
+ reverse_table += ([room:num]);
+ lists[list] += ({num});
+ }
+ return;
+}
+
+int QueryActive(mixed potion) {
+ if ( extern_call() && !secure() )
+ return POT_ACCESS_DENIED;
+ int ret;
+ // Wenn nach dem Pfad des ZTs gefragt wird, diesen zuerst in die Nummer
+ // umwandeln durch Lookup im reverse_table Cache
+ if ( stringp(potion) ) {
+ potion = reverse_table[potion];
+ }
+ // Ein ZT ist aktiv, wenn er in der Liste potions steht und nicht in der
+ // Liste der inaktiven ZTs (inactive) steht. Dann dessen Nummer
+ // zurueckgeben; inaktive zuerst pruefen, weil die inaktiven auch in
+ // "potions" enthalten sind und somit alle ZTs ausser den ungueltigen
+ // als aktiv gemeldet wuerden.
+ if ( member(inactive,potion)>-1 )
+ ret = POT_NOT_ACTIVE;
+ else if ( member(potions,potion) && potions[potion,POT_LISTNO]!=-1 )
+ ret = potion;
+ // ansonsten ist das kein gueltiger ZT
+ else
+ ret = POT_INVALID_POTION;
+ return ret;
+}
+
+private void save_info() {
+ save_object(SAVEFILE);
+}
+
+protected void create() {
+ seteuid(getuid(this_object()));
+ if ( !restore_object(SAVEFILE) ) {
+ // Fehler ausgeben, damit es jemand merkt. Dennoch wird jetzt im Anschluss
+ // der Cache neu aufgebaut (damit die Datenstrukturen gueltig sind).
+ catch(raise_error("Potionmaster: no/corrupt savefile! Reinitializing.\n"); publish);
+ }
+ RebuildCache();
+}
+
+int AddPotionRoom(string room, int list) {
+ if ( !secure() )
+ return POT_ACCESS_DENIED;
+ // Neuer Raum muss ein gueltiger String sein.
+ if ( !stringp(room) )
+ return POT_WRONG_DATATYPE;
+ if ( !intp(list) || list<0 || list>7 )
+ return POT_WRONG_DATATYPE;
+
+ // Pfad mit Hilfe des Masters vervollstaendigen (+, ~ etc. ersetzen)
+ room=(string)master()->_get_path(room,0);
+
+ // Datei mit dem ZT-Spruch muss existieren.
+ if ( file_size( TIPS(to_string(nextroom)+".zt") ) < 0 ) {
+ raise_error("Potionmaster: Tipfile missing, please create "+
+ to_string(nextroom)+".zt");
+ return POT_NO_SUCH_FILE;
+ }
+ // Neuer Raum darf noch nicht in der Liste enthalten sein.
+ if ( member(m_values(potions,POT_ROOMNAME), room)!=-1)
+ return POT_ALREADY_REGISTERED;
+ // Neuer Raum muss ladbar sein.
+ if ( catch(load_object(room); publish) )
+ return POT_NO_SUCH_ROOM;
+
+ // Jetzt kann's endlich losgehen, Raum eintragen, nextroom hochzaehlen
+ potions += ([nextroom : room; list]);
+ MODLOG("ADD_POTION", nextroom, room);
+ // Neu eingetragene ZTs werden auch gleich aktiviert; ActivateRoom()
+ // baut den Cache selbst neu auf, daher kann das hier entfallen.
+ ActivateRoom(room);
+ nextroom++;
+ save_info();
+ return nextroom;
+}
+
+int ChangeRoomPath(string old, string new) {
+ if ( !secure() )
+ return POT_ACCESS_DENIED;
+
+ // beide Pfade muessen gueltige Strings sein
+ if ( !stringp(old) || !stringp(new) )
+ return POT_WRONG_DATATYPE;
+
+ // Pfad mit Hilfe des Masters vervollstaendigen (+, ~ etc. ersetzen)
+ old=(string)master()->_get_path(old,0);
+ new=(string)master()->_get_path(new,0);
+
+ // Der neue Raum darf nicht bereits eingetragen sein, ...
+ if ( member(reverse_table,new) )
+ return POT_ALREADY_REGISTERED;
+ // ... und der alte Raum muss noch eingetragen sein.
+ if ( !member(reverse_table,old) )
+ return POT_NO_SUCH_ROOM;
+ // Neuer Raum muss ladbar sein.
+ if (catch(load_object(new);publish))
+ return POT_NO_SUCH_ROOM;
+
+ // Aktuelle ZT-Nummer des alten Pfades ermitteln
+ int num = reverse_table[old];
+ // Pfad aendern, Cache neubauen und Savefile speichern
+ potions[num,POT_ROOMNAME] = new;
+ RebuildCache();
+ save_info();
+ MODLOG("MODIFY_PATH", num, old+" => "+new);
+ return num;
+}
+
+int ActivateRoom(string room) {
+ if ( !secure() )
+ return POT_ACCESS_DENIED;
+ // Aktuelle ZT-Nummer ermitteln. Etwas umstaendlich, da im Fehlerfall -1
+ // benoetigt wird.
+ int num = member(reverse_table,room) ? reverse_table[room] : -1;
+ // Nummer muss existieren
+ if ( num == -1 )
+ return POT_INVALID_POTION;
+ // ZT ist nicht inaktiv, dann kann man ihn auch nicht aktivieren.
+ if ( member(inactive, num)==-1 )
+ return POT_ALREADY_ACTIVE;
+ inactive -= ({num});
+ RebuildCache();
+ save_info();
+ MODLOG("ACTIVATE", num, room);
+ return num;
+}
+
+int SetListNr(string room, int list) {
+ if ( !secure() )
+ return POT_ACCESS_DENIED;
+ // Aktuelle ZT-Nummer ermitteln. Etwa umstaendlich, da im Fehlerfall -1
+ // benoetigt wird.
+ int num = member(reverse_table,room) ? reverse_table[room] : -1;
+ // Nummer muss existieren
+ if ( num == -1 )
+ return POT_INVALID_POTION;
+ // Listennummer muss zwischen 0 und 7 liegen.
+ if ( list < 0 || list > 7 )
+ return POT_INVALID_LIST_NUMBER;
+
+ // alte Nummer aufschreiben zum Loggen.
+ int oldlist = potions[num,POT_LISTNO];
+ // Listennummer in der ZT-Liste aktualisieren.
+ potions[num,POT_LISTNO] = list;
+ RebuildCache();
+ save_info();
+ MODLOG("MODIFY_LISTNO", num, oldlist+" -> "+list);
+ return num;
+}
+
+int DeactivateRoom(string room) {
+ if ( !secure() )
+ return POT_ACCESS_DENIED;
+ // Aktuelle ZT-Nummer ermitteln. Etwa umstaendlich, da im Fehlerfall -1
+ // benoetigt wird.
+ int num = member(reverse_table,room) ? reverse_table[room] : -1;
+ // Nummer muss existieren
+ if ( num == -1 )
+ return POT_INVALID_POTION;
+ // ZT darf nicht bereits inaktiv sein
+ if ( member(inactive,num)>-1 )
+ return POT_ALREADY_INACTIVE;
+ inactive += ({num});
+ RebuildCache();
+ save_info();
+ MODLOG("DEACTIVATE", num, room);
+ return num;
+}
+
+private int *_create_list(int listno, int anz) {
+ int *list = ({});
+ // Listenerzeugung lohnt nur dann, wenn mind. 1 Eintrag gefordert wird und
+ // die Listennummer im gueltigen Bereich zwischen 0 und 7 ist.
+ if ( anz>0 && listno>=0 && listno<=7 ) {
+ int *tmp = lists[listno] - inactive;
+ // Wenn die Listengroesse maximal genauso gross ist wie die angeforderte
+ // Anzahl, kann diese vollstaendig uebernommen werden.
+ if ( sizeof(tmp) <= anz ) {
+ list = tmp;
+ } else { // ansonsten soviele Eintraege erzeugen wie angefordert
+ foreach(int i: anz) {
+ int j=random(sizeof(tmp));
+ list += ({tmp[j]});
+ tmp -= ({tmp[j]});
+ }
+ }
+ }
+ return list;
+}
+
+mixed *InitialList() {
+ mixed *list=({});
+ foreach(int i : 8)
+ list+=_create_list(i,MAX_ROOMS_PER_LIST);
+ return list;
+}
+
+// Aufrufe aus den Spielershells und dem Potiontool sind erlaubt
+int HasPotion(object room) {
+ if ( !query_once_interactive(previous_object()) &&
+ load_name(previous_object()) != POTIONTOOL )
+ return POT_ACCESS_DENIED;
+ return objectp(room) ? reverse_table[object_name(room)] : POT_NO_SUCH_ROOM;
+}
+
+// Listennummer ermitteln, in der der ZT num enthalten ist.
+// Auch inaktive ZTs werden als zu einer Liste gehoerig gemeldet.
+int GetListByNumber(int num) {
+ return member(potions,num) ? potions[num,POT_LISTNO] : POT_INVALID_POTION;
+}
+
+// Listennummer ermitteln, in der der inaktive ZT num enthalten ist.
+// Da alle Zaubertraenke in einer Gesamtliste enthalten sind, kann direkt das
+// Resultat von GetListByNumber() zurueckgegeben werden.
+int GetInactListByNumber(int num) {
+ if ( member(inactive,num) > -1 )
+ return GetListByNumber(num);
+ else
+ return POT_NOT_INACTIVE;
+}
+
+// Wird nur von /obj/tools/ptool aufgerufen. Wenn der Aufruf zugelassen wird,
+// erfolgt von dort aus ein ChangeRoomPath(), und dort wird bereits geloggt.
+// Erzmagier duerfen auch aufrufen.
+mixed GetFilenameByNumber(int num) {
+ if ( extern_call() &&
+ object_name(previous_object()) != POTIONTOOL &&
+ !ARCH_SECURITY )
+ return POT_ACCESS_DENIED;
+ if ( !member(potions, num) )
+ return POT_INVALID_POTION;
+ return potions[num,POT_ROOMNAME];
+}
+
+// Wird vom Spielerobjekt gerufen; uebergeben werden der Raum, der initial
+// FindPotion() aufrief, die ZT-Liste des Spielers sowie die Liste bereits
+// gefundener ZTs.
+//
+// In std/player/potion.c ist sichergestellt, dass room ein Objekt ist und
+// dass die Listen als Int-Arrays uebergeben werden.
+//
+// Es werden aus historischen Gruenden (noch) beide ZT-Listen uebergeben,
+// aber es muss nur knownlist ueberprueft werden, da diese eine Teilmenge
+// von potionlist ist und somit jeder ZT in ersterer auf jeden Fall auch
+// in letzterer enthalten sein _muss_.
+int InList(object room, int *potionlist, int *knownlist) {
+ if ( !query_once_interactive(previous_object()) && !secure() )
+ return 0; //POT_ACCESS_DENIED;
+ int num = QueryActive(object_name(room));
+
+ // Zunaechst keinen Fehlercode zurueckgeben, weil das Spielerobjekt
+ // damit im Moment noch nicht umgehen kann.
+ if ( num < 0 )
+ return 0; //POT_INVALID_POTION;
+
+ return (member(knownlist,num)>-1);
+}
+
+// Wird vom Spielerobjekt gerufen, um einen gefundenen ZT aus dessen ZT-
+// Listen austragen zu lassen. Das Spielerobjekt stellt sicher, dass room
+// ein Objekt ist, und dass [known_]potionrooms Arrays sind.
+//
+// ACHTUNG! Sowohl potionrooms, als auch known_potionrooms sind die
+// Originaldaten aus dem Spielerobjekt, die hier ALS REFERENZ
+// uebergeben werden! Vorsicht bei Aenderungen an der Funktion!
+//
+// Wenn std/player/potions.c geaendert wird, ist diese Funktion obsolet
+// => ggf. komplett rauswerfen. Dann kann auch der Fehlercode in InList()
+// reaktiviert und deren Parameter korrigiert werden
+void RemoveList(object room, int* potionrooms, int* known_potionrooms) {
+ // ZT ist aktiv, das wurde bereits in InList() geprueft, das vor dem
+ // Aufruf von RemoveList() aus dem Spielerobjekt gerufen wird. Daher reicht
+ // es aus, die ZT-Nummer aus dem reverse_table Lookup zu holen.
+ int num = reverse_table[object_name(room)];
+ int tmp = member(potionrooms, num);
+ potionrooms[tmp] = -1;
+ tmp = member(known_potionrooms, num);
+ known_potionrooms[tmp] = -1;
+ return;
+}
+
+#define LISTHEADER "################## Liste %d ################## (%d)\n\n"
+#define INACT_HEADER "################## Inaktiv ################## (%d)\n\n"
+
+int DumpList() {
+ if ( !secure() )
+ return POT_ACCESS_DENIED;
+ // Zuerst die Caches neu aufbauen, um sicherzustellen, dass die Listen
+ // aktuell sind.
+ RebuildCache();
+ // Dumpfile loeschen
+ rm(POTIONDUMP);
+ // Alle Listen der Reihe nach ablaufen. Es wird in jedem Schleifendurchlauf
+ // einmal auf die Platte geschrieben. Das ist Absicht, weil es speicher-
+ // technisch sehr aufwendig waere, die Ausgabedaten stattdessen erst in
+ // einer Variablen zu sammeln und dann wegzuschreiben.
+ foreach(int *listno : sort_array(m_indices(lists),#'>)) {
+ // Zuerst den Header der ensprechenden Liste schreiben.
+ DUMP(sprintf(LISTHEADER, listno, sizeof(lists[listno])));
+ // Dann alle Potions der Liste durchgehen
+ foreach(int potion : lists[listno]) {
+ // Wenn der ZT inaktiv ist, ueberspringen wir den.
+ if ( member(inactive,potion)>-1 )
+ continue;
+ // Raumpfad aus der Gesamtliste holen.
+ string str = potions[potion,POT_ROOMNAME];
+ // Wenn der nicht existiert, entsprechenden Hinweis dumpen, ansonsten
+ // den Raumnamen.
+ //ZZ: ich finde ja, wir sollten sicherstellen, dass das nicht mehr
+ //passiert. Ist das mit dem AddPotionRoom oben nicht schon so?
+ if ( !stringp(str) || !sizeof(str) )
+ str="KEIN RAUM ZUGEORDNET!!!";
+ DUMP(sprintf("%3d. %s\n", potion, str));
+ }
+ // 2 Leerzeilen zwischen jeder Gruppe einfuegen
+ DUMP("\n\n");
+ }
+ // Zum Schluss den Header der Inaktiv-Liste schreiben.
+ DUMP(sprintf(INACT_HEADER, sizeof(inactive)));
+ // Dann alle inaktiven Potions ablaufen
+ foreach(int i : inactive) {
+ //ZZ: sonst muesste man hier wohl auch auf den fehlenden Raum pruefen.
+ DUMP(sprintf("%3d. (Liste %d) %s\n", i, GetListByNumber(i),
+ potions[i,POT_ROOMNAME]));
+ }
+ return 1;
+}
+
+//ORAKEL-Routinen
+
+/* Aufbau eines Tips:
+
+ Tralala, lalala
+ Dideldadeldum
+ Huppsdiwupps
+ XXXXX
+ Noch ein zweiter Tip
+ Dingeldong
+ %%%%%
+
+ Die Prozentzeichen sind das Endezeichen, ab hier koennen eventuelle
+ Kommentare stehen. Die 5 X sind das Trennzeichen zwischen zwei Tips
+ zum selben ZT.
+*/
+
+// TIPLOG() derzeit unbenutzt
+//#define LOGFILE_READ "ARCH/POTIONS_TIP_READ.log"
+//#define TIPLOG(x) log_file(LOGFILE_READ, x)
+
+mixed TipLesen(int num) {
+ string *ret = tipmap[num];
+ //Funktion darf nur vom Orakel und EM+ gerufen werden.
+ if ( previous_object() != find_object(ORAKEL) && !secure() )
+ return POT_ACCESS_DENIED;
+ // Derzeit kein Log, da diese Informationen tendentiell eher
+ // uninteressant sind.
+ // Timestamp ist vom Format "2012-Okt-03 14:18"
+ /*TIPLOG(sprintf("%s ZT-Tip gelesen: %d von TP: %O, PO: %O, TI: %O\n",
+ strftime("%Y-%b-%d %R"), num, this_player(), previous_object(),
+ this_interactive()));*/
+
+ // ZT-Spruch ist bereits im Tip-Cache enthalten, dann direkt ausgeben.
+ if ( pointerp(ret) )
+ return ret;
+
+ // ZT-Spruch auf grundlegende syntaktische Korrektheit pruefen:
+ // explode() liefert ein Array mit 2 Elementen, wenn die Ende-Markierung
+ // vorhanden und somit das File diesbezueglich korrekt aufgebaut ist.
+ string *tip=explode( read_file(TIPS(num+".zt"))||"", "%%%%%" );
+ if (sizeof(tip) >= 2) {
+ // Der erste Eintrag in dem Array ist dann der Spruch. Wenn dieser
+ // die Trenn-Markierung enthaelt, entstehen hier 2 Hinweise, ansonsten
+ // bleibt es bei einem.
+ tipmap[num] = explode(tip[0], "XXXXX\n");
+ return tipmap[num];
+ }
+ return POT_NO_SUCH_FILE;
+}
+
+// Datenkonvertierung alte Alists => Mappings
+/*mapping ConvertData() {
+ //if ( !secure()) ) return;
+ // all_rooms ist eine Alist der Form ({ string *room, int *number })
+ // als 3. Eintrag schreiben wir ueberall -1 rein, das wird spaeter durch die
+ // Listennummer des betreffenden ZTs ersetzt und erlaubt einen einfachen
+ // Check auf Datenkonsistenz.
+ potions = mkmapping( all_rooms[1],
+ all_rooms[0],
+ allocate(sizeof(all_rooms[0]), -1) );
+ // Alle Listen ablaufen
+ foreach(int *list: active_rooms ) {
+ // Alle ZTs dieser Liste ablaufen und die entsprechende Listennummer in
+ // der neuen Datenstruktur setzen.
+ foreach(int num: list) {
+ potions[num,POT_LISTNO] = member(active_rooms,list);
+ }
+ }
+ // inactive_rooms ist eine Alist der gleichen Form. Die Schleife addiert
+ // einfach alle Eintraege darin auf, so dass ein Int-Array entsteht, das
+ // alle inaktiven ZT-Nummern enthaelt; das allerdings nur fuer Listen mit
+ // mindestens einem Element.
+ foreach(int *list : inactive_rooms ) {
+ if ( sizeof(list) ) {
+ inactive += list;
+ // Alle ZTs dieser Liste ablaufen und die entsprechende Listennummer in
+ // der neuen Datenstruktur setzen.
+ foreach(int num : list) {
+ potions[num,POT_LISTNO] = member(inactive_rooms,list);
+ }
+ }
+ // unify array
+ inactive = m_indices(mkmapping(inactive));
+ }
+ mapping invalid = ([ POT_INVALID_LIST_NUMBER : ({}) ]);
+ walk_mapping(potions, function void (int pnum, string path, int listno) {
+ if ( listno == -1 )
+ invalid[POT_INVALID_LIST_NUMBER] += ({pnum});
+ });
+ return sizeof(invalid[POT_INVALID_LIST_NUMBER]) ? invalid : potions;
+}*/
+