blob: e83a2417fe3b91b2e10808027f6a63409eddc14c [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001//--------------------------------------------------------------------------------
2// Name des Objects: Aufbewahrungtruhe fuer Autoload-Objekte
3//
4// Magier: Zesstra
5//--------------------------------------------------------------------------------
6#pragma strong_types,rtt_checks
7
8#include "schrankladen.h"
9inherit LADEN("swift_std_container");
10
11//#include <ansi.h>
12#include <class.h>
13#include <wizlevels.h>
14#include "/d/seher/haeuser/haus.h"
15
16#define IDS 0
17#define NAME 1
18#define ALDATA 2
19
20#define LOG(x,y) log_file(x,sprintf("%s [%s, %O, %O]: %s\n",dtime(time()),\
21 (uuid?uuid:" "),PL,object_name(ME),y))
22#define STORELOG(x) LOG("zesstra/ALTRUHE_STORE.log",x)
23#define PICKLOG(x) LOG("zesstra/ALTRUHE_PICK.log",x)
24
25#define ITEMLOG(x) log_file("zesstra/ALTRUHE_ITEMS.log",\
26 sprintf("%s [%O]: %s\n",dtime(time()),\
27 this_interactive()||PL,x))
28
29#define ERRLOG(x) LOG("zesstra/ALTRUHE.ERR",x)
30
31#undef BS
32#define BS(x) break_string(x,78)
33
34#define VERSION_OBJ "5"
35
36#ifdef MAINTAINER
37#undef MAINTAINER
38#endif
39#define MAINTAINER ({"zesstra"})
40
41// Savefile der Blueprint
42#ifdef SAVEFILE
43#undef SAVEFILE
44#endif
45#define SAVEFILE __FILE__[..<3]
46
47#define DEBUG(x) if (funcall(symbol_function('find_player),MAINTAINER[0]))\
48 tell_object(funcall(symbol_function('find_player),MAINTAINER[0]),\
49 "ALTruhe: "+x+"\n")
50
51#define ACCESS (my_secure_level() >= ARCH_LVL)
52
53// Diese 4 sind fuer die Blueprint (Master aller Truhen)
54mapping whitelist=([]); // erlaubte Autoloader, alle anderen nicht erlaubt.
55mapping blacklist=([]); // bereits explizit durch EM+ abgelehnte Autoloader
56mapping vorschlaege=m_allocate(1,2); // vorschlaege der Spieler
57mapping data=([]); // hier speichert die BP alle Daten der Truhen
58 // WICHTIG: dieses Mapping wird in /secure/memory
59 // abgelegt und muss auch beim Neuladen ggf. von dort
60 // wieder abgeholt werden. Ausserdem teilen sich alle
61 // Truhen eines Spielers und diese Blueprint die Mappings
62 // darin, damit Aenderungen sofort in allen Truhen und dem
63 // Master bekannt sind.
64
65/* die einzelnen Truhen speichern in autoloader. Format:
66 3 Werte pro Key, Keys sind die Namen der Blueprints der Objekte:
67 ([<blueprint>: <P_IDS>; <ob->name()>; <P_AUTOLOADOBJ> ])
68 */
69nosave mapping autoloader=m_allocate(1,3);
70nosave string uuid; // UUID des Eigentuemers
71nosave object ob_in_bewegung; // uebler Hack eigentlich. :-(
72
73void NotifyInsert(object ob, object oldenv);
74static mapping QueryData();
75static mapping SetData(mixed data);
76protected void save_me();
77protected void check_content();
78
79nomask private int my_secure_level(); //Args.
80
81protected void create() {
82
83 // Ja, es is Absicht, dass das create der BP erst spaeter abgebrochen wird!
84 swift_std_container::create();
85
86 seteuid(getuid(ME));
87
88 // falls dies die BP ist:
89 // 1. das Savefile zu laden.
90 // 2. versuchen, die truhendaten von /secure/memory zu holen, damit nach dem
91 // Neuladen der Master und die Client immer nach _dieselben_ Mappings
92 // haben.
93 if (!clonep(ME))
94 {
95 // Savefile restaurieren (auch wenn data im Memory ist, muss das sein,
96 // damit die anderen Variablen wieder eingelesen sind).
97 restore_object(SAVEFILE);
98 // jetzt Daten aus Memory holen, sofern verfuegbar
99 mapping tmp = "/secure/memory"->Load("truhendaten");
100 if (mappingp(tmp))
101 {
102 // Daten aus Savefile durch die vom Memory ersetzen
103 data = tmp;
104 }
105 else
106 {
107 // Keine Daten in Memory. Jetzt in jedem Fall den Pointer auf das Mapping data in
108 // /secure/memory ablegen
109 if ("/secure/memory"->Save("truhendaten",data) != 1)
110 {
111 raise_error("Could not save memory to /secure/memory.");
112 }
113 }
114 }
115 else
116 {
117 // brauchen Clones nicht.
118 set_next_reset(-1);
119 data=0; // brauchen Clones nicht.
120 }
121
122 SetProp(P_SHORT, "Eine kleine, magische Holztruhe");
123 SetProp("cnt_version_obj", VERSION_OBJ);
124 SetProp(P_NAME, "Holztruhe");
125 SetProp(P_GENDER, FEMALE);
126 SetProp(P_NAME_ADJ,({"klein", "magisch"}));
127 SetProp(P_LONG, BS(
128 "Die kleine Holztruhe ist aus stabilem Eichenholz gefertigt. Eigentlich "
129 "saehe sie recht unscheinbar aus, waeren da nicht die vielen kunstvollen "
130 "Runen an den Seiten und auf dem Deckel.")
131 +"@@cnt_status@@");
132
Arathorn585d8442021-03-06 13:38:39 +0100133 SetProp(P_LEVEL, 1000); // wehrt bestimmte Zauber ab
134
MG Mud User88f12472016-06-24 23:31:02 +0200135 AddId(({"autoloadertruhe", "holztruhe", "truhe"}));
136
137 // den Rest vom Create braucht die BP nicht.
138 if (!clonep(ME)) return;
139
140 SetProp(P_LOG_FILE,"zesstra/ALTRUHE.rep");
141
142 SetProp(P_WEIGHT, 3000); // Gewicht 5 Kg
143 // die drei hier sind in diesme Fall eigentlich voellig ohne Bedeutung
144 SetProp(P_MAX_WEIGHT, 1000000); // Es passen fuer 1000 kg Sachen rein.
145 SetProp(P_WEIGHT_PERCENT, 100);
146 SetProp(P_MAX_OBJECTS, 100); // sind eh immer 0 echte Objekte drin.
147
148 SetProp(P_VALUE, 0); // Kein materieller Wert. Ist eh nicht verkaufbar.
149 SetProp(P_NOBUY, 1); // Wird im Laden zerstoert, falls er verkauft wird.
150 SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
151 SetProp(P_MATERIAL, ([MAT_OAK:49, MAT_MISC_MAGIC:50, MAT_JOFIUM: 1]) );
152 SetProp(P_INFO, BS("In diese stabile Truhe kannst Du bestimmte "
153 "Autoload-Objekte hineinlegen und sie lagern, wenn Du sie gerade "
154 "nicht brauchst. (Mit 'deponiere <was>' kannst Du etwas in der "
155 "Truhe zur Aufbewahrung deponieren, mit 'entnehme <was> oder "
156 "'nimm <was> aus truhe' kannst Du einen Gegenstand wieder "
157 "herausnehmen.)"));
158
159 // Prop fuer rebootfeste Moebel
160 // Der endgueltige Wert (UUID des Spielers) wird per SetBesitzer() gesetzt,
161 // sobald die Truhe das erste Mal in ein Seherhaus bewegt wurde.
162 SetProp(H_FURNITURE, 1);
163
164 AD(({"platz","groesse"}),
165 BS("Die Truhe ist recht klein, aber merkwuerdigerweise "
166 "scheint sie viel mehr an Gegenstaenden aufnehmen zu koennen, als "
167 "es von ihrer Groesse her scheint."));
168 AD(({"gegenstand","gegenstaende"}),
169 BS("Die Truhe scheint zwar vielen Gegenstaenden Platz zu bieten, aber "
170 "dafuer nimmt sie nicht jeden Gegenstand auf."));
171 AD(({"holz","eichenholz"}),
172 BS("Das Eichenholz ist sehr stabil. Es ist ganz glatt geschliffen und "
173 "traegt viele kunstvolle Runen in sich."));
174 AD(({"seiten","seite"}),
175 BS("Die Truhe hat 4 Seiten, die allesamt mit Runen verziert sind."));
176 AD(({"boden"}),
177 BS("Der Boden der Truhe traegt keinerlei Runen."));
178 AD(({"deckel"}),
179 BS("Auch der Deckel der Truhe ist mit Runen verziert."));
180 AD(({"runen","rune"}),
181 BS("Die Runen bedecken alle 4 Seiten und den Deckel der Truhe. Man "
182 "hat sie zweifellos in muehsamer Arbeit aus dem harten Holz "
183 "geschnitzt. Anschliessend wurden sie offenbar mit einem "
184 "Metall gefuellt. Du verstehst zwar ueberhaupt nicht, was "
185 "die Runen bedeuten, aber sie sind bestimmt magisch, denn wann immer "
186 "Du den Deckel oeffnest oder schliesst oder Gegenstaende hineinlegst "
187 "oder herausnimmst, leuchten die Runen hell auf."));
188 AD(({"metall"}),
189 BS("Was das wohl fuer ein Metall sein mag? Zweifellos hat es auch was "
190 "mit Magie zu tun."));
191 AD(({"magie"}),
192 BS("In dieser Truhe scheint viel Magie zu stecken, wenn selbst ein "
193 "Weltuntergang ihr nichts anhaben kann."));
194 AD(({"arbeit","fertigung"}),
195 BS("Die Fertigung dieser Truhe muss sehr aufwendig gewesen sein. "
196 "Kein Wunder, dass die Truhe so teuer ist."));
197 AD(({"wunder"}),
198 BS("Ein Wunder scheint es zu sein."));
199 AD(({"weltuntergang","armageddon"}),
200 BS("Dir schaudert beim Gedanken an den Weltuntergang."));
201 AD(({"gedanken"}),
202 BS("Denk doch lieber an was anderes..."));
203
204 AddCmd("deponier|deponiere&@PRESENT","cmd_deponiere",
205 "Was moechtest Du in der Eichenholztruhe deponieren?");
206 AddCmd("entnimm|entnehme|entnehm","cmd_entnehmen");
207
208 // bei dieser Truhe waere das Erlauben voellig sinnlos. ;-)
209 RemoveCmd(({"serlaube"}));
210
211}
212
213// keine Truhen zerstoeren, die irgendeinen INhalt haben.
214int zertruemmern(string str) {
215 // aus swift_std_container
216 string nf_str;
217 nf_str="Syntax: zertruemmer [Objekt-Id]\n"
218 +"Bsp.: zertruemmer "+QueryProp(P_IDS)[1]+"\n";
219 notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
220 if(!str) return 0;
221 notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
222 if(present(str)!=TO) // Ueberpruefe, ob auch dieses Objekt gemeint ist!
223 return 0;
224 if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
225 {
226 write( BS("Nur "+QueryHausbesitzer()+" darf "+name(WEN,1)+" zertruemmern!"));
227 return 1;
228 }
229 // Objekte enthalten? Wenn ja, abbruch.
230 if (sizeof(autoloader)) {
231 tell_object(PL,BS("Du willst gerade zum Schlag ausholen, um "
232 +name(WEN,1)+ " zu zertruemmern, als Dir einfaellt, dass "
233 +QueryPronoun(WER)+ " ja gar nicht leer ist! Nene, wer weiss, ob "
234 "Du das nicht noch brauchen koenntest."));
235 return 1;
236 }
237 // sonst geerbten Kram ausfuehren.
238 return ::zertruemmern(str);
239}
240
241// Zesstra, 1.7.07, fuers Hoerrohr
242string GetOwner() {return "zesstra";}
243
244// Prueft das Objekt auf Eignung fuer die Truhe, speichert seine Daten und
245// zerstoert es anschliessend. NODROP-Objekte koennen nur so in die Truhe
246// gelegt werden, alle anderen koennen auch mit "stecke ... in truhe"
247// deponiert werden, woraufhin ebenfalls PreventInsert() und NotifyInsert()
248// durchlaufen werden.
249protected int cmd_deponiere(string cmd, mixed args) {
250 if (!objectp(PL)) return 0;
251
252 notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
253 if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
254
255 notify_fail("Was moechtest Du in der Eichenholztruhe deponieren?\n");
256 if (!stringp(cmd) || !sizeof(cmd)
257 || !pointerp(args) || !sizeof(args)) return 0;
258 object ob=args[0];
Arathorna3352572021-04-02 16:03:47 +0200259
MG Mud User88f12472016-06-24 23:31:02 +0200260 if (!objectp(ob)) return 0;
Arathorna3352572021-04-02 16:03:47 +0200261
262 // Nur Objekte aus dem Inventar deponieren. Absichtlich nicht ueber
263 // @PUT_GET_DROP in AddCmd() geloest, damit die beiden Fehlerfaelle zwei
264 // getrennte Meldungen bewirken.
265 if (environment(ob) != PL) {
266 tell_object(PL, BS("Du musst "+ob->name(WEN,1)+"schon bei Dir haben, um "+
267 ob->QueryPronoun(WEN)+" deponieren zu koennen."));
268 return 1;
269 }
270
MG Mud User88f12472016-06-24 23:31:02 +0200271 // wuerde die Truhe das Objekt ueberhaupt aufnehmen? Fehlerausgabe durch
272 // PrevenInsert()
273 if (PreventInsert(ob)) return 1;
274 // Ausziehen... Schade, dass DoUnwear nix vernuenftiges an Rueckgabewert
275 // hat. :-(
276 if (objectp(ob->QueryProp(P_WORN))) {
277 ob->DoUnwear();
278 if (objectp(ob->QueryProp(P_WORN))) {
279 tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
280 "ausziehen!"));
281 return 1;
282 }
283 }
284 // wegstecken
285 if (objectp(ob->QueryProp(P_WIELDED))) {
286 ob->DoUnwield();
287 if (objectp(ob->QueryProp(P_WIELDED))) {
288 tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
289 "wegstecken!"));
290 return 1;
291 }
292 }
293 // NO_CHECK und Silent-Bewegung, Meldungen an den Spieler selber machen.
294 tell_object(PL, BS("Du steckst "+ ob->name(WEN,1) + " in " + name(WEN,1) +
295 "."));
296
297 tell_room(environment(),BS(PL->Name(WER) + " legt " + ob->name(WEN,0) +
298 " in " + name(WEN,1) + " hinein."),({PL}));
299 ob->move(ME, M_NOCHECK|M_SILENT);
300 return 1;
301}
302
303// alternative zum "nimm bla aus truhe". Spieler wollten was kurzes dafuer
304// haben.
305protected int cmd_entnehmen(string cmd) {
306 int res;
MG Mud User88f12472016-06-24 23:31:02 +0200307
308 if (!objectp(PL)) return 0;
309
310 notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
311 if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
312
313 notify_fail(BS("Was moechtest Du aus "+name(WEM,1)+ " entnehmen?\n"));
314 if (!stringp(cmd) || !sizeof(cmd)) return 0;
315
316 object *obs=present_objects(cmd);
317
318 if (!sizeof(obs) || !objectp(obs[0]))
319 return 0;
320
321 // NOGET ist hier bloed. So ist es zwar auch nicht richtig doll... *seufz*
322 // Die hier ist/waere aber nen uebler Hack, erstmal auskommentiert lassen.
323 // also, P_NOGET sichern.
Arathornc74a2532021-04-02 15:51:14 +0200324 /*mixed noget;
325 if (!closurep(noget=ob->Query(P_NOGET,F_QUERY_METHOD))) {
MG Mud User88f12472016-06-24 23:31:02 +0200326 noget=ob->Query(P_NOGET,F_VALUE);
327 ob->Set(P_NOGET,0,F_VALUE);
328 }
329 else {
330 ob->Set(P_NOGET,0,F_QUERY_METHOD);
331 }*/
332 // nehmen.
333 res=PL->pick_obj(obs[0]);
334 // P_NOGET zurueckschreiben. (Ja, wenn eine Closure als F_VALUE drinsteht,
335 // landet die jetzt als F_QUERY_METHOD im Objekt.
336 /*if (closurep(noget)) {
337 ob->Set(P_NOGET,noget,F_QUERY_METHOD);
338 }
339 else
340 ob->Set(P_NOGET,noget,F_VALUE);
341*/
342 return(res);
343}
344
345// Hier wird auch das PreventInsert() von der Blueprint gerufen. Das
346// erleichtert es, die Liste an erlaubten Objekten zu aendern, ohne dass man
347// alle Clones ersetzen muss.
348varargs int PreventInsert(object ob) {
349 string oname;
350 // Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
351 if (!objectp(ob) || ob_in_bewegung==ob) return 0;
352
353 oname=BLUE_NAME(ob);
354
355 // Pruefung in Clonen:
356 if (clonep(ME))
357 {
358 // nur Eigentuemer
359 if (!stringp(uuid) || !sizeof(uuid)) {
360 if (objectp(PL))
361 tell_object(PL,BS(sprintf("%s gehoert Dir nicht, daher kannst Du "
362 "auch keine Gegenstaende in %s legen.",
363 Name(WER,1),QueryPronoun(WEN))));
364 return 1;
365 }
366 if (!objectp(PL) || getuuid(PL)!=uuid) {
367 if (objectp(PL))
368 tell_object(PL,BS(sprintf("Nur %s darf Gegenstaende in %s "
369 "hineinlegen.",capitalize(explode(uuid,"_")[0]),name(WEN,1))));
370 return 1;
371 }
372 // jedes Objekt nur einmal.
373 if (member(autoloader,oname)) {
374 if (objectp(PL))
375 tell_object(PL,BS(Name(WER,1)+ " kann von einem Gegenstand jeweils "
376 "nur ein Exemplar aufnehmen, es ist aber bereits "
377 +ob->name(WER,0) + " in " + QueryPronoun(WEM) + "."));
378 return 1;
379 }
380 // jetzt Erlaubnisliste der BP fragen.
381 if (objectp(blueprint(ME)))
382 return blueprint(ME)->PreventInsert(ob);
383 else
384 return load_object(load_name(ME))->PreventInsert(ob);
385 }
386 // Ende fuer Pruefung fuer Clone.
387
388 // ab hier jetzt die Pruefung durch die BP.
389 // Keine (freigegebener) Autoloader? Hat in diesem Container nix verloren!
390 if( !ob->QueryProp(P_AUTOLOADOBJ) ) {
391 if (objectp(PL))
392 tell_object(PL,BS("In "+name(WEN,1)
393 +" kannst Du nur Autoload-Objekte hineinlegen. "));
394 return 1;
395 }
396 else if (member(blacklist,oname)) {
397 if (objectp(PL))
398 tell_object(PL,BS("In "+name(WEN,1)
399 +" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
400 +ob->Name(WER,1) + " wurde explizit als nicht erwuenscht "
401 "befunden."));
402 return 1;
403 }
404 else if (!member(whitelist,oname)) {
405 if (!member(vorschlaege,oname)) {
406 vorschlaege[oname,0]=ob->name(WER,0) || "";
407 vorschlaege[oname,1]=ob->short() || "";
408 }
409 if (objectp(PL))
410 tell_object(PL,BS("In "+name(WEN,1)
411 +" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
412 +ob->Name(WER,1) + " ist momentan nicht auf der Liste. Der "
413 "Gegenstand wurde jetzt als Vorschlag gespeichert. Bitte hab "
414 "etwas Geduld, bis sich jemand den Gegenstand angeschaut hat."));
415 // ggf. reset reaktivieren und Maintainer informieren.
416 if (!query_next_reset())
417 set_next_reset(1);
418 return 1;
419 }
420 // getragenes?
421 if (ob->QueryProp(P_WORN)) {
422 ob->DoUnwear(0);
423 if (ob->QueryProp(P_WORN)) { //ARGL. GRUMMEL.
424 if (objectp(PL))
425 tell_object(PL,BS("Willst Du "+ob->name(WEN,1) + " nicht erstmal "
426 "ausziehen?"));
427 return 1;
428 }
429 }
430 // enthaelt es irgendwelche anderen Objekte?
431 if (sizeof(all_inventory(ob))) {
432 if (objectp(PL))
433 tell_object(PL,BS(ob->Name(WER,1) + " ist nicht leer."));
434 return 1;
435 }
436
437 // andere Einschraenkungen, als hier geprueft werden, gibt es nicht.
438 return 0;
439}
440
441// hier ist das Objekt jetzt in der Truhe, d.h. Daten speichern und Objekt
442// destructen. ;)
443void NotifyInsert(object ob, object oldenv) {
444
445 // Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
446 if (!objectp(ob) || ob==ob_in_bewegung)
447 return;
448 STORELOG(sprintf("%s deponiert %s [%O], Daten: %O",getuid(PL),ob->name(WEN,0),
449 ob,ob->QueryProp(P_AUTOLOADOBJ)));
450 // noetig sind die IDs, den Namen und die AUTOLOADOBJ-Daten des Objekts.
451 // ;-)
452 autoloader[BLUE_NAME(ob),ALDATA]=ob->QueryProp(P_AUTOLOADOBJ);
453 autoloader[BLUE_NAME(ob),IDS]=ob->QueryProp(P_IDS);
454 // Objekte, die 0 als short liefern, kriegen eine Standard-Short und werden
455 // sichtbar gemacht, da sie sonst nicht wiederzufinden sind. Objekte, die
456 // unsichtbar sein sollen, duerfen nicht erlaubt werden.
457 // TODO eine ID als Short nehmen?
458 autoloader[BLUE_NAME(ob),NAME]=capitalize((ob->short()||"<Unbekannt>.\n")[..<3]);
459 // nach dem Move die realen Objekte destructen.
460 call_out(#'check_content, 0);
461 save_me();
462}
463
464// Objekt wurde entnommen, also aus der Liste der enthaltenen Autoloader
465// loeschen. Ausserdem Objekt konfigurieren. Dies wird erst hier gemacht und
466// nicht in create_object(), damit es so aehnlich wie moeglich zum clonen der
467// Autoloader beim erstellen des Spielerobjektes wird (dort wird erst bewegt,
468// dann konfiguriert).
469void NotifyLeave(object ob, object dest) {
Arathorn4b3b2d12021-04-02 15:53:48 +0200470 string oname;
MG Mud User88f12472016-06-24 23:31:02 +0200471
472 if (!objectp(ob))
473 return;
474
475 oname=BLUE_NAME(ob);
476 if (!member(autoloader,oname)) {
477 // Das sollte definitiv nicht passieren.
478 ERRLOG(sprintf("Gegenstand (%O) wurde entnommen, der nicht "
479 "gespeichert war!",ob));
480 return;
481 }
482
Arathorn4b3b2d12021-04-02 15:53:48 +0200483 PICKLOG(sprintf("Objekt (%O) wurde entnommen.",ob));
484 m_delete(autoloader,oname);
485 // speichern
486 save_me();
MG Mud User88f12472016-06-24 23:31:02 +0200487}
488
489protected void check_content() {
490 // wenn Objekte noch in der Truhe sind, also nicht erfolgreich in
491 // einen Spieler bewegt wurden, werden zerstoert. Datenverlust gibt es
492 // hierbei nicht, weil die Daten der Autoloader nur durch NotifyLeave()
493 // geloescht werden.
494 foreach(object ob: all_inventory()) {
495 ob->remove(1);
496 if (objectp(ob)) destruct(ob);
497 }
498}
499
500// hier nochmal schauen, ob das Objekt auch in den richtigen Spieler bewegt
501// wird... Falls das move dann hier abgebrochen wird, wird das Objekt im
502// naechsten HB von der Truhe zerstoert. (chk_contents())
503varargs int PreventLeave(object ob, mixed dest) {
504 object ob2;
505
506 //DEBUG(sprintf("PreventLeave(): Ob: %O, Dest: %O (%O)",
507 // ob,dest,(objectp(dest)?"Objekt":"Non-Object")));
508
509 if (!objectp(ob)) return 0;
510 // falls string uebergeben, erstmal zug. Spieler finden.
511 if (stringp(dest) && sizeof(dest))
512 dest=find_player(dest);
513
514 // richtiges INteractive? Dann darf das Objekt raus. Sonst nicht. ;-)
515 if (!objectp(dest) || !interactive(dest) || getuuid(dest)!=uuid)
516 return 1;
517
518 // pruefen, ob der Spieler schon ein Objekt dieser Blueprint dabei hat. Wenn
519 // ja, Abbruch, das koennte sonst zuviele Probleme geben.
520 if (objectp(ob2=present_clone(ob,dest))) {
521 tell_object(dest,BS("Du hast bereits "+ob2->name(WEN,0) +
522 " dabei. Zwei Gegenstaende dieser Art kannst du nicht gleichzeitig "
523 "mit Dir herumtragen."));
524 return 1; //nicht rausnehmen.
525 }
526 return 0;
527}
528
529// Objekte ausgeben, die hier angeblich drin sind. ;-) Gibt aber nur die
530// Autoloader aus, keine echten Objekt, die Container sind. Allerdings sollte
531// sowas eh nicht vorkommen. ;-)
532// flags: 1 - return array, 2 - don't collect equal objects '
533// flags: 4 - don't append infos for wizards
534varargs mixed make_invlist(object viewer, mixed inv, int flags) {
535 int iswiz;
536 mixed objs;
537
538 iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
539 // Mapping benutzen, um multiplen Overhead fuer allokation im foreach() zu
540 // vermeiden.
541 objs=m_allocate(sizeof(autoloader),1);
542 foreach(string oname, string *ids, string sh: autoloader) {
543 if (iswiz && !(flags & 4))
544 objs[oname]=sh + ". ["+oname+"]";
545 else
546 objs[oname]=sh + ".";
547 }
548 if(flags & 1) return(m_values(objs)-({""}));
549 if(!sizeof(autoloader)) return "";
550 return sprintf("%"+(sizeof(objs) > 6 ? "#" : "=")+"-78s",
551 implode(m_values(objs)-({""}), "\n")) + "\n";
552}
553
554// erzeugt das benannte Objekt und liefert es zurueck. Liefert 0 im Fehlerfall.
555// ausserdem bewegt es das Objekt in die Truhe, damit es ein Env hat und die
556// Truhe via NotifyLeave() mitkriegt, dass es tatsaechlich entnommen wurde.
557private object create_object(string oname) {
558 string error;
559 object ob;
MG Mud User88f12472016-06-24 23:31:02 +0200560 if (!member(autoloader,oname)) return 0;
561
562 //Blueprint finden (ja, das ist nicht unbedingt noetig, man koennte auch
563 //direkt clonen)
564 if (error=catch(ob=load_object(oname);publish) ||
565 !objectp(ob)) {
566 ERRLOG(sprintf("Konnte %s nicht laden/finden. Fehler: %O",
567 oname,error));
568 return 0;
569 }
570 // clonen
571 if (error=catch(ob=clone_object(oname);publish) ||
572 !objectp(ob)) {
573 ERRLOG(sprintf("Konnte %s nicht clonen. Fehler: %O",
574 oname,error));
575 return 0;
576 }
577 // konfigurieren
578 error=catch(ob->SetProp(P_AUTOLOADOBJ,autoloader[oname,ALDATA]);publish);
579
580 //Objekt bewegen, dabei Objekt in glob. Var. merken, damit PreventInsert()
581 //und NotifyInsert() es ignorieren. *seufz*
582 ob_in_bewegung=ob;
583 ob->move(ME,M_NOCHECK);
584 ob_in_bewegung=0;
585
586 // jetzt noch nen Callout starten, damit das Objekt zerstoert wird, wenn es
587 // nicht wirklich in einen Spieler bewegt wird.
588 call_out(#'check_content,1);
589
590 return(ob);
591}
592
593// Schauen, ob die truhe ein Objekt mit passender ID enthaelt. Wenn
594// ja, das Objekt erzeugen und zurueckliefern.
595// Diese Funktion hat eine Reihe von Nachteilen bzw. Defiziten ggue. der
596// normalerweise in Containern definierten Funktion:
597// - Nur das erste Objekt, auf das id passt. Ich hab erstmal keine Lust,
598// hier mehrere Objekte zu erzeugen. Mal schauen, ob es so geht. Auch waere
599// es ein Problem, wenn viele Objekt die Evalgrenze bzw. nicht alle in den
600// Spieler bewegt werden, nachdem sie erzeugt wurden.
601// - keine komplexen Ausdruecke, nur reine IDs.
602// Ich halte den Aufwand fuer nicht gerechtfertigt.
603object *present_objects( string complex_desc ) {
604 object ob;
605 if (!stringp(complex_desc) || !sizeof(complex_desc))
606 return ({});
607 // diese Funktion liefert nur Objete zurueck, wenn der richtige Interactive
608 // versucht, sie zu entnehmen. ;-)
609 if (!objectp(this_interactive()) ||
610 getuuid(this_interactive())!=uuid)
611 return ({});
612
613 // "alles" liefert das erstbeste Objekt.
614 if (complex_desc=="alles" && sizeof(autoloader))
615 {
616 string oname=m_indices(autoloader)[0];
617 ob=create_object(oname);
618 if (objectp(ob)) return ({ob});
619 }
620
621 // ueber alle Eintraege gehen, bis eine ID stimmt, erstes passendes Objekt
622 // erzeugen und in einem Array zurueckliefern.
623 foreach(string oname, string *ids: autoloader) {
624 if (member(ids,complex_desc)==-1) continue;
625 ob=create_object(oname);
626 break; //objekt gefunden, fertig hier
627 }
628 if (objectp(ob)) return ({ob});
629 return ({}); // nix gefunden
630}
631
632
633
634// ******************* Verwaltung *********************************
635
636// registriert die Truhe auf den jeweiligen Eigentuemer.
637protected void SetBesitzer(string unused, string newuuid) {
638 if (!stringp(newuuid) || !sizeof(newuuid)) return;
639 // wenn schon registriert, abbrechen
640 if (stringp(uuid) && sizeof(uuid)) return;
641
642 uuid=newuuid;
643 Set(H_FURNITURE,uuid,F_VALUE); //Setmethode umgehen, sonst Rekursion
644 // ab jetzt nur noch von der Truhe selber.
645 Set(H_FURNITURE,SECURED,F_MODE_AS);
646
647 // Daten fuer den Benutzer aus der Blueprint holen (BP liefert KEINE Kopie
648 // und es darf KEINE gemacht werden!):
Arathornae0fb0b2020-12-01 23:59:55 +0100649 autoloader=({mapping})load_name()->GetData(uuid);
MG Mud User88f12472016-06-24 23:31:02 +0200650
651 // keine Daten gekriegt? -> Fehler loggen
652 if (!mappingp(autoloader))
653 {
654 ERRLOG(sprintf("Keine gueltigen Daten vom Truhenmaster (BP) erhalten. "
655 "initialisiere mit leerem Mapping. :-("));
656 raise_error(sprintf(
657 "Keine gueltigen Daten vom Truhenmaster (BP) fuer UUID %s "
658 "erhalten.\n",uuid));
659 }
660}
661
662// Set-Funktion
663string _set_h_furniture(mixed arg) {
664 if (stringp(arg))
665 {
666 SetBesitzer(0,arg); // bricht ab, wenn bereits registriert.
667 }
668 return(uuid);
669}
670
671// Falls das Speichern in der BP nicht klappte, rufen die Clones diese
672// Funktion. Schreiben der Daten in in Logfile zur Restaurieren per Hand.
673private void EmergencyStore(int res) {
674 ERRLOG(sprintf("EmergencyStore() called. Rueckgabewert des "
675 "Truhenmaster (BP) war: %O. Speichere Daten in Logfile. ",res));
676 write_file(__DIR__+"ALTRUHE.NOTFALLDATEN",
677 sprintf("Daten fuer %O:\n%O\n",uuid,autoloader));
678}
679
680protected void save_me() {
681 int res;
682 // nur BP speichern
683 if (!clonep(ME))
684 save_object(SAVEFILE);
685 else
686 {
687 if (objectp(blueprint(ME)))
Arathornae0fb0b2020-12-01 23:59:55 +0100688 res=({int})blueprint(ME)->StoreData(uuid,autoloader);
MG Mud User88f12472016-06-24 23:31:02 +0200689 else
Arathornae0fb0b2020-12-01 23:59:55 +0100690 res=({int})load_object(load_name(ME))->StoreData(uuid,autoloader);
MG Mud User88f12472016-06-24 23:31:02 +0200691
692 if (res!=1)
693 EmergencyStore(res); // Daten in einem Notfall-Logfile ablegen.
694 }
695}
696
697
698// diese Funktion wird vom Seherhausraum gerufen, sobald ein rebootfestes
699// Moebelstueck erzeugt und konfiguriert wurde.
700void post_create() {
701 if (!clonep()) return;
702
703 // wenn jetzt kein Env: destructen
704 if (!objectp(environment())) {
705 remove(1);
706 return;
707 }
708 //beim Schrankmaster registrieren
709 SCHRANKMASTER->RegisterCnt(ME, QueryProp("cnt_version_std")
710 +":"+QueryProp("cnt_version_obj"), environment()->QueryOwner(),
711 environment());
712}
713
714// diese Funktion wird vom Schrankmaster gerufen, wenn dieser meint, dass
715// dieses Objekt neu erstellt werden sollte.
716int UpdateMe()
717{
718 if (!clonep())
719 return 0;
720 if (!objectp(environment()))
721 return 1;
722 object ob=clone_object(load_name(ME));
723 if (objectp(ob)) {
724 object oldenv=environment();
725 // UUID uebertragen
726 ob->SetProp(H_FURNITURE, uuid);
727 // hierhier bewegen, dabei werden UUID und Daten von der neuen Truhe meist
728 // automatisch geholt.
729 ob->move(oldenv,M_NOCHECK);
730 // jetzt erst post_create() rufen!
731 ob->post_create();
732 // dieses Objekt rausbewegen (damit das Seherhaus es austraegt).
733 move("/room/void",M_NOCHECK);
734 // Seherhausraum speichern (koennte teuer sein!)
735 oldenv->Save(1);
736 // selbstzerstoeren
737 // __INT_MAX__ bedeutet: nicht speichern, die neue Truhe hat die daten
738 // schon aus der BP abgefragt.
739 remove(__INT_MAX__);
740 }
741 return(1);
742}
743
744// bei Selbstzerstoerung speichern bzw. Daten an die Blueprint uebermitteln
745// und beim Schrankmaster abmelden.
746varargs int remove(int silent) {
747 string uid="";
748
749 // Blueprint speichern im Savefile, Clones uebertragen die Daten hier an die
750 // Blueprint. Clones nur, wenn nicht __INT_MAX__ uebergeben wurde.
751 if (silent!=__INT_MAX__ || !clonep())
752 save_me();
753
754 if (clonep()) {
755 // Clone melden sich beim Schrankmaster ab.
756 if (objectp(environment())) {
757 uid=environment()->QueryOwner();
758 }
759 //beim Schrankmaster deregistrieren
760 SCHRANKMASTER->RemoveCnt(ME,uid);
761 }
762 return(::remove(silent));
763}
764
765// ***************** NUR BLUEPRINTS *********************************
766
767// neuen Autoloader zulassen (nur EM+!)
768varargs int AddAutoloader(string path,string nam) {
769 object ob;
770 if (clonep(ME)) return 0;
771 if (!stringp(path) || !sizeof(path)) return -1;
772 if (!ACCESS) return -2;
773 //if (!ARCH_SECURITY) return -2;
774 if (member(whitelist,path)) return -3;
775 if (catch(ob=load_object(path);publish)
776 || !objectp(ob))
777 return -4;
778 // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
779 if (!stringp(nam) || !sizeof(nam)) {
780 nam=ob->name(WER,0);
781 if (!stringp(nam) || !sizeof(nam)) return -5;
782 }
783 ITEMLOG(sprintf("%s erlaubt: %s (%s)\n",getuid(this_interactive()),
784 path,nam));
785 whitelist+=([path:capitalize(nam)]);
786 if (member(vorschlaege,path))
787 m_delete(vorschlaege,path);
788 save_me();
789 return(1);
790}
791
792// Autoloader aus Erlaubnisliste entfernen (nur EM+!)
793int RemoveAutoloader(string path) {
794 if (clonep(ME)) return 0;
795 if (!stringp(path) || !sizeof(path)) return -1;
796 if (!ACCESS) return -2;
797 //if (!ARCH_SECURITY) return -2;
798 if (!member(whitelist,path)) return -3;
799 ITEMLOG(sprintf("%s widerruft Erlaubnis: %s (%s)\n",getuid(this_interactive()),
800 path,whitelist[path]));
801 whitelist-=([path]);
802 save_me();
803 return(1);
804}
805
806// erlaubte Autoloader abfragen
807mapping QueryAutoloader() {
808 return(copy(whitelist));
809}
810
811// neuen Autoloader in Blacklist eintragen (nur EM+!)
812varargs int AddBlacklist(string path, string nam) {
813 object ob;
814 if (clonep(ME)) return 0;
815 if (!stringp(path) || !sizeof(path)) return -1;
816 if (!ACCESS) return -2;
817 //if (!ARCH_SECURITY) return -2;
818
819 if (member(blacklist,path)) return -3;
820
821 if (catch(ob=load_object(path);publish)
822 || !objectp(ob))
823 return -4;
824 // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
825 if (!stringp(nam) || !sizeof(nam)) {
826 nam=ob->name(WER,0);
827 if (!stringp(nam) || !sizeof(nam)) return -5;
828 }
829 // ggf. erlaubten entfernen.
830 if (member(whitelist,path))
831 RemoveAutoloader(path);
832 ITEMLOG(sprintf("%s verbietet: %s (%s)\n",getuid(this_interactive()),
833 path,nam));
834
835 blacklist+=([path:capitalize(nam)]);
836
837 if (member(vorschlaege,path))
838 m_delete(vorschlaege,path);
839
840 save_me();
841 return(1);
842}
843
844// Autoloader aus Blacklist entfernen (nur EM+!)
845int RemoveBlacklist(string path) {
846 if (clonep(ME)) return 0;
847 if (!stringp(path) || !sizeof(path)) return -1;
848 if (!ACCESS) return -2;
849 //if (!ARCH_SECURITY) return -2;
850 if (member(blacklist,path)==-1) return -3;
851 ITEMLOG(sprintf("%s loescht: %s (%s) von Blacklist\n",getuid(this_interactive()),
852 path,blacklist[path]));
853 blacklist-=([path]);
854 save_me();
855 return(1);
856}
857
858// gesperrte Autoloader abfragen
859mapping QueryBlacklist() {
860 return(copy(blacklist));
861}
862
863// vorschlaege abfragen
864varargs mixed QueryVorschlaege(int format) {
865 string res="\n";
866 if (!format) return(copy(vorschlaege));
867
868 foreach(string oname, string nam, string sh: vorschlaege) {
869 res+=sprintf("%.78s:\n %.37s,%.37s\n",oname,nam,sh);
870 }
871
872 if (format==2 && objectp(PL))
873 tell_object(PL,res);
874 else
875 return res;
876 return 0;
877}
878
879// Wird diese funktion in der Blueprint gerufen, liefert sie die Daten fuer
880// eine Truhe mit dieser UUID zurueck. Aber nur dann, wenn die auch von einer
881// truhe abgerufen wird und keinem beliebigen anderen Objekt oder wenn ein EM
882// abfragt.
883mapping GetData(string uid) {
884 if (clonep(ME)) return 0;
885 if (extern_call())
886 {
887 if (!objectp(previous_object())) return 0;
888 if (blueprint(previous_object()) != ME
889 && load_name(previous_object()) != __FILE__[..<3]
890 && !ACCESS)
891 return(0);
892 }
893 if (!stringp(uid)) return ([]);
894
895 if (!member(data, uid))
896 data[uid] = m_allocate(1,3);
897
898 // Absichtlich keine Kopie, damit die Truhen (falls jemand mehrere kauft)
899 // sich alle eine Kopie des Mappings teilen.
900 return data[uid];
901}
902
903// Loest die Speicherung der Daten im Savefile aus, denn irgendwelche Daten
904// haben sich in einem Clone geaendert, welche noch auf die Platte muessen.
905// Diese Funktion speicherte frueher Daten aus den Clones wieder im Mapping
906// des Masters (der Blueprint). Dies ist heute nicht mehr so, weil alle Truhen
907// (eines Benutzers) sich data[uid] teilen. Daher wird hier ein Fehler
908// ausgeloest, wenn es von einer Truhe ein Mapping kriegt, welches nicht
909// identisch (!) mit dem data[uid] ist.
910// liefert 0 im Fehlerfall!
911int StoreData(string uid, mapping tmp) {
912
913 if (clonep(ME)) return 0;
914
915 // Aber nur dann, wenn die auch von einer truhe abgerufen wird und keinem
916 // beliebigen anderen Objekt oder wenn ein EM setzt.
917 if (extern_call())
918 {
919 if (!objectp(previous_object()))
920 return 0;
921 if (blueprint(previous_object()) != ME
922 && load_name(previous_object()) != __FILE__[..<3]
923 && !ACCESS)
924 return 0;
925 }
926 if (!stringp(uid) || !mappingp(tmp))
927 return(0);
928
929 // tmp muss auf _dasselbe_ Mapping zeigen wie data[uid]. Wenn das nicht der
930 // Fall ist, ist was schiefgelaufen. Jedenfalls wird dann hier nen Fehler
931 // ausgeloest.
932 if (tmp != data[uid])
933 {
934// if(program_time(previous_object()) < 1400525694)
935// data[uid]=tmp;
936// else
937 raise_error("StoreData() gerufen und Daten sind nicht identisch "
938 "mit den bereits bekannten.\n");
939 }
940
941 // Absichtlich keine Kopie, damit sich alle Truhen das Mapping teilen.
942 //data[uid]=tmp;
943
944 // Savefile muss natuerlich geschrieben werden, fuer den naechsten Reboot.
945 if (find_call_out(#'save_me)==-1)
946 call_out(#'save_me,10); // Speichervorgaenge ggf. sammeln (upd -ar ...)
947 return(1);
948}
949
950// Maintainer ueber Truhenvorschlaege informieren
951void reset() {
952
953 if (clonep() || !sizeof(vorschlaege)) {
954 // ohne Vorschlaege ist auch kein reset noetig.
955 set_next_reset(-1);
956 return;
957 }
958 set_next_reset(12000); // alle 3-4h reicht.
959 foreach(string uid: MAINTAINER) {
960 object pl=find_player(uid);
961 if (objectp(pl) && query_idle(pl) < 1800)
962 tell_object(pl,BS("Es gibt neue Objektvorschlaege fuer die "
963 "Autoloadertruhe. Bitt schau da doch bei Gelegenheit mal drueber."));
964 }
965}
966
967
968// *************************************************************************
969
970// **************** SONSTIGES **********************************************
971
972// *seufz*
973// secure_level() aus der simul_efun ist hier momentan nicht brauchbar, weil
974// dort auch p/seher/moebel/autoloadertruhe in der Callerkette steht und das
975// ein levle von 0 hat. *seufz*
976nomask private int my_secure_level() {
977 int *level;
978 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
979 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
980 //von 0.
981 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
982 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
983 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
984 //INteractive geben muss.
985 level=map(caller_stack(1),function int (object caller)
986 {if (objectp(caller))
987 return(query_wiz_level(geteuid(caller)));
988 return(0); // kein Objekt da, 0.
989 } );
990 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
991}
992
993
994// debugkram
995mixed _query_content() {
996 //deep_copy, damit nicht jemand einfach so an den Daten rumbasteln kann.
997 return(deep_copy(autoloader));
998}
999
1000mixed _query_owner() {
1001 return(uuid);
1002}
1003
1004int DeleteData(string uid) {
1005 if (clonep(ME)) return 0;
1006 if (!stringp(uid) || !sizeof(uid)) return -1;
1007 if (!ACCESS) return -2;
1008 //if (!ARCH_SECURITY) return -2;
1009 m_delete(data,uid);
1010 save_me();
1011 return(1);
1012}
1013
1014mixed testfun() {return "bin da\n";}
1015
Arathornb4c41eb2023-06-14 22:29:05 +02001016public int HasAutoloader(string uuid, string loadname) {
1017 if (!sizeof(uuid) || !sizeof(loadname) || clonep() || !(uuid in data))
1018 return -1;
1019
1020 return (loadname in data[uuid]);
1021}
1022