blob: 461d5d02188323a7aa1e87d5a6a0451e49ebf2c7 [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) {
459 string error, oname;
460
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
472 // wenn kein Fehler: Objekt aus Liste loeschen. Sonst wird das Objekt
473 // zerstoert. Damit bleiben die Autoloader-Daten hier erhalten und der
474 // Spieler hat kein disfunktionales Objekt im Inventar.
475 if (error) {
476 ERRLOG(sprintf("Fehler beim Konfigurieren von %O. Fehlermeldung: %O."
477 "Daten: %O",ob,error,autoloader[oname,ALDATA]));
478 ob->remove(1);
479 if (objectp(ob))
480 destruct(ob);
481 }
482 else {
483 PICKLOG(sprintf("Objekt (%O) wurde entnommen.",ob));
484 m_delete(autoloader,oname);
485 // speichern
486 save_me();
487 }
488}
489
490protected void check_content() {
491 // wenn Objekte noch in der Truhe sind, also nicht erfolgreich in
492 // einen Spieler bewegt wurden, werden zerstoert. Datenverlust gibt es
493 // hierbei nicht, weil die Daten der Autoloader nur durch NotifyLeave()
494 // geloescht werden.
495 foreach(object ob: all_inventory()) {
496 ob->remove(1);
497 if (objectp(ob)) destruct(ob);
498 }
499}
500
501// hier nochmal schauen, ob das Objekt auch in den richtigen Spieler bewegt
502// wird... Falls das move dann hier abgebrochen wird, wird das Objekt im
503// naechsten HB von der Truhe zerstoert. (chk_contents())
504varargs int PreventLeave(object ob, mixed dest) {
505 object ob2;
506
507 //DEBUG(sprintf("PreventLeave(): Ob: %O, Dest: %O (%O)",
508 // ob,dest,(objectp(dest)?"Objekt":"Non-Object")));
509
510 if (!objectp(ob)) return 0;
511 // falls string uebergeben, erstmal zug. Spieler finden.
512 if (stringp(dest) && sizeof(dest))
513 dest=find_player(dest);
514
515 // richtiges INteractive? Dann darf das Objekt raus. Sonst nicht. ;-)
516 if (!objectp(dest) || !interactive(dest) || getuuid(dest)!=uuid)
517 return 1;
518
519 // pruefen, ob der Spieler schon ein Objekt dieser Blueprint dabei hat. Wenn
520 // ja, Abbruch, das koennte sonst zuviele Probleme geben.
521 if (objectp(ob2=present_clone(ob,dest))) {
522 tell_object(dest,BS("Du hast bereits "+ob2->name(WEN,0) +
523 " dabei. Zwei Gegenstaende dieser Art kannst du nicht gleichzeitig "
524 "mit Dir herumtragen."));
525 return 1; //nicht rausnehmen.
526 }
527 return 0;
528}
529
530// Objekte ausgeben, die hier angeblich drin sind. ;-) Gibt aber nur die
531// Autoloader aus, keine echten Objekt, die Container sind. Allerdings sollte
532// sowas eh nicht vorkommen. ;-)
533// flags: 1 - return array, 2 - don't collect equal objects '
534// flags: 4 - don't append infos for wizards
535varargs mixed make_invlist(object viewer, mixed inv, int flags) {
536 int iswiz;
537 mixed objs;
538
539 iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
540 // Mapping benutzen, um multiplen Overhead fuer allokation im foreach() zu
541 // vermeiden.
542 objs=m_allocate(sizeof(autoloader),1);
543 foreach(string oname, string *ids, string sh: autoloader) {
544 if (iswiz && !(flags & 4))
545 objs[oname]=sh + ". ["+oname+"]";
546 else
547 objs[oname]=sh + ".";
548 }
549 if(flags & 1) return(m_values(objs)-({""}));
550 if(!sizeof(autoloader)) return "";
551 return sprintf("%"+(sizeof(objs) > 6 ? "#" : "=")+"-78s",
552 implode(m_values(objs)-({""}), "\n")) + "\n";
553}
554
555// erzeugt das benannte Objekt und liefert es zurueck. Liefert 0 im Fehlerfall.
556// ausserdem bewegt es das Objekt in die Truhe, damit es ein Env hat und die
557// Truhe via NotifyLeave() mitkriegt, dass es tatsaechlich entnommen wurde.
558private object create_object(string oname) {
559 string error;
560 object ob;
MG Mud User88f12472016-06-24 23:31:02 +0200561 if (!member(autoloader,oname)) return 0;
562
563 //Blueprint finden (ja, das ist nicht unbedingt noetig, man koennte auch
564 //direkt clonen)
565 if (error=catch(ob=load_object(oname);publish) ||
566 !objectp(ob)) {
567 ERRLOG(sprintf("Konnte %s nicht laden/finden. Fehler: %O",
568 oname,error));
569 return 0;
570 }
571 // clonen
572 if (error=catch(ob=clone_object(oname);publish) ||
573 !objectp(ob)) {
574 ERRLOG(sprintf("Konnte %s nicht clonen. Fehler: %O",
575 oname,error));
576 return 0;
577 }
578 // konfigurieren
579 error=catch(ob->SetProp(P_AUTOLOADOBJ,autoloader[oname,ALDATA]);publish);
580
581 //Objekt bewegen, dabei Objekt in glob. Var. merken, damit PreventInsert()
582 //und NotifyInsert() es ignorieren. *seufz*
583 ob_in_bewegung=ob;
584 ob->move(ME,M_NOCHECK);
585 ob_in_bewegung=0;
586
587 // jetzt noch nen Callout starten, damit das Objekt zerstoert wird, wenn es
588 // nicht wirklich in einen Spieler bewegt wird.
589 call_out(#'check_content,1);
590
591 return(ob);
592}
593
594// Schauen, ob die truhe ein Objekt mit passender ID enthaelt. Wenn
595// ja, das Objekt erzeugen und zurueckliefern.
596// Diese Funktion hat eine Reihe von Nachteilen bzw. Defiziten ggue. der
597// normalerweise in Containern definierten Funktion:
598// - Nur das erste Objekt, auf das id passt. Ich hab erstmal keine Lust,
599// hier mehrere Objekte zu erzeugen. Mal schauen, ob es so geht. Auch waere
600// es ein Problem, wenn viele Objekt die Evalgrenze bzw. nicht alle in den
601// Spieler bewegt werden, nachdem sie erzeugt wurden.
602// - keine komplexen Ausdruecke, nur reine IDs.
603// Ich halte den Aufwand fuer nicht gerechtfertigt.
604object *present_objects( string complex_desc ) {
605 object ob;
606 if (!stringp(complex_desc) || !sizeof(complex_desc))
607 return ({});
608 // diese Funktion liefert nur Objete zurueck, wenn der richtige Interactive
609 // versucht, sie zu entnehmen. ;-)
610 if (!objectp(this_interactive()) ||
611 getuuid(this_interactive())!=uuid)
612 return ({});
613
614 // "alles" liefert das erstbeste Objekt.
615 if (complex_desc=="alles" && sizeof(autoloader))
616 {
617 string oname=m_indices(autoloader)[0];
618 ob=create_object(oname);
619 if (objectp(ob)) return ({ob});
620 }
621
622 // ueber alle Eintraege gehen, bis eine ID stimmt, erstes passendes Objekt
623 // erzeugen und in einem Array zurueckliefern.
624 foreach(string oname, string *ids: autoloader) {
625 if (member(ids,complex_desc)==-1) continue;
626 ob=create_object(oname);
627 break; //objekt gefunden, fertig hier
628 }
629 if (objectp(ob)) return ({ob});
630 return ({}); // nix gefunden
631}
632
633
634
635// ******************* Verwaltung *********************************
636
637// registriert die Truhe auf den jeweiligen Eigentuemer.
638protected void SetBesitzer(string unused, string newuuid) {
639 if (!stringp(newuuid) || !sizeof(newuuid)) return;
640 // wenn schon registriert, abbrechen
641 if (stringp(uuid) && sizeof(uuid)) return;
642
643 uuid=newuuid;
644 Set(H_FURNITURE,uuid,F_VALUE); //Setmethode umgehen, sonst Rekursion
645 // ab jetzt nur noch von der Truhe selber.
646 Set(H_FURNITURE,SECURED,F_MODE_AS);
647
648 // Daten fuer den Benutzer aus der Blueprint holen (BP liefert KEINE Kopie
649 // und es darf KEINE gemacht werden!):
Arathornae0fb0b2020-12-01 23:59:55 +0100650 autoloader=({mapping})load_name()->GetData(uuid);
MG Mud User88f12472016-06-24 23:31:02 +0200651
652 // keine Daten gekriegt? -> Fehler loggen
653 if (!mappingp(autoloader))
654 {
655 ERRLOG(sprintf("Keine gueltigen Daten vom Truhenmaster (BP) erhalten. "
656 "initialisiere mit leerem Mapping. :-("));
657 raise_error(sprintf(
658 "Keine gueltigen Daten vom Truhenmaster (BP) fuer UUID %s "
659 "erhalten.\n",uuid));
660 }
661}
662
663// Set-Funktion
664string _set_h_furniture(mixed arg) {
665 if (stringp(arg))
666 {
667 SetBesitzer(0,arg); // bricht ab, wenn bereits registriert.
668 }
669 return(uuid);
670}
671
672// Falls das Speichern in der BP nicht klappte, rufen die Clones diese
673// Funktion. Schreiben der Daten in in Logfile zur Restaurieren per Hand.
674private void EmergencyStore(int res) {
675 ERRLOG(sprintf("EmergencyStore() called. Rueckgabewert des "
676 "Truhenmaster (BP) war: %O. Speichere Daten in Logfile. ",res));
677 write_file(__DIR__+"ALTRUHE.NOTFALLDATEN",
678 sprintf("Daten fuer %O:\n%O\n",uuid,autoloader));
679}
680
681protected void save_me() {
682 int res;
683 // nur BP speichern
684 if (!clonep(ME))
685 save_object(SAVEFILE);
686 else
687 {
688 if (objectp(blueprint(ME)))
Arathornae0fb0b2020-12-01 23:59:55 +0100689 res=({int})blueprint(ME)->StoreData(uuid,autoloader);
MG Mud User88f12472016-06-24 23:31:02 +0200690 else
Arathornae0fb0b2020-12-01 23:59:55 +0100691 res=({int})load_object(load_name(ME))->StoreData(uuid,autoloader);
MG Mud User88f12472016-06-24 23:31:02 +0200692
693 if (res!=1)
694 EmergencyStore(res); // Daten in einem Notfall-Logfile ablegen.
695 }
696}
697
698
699// diese Funktion wird vom Seherhausraum gerufen, sobald ein rebootfestes
700// Moebelstueck erzeugt und konfiguriert wurde.
701void post_create() {
702 if (!clonep()) return;
703
704 // wenn jetzt kein Env: destructen
705 if (!objectp(environment())) {
706 remove(1);
707 return;
708 }
709 //beim Schrankmaster registrieren
710 SCHRANKMASTER->RegisterCnt(ME, QueryProp("cnt_version_std")
711 +":"+QueryProp("cnt_version_obj"), environment()->QueryOwner(),
712 environment());
713}
714
715// diese Funktion wird vom Schrankmaster gerufen, wenn dieser meint, dass
716// dieses Objekt neu erstellt werden sollte.
717int UpdateMe()
718{
719 if (!clonep())
720 return 0;
721 if (!objectp(environment()))
722 return 1;
723 object ob=clone_object(load_name(ME));
724 if (objectp(ob)) {
725 object oldenv=environment();
726 // UUID uebertragen
727 ob->SetProp(H_FURNITURE, uuid);
728 // hierhier bewegen, dabei werden UUID und Daten von der neuen Truhe meist
729 // automatisch geholt.
730 ob->move(oldenv,M_NOCHECK);
731 // jetzt erst post_create() rufen!
732 ob->post_create();
733 // dieses Objekt rausbewegen (damit das Seherhaus es austraegt).
734 move("/room/void",M_NOCHECK);
735 // Seherhausraum speichern (koennte teuer sein!)
736 oldenv->Save(1);
737 // selbstzerstoeren
738 // __INT_MAX__ bedeutet: nicht speichern, die neue Truhe hat die daten
739 // schon aus der BP abgefragt.
740 remove(__INT_MAX__);
741 }
742 return(1);
743}
744
745// bei Selbstzerstoerung speichern bzw. Daten an die Blueprint uebermitteln
746// und beim Schrankmaster abmelden.
747varargs int remove(int silent) {
748 string uid="";
749
750 // Blueprint speichern im Savefile, Clones uebertragen die Daten hier an die
751 // Blueprint. Clones nur, wenn nicht __INT_MAX__ uebergeben wurde.
752 if (silent!=__INT_MAX__ || !clonep())
753 save_me();
754
755 if (clonep()) {
756 // Clone melden sich beim Schrankmaster ab.
757 if (objectp(environment())) {
758 uid=environment()->QueryOwner();
759 }
760 //beim Schrankmaster deregistrieren
761 SCHRANKMASTER->RemoveCnt(ME,uid);
762 }
763 return(::remove(silent));
764}
765
766// ***************** NUR BLUEPRINTS *********************************
767
768// neuen Autoloader zulassen (nur EM+!)
769varargs int AddAutoloader(string path,string nam) {
770 object ob;
771 if (clonep(ME)) return 0;
772 if (!stringp(path) || !sizeof(path)) return -1;
773 if (!ACCESS) return -2;
774 //if (!ARCH_SECURITY) return -2;
775 if (member(whitelist,path)) return -3;
776 if (catch(ob=load_object(path);publish)
777 || !objectp(ob))
778 return -4;
779 // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
780 if (!stringp(nam) || !sizeof(nam)) {
781 nam=ob->name(WER,0);
782 if (!stringp(nam) || !sizeof(nam)) return -5;
783 }
784 ITEMLOG(sprintf("%s erlaubt: %s (%s)\n",getuid(this_interactive()),
785 path,nam));
786 whitelist+=([path:capitalize(nam)]);
787 if (member(vorschlaege,path))
788 m_delete(vorschlaege,path);
789 save_me();
790 return(1);
791}
792
793// Autoloader aus Erlaubnisliste entfernen (nur EM+!)
794int RemoveAutoloader(string path) {
795 if (clonep(ME)) return 0;
796 if (!stringp(path) || !sizeof(path)) return -1;
797 if (!ACCESS) return -2;
798 //if (!ARCH_SECURITY) return -2;
799 if (!member(whitelist,path)) return -3;
800 ITEMLOG(sprintf("%s widerruft Erlaubnis: %s (%s)\n",getuid(this_interactive()),
801 path,whitelist[path]));
802 whitelist-=([path]);
803 save_me();
804 return(1);
805}
806
807// erlaubte Autoloader abfragen
808mapping QueryAutoloader() {
809 return(copy(whitelist));
810}
811
812// neuen Autoloader in Blacklist eintragen (nur EM+!)
813varargs int AddBlacklist(string path, string nam) {
814 object ob;
815 if (clonep(ME)) return 0;
816 if (!stringp(path) || !sizeof(path)) return -1;
817 if (!ACCESS) return -2;
818 //if (!ARCH_SECURITY) return -2;
819
820 if (member(blacklist,path)) return -3;
821
822 if (catch(ob=load_object(path);publish)
823 || !objectp(ob))
824 return -4;
825 // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
826 if (!stringp(nam) || !sizeof(nam)) {
827 nam=ob->name(WER,0);
828 if (!stringp(nam) || !sizeof(nam)) return -5;
829 }
830 // ggf. erlaubten entfernen.
831 if (member(whitelist,path))
832 RemoveAutoloader(path);
833 ITEMLOG(sprintf("%s verbietet: %s (%s)\n",getuid(this_interactive()),
834 path,nam));
835
836 blacklist+=([path:capitalize(nam)]);
837
838 if (member(vorschlaege,path))
839 m_delete(vorschlaege,path);
840
841 save_me();
842 return(1);
843}
844
845// Autoloader aus Blacklist entfernen (nur EM+!)
846int RemoveBlacklist(string path) {
847 if (clonep(ME)) return 0;
848 if (!stringp(path) || !sizeof(path)) return -1;
849 if (!ACCESS) return -2;
850 //if (!ARCH_SECURITY) return -2;
851 if (member(blacklist,path)==-1) return -3;
852 ITEMLOG(sprintf("%s loescht: %s (%s) von Blacklist\n",getuid(this_interactive()),
853 path,blacklist[path]));
854 blacklist-=([path]);
855 save_me();
856 return(1);
857}
858
859// gesperrte Autoloader abfragen
860mapping QueryBlacklist() {
861 return(copy(blacklist));
862}
863
864// vorschlaege abfragen
865varargs mixed QueryVorschlaege(int format) {
866 string res="\n";
867 if (!format) return(copy(vorschlaege));
868
869 foreach(string oname, string nam, string sh: vorschlaege) {
870 res+=sprintf("%.78s:\n %.37s,%.37s\n",oname,nam,sh);
871 }
872
873 if (format==2 && objectp(PL))
874 tell_object(PL,res);
875 else
876 return res;
877 return 0;
878}
879
880// Wird diese funktion in der Blueprint gerufen, liefert sie die Daten fuer
881// eine Truhe mit dieser UUID zurueck. Aber nur dann, wenn die auch von einer
882// truhe abgerufen wird und keinem beliebigen anderen Objekt oder wenn ein EM
883// abfragt.
884mapping GetData(string uid) {
885 if (clonep(ME)) return 0;
886 if (extern_call())
887 {
888 if (!objectp(previous_object())) return 0;
889 if (blueprint(previous_object()) != ME
890 && load_name(previous_object()) != __FILE__[..<3]
891 && !ACCESS)
892 return(0);
893 }
894 if (!stringp(uid)) return ([]);
895
896 if (!member(data, uid))
897 data[uid] = m_allocate(1,3);
898
899 // Absichtlich keine Kopie, damit die Truhen (falls jemand mehrere kauft)
900 // sich alle eine Kopie des Mappings teilen.
901 return data[uid];
902}
903
904// Loest die Speicherung der Daten im Savefile aus, denn irgendwelche Daten
905// haben sich in einem Clone geaendert, welche noch auf die Platte muessen.
906// Diese Funktion speicherte frueher Daten aus den Clones wieder im Mapping
907// des Masters (der Blueprint). Dies ist heute nicht mehr so, weil alle Truhen
908// (eines Benutzers) sich data[uid] teilen. Daher wird hier ein Fehler
909// ausgeloest, wenn es von einer Truhe ein Mapping kriegt, welches nicht
910// identisch (!) mit dem data[uid] ist.
911// liefert 0 im Fehlerfall!
912int StoreData(string uid, mapping tmp) {
913
914 if (clonep(ME)) return 0;
915
916 // Aber nur dann, wenn die auch von einer truhe abgerufen wird und keinem
917 // beliebigen anderen Objekt oder wenn ein EM setzt.
918 if (extern_call())
919 {
920 if (!objectp(previous_object()))
921 return 0;
922 if (blueprint(previous_object()) != ME
923 && load_name(previous_object()) != __FILE__[..<3]
924 && !ACCESS)
925 return 0;
926 }
927 if (!stringp(uid) || !mappingp(tmp))
928 return(0);
929
930 // tmp muss auf _dasselbe_ Mapping zeigen wie data[uid]. Wenn das nicht der
931 // Fall ist, ist was schiefgelaufen. Jedenfalls wird dann hier nen Fehler
932 // ausgeloest.
933 if (tmp != data[uid])
934 {
935// if(program_time(previous_object()) < 1400525694)
936// data[uid]=tmp;
937// else
938 raise_error("StoreData() gerufen und Daten sind nicht identisch "
939 "mit den bereits bekannten.\n");
940 }
941
942 // Absichtlich keine Kopie, damit sich alle Truhen das Mapping teilen.
943 //data[uid]=tmp;
944
945 // Savefile muss natuerlich geschrieben werden, fuer den naechsten Reboot.
946 if (find_call_out(#'save_me)==-1)
947 call_out(#'save_me,10); // Speichervorgaenge ggf. sammeln (upd -ar ...)
948 return(1);
949}
950
951// Maintainer ueber Truhenvorschlaege informieren
952void reset() {
953
954 if (clonep() || !sizeof(vorschlaege)) {
955 // ohne Vorschlaege ist auch kein reset noetig.
956 set_next_reset(-1);
957 return;
958 }
959 set_next_reset(12000); // alle 3-4h reicht.
960 foreach(string uid: MAINTAINER) {
961 object pl=find_player(uid);
962 if (objectp(pl) && query_idle(pl) < 1800)
963 tell_object(pl,BS("Es gibt neue Objektvorschlaege fuer die "
964 "Autoloadertruhe. Bitt schau da doch bei Gelegenheit mal drueber."));
965 }
966}
967
968
969// *************************************************************************
970
971// **************** SONSTIGES **********************************************
972
973// *seufz*
974// secure_level() aus der simul_efun ist hier momentan nicht brauchbar, weil
975// dort auch p/seher/moebel/autoloadertruhe in der Callerkette steht und das
976// ein levle von 0 hat. *seufz*
977nomask private int my_secure_level() {
978 int *level;
979 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
980 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
981 //von 0.
982 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
983 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
984 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
985 //INteractive geben muss.
986 level=map(caller_stack(1),function int (object caller)
987 {if (objectp(caller))
988 return(query_wiz_level(geteuid(caller)));
989 return(0); // kein Objekt da, 0.
990 } );
991 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
992}
993
994
995// debugkram
996mixed _query_content() {
997 //deep_copy, damit nicht jemand einfach so an den Daten rumbasteln kann.
998 return(deep_copy(autoloader));
999}
1000
1001mixed _query_owner() {
1002 return(uuid);
1003}
1004
1005int DeleteData(string uid) {
1006 if (clonep(ME)) return 0;
1007 if (!stringp(uid) || !sizeof(uid)) return -1;
1008 if (!ACCESS) return -2;
1009 //if (!ARCH_SECURITY) return -2;
1010 m_delete(data,uid);
1011 save_me();
1012 return(1);
1013}
1014
1015mixed testfun() {return "bin da\n";}
1016