blob: beab5fd761ff4425b14d6c844589329010f10254 [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];
259 if (!objectp(ob)) return 0;
260 // wuerde die Truhe das Objekt ueberhaupt aufnehmen? Fehlerausgabe durch
261 // PrevenInsert()
262 if (PreventInsert(ob)) return 1;
263 // Ausziehen... Schade, dass DoUnwear nix vernuenftiges an Rueckgabewert
264 // hat. :-(
265 if (objectp(ob->QueryProp(P_WORN))) {
266 ob->DoUnwear();
267 if (objectp(ob->QueryProp(P_WORN))) {
268 tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
269 "ausziehen!"));
270 return 1;
271 }
272 }
273 // wegstecken
274 if (objectp(ob->QueryProp(P_WIELDED))) {
275 ob->DoUnwield();
276 if (objectp(ob->QueryProp(P_WIELDED))) {
277 tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
278 "wegstecken!"));
279 return 1;
280 }
281 }
282 // NO_CHECK und Silent-Bewegung, Meldungen an den Spieler selber machen.
283 tell_object(PL, BS("Du steckst "+ ob->name(WEN,1) + " in " + name(WEN,1) +
284 "."));
285
286 tell_room(environment(),BS(PL->Name(WER) + " legt " + ob->name(WEN,0) +
287 " in " + name(WEN,1) + " hinein."),({PL}));
288 ob->move(ME, M_NOCHECK|M_SILENT);
289 return 1;
290}
291
292// alternative zum "nimm bla aus truhe". Spieler wollten was kurzes dafuer
293// haben.
294protected int cmd_entnehmen(string cmd) {
295 int res;
MG Mud User88f12472016-06-24 23:31:02 +0200296
297 if (!objectp(PL)) return 0;
298
299 notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
300 if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
301
302 notify_fail(BS("Was moechtest Du aus "+name(WEM,1)+ " entnehmen?\n"));
303 if (!stringp(cmd) || !sizeof(cmd)) return 0;
304
305 object *obs=present_objects(cmd);
306
307 if (!sizeof(obs) || !objectp(obs[0]))
308 return 0;
309
310 // NOGET ist hier bloed. So ist es zwar auch nicht richtig doll... *seufz*
311 // Die hier ist/waere aber nen uebler Hack, erstmal auskommentiert lassen.
312 // also, P_NOGET sichern.
Arathornc74a2532021-04-02 15:51:14 +0200313 /*mixed noget;
314 if (!closurep(noget=ob->Query(P_NOGET,F_QUERY_METHOD))) {
MG Mud User88f12472016-06-24 23:31:02 +0200315 noget=ob->Query(P_NOGET,F_VALUE);
316 ob->Set(P_NOGET,0,F_VALUE);
317 }
318 else {
319 ob->Set(P_NOGET,0,F_QUERY_METHOD);
320 }*/
321 // nehmen.
322 res=PL->pick_obj(obs[0]);
323 // P_NOGET zurueckschreiben. (Ja, wenn eine Closure als F_VALUE drinsteht,
324 // landet die jetzt als F_QUERY_METHOD im Objekt.
325 /*if (closurep(noget)) {
326 ob->Set(P_NOGET,noget,F_QUERY_METHOD);
327 }
328 else
329 ob->Set(P_NOGET,noget,F_VALUE);
330*/
331 return(res);
332}
333
334// Hier wird auch das PreventInsert() von der Blueprint gerufen. Das
335// erleichtert es, die Liste an erlaubten Objekten zu aendern, ohne dass man
336// alle Clones ersetzen muss.
337varargs int PreventInsert(object ob) {
338 string oname;
339 // Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
340 if (!objectp(ob) || ob_in_bewegung==ob) return 0;
341
342 oname=BLUE_NAME(ob);
343
344 // Pruefung in Clonen:
345 if (clonep(ME))
346 {
347 // nur Eigentuemer
348 if (!stringp(uuid) || !sizeof(uuid)) {
349 if (objectp(PL))
350 tell_object(PL,BS(sprintf("%s gehoert Dir nicht, daher kannst Du "
351 "auch keine Gegenstaende in %s legen.",
352 Name(WER,1),QueryPronoun(WEN))));
353 return 1;
354 }
355 if (!objectp(PL) || getuuid(PL)!=uuid) {
356 if (objectp(PL))
357 tell_object(PL,BS(sprintf("Nur %s darf Gegenstaende in %s "
358 "hineinlegen.",capitalize(explode(uuid,"_")[0]),name(WEN,1))));
359 return 1;
360 }
361 // jedes Objekt nur einmal.
362 if (member(autoloader,oname)) {
363 if (objectp(PL))
364 tell_object(PL,BS(Name(WER,1)+ " kann von einem Gegenstand jeweils "
365 "nur ein Exemplar aufnehmen, es ist aber bereits "
366 +ob->name(WER,0) + " in " + QueryPronoun(WEM) + "."));
367 return 1;
368 }
369 // jetzt Erlaubnisliste der BP fragen.
370 if (objectp(blueprint(ME)))
371 return blueprint(ME)->PreventInsert(ob);
372 else
373 return load_object(load_name(ME))->PreventInsert(ob);
374 }
375 // Ende fuer Pruefung fuer Clone.
376
377 // ab hier jetzt die Pruefung durch die BP.
378 // Keine (freigegebener) Autoloader? Hat in diesem Container nix verloren!
379 if( !ob->QueryProp(P_AUTOLOADOBJ) ) {
380 if (objectp(PL))
381 tell_object(PL,BS("In "+name(WEN,1)
382 +" kannst Du nur Autoload-Objekte hineinlegen. "));
383 return 1;
384 }
385 else if (member(blacklist,oname)) {
386 if (objectp(PL))
387 tell_object(PL,BS("In "+name(WEN,1)
388 +" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
389 +ob->Name(WER,1) + " wurde explizit als nicht erwuenscht "
390 "befunden."));
391 return 1;
392 }
393 else if (!member(whitelist,oname)) {
394 if (!member(vorschlaege,oname)) {
395 vorschlaege[oname,0]=ob->name(WER,0) || "";
396 vorschlaege[oname,1]=ob->short() || "";
397 }
398 if (objectp(PL))
399 tell_object(PL,BS("In "+name(WEN,1)
400 +" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
401 +ob->Name(WER,1) + " ist momentan nicht auf der Liste. Der "
402 "Gegenstand wurde jetzt als Vorschlag gespeichert. Bitte hab "
403 "etwas Geduld, bis sich jemand den Gegenstand angeschaut hat."));
404 // ggf. reset reaktivieren und Maintainer informieren.
405 if (!query_next_reset())
406 set_next_reset(1);
407 return 1;
408 }
409 // getragenes?
410 if (ob->QueryProp(P_WORN)) {
411 ob->DoUnwear(0);
412 if (ob->QueryProp(P_WORN)) { //ARGL. GRUMMEL.
413 if (objectp(PL))
414 tell_object(PL,BS("Willst Du "+ob->name(WEN,1) + " nicht erstmal "
415 "ausziehen?"));
416 return 1;
417 }
418 }
419 // enthaelt es irgendwelche anderen Objekte?
420 if (sizeof(all_inventory(ob))) {
421 if (objectp(PL))
422 tell_object(PL,BS(ob->Name(WER,1) + " ist nicht leer."));
423 return 1;
424 }
425
426 // andere Einschraenkungen, als hier geprueft werden, gibt es nicht.
427 return 0;
428}
429
430// hier ist das Objekt jetzt in der Truhe, d.h. Daten speichern und Objekt
431// destructen. ;)
432void NotifyInsert(object ob, object oldenv) {
433
434 // Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
435 if (!objectp(ob) || ob==ob_in_bewegung)
436 return;
437 STORELOG(sprintf("%s deponiert %s [%O], Daten: %O",getuid(PL),ob->name(WEN,0),
438 ob,ob->QueryProp(P_AUTOLOADOBJ)));
439 // noetig sind die IDs, den Namen und die AUTOLOADOBJ-Daten des Objekts.
440 // ;-)
441 autoloader[BLUE_NAME(ob),ALDATA]=ob->QueryProp(P_AUTOLOADOBJ);
442 autoloader[BLUE_NAME(ob),IDS]=ob->QueryProp(P_IDS);
443 // Objekte, die 0 als short liefern, kriegen eine Standard-Short und werden
444 // sichtbar gemacht, da sie sonst nicht wiederzufinden sind. Objekte, die
445 // unsichtbar sein sollen, duerfen nicht erlaubt werden.
446 // TODO eine ID als Short nehmen?
447 autoloader[BLUE_NAME(ob),NAME]=capitalize((ob->short()||"<Unbekannt>.\n")[..<3]);
448 // nach dem Move die realen Objekte destructen.
449 call_out(#'check_content, 0);
450 save_me();
451}
452
453// Objekt wurde entnommen, also aus der Liste der enthaltenen Autoloader
454// loeschen. Ausserdem Objekt konfigurieren. Dies wird erst hier gemacht und
455// nicht in create_object(), damit es so aehnlich wie moeglich zum clonen der
456// Autoloader beim erstellen des Spielerobjektes wird (dort wird erst bewegt,
457// dann konfiguriert).
458void NotifyLeave(object ob, object dest) {
Arathorn4b3b2d12021-04-02 15:53:48 +0200459 string oname;
MG Mud User88f12472016-06-24 23:31:02 +0200460
461 if (!objectp(ob))
462 return;
463
464 oname=BLUE_NAME(ob);
465 if (!member(autoloader,oname)) {
466 // Das sollte definitiv nicht passieren.
467 ERRLOG(sprintf("Gegenstand (%O) wurde entnommen, der nicht "
468 "gespeichert war!",ob));
469 return;
470 }
471
Arathorn4b3b2d12021-04-02 15:53:48 +0200472 PICKLOG(sprintf("Objekt (%O) wurde entnommen.",ob));
473 m_delete(autoloader,oname);
474 // speichern
475 save_me();
MG Mud User88f12472016-06-24 23:31:02 +0200476}
477
478protected void check_content() {
479 // wenn Objekte noch in der Truhe sind, also nicht erfolgreich in
480 // einen Spieler bewegt wurden, werden zerstoert. Datenverlust gibt es
481 // hierbei nicht, weil die Daten der Autoloader nur durch NotifyLeave()
482 // geloescht werden.
483 foreach(object ob: all_inventory()) {
484 ob->remove(1);
485 if (objectp(ob)) destruct(ob);
486 }
487}
488
489// hier nochmal schauen, ob das Objekt auch in den richtigen Spieler bewegt
490// wird... Falls das move dann hier abgebrochen wird, wird das Objekt im
491// naechsten HB von der Truhe zerstoert. (chk_contents())
492varargs int PreventLeave(object ob, mixed dest) {
493 object ob2;
494
495 //DEBUG(sprintf("PreventLeave(): Ob: %O, Dest: %O (%O)",
496 // ob,dest,(objectp(dest)?"Objekt":"Non-Object")));
497
498 if (!objectp(ob)) return 0;
499 // falls string uebergeben, erstmal zug. Spieler finden.
500 if (stringp(dest) && sizeof(dest))
501 dest=find_player(dest);
502
503 // richtiges INteractive? Dann darf das Objekt raus. Sonst nicht. ;-)
504 if (!objectp(dest) || !interactive(dest) || getuuid(dest)!=uuid)
505 return 1;
506
507 // pruefen, ob der Spieler schon ein Objekt dieser Blueprint dabei hat. Wenn
508 // ja, Abbruch, das koennte sonst zuviele Probleme geben.
509 if (objectp(ob2=present_clone(ob,dest))) {
510 tell_object(dest,BS("Du hast bereits "+ob2->name(WEN,0) +
511 " dabei. Zwei Gegenstaende dieser Art kannst du nicht gleichzeitig "
512 "mit Dir herumtragen."));
513 return 1; //nicht rausnehmen.
514 }
515 return 0;
516}
517
518// Objekte ausgeben, die hier angeblich drin sind. ;-) Gibt aber nur die
519// Autoloader aus, keine echten Objekt, die Container sind. Allerdings sollte
520// sowas eh nicht vorkommen. ;-)
521// flags: 1 - return array, 2 - don't collect equal objects '
522// flags: 4 - don't append infos for wizards
523varargs mixed make_invlist(object viewer, mixed inv, int flags) {
524 int iswiz;
525 mixed objs;
526
527 iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
528 // Mapping benutzen, um multiplen Overhead fuer allokation im foreach() zu
529 // vermeiden.
530 objs=m_allocate(sizeof(autoloader),1);
531 foreach(string oname, string *ids, string sh: autoloader) {
532 if (iswiz && !(flags & 4))
533 objs[oname]=sh + ". ["+oname+"]";
534 else
535 objs[oname]=sh + ".";
536 }
537 if(flags & 1) return(m_values(objs)-({""}));
538 if(!sizeof(autoloader)) return "";
539 return sprintf("%"+(sizeof(objs) > 6 ? "#" : "=")+"-78s",
540 implode(m_values(objs)-({""}), "\n")) + "\n";
541}
542
543// erzeugt das benannte Objekt und liefert es zurueck. Liefert 0 im Fehlerfall.
544// ausserdem bewegt es das Objekt in die Truhe, damit es ein Env hat und die
545// Truhe via NotifyLeave() mitkriegt, dass es tatsaechlich entnommen wurde.
546private object create_object(string oname) {
547 string error;
548 object ob;
MG Mud User88f12472016-06-24 23:31:02 +0200549 if (!member(autoloader,oname)) return 0;
550
551 //Blueprint finden (ja, das ist nicht unbedingt noetig, man koennte auch
552 //direkt clonen)
553 if (error=catch(ob=load_object(oname);publish) ||
554 !objectp(ob)) {
555 ERRLOG(sprintf("Konnte %s nicht laden/finden. Fehler: %O",
556 oname,error));
557 return 0;
558 }
559 // clonen
560 if (error=catch(ob=clone_object(oname);publish) ||
561 !objectp(ob)) {
562 ERRLOG(sprintf("Konnte %s nicht clonen. Fehler: %O",
563 oname,error));
564 return 0;
565 }
566 // konfigurieren
567 error=catch(ob->SetProp(P_AUTOLOADOBJ,autoloader[oname,ALDATA]);publish);
568
569 //Objekt bewegen, dabei Objekt in glob. Var. merken, damit PreventInsert()
570 //und NotifyInsert() es ignorieren. *seufz*
571 ob_in_bewegung=ob;
572 ob->move(ME,M_NOCHECK);
573 ob_in_bewegung=0;
574
575 // jetzt noch nen Callout starten, damit das Objekt zerstoert wird, wenn es
576 // nicht wirklich in einen Spieler bewegt wird.
577 call_out(#'check_content,1);
578
579 return(ob);
580}
581
582// Schauen, ob die truhe ein Objekt mit passender ID enthaelt. Wenn
583// ja, das Objekt erzeugen und zurueckliefern.
584// Diese Funktion hat eine Reihe von Nachteilen bzw. Defiziten ggue. der
585// normalerweise in Containern definierten Funktion:
586// - Nur das erste Objekt, auf das id passt. Ich hab erstmal keine Lust,
587// hier mehrere Objekte zu erzeugen. Mal schauen, ob es so geht. Auch waere
588// es ein Problem, wenn viele Objekt die Evalgrenze bzw. nicht alle in den
589// Spieler bewegt werden, nachdem sie erzeugt wurden.
590// - keine komplexen Ausdruecke, nur reine IDs.
591// Ich halte den Aufwand fuer nicht gerechtfertigt.
592object *present_objects( string complex_desc ) {
593 object ob;
594 if (!stringp(complex_desc) || !sizeof(complex_desc))
595 return ({});
596 // diese Funktion liefert nur Objete zurueck, wenn der richtige Interactive
597 // versucht, sie zu entnehmen. ;-)
598 if (!objectp(this_interactive()) ||
599 getuuid(this_interactive())!=uuid)
600 return ({});
601
602 // "alles" liefert das erstbeste Objekt.
603 if (complex_desc=="alles" && sizeof(autoloader))
604 {
605 string oname=m_indices(autoloader)[0];
606 ob=create_object(oname);
607 if (objectp(ob)) return ({ob});
608 }
609
610 // ueber alle Eintraege gehen, bis eine ID stimmt, erstes passendes Objekt
611 // erzeugen und in einem Array zurueckliefern.
612 foreach(string oname, string *ids: autoloader) {
613 if (member(ids,complex_desc)==-1) continue;
614 ob=create_object(oname);
615 break; //objekt gefunden, fertig hier
616 }
617 if (objectp(ob)) return ({ob});
618 return ({}); // nix gefunden
619}
620
621
622
623// ******************* Verwaltung *********************************
624
625// registriert die Truhe auf den jeweiligen Eigentuemer.
626protected void SetBesitzer(string unused, string newuuid) {
627 if (!stringp(newuuid) || !sizeof(newuuid)) return;
628 // wenn schon registriert, abbrechen
629 if (stringp(uuid) && sizeof(uuid)) return;
630
631 uuid=newuuid;
632 Set(H_FURNITURE,uuid,F_VALUE); //Setmethode umgehen, sonst Rekursion
633 // ab jetzt nur noch von der Truhe selber.
634 Set(H_FURNITURE,SECURED,F_MODE_AS);
635
636 // Daten fuer den Benutzer aus der Blueprint holen (BP liefert KEINE Kopie
637 // und es darf KEINE gemacht werden!):
Arathornae0fb0b2020-12-01 23:59:55 +0100638 autoloader=({mapping})load_name()->GetData(uuid);
MG Mud User88f12472016-06-24 23:31:02 +0200639
640 // keine Daten gekriegt? -> Fehler loggen
641 if (!mappingp(autoloader))
642 {
643 ERRLOG(sprintf("Keine gueltigen Daten vom Truhenmaster (BP) erhalten. "
644 "initialisiere mit leerem Mapping. :-("));
645 raise_error(sprintf(
646 "Keine gueltigen Daten vom Truhenmaster (BP) fuer UUID %s "
647 "erhalten.\n",uuid));
648 }
649}
650
651// Set-Funktion
652string _set_h_furniture(mixed arg) {
653 if (stringp(arg))
654 {
655 SetBesitzer(0,arg); // bricht ab, wenn bereits registriert.
656 }
657 return(uuid);
658}
659
660// Falls das Speichern in der BP nicht klappte, rufen die Clones diese
661// Funktion. Schreiben der Daten in in Logfile zur Restaurieren per Hand.
662private void EmergencyStore(int res) {
663 ERRLOG(sprintf("EmergencyStore() called. Rueckgabewert des "
664 "Truhenmaster (BP) war: %O. Speichere Daten in Logfile. ",res));
665 write_file(__DIR__+"ALTRUHE.NOTFALLDATEN",
666 sprintf("Daten fuer %O:\n%O\n",uuid,autoloader));
667}
668
669protected void save_me() {
670 int res;
671 // nur BP speichern
672 if (!clonep(ME))
673 save_object(SAVEFILE);
674 else
675 {
676 if (objectp(blueprint(ME)))
Arathornae0fb0b2020-12-01 23:59:55 +0100677 res=({int})blueprint(ME)->StoreData(uuid,autoloader);
MG Mud User88f12472016-06-24 23:31:02 +0200678 else
Arathornae0fb0b2020-12-01 23:59:55 +0100679 res=({int})load_object(load_name(ME))->StoreData(uuid,autoloader);
MG Mud User88f12472016-06-24 23:31:02 +0200680
681 if (res!=1)
682 EmergencyStore(res); // Daten in einem Notfall-Logfile ablegen.
683 }
684}
685
686
687// diese Funktion wird vom Seherhausraum gerufen, sobald ein rebootfestes
688// Moebelstueck erzeugt und konfiguriert wurde.
689void post_create() {
690 if (!clonep()) return;
691
692 // wenn jetzt kein Env: destructen
693 if (!objectp(environment())) {
694 remove(1);
695 return;
696 }
697 //beim Schrankmaster registrieren
698 SCHRANKMASTER->RegisterCnt(ME, QueryProp("cnt_version_std")
699 +":"+QueryProp("cnt_version_obj"), environment()->QueryOwner(),
700 environment());
701}
702
703// diese Funktion wird vom Schrankmaster gerufen, wenn dieser meint, dass
704// dieses Objekt neu erstellt werden sollte.
705int UpdateMe()
706{
707 if (!clonep())
708 return 0;
709 if (!objectp(environment()))
710 return 1;
711 object ob=clone_object(load_name(ME));
712 if (objectp(ob)) {
713 object oldenv=environment();
714 // UUID uebertragen
715 ob->SetProp(H_FURNITURE, uuid);
716 // hierhier bewegen, dabei werden UUID und Daten von der neuen Truhe meist
717 // automatisch geholt.
718 ob->move(oldenv,M_NOCHECK);
719 // jetzt erst post_create() rufen!
720 ob->post_create();
721 // dieses Objekt rausbewegen (damit das Seherhaus es austraegt).
722 move("/room/void",M_NOCHECK);
723 // Seherhausraum speichern (koennte teuer sein!)
724 oldenv->Save(1);
725 // selbstzerstoeren
726 // __INT_MAX__ bedeutet: nicht speichern, die neue Truhe hat die daten
727 // schon aus der BP abgefragt.
728 remove(__INT_MAX__);
729 }
730 return(1);
731}
732
733// bei Selbstzerstoerung speichern bzw. Daten an die Blueprint uebermitteln
734// und beim Schrankmaster abmelden.
735varargs int remove(int silent) {
736 string uid="";
737
738 // Blueprint speichern im Savefile, Clones uebertragen die Daten hier an die
739 // Blueprint. Clones nur, wenn nicht __INT_MAX__ uebergeben wurde.
740 if (silent!=__INT_MAX__ || !clonep())
741 save_me();
742
743 if (clonep()) {
744 // Clone melden sich beim Schrankmaster ab.
745 if (objectp(environment())) {
746 uid=environment()->QueryOwner();
747 }
748 //beim Schrankmaster deregistrieren
749 SCHRANKMASTER->RemoveCnt(ME,uid);
750 }
751 return(::remove(silent));
752}
753
754// ***************** NUR BLUEPRINTS *********************************
755
756// neuen Autoloader zulassen (nur EM+!)
757varargs int AddAutoloader(string path,string nam) {
758 object ob;
759 if (clonep(ME)) return 0;
760 if (!stringp(path) || !sizeof(path)) return -1;
761 if (!ACCESS) return -2;
762 //if (!ARCH_SECURITY) return -2;
763 if (member(whitelist,path)) return -3;
764 if (catch(ob=load_object(path);publish)
765 || !objectp(ob))
766 return -4;
767 // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
768 if (!stringp(nam) || !sizeof(nam)) {
769 nam=ob->name(WER,0);
770 if (!stringp(nam) || !sizeof(nam)) return -5;
771 }
772 ITEMLOG(sprintf("%s erlaubt: %s (%s)\n",getuid(this_interactive()),
773 path,nam));
774 whitelist+=([path:capitalize(nam)]);
775 if (member(vorschlaege,path))
776 m_delete(vorschlaege,path);
777 save_me();
778 return(1);
779}
780
781// Autoloader aus Erlaubnisliste entfernen (nur EM+!)
782int RemoveAutoloader(string path) {
783 if (clonep(ME)) return 0;
784 if (!stringp(path) || !sizeof(path)) return -1;
785 if (!ACCESS) return -2;
786 //if (!ARCH_SECURITY) return -2;
787 if (!member(whitelist,path)) return -3;
788 ITEMLOG(sprintf("%s widerruft Erlaubnis: %s (%s)\n",getuid(this_interactive()),
789 path,whitelist[path]));
790 whitelist-=([path]);
791 save_me();
792 return(1);
793}
794
795// erlaubte Autoloader abfragen
796mapping QueryAutoloader() {
797 return(copy(whitelist));
798}
799
800// neuen Autoloader in Blacklist eintragen (nur EM+!)
801varargs int AddBlacklist(string path, string nam) {
802 object ob;
803 if (clonep(ME)) return 0;
804 if (!stringp(path) || !sizeof(path)) return -1;
805 if (!ACCESS) return -2;
806 //if (!ARCH_SECURITY) return -2;
807
808 if (member(blacklist,path)) return -3;
809
810 if (catch(ob=load_object(path);publish)
811 || !objectp(ob))
812 return -4;
813 // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
814 if (!stringp(nam) || !sizeof(nam)) {
815 nam=ob->name(WER,0);
816 if (!stringp(nam) || !sizeof(nam)) return -5;
817 }
818 // ggf. erlaubten entfernen.
819 if (member(whitelist,path))
820 RemoveAutoloader(path);
821 ITEMLOG(sprintf("%s verbietet: %s (%s)\n",getuid(this_interactive()),
822 path,nam));
823
824 blacklist+=([path:capitalize(nam)]);
825
826 if (member(vorschlaege,path))
827 m_delete(vorschlaege,path);
828
829 save_me();
830 return(1);
831}
832
833// Autoloader aus Blacklist entfernen (nur EM+!)
834int RemoveBlacklist(string path) {
835 if (clonep(ME)) return 0;
836 if (!stringp(path) || !sizeof(path)) return -1;
837 if (!ACCESS) return -2;
838 //if (!ARCH_SECURITY) return -2;
839 if (member(blacklist,path)==-1) return -3;
840 ITEMLOG(sprintf("%s loescht: %s (%s) von Blacklist\n",getuid(this_interactive()),
841 path,blacklist[path]));
842 blacklist-=([path]);
843 save_me();
844 return(1);
845}
846
847// gesperrte Autoloader abfragen
848mapping QueryBlacklist() {
849 return(copy(blacklist));
850}
851
852// vorschlaege abfragen
853varargs mixed QueryVorschlaege(int format) {
854 string res="\n";
855 if (!format) return(copy(vorschlaege));
856
857 foreach(string oname, string nam, string sh: vorschlaege) {
858 res+=sprintf("%.78s:\n %.37s,%.37s\n",oname,nam,sh);
859 }
860
861 if (format==2 && objectp(PL))
862 tell_object(PL,res);
863 else
864 return res;
865 return 0;
866}
867
868// Wird diese funktion in der Blueprint gerufen, liefert sie die Daten fuer
869// eine Truhe mit dieser UUID zurueck. Aber nur dann, wenn die auch von einer
870// truhe abgerufen wird und keinem beliebigen anderen Objekt oder wenn ein EM
871// abfragt.
872mapping GetData(string uid) {
873 if (clonep(ME)) return 0;
874 if (extern_call())
875 {
876 if (!objectp(previous_object())) return 0;
877 if (blueprint(previous_object()) != ME
878 && load_name(previous_object()) != __FILE__[..<3]
879 && !ACCESS)
880 return(0);
881 }
882 if (!stringp(uid)) return ([]);
883
884 if (!member(data, uid))
885 data[uid] = m_allocate(1,3);
886
887 // Absichtlich keine Kopie, damit die Truhen (falls jemand mehrere kauft)
888 // sich alle eine Kopie des Mappings teilen.
889 return data[uid];
890}
891
892// Loest die Speicherung der Daten im Savefile aus, denn irgendwelche Daten
893// haben sich in einem Clone geaendert, welche noch auf die Platte muessen.
894// Diese Funktion speicherte frueher Daten aus den Clones wieder im Mapping
895// des Masters (der Blueprint). Dies ist heute nicht mehr so, weil alle Truhen
896// (eines Benutzers) sich data[uid] teilen. Daher wird hier ein Fehler
897// ausgeloest, wenn es von einer Truhe ein Mapping kriegt, welches nicht
898// identisch (!) mit dem data[uid] ist.
899// liefert 0 im Fehlerfall!
900int StoreData(string uid, mapping tmp) {
901
902 if (clonep(ME)) return 0;
903
904 // Aber nur dann, wenn die auch von einer truhe abgerufen wird und keinem
905 // beliebigen anderen Objekt oder wenn ein EM setzt.
906 if (extern_call())
907 {
908 if (!objectp(previous_object()))
909 return 0;
910 if (blueprint(previous_object()) != ME
911 && load_name(previous_object()) != __FILE__[..<3]
912 && !ACCESS)
913 return 0;
914 }
915 if (!stringp(uid) || !mappingp(tmp))
916 return(0);
917
918 // tmp muss auf _dasselbe_ Mapping zeigen wie data[uid]. Wenn das nicht der
919 // Fall ist, ist was schiefgelaufen. Jedenfalls wird dann hier nen Fehler
920 // ausgeloest.
921 if (tmp != data[uid])
922 {
923// if(program_time(previous_object()) < 1400525694)
924// data[uid]=tmp;
925// else
926 raise_error("StoreData() gerufen und Daten sind nicht identisch "
927 "mit den bereits bekannten.\n");
928 }
929
930 // Absichtlich keine Kopie, damit sich alle Truhen das Mapping teilen.
931 //data[uid]=tmp;
932
933 // Savefile muss natuerlich geschrieben werden, fuer den naechsten Reboot.
934 if (find_call_out(#'save_me)==-1)
935 call_out(#'save_me,10); // Speichervorgaenge ggf. sammeln (upd -ar ...)
936 return(1);
937}
938
939// Maintainer ueber Truhenvorschlaege informieren
940void reset() {
941
942 if (clonep() || !sizeof(vorschlaege)) {
943 // ohne Vorschlaege ist auch kein reset noetig.
944 set_next_reset(-1);
945 return;
946 }
947 set_next_reset(12000); // alle 3-4h reicht.
948 foreach(string uid: MAINTAINER) {
949 object pl=find_player(uid);
950 if (objectp(pl) && query_idle(pl) < 1800)
951 tell_object(pl,BS("Es gibt neue Objektvorschlaege fuer die "
952 "Autoloadertruhe. Bitt schau da doch bei Gelegenheit mal drueber."));
953 }
954}
955
956
957// *************************************************************************
958
959// **************** SONSTIGES **********************************************
960
961// *seufz*
962// secure_level() aus der simul_efun ist hier momentan nicht brauchbar, weil
963// dort auch p/seher/moebel/autoloadertruhe in der Callerkette steht und das
964// ein levle von 0 hat. *seufz*
965nomask private int my_secure_level() {
966 int *level;
967 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
968 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
969 //von 0.
970 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
971 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
972 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
973 //INteractive geben muss.
974 level=map(caller_stack(1),function int (object caller)
975 {if (objectp(caller))
976 return(query_wiz_level(geteuid(caller)));
977 return(0); // kein Objekt da, 0.
978 } );
979 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
980}
981
982
983// debugkram
984mixed _query_content() {
985 //deep_copy, damit nicht jemand einfach so an den Daten rumbasteln kann.
986 return(deep_copy(autoloader));
987}
988
989mixed _query_owner() {
990 return(uuid);
991}
992
993int DeleteData(string uid) {
994 if (clonep(ME)) return 0;
995 if (!stringp(uid) || !sizeof(uid)) return -1;
996 if (!ACCESS) return -2;
997 //if (!ARCH_SECURITY) return -2;
998 m_delete(data,uid);
999 save_me();
1000 return(1);
1001}
1002
1003mixed testfun() {return "bin da\n";}
1004