blob: 1b4af5f8ca2168c4751467a353a31b9281990d47 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// simul_efun.c -- simul efun's
4//
5// $Id: simul_efun.c 7408 2010-02-06 00:27:25Z Zesstra $
Zesstra4dbb9882019-11-26 21:26:36 +01006#pragma strong_types,save_types,rtt_checks
MG Mud User88f12472016-06-24 23:31:02 +02007#pragma no_clone,no_shadow,no_inherit
Zesstra4dbb9882019-11-26 21:26:36 +01008#pragma range_check,warn_deprecated
MG Mud User88f12472016-06-24 23:31:02 +02009#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
10
11// Absolute Pfade erforderlich - zum Zeitpunkt, wo der Master geladen
12// wird, sind noch keine Include-Pfade da ...
13
14#define SNOOPLOGFILE "SNOOP"
15#define ASNOOPLOGFILE "ARCH/SNOOP"
16
Zesstradf157462018-07-31 21:49:59 +020017public int mkdirp(string dir);
18
MG Mud User88f12472016-06-24 23:31:02 +020019#include "/secure/config.h"
20#include "/secure/wizlevels.h"
21#include "/sys/snooping.h"
22#include "/sys/language.h"
23#include "/sys/thing/properties.h"
24#include "/sys/wizlist.h"
25#include "/sys/erq.h"
26#include "/sys/lpctypes.h"
27#include "/sys/daemon.h"
28#include "/sys/player/base.h"
29#include "/sys/thing/description.h"
30#include "/sys/container.h"
31#include "/sys/defines.h"
32#include "/sys/telnet.h"
33#include "/sys/objectinfo.h"
34#include "/sys/files.h"
35#include "/sys/strings.h"
36#include "/sys/time.h"
37#include "/sys/lpctypes.h"
38#include "/sys/notify_fail.h"
39#include "/sys/tls.h"
40#include "/sys/input_to.h"
41#include "/sys/object_info.h"
42
43/* function prototypes
44 */
45string dtime(int wann);
46varargs int log_file(string file, string txt, int size_to_break);
Zesstra39d284a2020-08-30 23:55:47 +020047int query_wiz_level(object|string player);
Zesstra8d245512020-04-28 20:35:34 +020048nomask varargs int snoop(object snooper, object snoopee);
MG Mud User88f12472016-06-24 23:31:02 +020049varargs string country(mixed ip, string num);
Zesstra39d284a2020-08-30 23:55:47 +020050int query_wiz_grp(object|string wiz);
Zesstraddddbf72021-05-14 16:52:16 +020051public object deep_present(string|object what, object ob=previous_object());
MG Mud User88f12472016-06-24 23:31:02 +020052nomask int secure_level();
53nomask string secure_euid();
54public nomask int process_call();
55nomask mixed __create_player_dummy(string name);
Zesstra953b7ea2019-11-28 19:29:37 +010056varargs string replace_personal(string str, <string|object>* obs, int caps);
MG Mud User88f12472016-06-24 23:31:02 +020057
58//replacements for dropped efuns in LD
59#if !__EFUN_DEFINED__(extract)
60varargs string extract(string str, int from, int to);
61#endif
62#if !__EFUN_DEFINED__(slice_array)
63varargs mixed slice_array(mixed array, int from, int to);
64#endif
65#if !__EFUN_DEFINED__(member_array)
66int member_array(mixed item, mixed arraystring);
67#endif
68
69// Include the different sub 'modules' of the simul_efun
70#include __DIR__"debug_info.c"
71#include __DIR__"enable_commands.c"
72#include __DIR__"hash.c"
73#include __DIR__"object_info.c"
74#include __DIR__"query_editing.c"
75#include __DIR__"query_idle.c"
76#include __DIR__"query_input_pending.c"
77#include __DIR__"query_ip_name.c"
78#include __DIR__"query_limits.c"
79#include __DIR__"query_load_average.c"
80#include __DIR__"query_mud_port.c"
81#include __DIR__"query_once_interactive.c"
82#include __DIR__"query_snoop.c"
83#include __DIR__"set_heart_beat.c"
84#if __BOOT_TIME__ < 1456261859
85#include __DIR__"set_prompt.c"
86#endif
87#include __DIR__"shadow.c"
88#include __DIR__"livings.c"
89#include __DIR__"comm.c"
Zesstra19d657d2017-01-29 21:40:38 +010090#include __DIR__"files.c"
Zesstra7b2fbe72017-06-17 19:05:06 +020091#include __DIR__"seteuid.c"
MG Mud User88f12472016-06-24 23:31:02 +020092
93#define TO efun::this_object()
94#define TI efun::this_interactive()
95#define TP efun::this_player()
96#define PO efun::previous_object(0)
97#define LEVEL(x) query_wiz_level(x)
98#define NAME(x) capitalize(getuid(x))
99
100#define DEBUG(x) if (find_player("zesstra")) \
101 tell_object(find_player("zesstra"),x)
102
103mixed dtime_cache = ({-1,""});
104
105#ifdef IOSTATS
106struct iostat_s {
107 string oname;
108 int time;
109 int size;
110};
111mixed saveo_stat = ({ 0,allocate(200, 0) });
112mixed restoreo_stat = ({ 0,allocate(200,0) });
113//mixed writefile_stat = ({ 0,allocate(100,(<iostat_s>)) });
114//mixed readfile_stat = ({ 0,allocate(100,(<iostat_s>)) });
115//mixed log_stat = ({ 0,allocate(100,(<iostat_s>)) });
116
117mixed ___iostats(int type) {
118 switch(type) {
119 case 1:
120 return saveo_stat;
121 case 2:
122 return restoreo_stat;
123/* case 3:
124 return writefile_stat;
125 case 4:
126 return readfile_stat;
127 case 5:
128 return log_stat;
129 */
130 }
131 return 0;
132}
133#endif
134
135// Nicht jeder Magier muss die simul_efun entsorgen koennen.
136string NotifyDestruct(object caller) {
137 if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
138 return "Du darfst das simul_efun Objekt nicht zerstoeren!\n";
139 }
140 return 0;
141}
142
143public nomask void remove_interactive( object ob )
144{
145 if ( objectp(ob) && previous_object()
146 && object_name(previous_object())[0..7] != "/secure/"
147 && ((previous_object() != ob
148 && (ob != this_player() || ob != this_interactive()))
149 || (previous_object() == ob
150 && (this_player() && this_player() != ob
151 || this_interactive() && this_interactive() != ob)) ) )
152
153 log_file( "PLAYERDEST",
154 sprintf( "%s: %O ausgeloggt von PO %O, TI %O, TP %O\n",
155 dtime(time()), ob, previous_object(),
156 this_interactive(), this_player() ) );
157
158 efun::remove_interactive(ob);
159}
160
161
162void ___updmaster()
163{
164 object ob;
165
166 //sollte nicht jeder duerfen.
167 if (process_call() || !ARCH_SECURITY)
168 raise_error("Illegal use of ___updmaster()!");
169
170 write("Removing old master ... ");
171 foreach(string file:
172 get_dir("/secure/master/*.c",GETDIR_NAMES|GETDIR_UNSORTED|GETDIR_PATH)) {
173 if (ob = find_object(file))
174 efun::destruct(ob);
175 }
176 efun::destruct(efun::find_object("/secure/master"));
177 write("done.\nLoading again ... ");
178 load_object("/secure/master");
179
180 write("done.\n");
181}
182
183varargs string country(mixed ip, string num) {
184 mixed ret;
185
Zesstra4dbb9882019-11-26 21:26:36 +0100186 if(ret = "/p/daemon/iplookup"->country(num || ip)) {
MG Mud User88f12472016-06-24 23:31:02 +0200187 return ret;
188 } else return "???";
189}
190
191
192// * Snoopen und was dazugehoert
MG Mud User88f12472016-06-24 23:31:02 +0200193
MG Mud User88f12472016-06-24 23:31:02 +0200194private string Lcut(string str) {
195 return str[5..11]+str[18..];
196}
197
Zesstra8d245512020-04-28 20:35:34 +0200198nomask varargs int snoop( object snooper, object snoopee )
MG Mud User88f12472016-06-24 23:31:02 +0200199{
200 int ret;
MG Mud User88f12472016-06-24 23:31:02 +0200201
Zesstra8d245512020-04-28 20:35:34 +0200202 if( !objectp(snooper) || snooper == snoopee || !PO )
MG Mud User88f12472016-06-24 23:31:02 +0200203 return 0;
204
Zesstra2e499d62020-04-28 22:13:53 +0200205 // Evtl. gibt es bereits einen snoopee, der von snopper gesnoopt wird?
Zesstra69174b42020-04-28 22:48:45 +0200206 object existing_snoopee = efun::interactive_info(snooper, II_SNOOP_PREV);
MG Mud User88f12472016-06-24 23:31:02 +0200207
Zesstra69174b42020-04-28 22:48:45 +0200208 // soll jemand neues gesnoopt werden?
Zesstra8d245512020-04-28 20:35:34 +0200209 if(snoopee)
210 {
Zesstra69174b42020-04-28 22:48:45 +0200211 // Jemand mit niedrigerem Level kann keinen hoeherleveligen snoopen
212 // lassen.
Zesstra8d245512020-04-28 20:35:34 +0200213 if ( PO != snooper
214 && query_wiz_grp(snooper) >= query_wiz_grp(geteuid(PO)) )
MG Mud User88f12472016-06-24 23:31:02 +0200215 return 0;
216
Zesstra69174b42020-04-28 22:48:45 +0200217 // Niedriglevelige User koennen nur mit Einverstaendnis hoeherlevelige
218 // snoopen.
219 if ( query_wiz_grp(snooper) <= query_wiz_grp(snoopee)
220 && !(snoopee->QueryAllowSnoop(snooper)) )
Zesstra6c333cc2020-04-28 20:18:45 +0200221 {
Zesstra69174b42020-04-28 22:48:45 +0200222 // es sei denn der snooper ist Sheriff und der snoopee ist kein
223 // EM+
Zesstra8d245512020-04-28 20:35:34 +0200224 if ( !IS_DEPUTY(snooper) || IS_ARCH(snoopee) )
MG Mud User88f12472016-06-24 23:31:02 +0200225 return 0;
Zesstra6c333cc2020-04-28 20:18:45 +0200226 }
Zesstra69174b42020-04-28 22:48:45 +0200227 // Wird der snoopee bereits gesnoopt? Dann darf sich der neue snooper
228 // nur unter Umstaenden in die Snoop-Kette einreihen...
229 object existing_snooper;
230 if ( (existing_snooper = efun::interactive_info(snoopee, II_SNOOP_NEXT))
231 && query_wiz_grp(existing_snooper) >= query_wiz_grp(snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200232 {
Zesstra69174b42020-04-28 22:48:45 +0200233 // ... naemlich nur dann, wenn der bestehende Snooper kein
234 // SF_LOCKED gesetzt hat.
235 if ( existing_snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
MG Mud User88f12472016-06-24 23:31:02 +0200236 return 0;
237
Zesstra69174b42020-04-28 22:48:45 +0200238 tell_object( existing_snooper, sprintf( "%s snooped jetzt %s.\n",
Zesstra8d245512020-04-28 20:35:34 +0200239 snooper->name(WER), snoopee->name(WER) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200240
Zesstra69174b42020-04-28 22:48:45 +0200241 // Evtl. wird der neue snooper selber gesnoopt. Dafuer wird jetzt
242 // ggf. die Kette von *ihren* snoopern verfolgt.
243 object snooper_of_new_snooper = snooper;
244 object snooper_rover;
245 while ( snooper_rover = interactive_info(snooper_of_new_snooper, II_SNOOP_NEXT) )
Zesstra6c333cc2020-04-28 20:18:45 +0200246 {
Zesstra69174b42020-04-28 22:48:45 +0200247 tell_object( existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200248 sprintf( "%s wird seinerseits von %s gesnooped.\n"
Zesstra69174b42020-04-28 22:48:45 +0200249 ,snooper_of_new_snooper->name(WER),
250 snooper_rover->name(WEM) ) );
251 snooper_of_new_snooper = snooper_rover;
MG Mud User88f12472016-06-24 23:31:02 +0200252 }
253
Zesstra69174b42020-04-28 22:48:45 +0200254 // Der letzt snooper des hier anzumeldenden snoopers wird nun vom
255 // bestehenden snooper gesnoopt, falls moeglich.
256 efun::snoop( existing_snooper, snooper_of_new_snooper );
MG Mud User88f12472016-06-24 23:31:02 +0200257
Zesstra69174b42020-04-28 22:48:45 +0200258 if ( efun::interactive_info(snooper_of_new_snooper, II_SNOOP_NEXT)
259 != existing_snooper )
260 tell_object( existing_snooper, sprintf( "Du kannst %s nicht snoopen.\n",
261 snooper_of_new_snooper->name(WEN) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200262 else
263 {
Zesstra69174b42020-04-28 22:48:45 +0200264 tell_object( existing_snooper, sprintf( "Du snoopst jetzt %s.\n",
265 snooper_of_new_snooper->name(WEN) ) );
266 if ( !IS_DEPUTY(existing_snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200267 {
MG Mud User88f12472016-06-24 23:31:02 +0200268 log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
269 dtime(time()),
Zesstra69174b42020-04-28 22:48:45 +0200270 existing_snooper,
271 snooper_of_new_snooper,
272 environment(snooper_of_new_snooper) ),
MG Mud User88f12472016-06-24 23:31:02 +0200273 100000 );
Zesstra69174b42020-04-28 22:48:45 +0200274 if (existing_snoopee)
275 CHMASTER->send( "Snoop", existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200276 sprintf( "%s *OFF* %s (%O)",
Zesstra69174b42020-04-28 22:48:45 +0200277 capitalize(getuid(existing_snooper)),
278 capitalize(getuid(existing_snoopee)),
279 environment(existing_snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200280
Zesstra69174b42020-04-28 22:48:45 +0200281 CHMASTER->send( "Snoop", existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200282 sprintf("%s -> %s (%O)",
Zesstra69174b42020-04-28 22:48:45 +0200283 capitalize(getuid(existing_snooper)),
284 capitalize(getuid(snooper_of_new_snooper)),
285 environment(snooper_of_new_snooper)));
MG Mud User88f12472016-06-24 23:31:02 +0200286 }
Zesstra6c333cc2020-04-28 20:18:45 +0200287 else
288 {
MG Mud User88f12472016-06-24 23:31:02 +0200289 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
290 dtime(time()),
Zesstra69174b42020-04-28 22:48:45 +0200291 existing_snooper,
292 snooper_of_new_snooper,
293 environment(snooper_of_new_snooper) )
MG Mud User88f12472016-06-24 23:31:02 +0200294 ,100000 );
295 }
296 }
297 }
298 else
Zesstra6c333cc2020-04-28 20:18:45 +0200299 {
Zesstra69174b42020-04-28 22:48:45 +0200300 if (existing_snooper)
Zesstra6c333cc2020-04-28 20:18:45 +0200301 {
Zesstra8d245512020-04-28 20:35:34 +0200302 if ( !snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
Zesstra6c333cc2020-04-28 20:18:45 +0200303 {
MG Mud User88f12472016-06-24 23:31:02 +0200304 printf( "%s wird bereits von %s gesnooped. Benutze das "
305 "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
Zesstra69174b42020-04-28 22:48:45 +0200306 snoopee->name(WER), existing_snooper->name(WEM) );
MG Mud User88f12472016-06-24 23:31:02 +0200307 return 0;
308 }
Zesstra6c333cc2020-04-28 20:18:45 +0200309 }
310 }
Zesstra8d245512020-04-28 20:35:34 +0200311 ret = efun::snoop( snooper, snoopee );
MG Mud User88f12472016-06-24 23:31:02 +0200312
Zesstra8d245512020-04-28 20:35:34 +0200313 if ( !IS_DEPUTY(snooper)
314 && efun::interactive_info(snoopee, II_SNOOP_NEXT) == snooper)
Zesstra6c333cc2020-04-28 20:18:45 +0200315 {
MG Mud User88f12472016-06-24 23:31:02 +0200316 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
317 Lcut(dtime(time())),
Zesstra8d245512020-04-28 20:35:34 +0200318 snooper, snoopee, environment(snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200319 100000 );
320
Zesstra69174b42020-04-28 22:48:45 +0200321 if (existing_snoopee)
Zesstra6c333cc2020-04-28 20:18:45 +0200322 {
Zesstra8d245512020-04-28 20:35:34 +0200323 CHMASTER->send( "Snoop", snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200324 sprintf( "%s *OFF* %s (%O).",
Zesstra8d245512020-04-28 20:35:34 +0200325 capitalize(getuid(snooper)),
Zesstra69174b42020-04-28 22:48:45 +0200326 capitalize(getuid(existing_snoopee)),
327 environment(existing_snoopee) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200328 }
MG Mud User88f12472016-06-24 23:31:02 +0200329
Zesstra8d245512020-04-28 20:35:34 +0200330 CHMASTER->send( "Snoop", snooper, sprintf( "%s -> %s (%O).",
331 capitalize(getuid(snooper)),
332 capitalize(getuid(snoopee)),
333 environment(snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200334 }
Zesstra6c333cc2020-04-28 20:18:45 +0200335 else
336 {
Zesstra8d245512020-04-28 20:35:34 +0200337 if ( efun::interactive_info(snoopee, II_SNOOP_NEXT) == snooper )
Zesstra6c333cc2020-04-28 20:18:45 +0200338 {
MG Mud User88f12472016-06-24 23:31:02 +0200339 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
340 Lcut(dtime(time())),
Zesstra8d245512020-04-28 20:35:34 +0200341 snooper, snoopee, environment(snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200342 100000 );
343 }
344 }
345
Zesstra8d245512020-04-28 20:35:34 +0200346 if ( ret && query_wiz_grp(snooper) <= query_wiz_grp(snoopee) &&
347 !IS_DEPUTY(snooper) )
348 tell_object( snoopee, "*** " + NAME(snooper) + " snoopt Dich!\n" );
MG Mud User88f12472016-06-24 23:31:02 +0200349
350 return ret;
351 }
Zesstra69174b42020-04-28 22:48:45 +0200352 // Ansonsten soll ein bestehender snoop beendet werden.
Zesstra6c333cc2020-04-28 20:18:45 +0200353 else
354 {
Zesstra69174b42020-04-28 22:48:45 +0200355 // Das beenden duerfen aber nur Aufrufer selber oder hoeherlevelige
356 // ausloesen oder gleichen levels, wenn sie selber gerade vom snooper
357 // gesnoopt werden.
Zesstra8d245512020-04-28 20:35:34 +0200358 if ( (snooper == PO ||
359 query_wiz_grp(geteuid(PO)) > query_wiz_grp(snooper) ||
360 (query_wiz_grp(geteuid(PO)) == query_wiz_grp(snooper) &&
Zesstra69174b42020-04-28 22:48:45 +0200361 efun::interactive_info(PO, II_SNOOP_NEXT) == snooper) )
362 && existing_snoopee )
Zesstra6c333cc2020-04-28 20:18:45 +0200363 {
Zesstra8d245512020-04-28 20:35:34 +0200364 if ( !IS_DEPUTY(snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200365 {
MG Mud User88f12472016-06-24 23:31:02 +0200366 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
Zesstra8d245512020-04-28 20:35:34 +0200367 Lcut(dtime(time())), snooper,
Zesstra69174b42020-04-28 22:48:45 +0200368 existing_snoopee,
369 environment(existing_snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200370 100000 );
371
Zesstra8d245512020-04-28 20:35:34 +0200372 CHMASTER->send( "Snoop", snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200373 sprintf( "%s *OFF* %s (%O).",
Zesstra8d245512020-04-28 20:35:34 +0200374 capitalize(getuid(snooper)),
Zesstra69174b42020-04-28 22:48:45 +0200375 capitalize(getuid(existing_snoopee)),
376 environment(existing_snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200377 }
Zesstra6c333cc2020-04-28 20:18:45 +0200378 else
379 {
MG Mud User88f12472016-06-24 23:31:02 +0200380 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
Zesstra8d245512020-04-28 20:35:34 +0200381 Lcut(dtime(time())), snooper,
Zesstra69174b42020-04-28 22:48:45 +0200382 existing_snoopee,
383 environment(existing_snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200384 100000 );
385 }
Zesstra8d245512020-04-28 20:35:34 +0200386 return efun::snoop(snooper);
MG Mud User88f12472016-06-24 23:31:02 +0200387 }
388 }
389 return 0;
390}
391
392
393
394// * Emulation des 'alten' explode durch das neue
395string *old_explode(string str, string del) {
396 int s, t;
397 string *strs;
398
399 if (!stringp(str)) {
400 set_this_object(previous_object());
401 raise_error(sprintf(
402 "Invalid argument 1 to old_explode()! Expected <string>, got: "
403 "%.30O\n",str));
404 }
405 if (!stringp(del)) {
406 set_this_object(previous_object());
407 raise_error(sprintf(
408 "Invalid argument 2 to old_explode()! Expected <string>, got: "
409 "%.30O\n",del));
410 }
411 if(del == "")
412 return ({str});
413 strs=efun::explode(str, del);
414 t=sizeof(strs)-1;
415 while(s<=t && strs[s++] == "");s--;
416 while(t>=0 && strs[t--] == "");t++;
417 if(s<=t)
418 return strs[s..t];
419 return ({});
420}
421
422int file_time(string path) {
423 mixed *v;
424
425 set_this_object(previous_object());
426 if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
427 return(0); //sonst
428}
429
MG Mud User88f12472016-06-24 23:31:02 +0200430// * Magier-Level abfragen
Zesstrad36ce392020-08-30 23:42:04 +0200431int query_wiz_level(object|string player) {
Zesstra4dbb9882019-11-26 21:26:36 +0100432 return "/secure/master"->query_wiz_level(player);
MG Mud User88f12472016-06-24 23:31:02 +0200433}
434
MG Mud User88f12472016-06-24 23:31:02 +0200435// * German version of ctime()
436#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
437#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
438 "Sep","Okt","Nov","Dez"})
439string dtime(int wann) {
440
441 if (wann == dtime_cache[0])
442 return(dtime_cache[1]);
443
444 int *lt = localtime(wann);
445 return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
446 TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]],
447 lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
448}
449
450// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
451// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
452// ein ordentliches Datum/Uhrzeit.
453#if !__EFUN_DEFINED__(strftime)
454varargs string strftime(mixed fmt, int clock, int localized) {
455 if (intp(clock) && clock >= 0)
456 return dtime(clock);
457 else if (intp(fmt) && fmt >= 0)
458 return dtime(fmt);
459
460 return dtime(time());
461}
462#endif //!__EFUN_DEFINED__(strftime)
463
464// * Shutdown mit zusaetzlichem logging
465nomask int shutdown(string reason)
466{
467 string name;
468 string obname;
469 string output;
470
471 if (!reason)
472 return 0;
473 if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
474 object_name(previous_object())!="/obj/shut" )
475 {
476 write("You have no permission to shut down the gamedriver!\n");
477 return 0;
478 }
479 if ((this_interactive())&&(name=getuid(this_interactive())))
480 {
481 name=capitalize(name);
482 filter(users(),#'tell_object,//'
483 capitalize(name)+" faehrt das Spiel herunter!\n");
484 }
485 else
486 name="ANONYMOUS";
487 if (previous_object()) obname=capitalize(getuid(previous_object()));
488 output=name;
489 if (obname && name!=obname) output=output+" ("+obname+")";
490 if (previous_object()&&object_name(previous_object())=="/obj/shut"){
491 output+=" faehrt das Spiel via Armageddon herunter.\n";
492 output=dtime(time())+": "+output;
493 log_file("GAME_LOG",output+"\n",-1);
494 efun::shutdown();
495 return 1;
496 }
497 output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
498 output+=" Grund: "+reason;
499 log_file("GAME_LOG",output+"\n",-1);
500 efun::shutdown();
501 return 1;
502}
503
504// * lowerchar
505
506int lowerchar(int char) {
507 if (char<'A' || char>'Z') return char;
508 return char+32;
509}
510
511// * upperstring
512
513string upperstring(string s)
514{
515#if __EFUN_DEFINED__(upper_case)
516 return(upper_case(s));
517#else
518 int i;
519 if (!stringp(s)) return 0;
520 for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
521 return s;
522#endif
523}
524
525// * lowerstring
526
527string lowerstring(string s)
528{
529 if (!stringp(s)) return 0;
530 return lower_case(s);
531}
532
533
534// * GD version
535string version()
536{
537 return __VERSION__;
538}
539
540// * break_string
541// stretch() -- stretch a line to fill a given width
542private string stretch(string s, int width) {
543 int len=sizeof(s);
544 if (len==width) return s;
545
546 // reine Leerzeilen, direkt zurueckgeben
547 string trimmed=trim(s,TRIM_LEFT," ");
548 if (trimmed=="") return s;
549 int start_spaces = len - sizeof(trimmed);
550
551 string* words = explode(trimmed, " ");
552 // der letzte kriegt keine Spaces
553 int word_count=sizeof(words) - 1;
554 // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
555 if (!word_count)
556 return " "*start_spaces + words[0];
557
558 int space_count = width - len;
559
560 int space_per_word=(word_count+space_count) / word_count;
561 // Anz.Woerter mit Zusatz-Space
562 int rest=(word_count+space_count) % word_count;
563 // Rest-Spaces Verteilen
564 foreach (int pos : rest) words[pos]+=" ";
565 return (" "*start_spaces) + implode( words, " "*space_per_word );
566}
567
568// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
569// eigene Funktion bekommen:
570private varargs string block_string(string s, int width, int flags) {
571 // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
572 // einem Leerzeichen begonnen.
573 // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
574 // ersetzt.
575 if ( (flags & BS_LEAVE_MY_LFS)
576 && !(flags & BS_NO_PARINDENT))
577 {
578 s = " "+regreplace(s,"\n","\n ",1);
579 }
580
581 // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
582 // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
583 // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
584 // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
585 // wieder rueckgaengig zu machen.
586 s = sprintf( "%-*=s", width, s);
587
588 string *tmp=explode(s, "\n");
589 // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
590 // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
591 // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
592 // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
593 // Ende der letzten Zeile hat), das macht der Aufrufer...
594 if (sizeof(tmp) > 1)
595 return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" )
596 + "\n" + tmp[<1];
597
598 return s;
599}
600
601public varargs string break_string(string s, int w, mixed indent, int flags)
602{
603 if ( !s || s == "" ) return "";
604
605 if ( !w ) w=78;
606
607 if( intp(indent) )
608 indent = indent ? " "*indent : "";
609
610 int indentlen=stringp(indent) ? sizeof(indent) : 0;
611
612 if (indentlen>w) {
613 set_this_object(previous_object());
614 raise_error(sprintf("break_string: indent longer %d than width %d\n",
615 indentlen,w));
616 // w=((indentlen/w)+1)*w;
617 }
618
619 if (!(flags & BS_LEAVE_MY_LFS))
620 s=regreplace( s, "\n", " ", 1 );
621
622 if ( flags & BS_SINGLE_SPACE )
623 s = regreplace( s, "(^|\n| ) *", "\\1", 1 );
624
625 string prefix="";
626 if (indentlen && flags & BS_PREPEND_INDENT) {
627 if (indentlen+sizeof(s) > w ||
628 (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
629 prefix=indent+"\n";
630 indent=(flags & BS_NO_PARINDENT) ? "" : " ";
631 indentlen=sizeof(indent);
632 }
633 }
634
635 if ( flags & BS_BLOCK ) {
636 /*
637 s = implode( map( explode( s, "\n" ),
638 #'block_string, w, indentlen, flags),
639 "" );
640 */
641 s = block_string( s , w - indentlen, flags );
642 }
643 else {
644 s = sprintf("%-1.*=s",w-indentlen,s);
645 }
646 if ( s[<1] != '\n' ) s += "\n";
647
648 if ( !indentlen ) return prefix + s;
649
650 string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
651
652 return prefix + indent +
653 regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
654 /*
655 string *buf;
656
657 buf = explode( s, "\n" );
658 return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
659 */
660}
661
662// * Elemente aus mapping loeschen - mapping vorher kopieren
663
664mapping m_copy_delete(mapping m, mixed key) {
665 return m_delete(copy(m), key);
666}
667
668// * times
669int last_reboot_time()
670{
671 return __BOOT_TIME__;
672}
673
674int first_boot_time()
675{
676 return 701517600;
677}
678
679int exist_days()
680{
681 return (((time()-first_boot_time())/8640)+5)/10;
682}
683
684// * uptime :)
685string uptime()
686{
687 int t;
688 int tmp;
689 string s;
690
691 t=time()-__BOOT_TIME__;
692 s="";
693 if (t>=86400)
694 s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
695 if (t>=3600)
696 s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
697 if (t>60)
698 s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
699 return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
700}
701
702// * Was tun bei 'dangling' lfun-closures ?
703void dangling_lfun_closure() {
704 raise_error("dangling lfun closure\n");
705}
706
707// * Sperren ausser fuer master/simul_efun
708
709#if __EFUN_DEFINED__(set_environment)
710nomask void set_environment(object o1, object o2) {
711 raise_error("Available only for root\n");
712}
713#endif
714
715nomask void set_this_player(object pl) {
716 raise_error("Available only for root\n");
717}
718
MG Mud User88f12472016-06-24 23:31:02 +0200719// * Jetzt auch closures
720int process_flag;
721
722public nomask int process_call()
723{
724 if (process_flag>0)
725 return process_flag;
726 else return(0);
727}
728
Zesstraddddbf72021-05-14 16:52:16 +0200729private nomask string _process_string(string str, object|lwobject po)
730{
731 set_this_object(po);
732 return(efun::process_string(str));
MG Mud User88f12472016-06-24 23:31:02 +0200733}
734
Zesstra16158af2020-09-01 00:07:59 +0200735private nomask int _illegal_ps_call(string s)
736{
737 int i_arg = strstr(s,"|"); // erst arg-trenner suchen
738 // dann ggf. von dort rueckwaerts den obj-trenner suchen
739 int i_ob = i_arg!=-1 ? strrstr(s, ":", i_arg)
740 : strstr(s,":");
741 // Wenn kein ":" vorkommt, ists max. ein objektinterner Call und erlaubt
742 if (i_ob == -1)
743 return 0;
744 string obname = (i_arg != -1) ? s[i_ob+1..i_arg-1]
745 : s[i_ob+1..];
746 // Wenn es das nicht gibt, ist es auch OK - process_string laedt keine
747 // nicht-geladenen Objekte. Das hier ist auch der Fall, wenn in
748 // dem Substring nen : vorkommt, es aber gar kein process_call ist...
749 object ob = find_object(obname);
750 if (!ob)
751 return 0;
752 // Es gibt ein Objekt. Jetzt wird es spannend. Erlaubt sind calls zwischen
Zesstra2ad183a2020-09-03 20:05:18 +0200753 // Objekten, welche dieselbe UID haben oder vom gleichen Magier stammen.
754 if (getuid(ob) == getuid(previous_object()) ||
755 REAL_UID(ob) == REAL_UID(previous_object()))
Zesstra16158af2020-09-01 00:07:59 +0200756 return 0;
757 // Alles andere ist nicht erlaubt
758 return 1;
759}
760
Zesstra739c58a2020-08-30 23:45:26 +0200761nomask string process_string( string|closure str )
MG Mud User88f12472016-06-24 23:31:02 +0200762{
763 string tmp, err;
Zesstrac5af6a72020-09-03 20:03:06 +0200764 int flag;
765
766 // Hmpf, es wird tatsaechlich reihenweise mit 0 gerufen.
767 if (!str)
768 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200769
Zesstra739c58a2020-08-30 23:45:26 +0200770 // process_string() wird nur noch ausgewertet, wenn der Aufrufer einen
Zesstra16158af2020-09-01 00:07:59 +0200771 // Level von maximal 30 hat. Das schliesst alle Objekten in /d/, /p/ und den
Zesstra739c58a2020-08-30 23:45:26 +0200772 // Gilden ein, aber verhindert es fuer alle hochstufigen Magier und ihre
773 // Objekte. Ausserdem erlauben wir keine Auswertung mehr fuer
774 // Spielerobjekte, wenn sie mehr als Seher sind.
Zesstra16158af2020-09-01 00:07:59 +0200775 // TODO: aus Spielershells ausbauen
Zesstra739c58a2020-08-30 23:45:26 +0200776 // TODO 2: ganz ausbauen.
777 if ( (query_once_interactive(previous_object())
778 && query_wiz_level(previous_object()) > SEER_LVL
779 )
Zesstrac8030682020-08-31 20:50:08 +0200780 || query_wiz_level(getuid(previous_object())) > SPECIAL_LVL)
Zesstra739c58a2020-08-30 23:45:26 +0200781 {
Zesstra86e26da2020-08-31 21:13:17 +0200782 // Ein Fehler wird aber nur ausgeloest, falls der String ein @@ enthaelt,
Zesstra284a35f2020-08-31 21:11:04 +0200783 // ansonsten koennen wir den ohne Fehler returnieren.
784 if (stringp(str) && strstr(str, "@@") == -1)
785 return str;
786 else
787 {
788 set_this_object(previous_object());
789 raise_error("Illegale Benutzung von process_string(). Aufrufer "
790 "ist Magiershell oder Objekt mit Level > 30.\n");
791 }
Zesstra739c58a2020-08-30 23:45:26 +0200792 }
Zesstra16158af2020-09-01 00:07:59 +0200793 // Kein Aufruf von Funktionen in Objekten anderer Magier erlaubt.
Zesstra98c5d582020-09-03 21:36:19 +0200794 if (stringp(str))
Zesstra16158af2020-09-01 00:07:59 +0200795 {
Zesstra98c5d582020-09-03 21:36:19 +0200796 foreach(string s: explode(str, "@@"))
Zesstra16158af2020-09-01 00:07:59 +0200797 {
Zesstra98c5d582020-09-03 21:36:19 +0200798 if (sizeof(s) && _illegal_ps_call(s))
799 {
800 set_this_object(previous_object());
801 raise_error("Illegale Benutzung von process_string(). Aufruf in "
802 "in fremder UID nicht erlaubt.\n");
803 }
Zesstra16158af2020-09-01 00:07:59 +0200804 }
805 }
Zesstra98c5d582020-09-03 21:36:19 +0200806 else if ( closurep(str) ) {
MG Mud User88f12472016-06-24 23:31:02 +0200807 set_this_object( previous_object() );
808 return funcall(str);
809 }
MG Mud User88f12472016-06-24 23:31:02 +0200810
811 if ( !(flag = process_flag > time() - 60))
812 process_flag=time();
813
814 err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
815
816 if ( !flag )
817 process_flag=0;
818
819 if (err) {
820 // Verarbeitung abbrechen
821 set_this_object(previous_object());
822 raise_error(err);
823 }
824 return tmp;
825}
826
827// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
828// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
829// zurueckgeliefert, sonst 0.
830// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
831public int mkdirp(string dir) {
832 // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
833 if (strstr(dir, "/") != 0)
834 raise_error("mkdirp(): Pfad ist nicht absolute.\n");
835 // cut off trailing /...
836 if (dir[<1]=='/')
837 dir = dir[0..<2];
838
839 int fstat = file_size(dir);
840 // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
841 if (fstat == FSIZE_DIR)
842 return 1;
843 // wenn schon ne Datei existiert, geht es nicht.
844 if (fstat != FSIZE_NOFILE)
845 return 0;
846 // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
847 // toplevel-verzeichnis, was direkt angelegt wird.
848 if (strrstr(dir,"/")==0) {
849 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
850 }
851
852 // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
853 // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
854 // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
855
856 // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
857 // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
858 if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
859 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
Zesstraa25c9c12018-11-17 00:09:25 +0100860 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200861}
862
863
864// * Properties ggfs. mitspeichern
Zesstraddddbf72021-05-14 16:52:16 +0200865mixed save_object(string|int name)
MG Mud User88f12472016-06-24 23:31:02 +0200866{
867 mapping properties;
868 mapping save;
869 mixed index, res;
870 int i;
Zesstraf84b6272013-01-26 00:14:12 +0100871 string oldpath;
MG Mud User88f12472016-06-24 23:31:02 +0200872
873 // nur Strings und 0 zulassen
Zesstraddddbf72021-05-14 16:52:16 +0200874 if ((!stringp(name) || !sizeof(name)) &&
875 (!intp(name) || name!=0))
876 {
MG Mud User88f12472016-06-24 23:31:02 +0200877 set_this_object(previous_object());
878 raise_error(sprintf(
879 "Only non-empty strings and 0 may be used as filename in "
880 "sefun::save_object()! Argument was %O\n",name));
881 }
Zesstraddddbf72021-05-14 16:52:16 +0200882 if(!objectp(previous_object()))
883 {
884 set_this_object(previous_object());
885 raise_error(sprintf("save_object() only calleable by objects!\n"));
886 }
MG Mud User88f12472016-06-24 23:31:02 +0200887
Zesstraf84b6272013-01-26 00:14:12 +0100888 if (stringp(name)) {
889 // abs. Pfad erzeugen. *seufz*
890 if (name[0]!='/')
891 name = "/" + name;
Zesstraf84b6272013-01-26 00:14:12 +0100892 // automatisch in LIBDATADIR speichern
893 if (strstr(name,"/"LIBDATADIR"/") != 0) {
Zesstrad43edf72017-01-31 15:43:35 +0100894 oldpath = name;
Zesstraf84b6272013-01-26 00:14:12 +0100895 name = "/"LIBDATADIR + name;
896 // wenn das Verzeichnis nicht existiert, ggf. anlegen
897 string dir = name[0..strrstr(name,"/")-1];
898 if (file_size(dir) != FSIZE_DIR) {
899 if (mkdirp(dir) != 1)
900 raise_error("save_object(): kann Verzeichnis " + dir
901 + " nicht anlegen!");
902 }
903 }
904 }
905
MG Mud User88f12472016-06-24 23:31:02 +0200906 save = m_allocate(0, 2);
Zesstra4dbb9882019-11-26 21:26:36 +0100907 properties = previous_object()->QueryProperties();
MG Mud User88f12472016-06-24 23:31:02 +0200908
909 if(mappingp(properties))
910 {
911 // delete all entries in mapping properties without SAVE flag!
912 index = m_indices(properties);
913 for(i = sizeof(index)-1; i>=0;i--)
914 {
915 if(properties[index[i], F_MODE] & SAVE)
916 {
917 save[index[i]] = properties[index[i]];
918 save[index[i], F_MODE] =
919 properties[index[i], F_MODE] &
920 (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
921 }
922 }
923 }
924 else save = ([]);
925
926 // save object!
927 previous_object()->_set_save_data(save);
928 // format: wie definiert in config.h
Zesstraf84b6272013-01-26 00:14:12 +0100929 if (stringp(name)) {
MG Mud User88f12472016-06-24 23:31:02 +0200930 res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
931 __LIB__SAVE_FORMAT_VERSION__);
Zesstraf84b6272013-01-26 00:14:12 +0100932 // wenn erfolgreich und noch nen Savefile existiert, was nicht unter
933 // /data/ liegt, wird das geloescht.
934 if (!res && oldpath
935 && file_size(oldpath+".o") >= 0)
936 rm(oldpath+".o");
937 }
MG Mud User88f12472016-06-24 23:31:02 +0200938 else
939 res = funcall(bind_lambda(#'efun::save_object, previous_object()),
940 __LIB__SAVE_FORMAT_VERSION__);
Zesstraf84b6272013-01-26 00:14:12 +0100941
MG Mud User88f12472016-06-24 23:31:02 +0200942 previous_object()->_set_save_data(0);
943
944#ifdef IOSTATS
945 // Stats...
946 struct iostat_s stat = (<iostat_s>);
947 stat->oname = object_name(previous_object());
948 stat->time = time();
949 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
950 // OIM_TOTAL_DATA_SIZE);
951 if (stringp(name))
952 stat->size = file_size(name + ".o");
953 else
954 stat->sizeof(res);
955 //debug_message("saveo: "+saveo_stat[0]+"\n");
956 saveo_stat[1][saveo_stat[0]] = stat;
957 saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
958 //debug_message("saveo 2: "+saveo_stat[0]+"\n");
959#endif
960
961 return res;
962}
963
964// * Auch Properties laden
965int restore_object(string name)
966{
967 int result;
968 mixed index;
969 mixed save;
970 mapping properties;
971 int i;
MG Mud User88f12472016-06-24 23:31:02 +0200972
Zesstra94381a42017-01-29 22:37:52 +0100973 if (sizeof(name) < 1)
974 {
975 set_this_object(previous_object());
976 raise_error("Bad arg 1 to restore_object(): expected non-empty "
977 "'string'.\n");
978 }
Zesstraddddbf72021-05-14 16:52:16 +0200979 if(!objectp(previous_object()))
980 {
981 set_this_object(previous_object());
982 raise_error(sprintf("restore_object() only calleable by objects!\n"));
983 }
Zesstra94381a42017-01-29 22:37:52 +0100984
Zesstra87166e12017-01-29 20:12:37 +0100985 // Wenn name vermutlich ein Pfad (also nicht mit #x:y anfaengt)
986 if (name[0] != '#')
987 {
988 // abs. Pfad erzeugen *seufz*
Zesstra0fbf95d2017-01-29 21:48:02 +0100989 if (name[0]!='/')
Zesstra87166e12017-01-29 20:12:37 +0100990 name = "/" + name;
Zesstraf84b6272013-01-26 00:14:12 +0100991
Bugfixb9fa6e82017-04-28 14:42:27 +0200992 // .c am Ende loeschen, sonst wird das File ggf. nicht gefunden.
993 if(name[<2..]==".c")
994 name=name[..<3];
995
Zesstra87166e12017-01-29 20:12:37 +0100996 // wenn kein /data/ vorn steht, erstmal gucken, ob das Savefile unter
997 // /data/ existiert. Wenn ja, wird das geladen.
Zesstra0fbf95d2017-01-29 21:48:02 +0100998 if (strstr(name,"/"LIBDATADIR"/") != 0)
Zesstra87166e12017-01-29 20:12:37 +0100999 {
1000 string path = "/"LIBDATADIR + name;
1001 if (file_size(path + ".o") >= 0)
1002 name = path;
1003 }
Zesstraf84b6272013-01-26 00:14:12 +01001004 }
1005
MG Mud User88f12472016-06-24 23:31:02 +02001006 // get actual property settings (by create())
Zesstra4dbb9882019-11-26 21:26:36 +01001007 properties = previous_object()->QueryProperties();
MG Mud User88f12472016-06-24 23:31:02 +02001008
1009// DEBUG(sprintf("RESTORE %O\n",name));
1010 // restore object
1011 result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
1012 //'))
1013 //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein
1014 //mapping sein sollte.
Zesstra4dbb9882019-11-26 21:26:36 +01001015 save = previous_object()->_get_save_data();
1016 if((save))
MG Mud User88f12472016-06-24 23:31:02 +02001017 {
1018 index = m_indices(save);
1019 for(i = sizeof(index)-1; i>=0; i--)
1020 {
1021 properties[index[i]] = save[index[i]];
1022 properties[index[i], F_MODE] = save[index[i], F_MODE]
1023 &~(SETCACHED|QUERYCACHED);
1024 }
1025 }
1026 else properties = ([]);
1027
1028 // restore properties
1029 funcall(
1030 bind_lambda(
1031 unbound_lambda(({'arg}), //'})
1032 ({#'call_other,({#'this_object}),
1033 "SetProperties",'arg})),//')
1034 previous_object()),properties);
1035 previous_object()->_set_save_data(0);
1036
1037#ifdef IOSTATS
1038 // Stats...
1039 //debug_message("restoreo: "+restoreo_stat[0]+"\n");
1040 struct iostat_s stat = (<iostat_s>);
1041 stat->oname = object_name(previous_object());
1042 stat->time = time();
1043 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
1044 // OIM_TOTAL_DATA_SIZE);
1045 stat->size = file_size(name + ".o");
1046 restoreo_stat[1][restoreo_stat[0]] = stat;
1047
1048 restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
1049#endif
1050
1051 return result;
1052}
1053
1054// * HB eines Objektes ein/ausschalten
1055int set_object_heart_beat(object ob, int flag)
1056{
1057 if (objectp(ob))
1058 return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
Zesstrab8a823b2021-05-14 16:59:32 +02001059 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02001060}
1061
1062// * Magierlevelgruppen ermitteln
Zesstrad36ce392020-08-30 23:42:04 +02001063int query_wiz_grp(object|string wiz)
MG Mud User88f12472016-06-24 23:31:02 +02001064{
1065 int lev;
1066
1067 lev=query_wiz_level(wiz);
1068 if (lev<SEER_LVL) return 0;
1069 if (lev>=GOD_LVL) return lev;
1070 if (lev>=ARCH_LVL) return ARCH_GRP;
1071 if (lev>=ELDER_LVL) return ELDER_GRP;
1072 if (lev>=LORD_LVL) return LORD_GRP;
1073 if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
1074 if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
1075 if (lev>=WIZARD_LVL) return WIZARD_GRP;
1076 if (lev>=LEARNER_LVL) return LEARNER_GRP;
1077 return SEER_GRP;
1078}
1079
1080mixed *wizlist_info()
1081{
1082 if (ARCH_SECURITY || !extern_call())
1083 return efun::wizlist_info();
1084 return 0;
1085}
1086
1087// * wizlist ausgeben
1088varargs void wizlist(string name, int sortkey ) {
1089
1090 if (!name)
1091 {
1092 if (this_player())
1093 name = getuid(this_player());
1094 if (!name)
1095 return;
1096 }
1097
1098 // Schluessel darf nur in einem gueltigen Bereich sein
1099 if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
1100
1101 mixed** wl = efun::wizlist_info();
1102 // nach <sortkey> sortieren
1103 wl = sort_array(wl, function int (mixed a, mixed b)
1104 {return a[sortkey] < b[sortkey]; } );
1105
1106 // Summe ueber alle Kommandos ermitteln.
1107 int total_cmd, i;
1108 int pos=-1;
1109 foreach(mixed entry : wl)
1110 {
1111 total_cmd += entry[WL_COMMANDS];
1112 if (entry[WL_NAME] == name)
1113 pos = i;
1114 ++i;
1115 }
1116
1117 if (pos < 0 && name != "ALL" && name != "TOP100")
1118 return;
1119
1120 if (name == "TOP100")
1121 {
1122 if (sizeof(wl) > 100)
1123 wl = wl[0..100];
1124 else
1125 wl = wl;
1126 }
1127 // um name herum schneiden
1128 else if (name != "ALL")
1129 {
1130 if (sizeof(wl) <= 21)
1131 wl = wl;
1132 else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
1133 wl = wl[pos-10..pos+10];
1134 else if (pos <=21)
1135 wl = wl[0..20];
1136 else if (pos >= sizeof(wl) - 21)
1137 wl = wl[<21..];
1138 else
1139 wl = wl;
1140 }
1141
1142 write("\nWizard top score list\n\n");
1143 if (total_cmd == 0)
1144 total_cmd = 1;
1145 printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
1146 "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
1147 foreach(mixed e: wl)
1148 {
1149 printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
1150 e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
1151 e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
1152 e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
1153 );
1154 }
1155 printf("\nTotal %7d (%d)\n\n", total_cmd, sizeof(wl));
1156}
1157
1158
1159// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
1160
1161// ermittelt das Objekt des Callouts.
1162private object _call_out_obj( mixed call_out ) {
1163 return pointerp(call_out) ? call_out[0] : 0;
1164}
1165
1166private void _same_object( object ob, mapping m ) {
1167 // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
1168 if ( m[ob] )
1169 m[ob] += ({ ob });
1170 else
1171 m[ob] = ({ ob });
1172}
1173
1174// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
1175// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
1176// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
1177private void _same_path( object key, object *obs, mapping m ) {
1178 string path;
1179 if (!objectp(key) || !pointerp(obs)) return;
1180
1181 path = load_name(key);
1182
1183 if ( m[path] )
1184 m[path] += obs;
1185 else
1186 m[path] = obs;
1187}
1188
1189// key kann object oder string sein.
1190private int _too_many( mixed key, mapping m, int i ) {
1191 return sizeof(m[key]) >= i;
1192}
1193
1194// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
1195// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
1196// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
1197private void _destroy( mixed key, object *obs, string text, int uid ) {
1198 if (!pointerp(obs)) return;
1199 // Array mit unique Eintraege erzeugen.
1200 obs = m_indices( mkmapping(obs) );
1201 // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
1202 // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
1203 // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
1204 // laeuft.
1205 catch( efun::raise_error(
1206 sprintf( text,
Zesstra4dbb9882019-11-26 21:26:36 +01001207 uid ? master()->creator_file(key) : key,
MG Mud User88f12472016-06-24 23:31:02 +02001208 sizeof(obs), object_name(obs[<1]) ) );publish);
1209 // Und weg mit dem Kram...
1210 filter( obs, #'efun::destruct/*'*/ );
1211}
1212
1213// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
1214// sind dabei nicht unique.
1215private void _same_uid( string key, object *obs, mapping m, closure cf ) {
1216 string uid;
1217
1218 if ( !pointerp(obs) || !sizeof(obs) )
1219 return;
1220
1221 uid = funcall( cf, key );
1222
1223 if ( m[uid] )
1224 m[uid] += obs; // obs ist nen Array
1225 else
1226 m[uid] = obs;
1227}
1228
1229private int _log_call_out(mixed co)
1230{
1231 log_file("TOO_MANY_CALLOUTS",
1232 sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
1233 200000);
1234 return 0;
1235}
1236
1237private int last_callout_log=0;
1238
1239nomask varargs void call_out( varargs mixed *args )
1240{
Zesstrab8a823b2021-05-14 16:59:32 +02001241 mixed *call_outs;
MG Mud User88f12472016-06-24 23:31:02 +02001242
1243 // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
1244 // haben.
1245 if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
1246 && geteuid(previous_object()) != ROOTID )
1247 {
1248 // Log erzeugen...
1249 if (last_callout_log <
1250 efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
1251 && get_eval_cost() > 200000)
1252 {
1253 last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1254 log_file("TOO_MANY_CALLOUTS",
1255 sprintf("\n%s: ############ Too many callouts: %d ##############\n",
1256 strftime("%y%m%d-%H%M%S"),
1257 efun::driver_info(DI_NUM_CALLOUTS)));
1258 filter(efun::call_out_info(), #'_log_call_out);
1259 }
1260 // Objekte aller Callouts ermitteln
1261 call_outs = map( efun::call_out_info(), #'_call_out_obj );
1262 mapping objectmap = ([]);
1263 filter( call_outs, #'_same_object, &objectmap );
1264 // Master nicht grillen...
1265 efun::m_delete( objectmap, master(1) );
1266 // alle Objekte raussuchen, die zuviele haben...
1267 mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
1268 // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
1269 // Objekten uebergeben (in diesem Fall sind Keys und Array mit
1270 // Objekten jeweils das gleiche Objekt).
1271 if ( sizeof(res) )
1272 walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "
1273 "object [%O]. Destructed %d objects. [%s]\n", 0 );
1274
1275 // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
1276 // Objekte killen, die mehr als 50 Callouts laufen haben - und
1277 // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
1278 // Clones einer BP gekillt, die Callouts laufen haben, falls alle
1279 // diese Objekte _zusammen_ mehr als 50 Callouts haben!
1280 if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
1281 // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
1282 // mehr drin, da sie dort als Keys verwendet wurden.
1283 mapping pathmap=([]);
1284 // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
1285 // von Objekten als Werte.
1286 walk_mapping( objectmap, #'_same_path, &pathmap);
1287 // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
1288 res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
1289 // und ueber alle Keys gehen, an _destroy() werden die Clones
1290 // uebergeben, die Callouts haben.
1291 if ( sizeof(res) )
1292 walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
1293 "'%s'. Destructed %d objects. [%s]\n", 0 );
1294
1295 // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
1296 // noch >1000 Callouts laufen, werden diesmal alle Callouts
1297 // einer UID zusammengezaehlt.
1298 // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
1299 // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
1300 if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
1301 {
1302 // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
1303 // zusammensortieren. Zerstoerte Clones filter _same_uid()
1304 // raus.
1305 mapping uidmap=([]);
1306 walk_mapping( pathmap, #'_same_uid, &uidmap,
1307 symbol_function( "creator_file",
1308 "/secure/master" ) );
1309 // In res nun UIDs als Keys und Arrays von Objekten als Werte.
1310 // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
1311 // 100 Callouts!) haben.
1312 res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
1313 // und erneut ueber die Keys laufen und jeweils die Arrays mit
1314 // den Objekten zur Zerstoerung an _destroy()...
1315 if ( sizeof(res) )
1316 walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
1317 "UID '%s'. Destructed %d objects. [%s]\n",
1318 1 );
1319 }
1320 }
1321 }
1322
1323 // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
1324 if ( !previous_object() )
1325 return;
1326
1327 set_this_object( previous_object() );
1328 apply( #'efun::call_out, args );
1329 return;
1330}
1331
1332mixed call_out_info() {
1333
Zesstraddddbf72021-05-14 16:52:16 +02001334 object|lwobject po = previous_object();
MG Mud User88f12472016-06-24 23:31:02 +02001335 mixed coi = efun::call_out_info();
1336
1337 // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
1338 // /obj haben die BackboneID.
1339 if (query_wiz_level(getuid(po)) >= ARCH_LVL
Zesstra4dbb9882019-11-26 21:26:36 +01001340 || master()->creator_file(load_name(po)) == BACKBONEID ) {
MG Mud User88f12472016-06-24 23:31:02 +02001341 return coi;
1342 }
1343 else {
1344 return filter(coi, function mixed (mixed arr) {
1345 if (pointerp(arr) && arr[0]==po)
1346 return 1;
1347 else return 0; });
1348 }
1349}
1350
1351// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
1352// NICHT das Objekt, was ggf. die lfun definiert!
1353mixed query_closure_object(closure c) {
1354 return
1355 CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
1356 0 :
1357 (to_object(c) || -1);
1358}
1359
1360// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
1361varargs void move_object(mixed what, mixed where)
1362{
Zesstraddddbf72021-05-14 16:52:16 +02001363 // Wenn nur ein Argument angegeben wird, ist es das Ziel und wir nehmen
1364 // previous_object() als zu bewegendes Objekt.
MG Mud User88f12472016-06-24 23:31:02 +02001365 if (!where)
1366 {
1367 where=what;
Zesstraddddbf72021-05-14 16:52:16 +02001368 what=previous_object();
MG Mud User88f12472016-06-24 23:31:02 +02001369 }
1370 if (((stringp(where) && where==NETDEAD_ROOM ) ||
1371 (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
1372 objectp(what) && object_name(what)!="/obj/sperrer")
1373 {
1374 if (!query_once_interactive(what))
1375 {
1376 what->remove();
1377 if (what) destruct(what);
1378 return;
1379 }
1380 if (living(what) || interactive(what))
1381 {
1382 log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
1383 return;
1384 }
1385 set_object_heart_beat(what,0);
1386 }
Zesstraddddbf72021-05-14 16:52:16 +02001387 object tmp=what;
MG Mud User88f12472016-06-24 23:31:02 +02001388 while (tmp=environment(tmp))
1389 // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
1390 // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
1391 // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
1392 // da um jedes bisschen Rechenzeit geht.
1393 // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
1394 //
1395 // Tiamak
1396 tmp->_set_last_content_change();
Zesstraddddbf72021-05-14 16:52:16 +02001397 funcall(bind_lambda(#'efun::move_object,previous_object()),what,where);
MG Mud User88f12472016-06-24 23:31:02 +02001398 if (tmp=what)
1399 while (tmp=environment(tmp))
1400 tmp->_set_last_content_change();
1401}
1402
1403
1404void start_simul_efun() {
1405 mixed *info;
1406
1407 // Falls noch nicht getan, extra_wizinfo initialisieren
1408 if ( !pointerp(info = get_extra_wizinfo(0)) )
1409 set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
1410
1411 InitLivingData(info);
1412
1413 set_next_reset(10); // direkt mal aufraeumen
1414}
1415
1416protected void reset() {
1417 set_next_reset(7200);
1418 CleanLivingData();
1419}
1420
1421#if !__EFUN_DEFINED__(absolute_hb_count)
1422int absolute_hb_count() {
1423 return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1424}
1425#endif
1426
1427void __set_environment(object ob, mixed target)
1428{
1429 string path;
1430 object obj;
1431
1432 if (!objectp(ob))
1433 return;
1434 if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
1435 return;
1436 if (objectp(target))
1437 {
1438 efun::set_environment(ob,target);
1439 return;
1440 }
Zesstra4dbb9882019-11-26 21:26:36 +01001441 path=master()->make_path_absolute(target);
MG Mud User88f12472016-06-24 23:31:02 +02001442 if (stringp(path) && file_size(path+".c")>=0 &&
1443 !catch(load_object(path);publish) )
1444 {
1445 obj=find_object(path);
1446 efun::set_environment(ob,obj);
1447 return;
1448 }
1449}
1450
1451void _dump_wizlist(string file, int sortby) {
1452 int i;
1453 mixed *a;
1454
1455 if (!LORD_SECURITY)
1456 return;
1457 if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
1458 {
1459 write("NO WRITE PERMISSION\n");
1460 return;
1461 }
1462 a = wizlist_info();
1463 a = sort_array(a, lambda( ({'a,'b}),
1464 ({#'<,
1465 ({#'[,'a,sortby}),
1466 ({#'[,'b,sortby})
1467 })));
1468 rm(file);
1469 for (i=sizeof(a)-1;i>=0;i--)
1470 write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
1471 a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
1472 a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
1473}
1474
Zesstraddddbf72021-05-14 16:52:16 +02001475public object deep_present(string|object what, object ob=previous_object())
1476{
MG Mud User88f12472016-06-24 23:31:02 +02001477 // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
1478 // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
1479 if(objectp(what)) {
1480 object *envs=all_environment(what);
1481 // wenn ob kein Environment hat, ist es offensichtlich nicht in what
1482 // enthalten.
1483 if (!pointerp(envs)) return 0;
Zesstraddddbf72021-05-14 16:52:16 +02001484 if (ob in envs) return what;
MG Mud User88f12472016-06-24 23:31:02 +02001485 }
1486 // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
1487 // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
1488 // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
1489 else if (stringp(what)) {
1490 int cnt;
1491 string newwhat;
1492 if(sscanf(what,"%s %d",newwhat,cnt)!=2)
1493 cnt=1;
1494 else
1495 what=newwhat;
1496 foreach(object invob: deep_inventory(ob)) {
1497 if (invob->id(what) && !--cnt)
1498 return invob;
1499 }
1500 }
1501 else {
1502 set_this_object(previous_object());
1503 raise_error(sprintf("Wrong argument 1 to deep_present(). "
1504 "Expected \"object\" or \"string\", got %.50O.\n",
1505 what));
1506 }
1507 return 0;
1508}
1509
1510mapping dump_ip_mapping()
1511{
1512 return 0;
1513}
1514
1515nomask void swap(object obj)
1516{
1517 write("Your are not allowed to swap objects by hand!\n");
1518 return;
1519}
1520
1521nomask varargs void garbage_collection(string str)
1522{
1523 if(previous_object()==0 || !IS_ARCH(geteuid(previous_object()))
1524 || !ARCH_SECURITY)
1525 {
Zesstraddddbf72021-05-14 16:52:16 +02001526 // historical info, but amusing message. ;-)
MG Mud User88f12472016-06-24 23:31:02 +02001527 write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
1528 return;
1529 }
1530 else if (stringp(str))
1531 {
1532 return efun::garbage_collection(str);
1533 }
1534 else
1535 return efun::garbage_collection();
1536}
1537
Zesstrae490a532020-09-22 22:07:54 +02001538varargs void notify_fail(string|closure nf, int prio)
1539{
MG Mud User88f12472016-06-24 23:31:02 +02001540 int oldprio;
Zesstrae490a532020-09-22 22:07:54 +02001541
Zesstrad7795012020-09-22 22:13:31 +02001542 if (!PL || !previous_object())
Zesstrae490a532020-09-22 22:07:54 +02001543 return;
MG Mud User88f12472016-06-24 23:31:02 +02001544
1545 // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
1546 // und vergleichen.
Zesstrad7795012020-09-22 22:13:31 +02001547 object oldo = query_notify_fail(1);
1548 if (oldo && previous_object()!=oldo)
1549 {
1550 if (!prio)
1551 {
MG Mud User88f12472016-06-24 23:31:02 +02001552 //Prioritaet dieses notify_fail() 'abschaetzen'
Zesstrad7795012020-09-22 22:13:31 +02001553 if (previous_object()==PL) // Spieler-interne (soul-cmds)
MG Mud User88f12472016-06-24 23:31:02 +02001554 prio=NF_NL_OWN;
Zesstrad7795012020-09-22 22:13:31 +02001555 else if (living(previous_object()))
MG Mud User88f12472016-06-24 23:31:02 +02001556 prio=NF_NL_LIVING;
Zesstrad7795012020-09-22 22:13:31 +02001557 else if (previous_object()->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001558 prio=NF_NL_ROOM;
1559 else
1560 prio=NF_NL_THING;
1561 }
1562 //Prioritaet des alten Setzers abschaetzen
1563 if (oldo==PL)
1564 oldprio=NF_NL_OWN;
1565 else if (living(oldo))
1566 oldprio=NF_NL_LIVING;
Zesstra4dbb9882019-11-26 21:26:36 +01001567 else if (oldo->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001568 oldprio=NF_NL_ROOM;
1569 else
1570 oldprio=NF_NL_THING;
1571 }
1572 else // wenn es noch kein Notify_fail gibt:
1573 oldprio=NF_NL_NONE;
1574
Zesstrad7795012020-09-22 22:13:31 +02001575 // vergleichen und ggf. setzen
1576 if (prio >= oldprio)
1577 {
1578 set_this_object(previous_object());
MG Mud User88f12472016-06-24 23:31:02 +02001579 efun::notify_fail(nf);
1580 }
1581
1582 return;
1583}
1584
Zesstrae490a532020-09-22 22:07:54 +02001585void _notify_fail(string|closure str)
MG Mud User88f12472016-06-24 23:31:02 +02001586{
Zesstra351ca4d2020-09-22 22:17:24 +02001587 notify_fail(str, NF_NL_NONE);
MG Mud User88f12472016-06-24 23:31:02 +02001588}
1589
1590string time2string( string format, int zeit )
1591{
1592 int i,ch,maxunit,dummy;
1593 string *parts, fmt;
1594
1595 int secs = zeit;
1596 int mins = (zeit/60);
1597 int hours = (zeit/3600);
1598 int days = (zeit/86400);
1599 int weeks = (zeit/604800);
1600 int months = (zeit/2419200);
1601 int abbr = 0;
1602
1603 parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
1604
1605 for( i=1; i<sizeof(parts); i+=2 )
1606 {
1607 ch = parts[i][<1];
1608 switch( parts[i][<1] )
1609 {
1610 case 'x': case 'X':
1611 abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
1612 // NO break !
1613 case 'n': case 'N':
1614 maxunit |= 31;
1615 break;
1616 case 'w': case 'W':
1617 maxunit |= 15;
1618 break;
1619 case 'd': case 'D':
1620 maxunit |= 7;
1621 break;
1622 case 'h': case 'H':
1623 maxunit |= 3;
1624 break;
1625 case 'm': case 'M':
1626 maxunit |= 1;
1627 break;
1628 }
1629 }
1630 if( maxunit & 16 ) weeks %= 4;
1631 if( maxunit & 8 ) days %= 7;
1632 if( maxunit & 4 ) hours %= 24;
1633 if( maxunit & 2 ) mins %= 60;
1634 if( maxunit ) secs %= 60;
1635
1636 for( i=1; i<sizeof(parts); i+=2 )
1637 {
1638 fmt = parts[i][0..<2];
1639 ch = parts[i][<1];
1640 if( ch=='x' )
1641 {
1642 if (months > 0) ch='n';
1643 else if( weeks>0 ) ch='w';
1644 else if( days>0 ) ch='d';
1645 else if( hours>0 ) ch='h';
1646 else if(mins > 0) ch = 'm';
1647 else ch = 's';
1648 }
1649 else if( ch=='X' )
1650 {
1651 if (months > 0) ch='N';
1652 else if( weeks>0 ) ch='W';
1653 else if( days>0 ) ch='D';
1654 else if( hours>0 ) ch='H';
1655 else if(mins > 0) ch = 'M';
1656 else ch = 'S';
1657 }
1658
1659 switch( ch )
1660 {
1661 case 'n': parts[i] = sprintf( fmt+"d", months ); break;
1662 case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
1663 case 'd': parts[i] = sprintf( fmt+"d", days ); break;
1664 case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
1665 case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
1666 case 's': parts[i] = sprintf( fmt+"d", secs ); break;
1667 case 'N':
1668 if(abbr) parts[i] = "M";
1669 else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
1670 break;
1671 case 'W':
1672 if(abbr) parts[i] = "w"; else
1673 parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
1674 break;
1675 case 'D':
1676 if(abbr) parts[i] = "d"; else
1677 parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
1678 break;
1679 case 'H':
1680 if(abbr) parts[i] = "h"; else
1681 parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
1682 break;
1683 case 'M':
1684 if(abbr) parts[i] = "m"; else
1685 parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
1686 break;
1687 case 'S':
1688 if(abbr) parts[i] = "s"; else
1689 parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
1690 break;
1691 case '%':
1692 parts[i] = "%";
1693 break;
1694 }
1695 }
1696 return implode( parts, "" );
1697}
1698
1699nomask mixed __create_player_dummy(string name)
1700{
1701 string err;
1702 object ob;
1703 mixed m;
1704 //hat nen Scherzkeks die Blueprint bewegt?
1705 if ((ob=find_object("/secure/login")) && environment(ob))
1706 catch(destruct(ob);publish);
1707 err = catch(ob = clone_object("secure/login");publish);
1708 if (err)
1709 {
1710 write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
1711 return 0;
1712 }
Zesstra4dbb9882019-11-26 21:26:36 +01001713 if (objectp(m=ob->new_logon(name))) netdead[name]=m;
MG Mud User88f12472016-06-24 23:31:02 +02001714 return m;
1715}
1716
1717nomask int secure_level()
1718{
1719 int *level;
1720 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
1721 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
1722 //von 0.
1723 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
1724 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
1725 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
1726 //INteractive geben muss.
1727 level=map(caller_stack(1),function int (object caller)
1728 {if (objectp(caller))
1729 return(query_wiz_level(geteuid(caller)));
1730 return(0); // kein Objekt da, 0.
1731 } );
1732 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
1733}
1734
1735nomask string secure_euid()
1736{
1737 string euid;
1738
1739 if (!this_interactive()) // Es muss einen interactive geben
1740 return 0;
1741 euid=geteuid(this_interactive());
1742 // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
1743 // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
1744 // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
1745 foreach(object caller: caller_stack()) {
1746 if (!objectp(caller) ||
1747 (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
1748 return 0;
1749 }
1750 return euid; // 'sichere' euid zurueckgeben
1751}
1752
Zesstra42324bf2021-05-14 15:25:20 +02001753deprecated nomask int set_light(int i)
MG Mud User88f12472016-06-24 23:31:02 +02001754// erhoeht das Lichtlevel eines Objekts um i
1755// result: das Lichtlevel innerhalb des Objekts
1756{
Zesstrab8a823b2021-05-14 16:59:32 +02001757 object ob;
MG Mud User88f12472016-06-24 23:31:02 +02001758
1759 if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
1760
1761 // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
1762 if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
1763
1764 // Lichtberechnung findet eigentlich in der Mudlib statt.
Zesstra4dbb9882019-11-26 21:26:36 +01001765 return ob->QueryProp(P_INT_LIGHT);
MG Mud User88f12472016-06-24 23:31:02 +02001766}
1767
MG Mud User88f12472016-06-24 23:31:02 +02001768public varargs string CountUp( string *s, string sep, string lastsep )
1769{
1770 string ret;
1771
1772 if ( !pointerp(s) )
1773 return "";
1774
1775 if (!sep) sep = ", ";
1776 if (!lastsep) lastsep = " und ";
1777
1778 switch (sizeof(s)) {
1779 case 0: ret=""; break;
1780 case 1: ret=s[0]; break;
1781 default:
1782 ret = implode(s[0..<2], sep);
1783 ret += lastsep + s[<1];
1784 }
1785 return ret;
1786}
1787
Zesstraddddbf72021-05-14 16:52:16 +02001788nomask int query_next_reset(object ob=previous_object())
1789{
MG Mud User88f12472016-06-24 23:31:02 +02001790 // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
1791 if (ob && !objectp(ob))
1792 raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
1793 "expected object.\n",ob));
1794
MG Mud User88f12472016-06-24 23:31:02 +02001795 return efun::object_info(ob, OI_NEXT_RESET_TIME);
1796}
1797
1798
MG Mud User88f12472016-06-24 23:31:02 +02001799// ### Ersatzaufloesung in Strings ###
Arathorn066820b2019-11-27 19:47:19 +01001800varargs string replace_personal(string str, <string|object>* obs, int caps) {
1801 string* parts = regexplode(str, "@WE[A-SU]*[0-9]");
1802 int i = sizeof(parts);
MG Mud User88f12472016-06-24 23:31:02 +02001803
Zesstra19102132016-09-01 22:50:36 +02001804 if (i>1)
1805 {
MG Mud User88f12472016-06-24 23:31:02 +02001806 int j, t;
MG Mud User88f12472016-06-24 23:31:02 +02001807 t = j = sizeof(obs);
1808
Arathorn066820b2019-11-27 19:47:19 +01001809 <string|closure>* name_cls = allocate(j);
1810 while (j--) {
MG Mud User88f12472016-06-24 23:31:02 +02001811 if (objectp(obs[j]))
1812 name_cls[j] = symbol_function("name", obs[j]);
1813 else if (stringp(obs[j]))
1814 name_cls[j] = obs[j];
Arathorn066820b2019-11-27 19:47:19 +01001815 }
MG Mud User88f12472016-06-24 23:31:02 +02001816
Zesstra19102132016-09-01 22:50:36 +02001817 while ((i-= 2)>0)
1818 {
MG Mud User88f12472016-06-24 23:31:02 +02001819 // zu ersetzendes Token in Fall und Objektindex aufspalten
Arathorn066820b2019-11-27 19:47:19 +01001820 int ob_nr = parts[i][<1]-'1';
MG Mud User88f12472016-06-24 23:31:02 +02001821 if (ob_nr<0 || ob_nr>=t) {
1822 set_this_object(previous_object());
Zesstrab8a823b2021-05-14 16:59:32 +02001823 raise_error(sprintf(
1824 "replace_personal: using wrong object index %d\n", ob_nr));
MG Mud User88f12472016-06-24 23:31:02 +02001825 }
1826
1827 // casus kann man schon hier entscheiden
1828 int casus;
1829 string part = parts[i];
1830 switch (part[3]) {
1831 case 'R': casus = WER; break;
1832 case 'S': casus = WESSEN; break;
1833 case 'M': casus = WEM; break;
1834 case 'N': casus = WEN; break;
1835 default: continue; // passt schon jetzt nicht in das Hauptmuster
1836 }
1837
1838 // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
1839 mixed tmp;
1840 switch (part[3..<2]) {
1841 case "R": case "SSEN": case "M": case "N": // Name
1842 parts[i] = funcall(name_cls[ob_nr], casus, 1); break;
1843 case "RU": case "SSENU": case "MU": case "NU": // unbestimmt
1844 parts[i] = funcall(name_cls[ob_nr], casus); break;
1845 case "RQP": case "SSENQP": case "MQP": case "NQP": // Pronoun
1846 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001847 parts[i] = tmp->QueryPronoun(casus);
MG Mud User88f12472016-06-24 23:31:02 +02001848 break;
1849 case "RQA": case "SSENQA": case "MQA": case "NQA": // Article
1850 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001851 tmp = tmp->QueryArticle(casus, 1, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001852 if (stringp(tmp) && !(tmp[<1]^' '))
1853 tmp = tmp[0..<2]; // Extra-Space wieder loeschen
1854 break;
1855 case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
1856 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001857 parts[i] = tmp->QueryPossPronoun(MALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001858 break;
1859 case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
1860 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001861 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001862 break;
1863 case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
1864 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001865 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001866 break;
1867 case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
1868 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001869 parts[i] = tmp->QueryPossPronoun(MALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001870 break;
1871 case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
1872 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001873 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001874 break;
1875 case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
1876 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001877 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001878 break;
1879 default:
1880 continue;
1881 }
Zesstra19102132016-09-01 22:50:36 +02001882
MG Mud User88f12472016-06-24 23:31:02 +02001883 // wenn tmp ein String war, weisen wir es hier pauschal zu
1884 if (stringp(tmp))
1885 parts[i] = tmp;
1886
1887 // auf Wunsch wird nach Satzenden gross geschrieben
1888 if (caps)
Zesstra19102132016-09-01 22:50:36 +02001889 {
1890 // Wenn das vorhergehende parts[i] == "" ist, sind wir am Anfang vom
1891 // String und dies wird wie ein Satzende vorher behandelt.
1892 if (parts[i-1] == "")
MG Mud User88f12472016-06-24 23:31:02 +02001893 parts[i] = capitalize(parts[i]);
Zesstra19102132016-09-01 22:50:36 +02001894 else
1895 {
1896 switch (parts[i-1][<2..])
1897 {
1898 case ". ": case "! ": case "? ":
1899 case ".": case "!": case "?":
1900 case ".\n": case "!\n": case "?\n":
1901 case "\" ": case "\"\n":
1902 parts[i] = capitalize(parts[i]);
1903 break;
1904 }
MG Mud User88f12472016-06-24 23:31:02 +02001905 }
Zesstra19102132016-09-01 22:50:36 +02001906 }
MG Mud User88f12472016-06-24 23:31:02 +02001907 }
1908 return implode(parts, "");
1909 }
1910 return str;
1911}
1912
MG Mud User88f12472016-06-24 23:31:02 +02001913//replacements for dropped efuns in LD
1914#if !__EFUN_DEFINED__(extract)
1915deprecated varargs string extract(string str, int from, int to) {
1916
1917 if(!stringp(str)) {
1918 set_this_object(previous_object());
1919 raise_error(sprintf("Bad argument 1 to extract(): %O",str));
1920 }
1921 if (intp(from) && intp(to)) {
1922 if (from>=0 && to>=0)
1923 return(str[from .. to]);
1924 else if (from>=0 && to<0)
1925 return(str[from .. <abs(to)]);
1926 else if (from<0 && to>=0)
1927 return(str[<abs(from) .. to]);
1928 else
1929 return(str[<abs(from) .. <abs(to)]);
1930 }
1931 else if (intp(from)) {
1932 if (from>=0)
1933 return(str[from .. ]);
1934 else
1935 return(str[<abs(from) .. ]);
1936 }
1937 else {
1938 return(str);
1939 }
1940}
1941#endif // !__EFUN_DEFINED__(extract)
1942
1943#if !__EFUN_DEFINED__(slice_array)
1944deprecated varargs mixed slice_array(mixed array, int from, int to) {
1945
1946 if(!pointerp(array)) {
1947 set_this_object(previous_object());
1948 raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
1949 }
1950 if (intp(from) && intp(to)) {
1951 if (from>=0 && to>=0)
1952 return(array[from .. to]);
1953 else if (from>=0 && to<0)
1954 return(array[from .. <abs(to)]);
1955 else if (from<0 && to>=0)
1956 return(array[<abs(from) .. to]);
1957 else
1958 return(array[<abs(from) .. <abs(to)]);
1959 }
1960 else if (intp(from)) {
1961 if (from>=0)
1962 return(array[from .. ]);
1963 else
1964 return(array[<abs(from) .. ]);
1965 }
1966 else {
1967 return(array);
1968 }
1969}
1970#endif // !__EFUN_DEFINED__(slice_array)
1971
1972#if !__EFUN_DEFINED__(member_array)
1973deprecated int member_array(mixed item, mixed arraystring) {
1974
1975 if (pointerp(arraystring)) {
1976 return(efun::member(arraystring,item));
1977 }
1978 else if (stringp(arraystring)) {
1979 return(efun::member(arraystring,to_int(item)));
1980 }
1981 else {
1982 set_this_object(previous_object());
1983 raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
1984 }
1985}
1986#endif // !__EFUN_DEFINED__(member_array)
1987
1988// The digit at the i'th position is the number of bits set in 'i'.
1989string count_table =
1990 "0112122312232334122323342334344512232334233434452334344534454556";
1991int broken_count_bits( string s ) {
1992 int i, res;
1993 if( !stringp(s) || !(i=sizeof(s)) ) return 0;
1994 for( ; i-->0; ) {
1995 // We are counting 6 bits at a time using a precompiled table.
1996 res += count_table[(s[i]-' ')&63]-'0';
1997 }
1998 return res;
1999}
2000
2001#if !__EFUN_DEFINED__(count_bits)
2002int count_bits( string s ) {
2003 return(broken_count_bits(s));
2004}
2005#endif
2006
2007
2008// * Teile aus einem Array entfernen *** OBSOLETE
2009deprecated mixed *exclude_array(mixed *arr,int from,int to)
2010{
2011 if (to<from)
2012 to = from;
2013 return arr[0..from-1]+arr[to+1..];
2014}
2015