blob: 595dd9b640940a0ef101f016bca9a5cc043ec1c9 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001#pragma strict_types
2#pragma no_clone
3#pragma no_shadow
4#pragma no_inherit
5#pragma verbose_errors
6#pragma combine_strings
7#pragma rtt_checks
8#pragma pedantic
9#pragma warn_deprecated
10
11#include "/secure/config.h"
12#include "/secure/wizlevels.h"
13
14#define MAX_ROOMS_PER_LIST 10
15
16// die div. Speicherorte und Pfade
17#define SAVEFILE "/secure/ARCH/potions"
18#define TIPS(x) "/secure/ARCH/ZT/"+x
19#define ORAKEL "/room/orakel"
20#define POTIONTOOL "/obj/tools/ptool"
21
22// Fuer die Dump-Funktion
23#define POTIONDUMP "/secure/ARCH/POTIONS.dump"
24#define DUMP(str) write_file(POTIONDUMP, str)
25
26// Modifikationen loggen. "event" ist eins der Events aus der Liste:
27// ADD_POTION, ACTIVATE, DEACTIVATE, MODIFY_PATH, MODIFY_LISTNO
28#define LOGFILE_MOD "/log/ARCH/POTIONS_MOD.log"
29#define MODLOG(event,num,data) write_file(LOGFILE_MOD, \
30 sprintf("%17s %-14s %-3d sEUID %s %s\n", \
31 strftime("%Y-%b-%d %R"), event, num, secure_euid(), data) )
32
33// Indizierungs-Konstanten fuer das potions-Mapping
34#define POT_ROOMNAME 0
35#define POT_LISTNO 1
36
37// Konstanten fuer div. Rueckgabewerte. Keine 0, da das eine gueltige ZT-ID
38// sein kann und abfragende Objekte verwirren koennte.
39#define POT_IS_ACTIVE 1
40#define POT_ACCESS_DENIED -1
41#define POT_WRONG_DATATYPE -2
42#define POT_NO_SUCH_ROOM -3
43#define POT_ALREADY_REGISTERED -4
44#define POT_INVALID_POTION -5
45#define POT_NO_SUCH_FILE -6
46#define POT_INVALID_LIST_NUMBER -7
47#define POT_ALREADY_ACTIVE -8
48#define POT_ALREADY_INACTIVE -9
49#define POT_NOT_INACTIVE -10
50#define POT_NOT_ACTIVE -11
51
52// Zaehler fuer den als naechsten anzulegenden ZT
53private int nextroom;
54
55// Liste aller ZTs einschl. inaktive, ([ int num : string room; int list ])
56private mapping potions = ([]);
57
58// Liste der inaktiven ZTs, ({ int num })
59private int *inactive = ({});
60
61// Cache mit den einzelnen Listen, ([ int list : int *potionlist ])
62private nosave mapping lists;
63
64// reverse_table Lookup Cache, ([string room: int number])
65private nosave mapping reverse_table = ([]);
66
67// Cache fuer die bereits von der Platte eingelesenen ZTs, um Plattenzugriffe
68// insbesondere beim Erzeugen der Tipliste durch das Orakel zu reduzieren.
69private nosave mapping tipmap = ([]);
70
71int ActivateRoom(string room);
72
73private int secure() {
74 return (!process_call() && ARCH_SECURITY);
75}
76
77mixed QueryPotionData(int num) {
78 if ( !secure() )
79 return POT_ACCESS_DENIED;
80 return ([num : potions[num,0]; potions[num,1] ]);
81}
82
83int *QueryInactivePotions() {
84 return copy(inactive);
85}
86
87private void RebuildCache() {
88 // Cache invalidieren; vor-initialisiert zur Beschleunigung des Rebuilds.
89 lists = ([0:({}),1:({}),2:({}),3:({}),4:({}),5:({}),6:({}),7:({})]);
Arathorn001e9d72018-09-01 12:42:34 +020090 reverse_table = ([]);
MG Mud User88f12472016-06-24 23:31:02 +020091 foreach (int num, string room, int list : potions) {
Arathorna586b162018-09-01 12:43:11 +020092 m_add(reverse_table, room, num);
MG Mud User88f12472016-06-24 23:31:02 +020093 lists[list] += ({num});
94 }
95 return;
96}
97
98int QueryActive(mixed potion) {
99 if ( extern_call() && !secure() )
100 return POT_ACCESS_DENIED;
101 int ret;
102 // Wenn nach dem Pfad des ZTs gefragt wird, diesen zuerst in die Nummer
103 // umwandeln durch Lookup im reverse_table Cache
104 if ( stringp(potion) ) {
105 potion = reverse_table[potion];
106 }
107 // Ein ZT ist aktiv, wenn er in der Liste potions steht und nicht in der
108 // Liste der inaktiven ZTs (inactive) steht. Dann dessen Nummer
109 // zurueckgeben; inaktive zuerst pruefen, weil die inaktiven auch in
110 // "potions" enthalten sind und somit alle ZTs ausser den ungueltigen
111 // als aktiv gemeldet wuerden.
112 if ( member(inactive,potion)>-1 )
113 ret = POT_NOT_ACTIVE;
114 else if ( member(potions,potion) && potions[potion,POT_LISTNO]!=-1 )
115 ret = potion;
116 // ansonsten ist das kein gueltiger ZT
117 else
118 ret = POT_INVALID_POTION;
119 return ret;
120}
121
122private void save_info() {
123 save_object(SAVEFILE);
124}
125
126protected void create() {
127 seteuid(getuid(this_object()));
128 if ( !restore_object(SAVEFILE) ) {
129 // Fehler ausgeben, damit es jemand merkt. Dennoch wird jetzt im Anschluss
130 // der Cache neu aufgebaut (damit die Datenstrukturen gueltig sind).
131 catch(raise_error("Potionmaster: no/corrupt savefile! Reinitializing.\n"); publish);
132 }
133 RebuildCache();
134}
135
136int AddPotionRoom(string room, int list) {
137 if ( !secure() )
138 return POT_ACCESS_DENIED;
139 // Neuer Raum muss ein gueltiger String sein.
140 if ( !stringp(room) )
141 return POT_WRONG_DATATYPE;
142 if ( !intp(list) || list<0 || list>7 )
143 return POT_WRONG_DATATYPE;
144
145 // Pfad mit Hilfe des Masters vervollstaendigen (+, ~ etc. ersetzen)
146 room=(string)master()->_get_path(room,0);
147
148 // Datei mit dem ZT-Spruch muss existieren.
149 if ( file_size( TIPS(to_string(nextroom)+".zt") ) < 0 ) {
150 raise_error("Potionmaster: Tipfile missing, please create "+
151 to_string(nextroom)+".zt");
152 return POT_NO_SUCH_FILE;
153 }
154 // Neuer Raum darf noch nicht in der Liste enthalten sein.
155 if ( member(m_values(potions,POT_ROOMNAME), room)!=-1)
156 return POT_ALREADY_REGISTERED;
157 // Neuer Raum muss ladbar sein.
158 if ( catch(load_object(room); publish) )
159 return POT_NO_SUCH_ROOM;
160
161 // Jetzt kann's endlich losgehen, Raum eintragen, nextroom hochzaehlen
Arathorna586b162018-09-01 12:43:11 +0200162 m_add(potions, nextroom, room, list);
MG Mud User88f12472016-06-24 23:31:02 +0200163 MODLOG("ADD_POTION", nextroom, room);
164 // Neu eingetragene ZTs werden auch gleich aktiviert; ActivateRoom()
165 // baut den Cache selbst neu auf, daher kann das hier entfallen.
166 ActivateRoom(room);
167 nextroom++;
168 save_info();
169 return nextroom;
170}
171
172int ChangeRoomPath(string old, string new) {
173 if ( !secure() )
174 return POT_ACCESS_DENIED;
175
176 // beide Pfade muessen gueltige Strings sein
177 if ( !stringp(old) || !stringp(new) )
178 return POT_WRONG_DATATYPE;
179
180 // Pfad mit Hilfe des Masters vervollstaendigen (+, ~ etc. ersetzen)
181 old=(string)master()->_get_path(old,0);
182 new=(string)master()->_get_path(new,0);
183
184 // Der neue Raum darf nicht bereits eingetragen sein, ...
185 if ( member(reverse_table,new) )
186 return POT_ALREADY_REGISTERED;
187 // ... und der alte Raum muss noch eingetragen sein.
188 if ( !member(reverse_table,old) )
189 return POT_NO_SUCH_ROOM;
190 // Neuer Raum muss ladbar sein.
191 if (catch(load_object(new);publish))
192 return POT_NO_SUCH_ROOM;
193
194 // Aktuelle ZT-Nummer des alten Pfades ermitteln
195 int num = reverse_table[old];
196 // Pfad aendern, Cache neubauen und Savefile speichern
197 potions[num,POT_ROOMNAME] = new;
198 RebuildCache();
199 save_info();
200 MODLOG("MODIFY_PATH", num, old+" => "+new);
201 return num;
202}
203
204int ActivateRoom(string room) {
205 if ( !secure() )
206 return POT_ACCESS_DENIED;
207 // Aktuelle ZT-Nummer ermitteln. Etwas umstaendlich, da im Fehlerfall -1
208 // benoetigt wird.
209 int num = member(reverse_table,room) ? reverse_table[room] : -1;
210 // Nummer muss existieren
211 if ( num == -1 )
212 return POT_INVALID_POTION;
213 // ZT ist nicht inaktiv, dann kann man ihn auch nicht aktivieren.
214 if ( member(inactive, num)==-1 )
215 return POT_ALREADY_ACTIVE;
216 inactive -= ({num});
217 RebuildCache();
218 save_info();
219 MODLOG("ACTIVATE", num, room);
220 return num;
221}
222
223int SetListNr(string room, int list) {
224 if ( !secure() )
225 return POT_ACCESS_DENIED;
226 // Aktuelle ZT-Nummer ermitteln. Etwa umstaendlich, da im Fehlerfall -1
227 // benoetigt wird.
228 int num = member(reverse_table,room) ? reverse_table[room] : -1;
229 // Nummer muss existieren
230 if ( num == -1 )
231 return POT_INVALID_POTION;
232 // Listennummer muss zwischen 0 und 7 liegen.
233 if ( list < 0 || list > 7 )
234 return POT_INVALID_LIST_NUMBER;
235
236 // alte Nummer aufschreiben zum Loggen.
237 int oldlist = potions[num,POT_LISTNO];
238 // Listennummer in der ZT-Liste aktualisieren.
239 potions[num,POT_LISTNO] = list;
240 RebuildCache();
241 save_info();
242 MODLOG("MODIFY_LISTNO", num, oldlist+" -> "+list);
243 return num;
244}
245
246int DeactivateRoom(string room) {
247 if ( !secure() )
248 return POT_ACCESS_DENIED;
249 // Aktuelle ZT-Nummer ermitteln. Etwa umstaendlich, da im Fehlerfall -1
250 // benoetigt wird.
251 int num = member(reverse_table,room) ? reverse_table[room] : -1;
252 // Nummer muss existieren
253 if ( num == -1 )
254 return POT_INVALID_POTION;
255 // ZT darf nicht bereits inaktiv sein
256 if ( member(inactive,num)>-1 )
257 return POT_ALREADY_INACTIVE;
258 inactive += ({num});
259 RebuildCache();
260 save_info();
261 MODLOG("DEACTIVATE", num, room);
262 return num;
263}
264
265private int *_create_list(int listno, int anz) {
266 int *list = ({});
267 // Listenerzeugung lohnt nur dann, wenn mind. 1 Eintrag gefordert wird und
268 // die Listennummer im gueltigen Bereich zwischen 0 und 7 ist.
269 if ( anz>0 && listno>=0 && listno<=7 ) {
270 int *tmp = lists[listno] - inactive;
271 // Wenn die Listengroesse maximal genauso gross ist wie die angeforderte
272 // Anzahl, kann diese vollstaendig uebernommen werden.
273 if ( sizeof(tmp) <= anz ) {
274 list = tmp;
275 } else { // ansonsten soviele Eintraege erzeugen wie angefordert
276 foreach(int i: anz) {
277 int j=random(sizeof(tmp));
278 list += ({tmp[j]});
279 tmp -= ({tmp[j]});
280 }
281 }
282 }
283 return list;
284}
285
286mixed *InitialList() {
287 mixed *list=({});
288 foreach(int i : 8)
289 list+=_create_list(i,MAX_ROOMS_PER_LIST);
290 return list;
291}
292
293// Aufrufe aus den Spielershells und dem Potiontool sind erlaubt
294int HasPotion(object room) {
295 if ( !query_once_interactive(previous_object()) &&
296 load_name(previous_object()) != POTIONTOOL )
297 return POT_ACCESS_DENIED;
298 return objectp(room) ? reverse_table[object_name(room)] : POT_NO_SUCH_ROOM;
299}
300
301// Listennummer ermitteln, in der der ZT num enthalten ist.
302// Auch inaktive ZTs werden als zu einer Liste gehoerig gemeldet.
303int GetListByNumber(int num) {
304 return member(potions,num) ? potions[num,POT_LISTNO] : POT_INVALID_POTION;
305}
306
307// Listennummer ermitteln, in der der inaktive ZT num enthalten ist.
308// Da alle Zaubertraenke in einer Gesamtliste enthalten sind, kann direkt das
309// Resultat von GetListByNumber() zurueckgegeben werden.
310int GetInactListByNumber(int num) {
311 if ( member(inactive,num) > -1 )
312 return GetListByNumber(num);
313 else
314 return POT_NOT_INACTIVE;
315}
316
317// Wird nur von /obj/tools/ptool aufgerufen. Wenn der Aufruf zugelassen wird,
318// erfolgt von dort aus ein ChangeRoomPath(), und dort wird bereits geloggt.
319// Erzmagier duerfen auch aufrufen.
320mixed GetFilenameByNumber(int num) {
321 if ( extern_call() &&
322 object_name(previous_object()) != POTIONTOOL &&
323 !ARCH_SECURITY )
324 return POT_ACCESS_DENIED;
325 if ( !member(potions, num) )
326 return POT_INVALID_POTION;
327 return potions[num,POT_ROOMNAME];
328}
329
330// Wird vom Spielerobjekt gerufen; uebergeben werden der Raum, der initial
331// FindPotion() aufrief, die ZT-Liste des Spielers sowie die Liste bereits
332// gefundener ZTs.
333//
334// In std/player/potion.c ist sichergestellt, dass room ein Objekt ist und
335// dass die Listen als Int-Arrays uebergeben werden.
336//
337// Es werden aus historischen Gruenden (noch) beide ZT-Listen uebergeben,
338// aber es muss nur knownlist ueberprueft werden, da diese eine Teilmenge
339// von potionlist ist und somit jeder ZT in ersterer auf jeden Fall auch
340// in letzterer enthalten sein _muss_.
341int InList(object room, int *potionlist, int *knownlist) {
342 if ( !query_once_interactive(previous_object()) && !secure() )
343 return 0; //POT_ACCESS_DENIED;
344 int num = QueryActive(object_name(room));
345
346 // Zunaechst keinen Fehlercode zurueckgeben, weil das Spielerobjekt
347 // damit im Moment noch nicht umgehen kann.
348 if ( num < 0 )
349 return 0; //POT_INVALID_POTION;
350
351 return (member(knownlist,num)>-1);
352}
353
354// Wird vom Spielerobjekt gerufen, um einen gefundenen ZT aus dessen ZT-
355// Listen austragen zu lassen. Das Spielerobjekt stellt sicher, dass room
356// ein Objekt ist, und dass [known_]potionrooms Arrays sind.
357//
358// ACHTUNG! Sowohl potionrooms, als auch known_potionrooms sind die
359// Originaldaten aus dem Spielerobjekt, die hier ALS REFERENZ
360// uebergeben werden! Vorsicht bei Aenderungen an der Funktion!
361//
362// Wenn std/player/potions.c geaendert wird, ist diese Funktion obsolet
363// => ggf. komplett rauswerfen. Dann kann auch der Fehlercode in InList()
364// reaktiviert und deren Parameter korrigiert werden
365void RemoveList(object room, int* potionrooms, int* known_potionrooms) {
366 // ZT ist aktiv, das wurde bereits in InList() geprueft, das vor dem
367 // Aufruf von RemoveList() aus dem Spielerobjekt gerufen wird. Daher reicht
368 // es aus, die ZT-Nummer aus dem reverse_table Lookup zu holen.
369 int num = reverse_table[object_name(room)];
370 int tmp = member(potionrooms, num);
371 potionrooms[tmp] = -1;
372 tmp = member(known_potionrooms, num);
373 known_potionrooms[tmp] = -1;
374 return;
375}
376
377#define LISTHEADER "################## Liste %d ################## (%d)\n\n"
378#define INACT_HEADER "################## Inaktiv ################## (%d)\n\n"
379
380int DumpList() {
381 if ( !secure() )
382 return POT_ACCESS_DENIED;
383 // Zuerst die Caches neu aufbauen, um sicherzustellen, dass die Listen
384 // aktuell sind.
385 RebuildCache();
386 // Dumpfile loeschen
387 rm(POTIONDUMP);
388 // Alle Listen der Reihe nach ablaufen. Es wird in jedem Schleifendurchlauf
389 // einmal auf die Platte geschrieben. Das ist Absicht, weil es speicher-
390 // technisch sehr aufwendig waere, die Ausgabedaten stattdessen erst in
391 // einer Variablen zu sammeln und dann wegzuschreiben.
392 foreach(int *listno : sort_array(m_indices(lists),#'>)) {
393 // Zuerst den Header der ensprechenden Liste schreiben.
394 DUMP(sprintf(LISTHEADER, listno, sizeof(lists[listno])));
395 // Dann alle Potions der Liste durchgehen
396 foreach(int potion : lists[listno]) {
397 // Wenn der ZT inaktiv ist, ueberspringen wir den.
398 if ( member(inactive,potion)>-1 )
399 continue;
400 // Raumpfad aus der Gesamtliste holen.
401 string str = potions[potion,POT_ROOMNAME];
402 // Wenn der nicht existiert, entsprechenden Hinweis dumpen, ansonsten
403 // den Raumnamen.
404 //ZZ: ich finde ja, wir sollten sicherstellen, dass das nicht mehr
405 //passiert. Ist das mit dem AddPotionRoom oben nicht schon so?
406 if ( !stringp(str) || !sizeof(str) )
407 str="KEIN RAUM ZUGEORDNET!!!";
408 DUMP(sprintf("%3d. %s\n", potion, str));
409 }
410 // 2 Leerzeilen zwischen jeder Gruppe einfuegen
411 DUMP("\n\n");
412 }
413 // Zum Schluss den Header der Inaktiv-Liste schreiben.
414 DUMP(sprintf(INACT_HEADER, sizeof(inactive)));
415 // Dann alle inaktiven Potions ablaufen
416 foreach(int i : inactive) {
417 //ZZ: sonst muesste man hier wohl auch auf den fehlenden Raum pruefen.
418 DUMP(sprintf("%3d. (Liste %d) %s\n", i, GetListByNumber(i),
419 potions[i,POT_ROOMNAME]));
420 }
421 return 1;
422}
423
424//ORAKEL-Routinen
425
426/* Aufbau eines Tips:
427
428 Tralala, lalala
429 Dideldadeldum
430 Huppsdiwupps
431 XXXXX
432 Noch ein zweiter Tip
433 Dingeldong
434 %%%%%
435
436 Die Prozentzeichen sind das Endezeichen, ab hier koennen eventuelle
437 Kommentare stehen. Die 5 X sind das Trennzeichen zwischen zwei Tips
438 zum selben ZT.
439*/
440
441// TIPLOG() derzeit unbenutzt
442//#define LOGFILE_READ "ARCH/POTIONS_TIP_READ.log"
443//#define TIPLOG(x) log_file(LOGFILE_READ, x)
444
445mixed TipLesen(int num) {
446 string *ret = tipmap[num];
447 //Funktion darf nur vom Orakel und EM+ gerufen werden.
448 if ( previous_object() != find_object(ORAKEL) && !secure() )
449 return POT_ACCESS_DENIED;
450 // Derzeit kein Log, da diese Informationen tendentiell eher
451 // uninteressant sind.
452 // Timestamp ist vom Format "2012-Okt-03 14:18"
453 /*TIPLOG(sprintf("%s ZT-Tip gelesen: %d von TP: %O, PO: %O, TI: %O\n",
454 strftime("%Y-%b-%d %R"), num, this_player(), previous_object(),
455 this_interactive()));*/
456
457 // ZT-Spruch ist bereits im Tip-Cache enthalten, dann direkt ausgeben.
458 if ( pointerp(ret) )
459 return ret;
460
461 // ZT-Spruch auf grundlegende syntaktische Korrektheit pruefen:
462 // explode() liefert ein Array mit 2 Elementen, wenn die Ende-Markierung
463 // vorhanden und somit das File diesbezueglich korrekt aufgebaut ist.
464 string *tip=explode( read_file(TIPS(num+".zt"))||"", "%%%%%" );
465 if (sizeof(tip) >= 2) {
466 // Der erste Eintrag in dem Array ist dann der Spruch. Wenn dieser
467 // die Trenn-Markierung enthaelt, entstehen hier 2 Hinweise, ansonsten
468 // bleibt es bei einem.
469 tipmap[num] = explode(tip[0], "XXXXX\n");
470 return tipmap[num];
471 }
472 return POT_NO_SUCH_FILE;
473}
474
475// Datenkonvertierung alte Alists => Mappings
476/*mapping ConvertData() {
477 //if ( !secure()) ) return;
478 // all_rooms ist eine Alist der Form ({ string *room, int *number })
479 // als 3. Eintrag schreiben wir ueberall -1 rein, das wird spaeter durch die
480 // Listennummer des betreffenden ZTs ersetzt und erlaubt einen einfachen
481 // Check auf Datenkonsistenz.
482 potions = mkmapping( all_rooms[1],
483 all_rooms[0],
484 allocate(sizeof(all_rooms[0]), -1) );
485 // Alle Listen ablaufen
486 foreach(int *list: active_rooms ) {
487 // Alle ZTs dieser Liste ablaufen und die entsprechende Listennummer in
488 // der neuen Datenstruktur setzen.
489 foreach(int num: list) {
490 potions[num,POT_LISTNO] = member(active_rooms,list);
491 }
492 }
493 // inactive_rooms ist eine Alist der gleichen Form. Die Schleife addiert
494 // einfach alle Eintraege darin auf, so dass ein Int-Array entsteht, das
495 // alle inaktiven ZT-Nummern enthaelt; das allerdings nur fuer Listen mit
496 // mindestens einem Element.
497 foreach(int *list : inactive_rooms ) {
498 if ( sizeof(list) ) {
499 inactive += list;
500 // Alle ZTs dieser Liste ablaufen und die entsprechende Listennummer in
501 // der neuen Datenstruktur setzen.
502 foreach(int num : list) {
503 potions[num,POT_LISTNO] = member(inactive_rooms,list);
504 }
505 }
506 // unify array
507 inactive = m_indices(mkmapping(inactive));
508 }
509 mapping invalid = ([ POT_INVALID_LIST_NUMBER : ({}) ]);
510 walk_mapping(potions, function void (int pnum, string path, int listno) {
511 if ( listno == -1 )
512 invalid[POT_INVALID_LIST_NUMBER] += ({pnum});
513 });
514 return sizeof(invalid[POT_INVALID_LIST_NUMBER]) ? invalid : potions;
515}*/
516