blob: a677902fa74a648854fb71575518150b9b37d9da [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// master.c -- master object
4//
5// $Id: master.c 9530 2016-03-17 19:59:01Z Zesstra $
6#pragma strict_types
7#pragma no_clone
8#pragma no_shadow
9#pragma no_inherit
10#pragma verbose_errors
11#pragma combine_strings
12#pragma pedantic
13#pragma range_check
14#pragma warn_deprecated
15
16#include "/sys/files.h"
17#include "/sys/interactive_info.h"
18#include "/sys/driver_info.h"
19
20inherit "/secure/master/misc";
21inherit "/secure/master/userinfo";
22inherit "/secure/master/network";
23inherit "/secure/master/domain";
24inherit "/secure/master/guild";
25inherit "/secure/master/file_access";
26inherit "/secure/master/players_deny";
27
28#include "/secure/master.h"
29
30//for limited, set_limit(), etc. Abs. Pfad noetig, da noch kein Include-Hook
31#include "/sys/rtlimits.h"
32#include "/sys/debug_info.h"
MG Mud User88f12472016-06-24 23:31:02 +020033#include "/sys/configuration.h"
34#include "/sys/regexp.h"
35
36// Module des Master einfuegen. Per #include, damits geringfuegig schneller.
37// Da die Module ja nirgendwo sonst geerbt werden koennten, ist dies
38// ausnahmsweise OK. ;)
39#include "/secure/master/destruct.c"
40#include "/secure/master/autoinclude.c"
41
42// Fuer Logfile(-Rotation)
43#define RESETINT 3600 // jede Stunde
44#define LOGROTATE (24*2) // alle 48h
45#define LOGFILEAGE (RESETINT*LOGROTATE) // alle 2 tage wechseln
46#define LOGNUM 3 // Anzahl der Logfiles
47
48private nosave int logrotate_counter; //READ_FILE alle LOGROTATE Resets rotieren
49// Wird gerade versucht, die simul_efuns zu laden? Wenn ja, duerfen keine
50// sefuns gerufen werden.
51private nosave int loading_simul_efuns;
52// wird gerade ein Fehler bearbeitet? Dient zur Erkennung von Rekursionen in
53// der Fehlerbehandlung
54private nosave int handling_error;
55
56// #####################
57//######################## Start des Masters ############################
58// #####################
59
60// Initialisierung des Masters
61//arg==0: Mud startet gerade, arg==1: Master wurde reaktiviert,
62//arg==2: Master reaktiviert (Variablen weg), arg==3: Master wurde
63//neugeladen.
64protected void inaugurate_master(int arg) {
65
66 set_driver_hook(H_REGEXP_PACKAGE, RE_TRADITIONAL);
Bugfix05c09d72017-02-14 21:26:20 +010067
Zesstra86607bb2017-06-17 19:02:40 +020068 efun::configure_object(this_object(), OC_EUID, ROOTID);
Bugfix05c09d72017-02-14 21:26:20 +010069
70 // Bei Neustart wizinfo initialisieren und ggf. Ordner in /data erstellen.
71 if (!arg)
72 {
73 set_extra_wizinfo(0, allocate(BACKBONE_WIZINFO_SIZE));
74 CreateDataDirectories();
75 }
76
MG Mud User88f12472016-06-24 23:31:02 +020077 userinfo::create();
78 LoadPLDenylists();
79
80 // Was soll vor jede Datei geschrieben werden?
81 set_driver_hook(H_AUTO_INCLUDE, #'autoincludehook);
82
83 //div. Listen einlesen
84 ReloadBanishFile();
85 ReloadDeputyFile();
86 ReloadInsecureFile();
Bugfix05c09d72017-02-14 21:26:20 +010087
MG Mud User88f12472016-06-24 23:31:02 +020088 //simul_efun.c nach inaugurate_master() starten und initialisieren,
89 //(so richtig seh ich den Sinn hier momentan nicht ein...
90 //call_out("start_simul_efun",0);
91
92 // Reset festsetzen (1h)
93 set_next_reset(RESETINT);
94
95 // Driver konfigurieren
96 // Lagerkennung erst ne Minute spaeter, waehrend preload darfs momentan
97 // ruhig laggen (Zeit: vorlaeufig 1min)
98 call_out(#'configure_driver, 60, DC_LONG_EXEC_TIME, 100000);
99
100 // Hooks setzen
101
102 // Standardincludeverzeichnisse fuer #include <>
103 set_driver_hook(H_INCLUDE_DIRS, ({"/secure/","/sys/"}) );
104
105 //Nach dem Laden/Clonen create() im Objekt rufen
106 set_driver_hook(H_CREATE_CLONE, "create");
107 set_driver_hook(H_CREATE_OB, "create");
108 set_driver_hook(H_CREATE_SUPER, "create_super");
109
110 // Bei Reset reset() im Objekt aufrufen
111 set_driver_hook(H_RESET, "reset");
112
113 // Zum Aufraeumen clean_up() im Objekt aufrufen
114 set_driver_hook(H_CLEAN_UP, "clean_up");
115
116 // Jede Eingabe wird ueber modify_command gelenkt
117 set_driver_hook(H_MODIFY_COMMAND, "modify_command");
118 // Dieser Hook ist mandatory, aber wird nur benutzt, wenn via
119 // set_modify_command() aktiviert - was wir nicht (mehr) tun. Insofern ist
120 // das Relikt aus alten Zeiten.
121 //set_driver_hook(H_MODIFY_COMMAND_FNAME, "modify_command");
122
123 // Standard-Fehlermeldung
124 //set_driver_hook(H_NOTIFY_FAIL, "Wie bitte?\n");
125 set_driver_hook(H_NOTIFY_FAIL, function string (string cmd, object tp)
Zesstra2bb35892016-12-20 20:56:35 +0100126 {if (tp && stringp(cmd=(string)tp->QueryProp(P_DEFAULT_NOTIFY_FAIL)))
MG Mud User88f12472016-06-24 23:31:02 +0200127 return(cmd);
128 return("Wie bitte?\n");});
129
130 // Was machen bei telnet_neg
131 set_driver_hook(H_TELNET_NEG,"telnet_neg");
MG Mud User88f12472016-06-24 23:31:02 +0200132
133 // Promptbehandlung: Defaultprompt setzen und dafuer sorgen, dass alle
134 // Promptausgaben durch print_prompt im Interactive laufen, damit das EOR
135 // angehaengt wird.
136 set_driver_hook(H_PRINT_PROMPT, "print_prompt");
137 set_driver_hook(H_DEFAULT_PROMPT, "> ");
138
139 // EUID und UID muessen neue Objekte auch noch kriegen, Hooks dafuer setzen
140 set_driver_hook(H_LOAD_UIDS, #'load_uid_hook);
141 set_driver_hook(H_CLONE_UIDS, #'clone_uid_hook);
142
143 // Meldung bei vollem Mud
144 set_driver_hook(H_NO_IPC_SLOT, "Momentan sind leider zuviele Spieler im "
145 +MUDNAME+" eingeloggt.\nBitte komm doch etwas spaeter nochmal "
146 "vorbei!\n");
147
148 // Nun der beruechtigte Move-Hook ...
149 // (bewegt objekt und ruft alle notwendigen inits auf)
150 // Hier wird H_MOVE_OBJECT0 benutzt, d.h. die lambda wird an das aktuelle
151 // Objekt vor Ausfuehrung gebunden, nicht an 'item', was move_objet()
152 // uebergeben wurde.
153 set_driver_hook(H_MOVE_OBJECT0,
154 unbound_lambda(({'item,'dest}),
155 ({#',, // item!=this_object?
156 ({#'?,({#'!=,'item,({#'this_object})}),
157 ({#'raise_error, // ->Fehler!
158 "Illegal to move other object than this_object()\n"}) }),
Zesstra1b809c72018-03-02 00:31:58 +0100159 ({#'=,'oldenv,({#'environment,'item}) }), // altes Env merken
MG Mud User88f12472016-06-24 23:31:02 +0200160 ({#'efun::set_environment,'item,'dest}), // item nach dest bewegen
161 ({#'?,
162 ({#'living,'item}), // living(item)?
163 ({#',,
164 ({#'efun::set_this_player,'item}), // set_this_player(item)
Zesstra1b809c72018-03-02 00:31:58 +0100165 ({#'call_other,'dest,"init",'oldenv}),// dest->init(oldenv)
MG Mud User88f12472016-06-24 23:31:02 +0200166 ({#'?!,
167 ({#'&&, // !objectp(item)||
168 ({#'objectp, 'item}), // env(item)!=dest?
169 ({#'==,({#'environment, 'item}),'dest})}),
170 ({#'return})})})}), // -> fertig
171 ({#'=,'others,({#'-,({#'all_inventory,'dest}),({#'({,'item})})}),
172#ifdef EMACS_NERV
173 }) // Emacs kann ({#'({ nicht parsen // others=all_inv(dest)-
174#endif // ({item})
Zesstra1b809c72018-03-02 00:31:58 +0100175 ({#'filter,'others,lambda(({'ob,'item,'lastenv}),
MG Mud User88f12472016-06-24 23:31:02 +0200176 ({#'?,
177 ({#'&&,
178 ({#'objectp,'item}), // objectp(item)&&
179 ({#'living,'ob}), // living(ob)&&
180 ({#'==,({#'environment,'item}),({#'environment,'ob})})}),
181 ({#',, // env(item)==env(ob)?
182 ({#'efun::set_this_player, 'ob}), // set_this_player(ob)
Zesstra1b809c72018-03-02 00:31:58 +0100183 ({#'call_other,'item, "init",'lastenv}) // item->init(lastenv)
184 })})),'item,'oldenv}),
MG Mud User88f12472016-06-24 23:31:02 +0200185 ({#'?,
186 ({#'living,'item}), // living(item)?
187 ({#',,
188 ({#'efun::set_this_player,'item}), // set_this_player(item)
Zesstra1b809c72018-03-02 00:31:58 +0100189 ({#'filter,'others,lambda(({'ob,'item,'lastenv}),
MG Mud User88f12472016-06-24 23:31:02 +0200190 ({#'?,
191 ({#'&&,
192 ({#'objectp,'item}), // objectp(item)&&
193 ({#'objectp,'ob}), // objectp(ob)&&
194 ({#'==,({#'environment,'item}),({#'environment,'ob})})
195 }), // env(item)==env(ob)?
Zesstra1b809c72018-03-02 00:31:58 +0100196 ({#'call_other,'ob,"init",'lastenv}) // ob->init(lastenv)
197 })),'item, 'oldenv})})}),
MG Mud User88f12472016-06-24 23:31:02 +0200198 ({#'?,
199 ({#'&&,
200 ({#'objectp,'item}), // objectp(item)&&
201 ({#'living,'dest}), // living(dest)&&
202 ({#'==,({#'environment,'item}),'dest})// env(item)==dest?
203 }),
204 ({#',,
205 ({#'efun::set_this_player,'dest}), // set_this_player(dest)
Zesstra1b809c72018-03-02 00:31:58 +0100206 ({#'call_other,'item,"init",'oldenv})})})}))); //item->init(oldenv)
MG Mud User88f12472016-06-24 23:31:02 +0200207 DEBUG("Master inaugurated");
208 return;
209}
210
211// epilog() gibt die Dateien zurueck, die vorgeladen werden muessen
212protected string *epilog(int eflag) {
213 string *files, *domains;
214 int i;
215
Zesstra86607bb2017-06-17 19:02:40 +0200216 efun::configure_object(this_object(), OC_EUID, ROOTID);
MG Mud User88f12472016-06-24 23:31:02 +0200217 ReloadBanishFile();
218 ReloadDeputyFile();
219 ReloadInsecureFile();
220
221 if (eflag) {
Zesstrad77ab422019-09-27 17:39:27 +0200222 debug_message(
223 "epilog(): -e angegeben -> Preloading unterdrueckt ...\n",
224 DMSG_STAMP);
MG Mud User88f12472016-06-24 23:31:02 +0200225 return 0;
226 }
227
Zesstrad77ab422019-09-27 17:39:27 +0200228 debug_message("Preloading gestartet.\n", DMSG_STAMP);
MG Mud User88f12472016-06-24 23:31:02 +0200229
230 //Files fuers Preloading und Regionen holen
231 files=explode_files("/std/preload_file") + explode_files("/d/preload_file");
232 domains=get_domains() + ({"erzmagier"}); // EM haben auch ein Preload File
233
234 for (i=sizeof(domains);i--;)
235 files+=explode_files("/d/"+domains[i]+"/preload_file");
Zesstrad77ab422019-09-27 17:39:27 +0200236
MG Mud User88f12472016-06-24 23:31:02 +0200237 return files;
238}
239
240// preload() wird fuer jeder Datei im Preload einmal durchlaufen
241protected void preload(string file) {
242 string err;
243 string name;
244 int *res, zeit;
245
246 // Kein richtiger Dateiname: fertig
247 if(!file || !file[0] || file[0] == ';' || file_size(file+".c") < 0) return;
248
249 // Kein Besitzer -> Meldung ausgeben, fertig
250 if (!(name=creator_file(file)))
251 {
Zesstrad77ab422019-09-27 17:39:27 +0200252 debug_message(
253 sprintf("preload: Kein Besitzer gefunden fuer Datei %s.\n",file),
254 DMSG_STDOUT|DMSG_STDERR);
MG Mud User88f12472016-06-24 23:31:02 +0200255 return;
256 }
257
Zesstra86607bb2017-06-17 19:02:40 +0200258 efun::configure_object(this_object(), OC_EUID, name);
MG Mud User88f12472016-06-24 23:31:02 +0200259
260 // Dann mal laden .. dabei Zeit messen
261 zeit = apply(#'+,rusage()[0..1]);
262
263 // Waehrend des Preloads sind 1.5MTicks leider nicht soviel, insb. wenn
264 // sowas wie der channeld jede menge Objekte laden muss, die wiederum erst
265 // das ganze Geraffel aus /std/ laden. Daher hier einfach mal fuers Preload
266 // die Grenze hoch.
267 err = catch(limited(#'load_object,({5000000}),file));
268
269 if (err != 0)
Zesstrad77ab422019-09-27 17:39:27 +0200270 debug_message(sprintf("\nFehler beim Laden von %s:\n%s\n",file,err),
271 DMSG_STDOUT|DMSG_STDERR);
MG Mud User88f12472016-06-24 23:31:02 +0200272 else
273 {
Zesstrad77ab422019-09-27 17:39:27 +0200274 // Datei, Besitzer und Ladezeit ausgeben
MG Mud User88f12472016-06-24 23:31:02 +0200275 zeit=apply(#'+,rusage()[0..1])-zeit;
Zesstrad77ab422019-09-27 17:39:27 +0200276 debug_message(sprintf("%-50s%-15s (%2d.%:02d s)\n",
277 file, name, zeit/1000,zeit%1000));
MG Mud User88f12472016-06-24 23:31:02 +0200278 }
279
280 // Noch EUID zuruecksetzen
Zesstra86607bb2017-06-17 19:02:40 +0200281 efun::configure_object(this_object(), OC_EUID, ROOTID);
MG Mud User88f12472016-06-24 23:31:02 +0200282
283 return;
284}
285
286//simul_efun.c laden oder die spare_simul_efun.c als Fallback
287//es ist moeglich, hier ein Array von strings zurueckzugeben. Die
288//nachfolgenden gelten als Backup-Objekte, falls eine sefun im Hauptobjekt
289//nicht gefunden wird.
290protected string *get_simul_efun() {
291 string err;
292
293 ++loading_simul_efuns;
294
295 if (!(err=catch(SIMUL_EFUN_FILE->start_simul_efun())) ) {
296 --loading_simul_efuns;
297 return ({SIMUL_EFUN_FILE});
298 }
Zesstrad77ab422019-09-27 17:39:27 +0200299
MG Mud User88f12472016-06-24 23:31:02 +0200300 debug_message("Failed to load simul efun " + SIMUL_EFUN_FILE +
Zesstrad77ab422019-09-27 17:39:27 +0200301 " " + err, DMSG_STDOUT | DMSG_LOGFILE | DMSG_STAMP);
MG Mud User88f12472016-06-24 23:31:02 +0200302
303 if (!(err=catch(SPARE_SIMUL_EFUN_FILE->start_simul_efun())) ) {
304 --loading_simul_efuns;
305 return ({SPARE_SIMUL_EFUN_FILE});
306 }
MG Mud User88f12472016-06-24 23:31:02 +0200307
Zesstrad77ab422019-09-27 17:39:27 +0200308 debug_message("Failed to load spare simul efun " + SPARE_SIMUL_EFUN_FILE +
309 " " + err, DMSG_STDOUT | DMSG_LOGFILE | DMSG_STAMP);
310
311
MG Mud User88f12472016-06-24 23:31:02 +0200312 efun::shutdown();
Zesstrad77ab422019-09-27 17:39:27 +0200313
MG Mud User88f12472016-06-24 23:31:02 +0200314 return 0;
315}
316
317protected mixed call_sefun(string sefun, varargs mixed args) {
318 if (!loading_simul_efuns)
319 return apply(symbol_function(sefun), args);
320 else {
321 switch(sefun) {
322 case "log_file":
323 case "secure_level":
324 case "secure_euid":
325 case "find_player":
326 case "find_netdead":
327 case "find_living":
328 case "process_call":
329 case "replace_personal":
330 case "file_time":
331 return 0;
332 case "dtime":
333 if (pointerp(args) && sizeof(args))
334 return strftime("%a %d. %b %Y, %H:%M:%S",args[0]);
335 else
336 return strftime("%a %d. %b %Y, %H:%M:%S");
337 case "uptime":
338 return to_string(time()-__BOOT_TIME__) + " Sekunden";
339 }
340 }
341 return 0;
342}
343
344
345// Preload manuell wiederholen; Keine GD-Funktion
346void redo_preload()
347{
348 string *to_preload;
349 int i,j;
350
351 to_preload=epilog(0);
352 j=sizeof(to_preload);
353 for (i=0;i<j;i++)
354 catch(preload(to_preload[i]));
355 return;
356}
357
358// ##################
359//########################## Standard-Lfuns #############################
360// ##################
361
362// Keine GD-Funktion, aber sinnvoll.
363public varargs int remove(int silent)
364{
365 write("Der Master will aber nicht zerstoert werden!\n");
366 return 0;
367}
368
369// Einige repetitiven Taetigkeiten kann der Reset erledigen
370protected void reset()
371{
372 int i, *date;
373 mixed *wl;
374
375 //Darf nicht von Hand aufgerufen werden!
376 if (TI||TP) return;
377
378 // Naechsten Reset setzen
379 set_next_reset(RESETINT);
380
381 // Userinfo aufraeumen
382 _cleanup_uinfo();
383
384 // Projekt-Cache loeschen
385 _cleanup_projects();
386
387 // Wenn Zeit abgelaufen, dann READ_FILE-Log rotieren
388 if (!logrotate_counter--)
389 {
390 i=time()/LOGFILEAGE;
391 date=get_dir(sprintf("%s.%d", READ_FILE,i%LOGNUM), GETDIR_DATES);
392 if (pointerp(date)&&sizeof(date)&&
393 (date[0]/LOGFILEAGE)==i) return;
394 if (file_size(READ_FILE)>0)
395 rename(READ_FILE, sprintf("%s.%d", READ_FILE,i%LOGNUM));
396 logrotate_counter=LOGROTATE;
397 }
398 // einmal am Tag die UIDAliase resetten. Hierzu wird der logrotate_counter
399 // missbraucht.
400 if (!(logrotate_counter%24)) {
401 ResetUIDAliase();
402#ifdef _PUREFTPD_
403 expire_ftp_user();
404#endif
405 }
406
407 // Wizlist altert
408 wl = wizlist_info();
409 for (i=sizeof(wl); i--; )
410 set_extra_wizinfo(wl[i][WL_NAME], wl[i][WL_EXTRA] * 99 / 100);
411
MG Mud User88f12472016-06-24 23:31:02 +0200412 return;
413}
414
415
416// #################
417//########################## ID-Management ##############################
418// #################
419
420// Magier-ID fuer Datei str (als Objekt oder als String) ermitteln
421string creator_file(mixed str) {
422 string *strs,tmp;
423 int s;
424
Zesstraa43be352018-11-06 22:40:32 +0100425 // path_array nach strs
MG Mud User88f12472016-06-24 23:31:02 +0200426 // TODO: was ist mit clones?
427 if(objectp(str))
Zesstraccc58df2018-11-07 23:25:20 +0100428 strs=path_array(object_name(str));
MG Mud User88f12472016-06-24 23:31:02 +0200429 else if(stringp(str))
Zesstraccc58df2018-11-07 23:25:20 +0100430 strs=path_array(str);
MG Mud User88f12472016-06-24 23:31:02 +0200431 else return NOBODY;
432
433 // absolute Pfade interessieren hier gerade nicht.
434 strs -= ({""});
435
436 s=sizeof(strs);
437 if(s<2) return NOBODY;
438
439 switch(strs[0]) {
440 case DOMAINDIR:
441 // Fuer Nicht-Magier bzw. "Pseudo-"Magier die Regionskennung
442 if( s==2 || WIZLVLS[strs[2]] || !IS_LEARNER(strs[2]) )
443 return strs[1];
444
445 // in /d/erzmagier gibt es Magier-IDs
446 if (strs[1]=="erzmagier") return strs[2];
447
448 // Ansonsten d.region.magiername
449 return sprintf("d.%s.%s", strs[1], strs[2]);
450
451 case PROJECTDIR:
452 // In p.service gibt es ids mit uid
453 if (s>2 && strs[1]=="service") return "p.service."+strs[2];
454
455 // Ansonsten nur p.projekt (p.service ist auch hier drin)
456 return "p."+strs[1];
457
458 case WIZARDDIR:
459 if (s>2)
460 return strs[1];
461 if (s==2 && file_size(strs[0]+"/"+strs[1])==FSIZE_DIR)
462 return strs[1];
463 return NOBODY;
464
465 case SECUREDIR:
466 return ROOTID;
467
468 case GUILDDIR:
469 case SPELLBOOKDIR:
470 tmp=strs[1];
471 if (tmp[<2..<2]==".") tmp=tmp[0..<3];
472 if ((s=member(tmp,'.'))>0) tmp=tmp[s+1..];
473 return "GUILD."+tmp;
474
475 case LIBROOMDIR:
476 return ROOMID;
477
478 case STDDIR:
479 case LIBOBJDIR:
480 return BACKBONEID;
481
482 case DOCDIR:
483 return DOCID;
484
485 case MAILDIR:
486 return MAILID;
487 case NEWSDIR:
488 return NEWSID;
489
490 case LIBITEMDIR:
491 return ITEMID;
492
493 /* Fall-Through */
494 default:
495 return NOBODY;
496
497 }
498 return NOBODY; //should never be reached.
499}
500
501// UID und EUID an Objekt geben (oder eben nicht)
502// Keine GD-Funktion, aber von Hooks aufgerufen
503protected mixed give_uid_to_object(string datei,object po)
504{
505 string creator,pouid;
506
507 // Parameter testen
508 if (!stringp(datei)||!objectp(po)) return 1;
509
510 // Keine Objekte in /log, /open oder /data
511 if (strstr(datei, "/"LIBDATADIR"/") == 0
512 || strstr(datei, "/"LIBLOGDIR"/") == 0
513 || strstr(datei, "/"FTPDIR"/") == 0
514 )
515 return 1;
516
517 // Datei muss Besitzer haben
518 if (!(creator=creator_file(datei))) return 1;
519
520 // Hack, damit simul_efun geladen werden kann
521 if (creator==ROOTID && po==TO) return ROOTID;
522
523 // Keine euid, kein Laden ...
524 if (!(pouid=geteuid(po))) return 1; // Disallow if no euid in po
525
526 // Root generiert keine Root-Objekte ;-)
527 // Nur UID setzen
528 if (pouid==ROOTID)
529 return ({creator,NOBODY}); // root does not create root objects!
530
531 // EUID mitsetzen, wenn PO==creator oder creator==BACKBONEID
532 if (creator==pouid || creator==BACKBONEID)
533 return pouid;
534
535 return ({creator,NOBODY});
536}
537
538// Die System-IDs muessen bekannt sein
539string get_master_uid() { return ROOTID;}
540string get_bb_uid() { return BACKBONEID; }
541// TODO: Bei get_wiz_name koennte man - so moeglich - auf 'wirkliche'
542// Magiernamen gehen (Idee von mandragon)
543string get_wiz_name(string file) { return creator_file(file);}
544
545// ##########################
546//###################### Sonstige GD-Funktionen #########################
547// ##########################
548
549// Spieler hat sich eingeloggt
550protected object connect()
551{
552 string err;
553 int errno;
554 object ob,bp;
555
556#if defined(SSLPORT) && __EFUN_DEFINED__(tls_available)
557 if (efun::interactive_info(this_player(), II_MUD_PORT) == SSLPORT) {
558 // reject connection of TLS is not available
559 if (!tls_available())
560 return 0;
561 // establish TLS
562 errno=tls_init_connection();
563 if (errno < 0) {
564 // Fehler im Vebindungsaufbau
565 printf("Can't establish a TLS/SSL encrypted connection: %s\n",
566 tls_error(errno));
567 return 0; // this will close the connection to the client.
568 }
569 }
570#endif
571
572 // Blueprint im Environment? Das geht nun wirklich nicht ...
573 if ((bp=find_object("/secure/login")) && environment(bp))
574 catch(destruct(bp);publish);
575
576 // Login-Shell clonen
577 err = catch(ob = clone_object("secure/login"); publish);
578
579 if (errno == 0 && err)
580 write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
581
582 return ob;
583}
584
585// Was machen bei disconnect?
586protected void disconnect(object who, string remaining) {
587 who->NetDead(); return;
588}
589
590// Es gibt kein File 'filename'. VC aktivieren so vorhanden ...
591protected object compile_object(string filename)
592{
593 object ret;
594 string *str;
595 string compiler;
596
597 if (!sizeof(filename)) return 0;
598 str=efun::explode(filename,"/");
599
600 if(sizeof(str)<2) return 0;
601 compiler=implode(str[0..<2],"/")+"/virtual_compiler";
602
603 if (find_object(compiler)
604 || file_size(compiler+".c")>0)
605 {
606 if(catch(
607 ret=(object)call_other(compiler,"compile_object",str[<1]); publish))
608 return 0;
609 }
610 else
611 return(0);
612
613 if ( objectp(ret) && efun::explode(creator_file(filename), ".")[<1] !=
614 REAL_EUID(ret) ){
615 funcall( symbol_function('log_file/*'*/), "ARCH/VC_MISSBRAUCH",
616 sprintf( "%s: %s versucht per VC %s zu %s umzubenennen!\n",
617 funcall( symbol_function('dtime/*'*/), time() ),
618 (this_interactive() ? getuid(this_interactive()):
619 object_name(previous_object())), object_name(ret),
620 filename) );
621
622 return 0;
623 }
624 return ret;
625}
626
627// ############
628//############################ Shutdown #################################
629// ############
630
631// Spieler bei Shutdown entfernen
632protected void remove_player(object victim)
633{
634 catch(victim->quit());
635 if (victim) catch(victim->remove());
636 if (victim) destruct(victim);
637 return;
638}
639
640// Langsamer Shutdown
641protected void slow_shut_down(int minutes)
642{
643 //sollte nur vom GD gerufen werden. Oder allenfalls Master selbst
644 filter(users(),#'tell_object,
645 "Der Gamedriver ruft: Der Speicher wird knapp ! Bereitet euch auf das Ende vor !\n");
646 "/obj/shut"->shut(minutes);
647 return;
648}
649
650
651// In LD varargs void notify_shutdown(string crash_reason)
652protected varargs void notify_shutdown (string crash_reason) {
653
654 if (PO && PO != TO)
655 return;
656 if (crash_reason) {
657 //Uhoh... Verdammter Crash. :-( Zumindest mal mitloggen,
658 //was der Driver sagt...
659 write_file(CRASH_LOG,sprintf(
660 "\n%s: The driver crashed! Reason: %s\n",
661 ctime(time()),crash_reason));
662 }
663
664 filter(users(), #'tell_object,
665 "Game driver shouts: LDmud shutting down immediately.\n");
666 save_wiz_file();
667 return;
668}
669
670// ##################
671//########################## Berechtigungen #############################
672// ##################
673
674// Darf Verbindung durch name von obfrom nach ob gelegt werden?
675
676//int valid_exec(string name) {return 1;}
677
678int valid_exec(string name, object ob, object obfrom)
679{
680 // Ungueltige Parameter oder Aufruf durch process_string -> Abbruch
681 if (!objectp(ob) || !objectp(obfrom)
682 || !stringp(name) || !sizeof(name)
683 || funcall(symbol_function('process_call)) )
684 return 0;
685
686 // renew_player_object() darf ...
687 if (name=="/secure/master/misc.c"
688 || name=="secure/master/misc.c")
689 return 1;
690
691 // Ansonsten darf sich nur die Shell selber aendern ...
692 if (previous_object() != obfrom) return 0;
693
694 // Die Login-Shell zu jedem Objekt ...
695 if (name=="/secure/login.c"
696 || name=="secure/login.c")
697 return 1;
698
699 // Magier per exec nur, wenn sie damit keine Fremde uid/euid bekommen
700 if (this_interactive() == obfrom && getuid(obfrom) == getuid(ob)
701 && geteuid(obfrom) == geteuid(ob))
702 return 1;
703
704 // Sonst darf niemand was
705 return 0;
706}
707
708
709// Darf me you snoopen?
710int valid_snoop(object me, object you) {
711 return getuid(PO) == ROOTID;
712}
713
714// Darf wiz wissen, ob er gesnoopt wird?
715int valid_query_snoop(object wiz) {
716 return getuid(PO) == ROOTID;
717}
718
719// Darf ob seine EUID auf neweuid setzen?
720int valid_seteuid(object ob, string neweuid)
721{
722 return (ob==this_object() ||
723 getuid(ob) == ROOTID ||
724 getuid(ob) == neweuid ||
725 creator_file(object_name(ob)) == neweuid);
726}
727
728// Darf getraced werden?
729int valid_trace(string what, mixed arg) {
730 return IS_ARCH(TI);
731}
732
733
734// valid_write() und
735// valid_read() befinden sich in file_access.c
736
737
738// Darf PO ob shadowen?
739int query_allow_shadow(object ob)
740{
741 // ROOT darf nicht geshadowed werden
742 if(getuid(ob) == ROOTID)
743 return 0;
744
745 // Access-Rights auch nicht
746 if (efun::explode(object_name(ob),"#")[0][<14..]=="/access_rights")
747 return 0;
748
749 // Ansonsten query_prevent_shadow fragen ...
750 return !(int)ob->query_prevent_shadow(PO);
751}
752
753
754/* privilege_violation is called when objects try to do illegal things,
755 * or files being compiled request a privileged efun.
756 *
757 * return values:
758 * 1: The caller/file is allowed to use the privilege.
759 * 0: The caller was probably misleaded; try to fix the error.
760 * -1: A real privilege violation. Handle it as error.
761 */
762int privilege_violation(string op, mixed who, mixed arg1, mixed arg2)
763{
764
765 if (objectp(who) &&
766 (who==this_object() || geteuid(who)==ROOTID))
767 return 1;
768
769 switch(op)
770 {
771 case "call_out_info":
772 return -1;
773 case "send_udp":
774 return 1;
775
776 case "nomask simul_efun":
777 // <who> ist in diesem Fall ein string (Filename) und damit von dem
778 // Check da oben nicht abgedeckt. Daher explizite Behandlung hier.
779 // Ausserdem hat der Pfad (zur Zeit noch) keinen fuehrenden '/'.
780 // Falls das jemand einschraenken will: der Kram fuer die simul_efuns
781 // muss das duerfen!
782 if (stringp(who)
783 && strstr(who,"secure/") == 0)
784 return 1;
785 return 0; // alle andere nicht.
786
787 case "get_extra_wizinfo":
788 // benutzt von /secure/memory.c und /secure/simul_efun.c
789 case "set_extra_wizinfo":
790 // wird benutzt von simul_efun.c, welche aber als ROOT-Objekt implizit
791 // erlaubt ist (s.o.)
792 return -1; // fuer allen nicht erlaubt.
793
794 case "rename_object":
795 if (object_name(who)=="/secure/impfetch" ||
796 BLUE_NAME(who)=="/secure/login")
797 return 1;
798 return(-1);
799
800 case "configure_driver":
801 return call_sefun("secure_level") >= ARCH_LVL;
802
803 case "limited":
804 // Spielershells duerfen sich zusaetzliche Ticks fuer den Aufruf von
805 // ::die() beschaffen, damit der Tod nicht wegen wenig Ticks buggt.
806 if (strstr(load_name(who), "/std/shells/") == 0
807 && get_type_info(arg1, 2) == who
808 && get_type_info(arg1, 3) == "/std/living/life"
809// && get_type_info(arg1, 4) == "die"
810 && arg2[LIMIT_EVAL] <= 10000000 ) {
811 return 1;
812 }
813 //DEBUG(sprintf("%O, %O, %O", who, arg1, arg2));
814 int *limits=efun::driver_info(DI_CURRENT_RUNTIME_LIMITS);
815 // LIMIT_EVAL darf verringert werden. Alle anderen Limits duerfen nicht
816 // geaendert werden. Achja, LIMIT_EVAL darf nicht 0 (LIMIT_UNLIMITED)
817 // sein. *g*
818 // LIMIT_COST darf entweder 0 oder -100 sein.
819 if (arg2[LIMIT_EVAL]>1000 && (arg2[LIMIT_EVAL] < get_eval_cost())-500
820 && arg2[LIMIT_ARRAY] == limits[LIMIT_ARRAY]
821 && arg2[LIMIT_MAPPING_KEYS] == limits[LIMIT_MAPPING_KEYS]
822 && arg2[LIMIT_MAPPING_SIZE] == limits[LIMIT_MAPPING_SIZE]
823 && arg2[LIMIT_BYTE] == limits[LIMIT_BYTE]
824 && arg2[LIMIT_FILE] == limits[LIMIT_FILE]
825 && arg2[LIMIT_CALLOUTS] == limits[LIMIT_CALLOUTS]
826 && (arg2[LIMIT_COST] == 0 || arg2[LIMIT_COST] == -100) )
827 return(1);
828 //sonst verweigern.
829 return(-1);
830
831 case "sqlite_pragma":
832 return 1;
833 case "attach_erq_demon":
834 case "bind_lambda":
835 case "configure_interactive":
836 case "configure_object":
837 case "execute_command":
838 case "erq":
839 case "input_to":
840 case "mysql":
841 case "net_connect":
842 case "pgsql":
843 case "set_driver_hook":
844 case "set_this_object":
845 case "shadow_add_action":
846 case "symbol_variable":
847 case "variable_list":
848 case "wizlist_info":
849 default:
850 return(-1);
851 }
852 return -1;
853}
854
855// ####################
856//######################## Fehlerbehandlung #############################
857// ####################
858
859// Behandlung von Fehler im Heart_Beat
860protected int heart_beat_error( object culprit, string error, string program,
861 string current_object, int line, int caught)
862{
863 if (!objectp(culprit)) return 0;
864 if (interactive(culprit))
865 {
866 tell_object(culprit,"Der GameDriver teilt Dir mit: "
867 "Dein Herzschlag hat ausgesetzt!\n");
868 if (IS_LEARNER(culprit))
869 tell_object(culprit,
870 sprintf("Fehler: %sProgamm: %s, CurrObj: %s, Zeile: %d, "
871 "the error was %scaught at higher level.\n",
872 error,program,current_object,line,
873 caught ? "" : "not"));
874 }
875
876 if (efun::object_info(culprit, OI_ONCE_INTERACTIVE))
877 call_out("restart_heart_beat", 5, culprit);
878 else
879 catch(culprit->make_immortal(); publish);
880 return 0;
881}
882
883// Ausgabe einer Meldung an Spieler mit Level >= minlevel. Wird genutzt, um
884// Magier ueber Fehler zu informieren, die nicht normal behandelt werden
885// (z.B. rekursive Fehler, d.h. Fehler in der Fehlerbehandlung)
886private void tell_players(string msg, int minlevel) {
887 msg = efun::sprintf("%=-78s","Master: " + msg); // umbrechen
888 efun::filter(efun::users(), function int (object pl)
889 {
890 if (query_wiz_level(pl) >= minlevel)
891 efun::tell_object(pl, msg);
892 return 0;
893 } );
894}
895
896/**
897 * \brief Error handling fuer Fehler beim Compilieren.
898 *
899 * log_error() wird vom GameDriver aufgerufen, wenn zur Compile-Zeit ein
900 * Fehler auftrat. Die Fehlermeldung wird unter /log/error geloggt.
901 * Falls this_interactive() ein Magier ist, bekommt er die Fehlermeldung
902 * direkt angezeigt.
903 * Sollte das Logfile 50kB ueberschreiten, wird es rotiert. Auf [Entwicklung]
904 * wird dann eine Fehlermeldung abgesetzt.
905 *
906 * @param file Die Datei, in der der Fehler auftrat.
907 * @param message Die Fehlermeldung.
908 * @param warn Warnung (warn!=0) oder Fehler (warn==0)?
909 * */
910
911protected void log_error(string file, string message, int warn) {
912 string lfile;
913 string cr;
914 mixed *lfile_size;
915
916 if (handling_error == efun::time()) {
917 // Fehler im Verlauf einer Fehlerbehandlung: Fehlerbehandlung minimieren,
918 // nur Meldung an eingeloggte Erzmagier.
919 tell_players("log_error(): Rekursiver Fehler in Fehlerbehandlung. "
920 "Bitte Debuglog beachten. Aktueller Fehler in " + file + ": "
921 + message, ARCH_LVL);
922 return;
923 }
924 handling_error = efun::time();
925
926 //Fehlerdaten an den Errord zur Speicherung weitergeben, falls wir sowas
927 //haben.
928#ifdef ERRORD
929 // Only call the errord if we are _not_ compiling the sefuns. It uses sefuns
930 // and it will cause a recursing call to get_simul_efuns() which is bound to
931 // fail.
932 if (!loading_simul_efuns) {
933 catch(limited(#'call_other,({200000}),ERRORD,"LogCompileProblem",
934 file,message,warn);publish );
935 }
936#endif // ERRORD
937
938 // Logfile bestimmen
939 cr=creator_file(file);
940 if (!cr) lfile="NOBODY.err";
941 else if (cr==ROOTID) lfile="ROOT";
942 else if (efun::member(cr,' ')!=-1) lfile="STD";
943 else lfile=cr;
944 //je nach Warnung oder Fehler Verzeichnis und Suffix bestimmen
945 if (warn) {
946 lfile="/log/warning/" + lfile + ".warn";
947 }
948 else {
949 lfile="/log/error/" + lfile + ".err";
950 }
951
952 // Bei Bedarf Rotieren des Logfiles
953 if ( !loading_simul_efuns
954 && sizeof(lfile_size = get_dir(lfile,2))
955 && lfile_size[0] >= MAX_ERRLOG_SIZE )
956 {
957 catch(rename(lfile, lfile + ".old"); publish); /* No panic if failure */
958 if (!loading_simul_efuns)
959 {
960 catch(send_channel_msg("Entwicklung","<MasteR>",
961 "Logfile rotiert: "+lfile+"."));
962 }
963 }
964
965 efun::write_file(lfile,message);
966
967 // Magier bekommen die Fehlermeldung angezeigt
968 if (IS_LEARNER(TI)) efun::tell_object(TI, message);
969
970 handling_error = 0;
971}
972
973/* Gegenmassnahme gegen 'too long eval' im Handler vom runtime error:
974 Aufsplitten des Handlers und Nutzung von limited() */
975//keine GD-Funktion
976private void handle_runtime_error(string err, string prg, string curobj,
977 int line, mixed culprit, int caught, object po, int issueid)
978{
979 string code;
980 string debug_txt;
981 object ob, titp;
982
983 //DEBUG(sprintf("Callerstack: (handle_runtime_error()): %O\n",
984 // caller_stack(1)));
985 // Fehlermeldung bauen
986 if (!stringp(err)) err="<NULL>";
987 if (!stringp(prg)) prg="<NULL>";
988 if (!stringp(curobj)) curobj="<NULL>";
989 debug_txt=efun::sprintf("Fehler: %O, Objekt: %s, Programm: %O, Zeile %O (%s), "
990 "ID: %d",
991 err[0..<2], curobj, (prg[0]!='<'?"/":"")+prg, line,
992 (TI?efun::capitalize(efun::getuid(TI)):"<Unbekannt>"),
993 issueid);
994
995 titp = TI || TP;
996 // Fehlermeldung an Kanaele schicken, aber nur wenn der aktuelle Fehler
997 // nicht waehrend des ladens der simulefuns auftrat, bei der Ausgabe der
998 // Meldung ueber die Kaenaele wieder sefuns genutzt werden.
999 if (!loading_simul_efuns) {
1000 if (titp && (IS_LEARNER(titp) || (titp->QueryProp(P_TESTPLAYER))))
1001 {
1002 catch(send_channel_msg("Entwicklung",
1003 capitalize(objectp(po) ? REAL_UID(po) : ""),
1004 debug_txt));
1005 }
1006 else
1007 {
1008 catch(send_channel_msg("Debug",
1009 capitalize(objectp(po) ? REAL_UID(po) : ""),
1010 debug_txt));
1011 }
1012 }
1013
1014 if (!titp) return;
1015
1016 // Fehlermeldung an Benutzer
1017 if (IS_LEARNER(titp))
1018 efun::tell_object(titp,
1019 efun::sprintf("%'-'78.78s\nfile: %s line: %d object: %s\n" +
1020 "%serror: %s%'-'78.78s\n","",prg,line,curobj,
1021 (prg&&(code=efun::read_file("/"+prg,line,1))?
1022 "\n"+code+"\n":""),err,""));
1023 else
1024 efun::tell_object(titp, "Du siehst einen Fehler im Raum-Zeit-Gefuege.\n");
1025}
1026
1027//Keine GD-Funktion, limitiert die Kosten fuer den handler
1028private void call_runtime_error(string err, string prg, string curobj,
1029 int line, mixed culprit, int caught, object po)
1030{
1031 if (handling_error == efun::time())
1032 {
1033 // Fehler im Verlauf einer Fehlerbehandlung: Fehlerbehandlung minimieren,
1034 // nur Meldung an eingeloggte Regionsmagier.
1035 tell_players("call_runtime_error(): Rekursiver Fehler in "
1036 "Fehlerbehandlung. Bitte Debuglog beachten. Aktueller Fehler in "
1037 + curobj + ": " + err, LORD_LVL);
1038 return;
1039 }
1040 handling_error = efun::time();
1041
1042 //Fehlerdaten an den Errord zur Speicherung weitergeben, falls wir sowas
1043 //haben.
MG Mud User88f12472016-06-24 23:31:02 +02001044 int issueid;
Christian Georg Beckerd66c9602017-01-09 10:37:51 +02001045#ifdef ERRORD
MG Mud User88f12472016-06-24 23:31:02 +02001046 // Wenn die sefuns gerade geladen werden, erfolgt keine Weitergabe an den
1047 // ErrorD, da dabei wieder sefuns gerufen werden, was zu einer Rekursion
1048 // fuehrt.
1049 if (!loading_simul_efuns) {
1050 catch(issueid=efun::limited(#'call_other,({200000}),ERRORD,"LogError",
1051 err,prg,curobj,line,culprit,caught);publish);
1052 }
1053#endif // ERRORD
1054
1055 //eigenen Errorhandler laufzeitbegrenzt rufen
1056 efun::limited(#'handle_runtime_error, ({ 200000 }), err, prg, curobj,
1057 line,culprit,caught, po, issueid);
1058
1059 handling_error = 0;
1060}
1061
1062// Laufzeitfehlerbehandlung, hebt Evalcost-Beschraenkung auf und reicht weiter
1063// an call_runtime_error, die die Grenze wieder auf einen bestimmten Wert
1064// festlegt.
1065protected void runtime_error(string err ,string prg, string curobj, int line,
1066 mixed culprit, int caught) {
1067
1068 //DEBUG(sprintf("Callerstack: (runtime_error()): %O\nPO: %O\nPO(0): %O\n",
1069 // caller_stack(1),previous_object(),previous_object(0)));
1070
1071 limited(#'call_runtime_error, ({ LIMIT_UNLIMITED }),
1072 err,prg,curobj,line,culprit,caught,previous_object());
1073}
1074
1075//Warnungen mitloggen
1076protected void runtime_warning( string msg, string curobj, string prog, int line,
1077 int inside_catch)
1078{
1079 string code;
1080 string debug_txt;
1081 object titp;
1082
1083 //DEBUG(sprintf("Callerstack: in runtime_warning(): %O\nPO(0): %O\n",
1084 // caller_stack(1),previous_object(0)));
1085
1086 //Daten der Warnung an den Errord zur Speicherung weitergeben, falls wir
1087 //sowas haben.
1088 int issueid;
1089#ifdef ERRORD
1090 catch(issueid=limited(#'call_other,({200000}),ERRORD,"LogWarning",
1091 msg,prog,curobj,line,inside_catch); publish);
1092#endif // ERRORD
1093
1094 // Fehlermeldung bauen
1095 if (!stringp(msg)) msg="<NULL>";
1096 if (!stringp(prog)) prog="<NULL>";
1097 if (!stringp(curobj)) curobj="<NULL>";
1098 debug_txt=sprintf("Warnung: %O, Objekt: %s, Programm: %O, Zeile %O (%s) "
1099 "ID: %d",
1100 msg[0..<2], curobj, (prog[0]!='<'?"/":"")+prog, line,
1101 (TI?capitalize(getuid(TI)):"<Unbekannt>"),
1102 issueid);
1103
1104 //Fehlermeldungen an -warnungen schicken
1105 catch(send_channel_msg("Warnungen",
1106 capitalize(objectp(previous_object()) ?
1107 REAL_UID(previous_object()) : ""),
1108 debug_txt));
1109
1110 titp = TI || TP;
1111
1112 if (!titp) return;
1113
1114 // Fehlermeldung an Benutzer
1115 if (IS_LEARNER(titp))
1116 tell_object(titp,
1117 sprintf("%'-'78.78s\nfile: %s line: %d object: %s\n" +
1118 "%sWarnung: %s%'-'78.78s\n","",prog,line,curobj,
1119 (prog&&(code=read_file("/"+prog,line,1))?
1120 "\n"+code+"\n":""),msg,""));
1121 return;
1122}
1123
1124
1125// #####################
1126//######################## Einrichten des ED ############################
1127// #####################
1128
1129// Setup speichern
1130protected int save_ed_setup(object who, int code)
1131{
1132 string file;
1133
1134 if (!intp(code)) return 0;
1135 file = sprintf("/players/%s/.edrc",geteuid(who));
1136 rm(file);
1137 return write_file(file,(string)code);
1138}
1139
1140// Setup einladen
1141protected int retrieve_ed_setup(object who)
1142{
1143 string file;
1144
1145 file = sprintf("/players/%s/.edrc",getuid(who));
1146 if (file_size(file)<1) return 0;
1147 return (int)read_file(file);
1148}
1149
1150// Wo werden Dateien gespeichert?
1151string get_ed_buffer_save_file_name(string file)
1152{
1153 return sprintf("/players/%s/.dead_ed_files/%s",
1154 getuid(this_player()),efun::explode(file, "/")[<1]);
1155}
1156
1157// ################################
1158//################### Ungenutzte Pflichtfunktionen ######################
1159// ################################
1160
1161// Nichts zu tun beim Master-Neustart
1162protected void external_master_reload() { return; }
1163
1164// Keine externen Master-Flags
1165protected void flag(string str) { return; }
1166
1167// Wird benoetigt, falls ERQ angeschlossen ist
1168protected void stale_erq(closure callback) { return; }
1169
1170// Zerstoerten Master wiederbeleben ...
1171// nicht viel zu tun, wenn flag gesetzt ist. Wenn aber nicht, sind alle
1172// Variablen auf 0. Nach dieser Funktion wird wieder inaugurate_master()
1173// gerufen.
1174protected void reactivate_destructed_master(int flag) {
1175 if (flag) {
1176 //re-init
1177 //was machen? TODO
1178 }
1179 return;
1180}
1181
1182// Kein Quota-Demon (potentielles TODO)
1183//Handle quotas in times of memory shortage.
1184//is called during the final phase of a garbage collection, if the user
1185//reserve could not be re-allocated. Last (!) Chance to free (active) objects
1186//from the system and prevent call to slow_shut_down()!
1187protected void quota_demon() {
1188 //was kann man machen?
1189 //uebervolle Seherhaeuser leeren?
1190 return;
1191}
1192
1193// Keine Prepositionen in parse_command
1194//string *parse_command_prepos_list() { return ({}); }
1195
1196// Wie lautet das Wort fuer 'alle' ?
1197//string *parse_command_all_word() { return ({}); }
1198
1199
1200// Keine Besonderen Objektnamen
1201string printf_obj_name(object ob) { return 0; }
1202
1203// ###################
1204//######################### Sonstiges/Hooks #############################
1205// ###################
1206
1207//TODO: save_wiz_file in shutdown() integrieren?
1208// Wizliste speichern: Zeile generieren
1209static string _save_wiz_file_loop(mixed *a)
1210{
1211 return sprintf("%s %d %d\n",a[WL_NAME],a[WL_COMMANDS],a[WL_EXTRA]);
1212}
1213
1214// Wizliste speichern
1215protected void save_wiz_file()
1216{
1217 rm("/WIZLIST");
1218 write_file("/WIZLIST",implode(
1219 map(wizlist_info(),#'_save_wiz_file_loop),""));
1220}
1221
MG Mud User88f12472016-06-24 23:31:02 +02001222// EUID und UID werden von give_uid_to_object() vergeben, diese sind in
1223// inaugurate_master() als driver hooks angemeldet.
1224
1225/*load_uid_hook() sollte machen, was diese Lambda machte...
1226 unbound_lambda( ({'printf_obj_name}),
1227 ({#'give_uid_to_object,'printf_obj_name,
1228 ({#'previous_object}),0}))); */
1229protected mixed load_uid_hook(string datei) {
1230 return(give_uid_to_object(datei,previous_object()));
1231}
1232
1233/* clone_uid_hook sollte machen, was diese Lambda machte...
1234 unbound_lambda( ({'blueprint, 'new_name}),
1235 ({#'give_uid_to_object,'new_name,
1236 ({#'previous_object}),1}))); */
1237protected mixed clone_uid_hook(string blueprint,string new_name) {
1238 return(give_uid_to_object(new_name,previous_object()));
1239}
1240