blob: 10cdc1d06cda367c7eb9109be7e556a239695e3c [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);
47int query_wiz_level(mixed player);
48nomask varargs int snoop(object me, object you);
49varargs string country(mixed ip, string num);
50int query_wiz_grp(mixed wiz);
51public varargs object deep_present(mixed what, object ob);
52nomask 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
193static object find_snooped(object who)
194{
195 object *u;
196 int i;
197
198 for (i=sizeof(u=users())-1;i>=0;i--)
199 if (who==efun::interactive_info(u[i], II_SNOOP_NEXT))
200 return u[i];
201 return 0;
202}
203
204private string Lcut(string str) {
205 return str[5..11]+str[18..];
206}
207
208nomask varargs int snoop( object me, object you )
209{
210 int ret;
211 object snooper0, snooper, snooper2, snooper3;
212
213 if( !objectp(me) || me == you || !PO )
214 return 0;
215
216 snooper0 = find_snooped(me);
217
218 if(you){
219 if ( PO != me && query_wiz_grp(me) >= query_wiz_grp(geteuid(PO)) )
220 return 0;
221
222 if ( query_wiz_grp(me) <= query_wiz_grp(you) &&
223 !(you->QueryAllowSnoop(me)) )
Zesstra6c333cc2020-04-28 20:18:45 +0200224 {
MG Mud User88f12472016-06-24 23:31:02 +0200225 if ( !IS_DEPUTY(me) || IS_ARCH(you) )
226 return 0;
Zesstra6c333cc2020-04-28 20:18:45 +0200227 }
MG Mud User88f12472016-06-24 23:31:02 +0200228
229 if ( (snooper = efun::interactive_info(you, II_SNOOP_NEXT)) &&
Zesstra6c333cc2020-04-28 20:18:45 +0200230 query_wiz_grp(snooper) >= query_wiz_grp(me) )
231 {
Zesstra4dbb9882019-11-26 21:26:36 +0100232 if ( snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
MG Mud User88f12472016-06-24 23:31:02 +0200233 return 0;
234
235 tell_object( snooper, sprintf( "%s snooped jetzt %s.\n",
236 me->name(WER), you->name(WER) ) );
237
238 snooper2 = me;
239
Zesstra6c333cc2020-04-28 20:18:45 +0200240 while ( snooper3 = interactive_info(snooper2, II_SNOOP_NEXT) )
241 {
MG Mud User88f12472016-06-24 23:31:02 +0200242 tell_object( snooper,
243 sprintf( "%s wird seinerseits von %s gesnooped.\n"
244 ,snooper2->name(WER),
245 snooper3->name(WEM) ) );
246 snooper2 = snooper3;
247 }
248
249 efun::snoop( snooper, snooper2 );
250
251 if ( efun::interactive_info(snooper2, II_SNOOP_NEXT) != snooper )
252 tell_object( snooper, sprintf( "Du kannst %s nicht snoopen.\n",
253 snooper2->name(WEN) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200254 else
255 {
MG Mud User88f12472016-06-24 23:31:02 +0200256 tell_object( snooper, sprintf( "Du snoopst jetzt %s.\n",
257 snooper2->name(WEN) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200258 if ( !IS_DEPUTY(snooper) )
259 {
MG Mud User88f12472016-06-24 23:31:02 +0200260 log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
261 dtime(time()),
262 snooper,
263 snooper2,
264 environment(snooper2) ),
265 100000 );
266 if (snooper0)
267 CHMASTER->send( "Snoop", snooper,
268 sprintf( "%s *OFF* %s (%O)",
269 capitalize(getuid(snooper)),
270 capitalize(getuid(snooper0)),
271 environment(snooper0) ) );
272
273 CHMASTER->send( "Snoop", snooper,
274 sprintf("%s -> %s (%O)",
275 capitalize(getuid(snooper)),
276 capitalize(getuid(snooper2)),
277 environment(snooper2)));
278 }
Zesstra6c333cc2020-04-28 20:18:45 +0200279 else
280 {
MG Mud User88f12472016-06-24 23:31:02 +0200281 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
282 dtime(time()),
283 snooper,
284 snooper2,
285 environment(snooper2) )
286 ,100000 );
287 }
288 }
289 }
290 else
Zesstra6c333cc2020-04-28 20:18:45 +0200291 {
MG Mud User88f12472016-06-24 23:31:02 +0200292 if (snooper)
Zesstra6c333cc2020-04-28 20:18:45 +0200293 {
294 if ( !me->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
295 {
MG Mud User88f12472016-06-24 23:31:02 +0200296 printf( "%s wird bereits von %s gesnooped. Benutze das "
297 "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
298 you->name(WER), snooper->name(WEM) );
299 return 0;
300 }
Zesstra6c333cc2020-04-28 20:18:45 +0200301 }
302 }
MG Mud User88f12472016-06-24 23:31:02 +0200303 ret = efun::snoop( me, you );
304
Zesstra6c333cc2020-04-28 20:18:45 +0200305 if ( !IS_DEPUTY(me) && efun::interactive_info(you, II_SNOOP_NEXT) == me)
306 {
MG Mud User88f12472016-06-24 23:31:02 +0200307 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
308 Lcut(dtime(time())),
309 me, you, environment(you) ),
310 100000 );
311
312 if (snooper0)
Zesstra6c333cc2020-04-28 20:18:45 +0200313 {
MG Mud User88f12472016-06-24 23:31:02 +0200314 CHMASTER->send( "Snoop", me,
315 sprintf( "%s *OFF* %s (%O).",
316 capitalize(getuid(me)),
317 capitalize(getuid(snooper0)),
318 environment(snooper0) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200319 }
MG Mud User88f12472016-06-24 23:31:02 +0200320
321 CHMASTER->send( "Snoop", me, sprintf( "%s -> %s (%O).",
322 capitalize(getuid(me)),
323 capitalize(getuid(you)),
324 environment(you) ) );
325 }
Zesstra6c333cc2020-04-28 20:18:45 +0200326 else
327 {
328 if ( efun::interactive_info(you, II_SNOOP_NEXT) == me )
329 {
MG Mud User88f12472016-06-24 23:31:02 +0200330 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
331 Lcut(dtime(time())),
332 me, you, environment(you) ),
333 100000 );
334 }
335 }
336
337 if ( ret && query_wiz_grp(me) <= query_wiz_grp(you) &&
338 !IS_DEPUTY(me) )
339 tell_object( you, "*** " + NAME(me) + " snoopt Dich!\n" );
340
341 return ret;
342 }
Zesstra6c333cc2020-04-28 20:18:45 +0200343 else
344 {
MG Mud User88f12472016-06-24 23:31:02 +0200345 if ( (me == PO ||
346 query_wiz_grp(geteuid(PO)) > query_wiz_grp(me) ||
347 (query_wiz_grp(geteuid(PO)) == query_wiz_grp(me) &&
Zesstra6c333cc2020-04-28 20:18:45 +0200348 efun::interactive_info(PO, II_SNOOP_NEXT) == me)) && snooper0 )
349 {
350 if ( !IS_DEPUTY(me) )
351 {
MG Mud User88f12472016-06-24 23:31:02 +0200352 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
353 Lcut(dtime(time())), me,
354 snooper0,
355 environment(snooper0) ),
356 100000 );
357
358 CHMASTER->send( "Snoop", me,
359 sprintf( "%s *OFF* %s (%O).",
360 capitalize(getuid(me)),
361 capitalize(getuid(snooper0)),
362 environment(snooper0) ) );
363 }
Zesstra6c333cc2020-04-28 20:18:45 +0200364 else
365 {
MG Mud User88f12472016-06-24 23:31:02 +0200366 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
367 Lcut(dtime(time())), me,
368 snooper0,
369 environment(snooper0) ),
370 100000 );
371 }
MG Mud User88f12472016-06-24 23:31:02 +0200372 return efun::snoop(me);
373 }
374 }
375 return 0;
376}
377
378
379
380// * Emulation des 'alten' explode durch das neue
381string *old_explode(string str, string del) {
382 int s, t;
383 string *strs;
384
385 if (!stringp(str)) {
386 set_this_object(previous_object());
387 raise_error(sprintf(
388 "Invalid argument 1 to old_explode()! Expected <string>, got: "
389 "%.30O\n",str));
390 }
391 if (!stringp(del)) {
392 set_this_object(previous_object());
393 raise_error(sprintf(
394 "Invalid argument 2 to old_explode()! Expected <string>, got: "
395 "%.30O\n",del));
396 }
397 if(del == "")
398 return ({str});
399 strs=efun::explode(str, del);
400 t=sizeof(strs)-1;
401 while(s<=t && strs[s++] == "");s--;
402 while(t>=0 && strs[t--] == "");t++;
403 if(s<=t)
404 return strs[s..t];
405 return ({});
406}
407
408int file_time(string path) {
409 mixed *v;
410
411 set_this_object(previous_object());
412 if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
413 return(0); //sonst
414}
415
MG Mud User88f12472016-06-24 23:31:02 +0200416// * Magier-Level abfragen
417int query_wiz_level(mixed player) {
Zesstra4dbb9882019-11-26 21:26:36 +0100418 return "/secure/master"->query_wiz_level(player);
MG Mud User88f12472016-06-24 23:31:02 +0200419}
420
421#ifdef __ALISTS__
422// * Element aus Alist loeschen (by key)
423mixed *remove_alist(mixed key,mixed *alist)
424{
425 int i,j;
426
427 if (!pointerp(alist) || !sizeof(alist))
428 return alist;
429 if (!pointerp(alist[0]))
430 {
431 if ((i=assoc(key,alist))<0)
432 return alist;
433 return alist[0..i-1]+alist[i+1..];
434 }
435 i = assoc(key,alist[0]);
436 if ((i=assoc(key,alist[0]))<0)
437 return alist;
438 alist=alist[0..];
439 for (j=sizeof(alist)-1;j>=0;j--)
440 alist[j]=alist[j][0..i-1]+alist[j][i+1..];
441 return alist;
442}
443
444// * Element aus Alist loeschen (by pos)
445mixed *exclude_alist(int i,mixed *alist)
446{
447 int j;
448 if (!pointerp(alist) || !sizeof(alist) || i<0)
449 return alist;
450 if (!pointerp(alist[0]))
451 return alist[0..i-1]+alist[i+1..];
452 alist=alist[0..]; /* Create PHYSICAL copy of alist */
453 for (j=sizeof(alist)-1;j>=0;j--)
454 alist[j]=alist[j][0..i-1]+alist[j][i+1..];
455 return alist; /* order_alist is NOT necessary - see /doc/LPC/alist */
456}
457#endif // __ALISTS__
458
459// * German version of ctime()
460#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
461#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
462 "Sep","Okt","Nov","Dez"})
463string dtime(int wann) {
464
465 if (wann == dtime_cache[0])
466 return(dtime_cache[1]);
467
468 int *lt = localtime(wann);
469 return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
470 TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]],
471 lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
472}
473
474// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
475// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
476// ein ordentliches Datum/Uhrzeit.
477#if !__EFUN_DEFINED__(strftime)
478varargs string strftime(mixed fmt, int clock, int localized) {
479 if (intp(clock) && clock >= 0)
480 return dtime(clock);
481 else if (intp(fmt) && fmt >= 0)
482 return dtime(fmt);
483
484 return dtime(time());
485}
486#endif //!__EFUN_DEFINED__(strftime)
487
488// * Shutdown mit zusaetzlichem logging
489nomask int shutdown(string reason)
490{
491 string name;
492 string obname;
493 string output;
494
495 if (!reason)
496 return 0;
497 if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
498 object_name(previous_object())!="/obj/shut" )
499 {
500 write("You have no permission to shut down the gamedriver!\n");
501 return 0;
502 }
503 if ((this_interactive())&&(name=getuid(this_interactive())))
504 {
505 name=capitalize(name);
506 filter(users(),#'tell_object,//'
507 capitalize(name)+" faehrt das Spiel herunter!\n");
508 }
509 else
510 name="ANONYMOUS";
511 if (previous_object()) obname=capitalize(getuid(previous_object()));
512 output=name;
513 if (obname && name!=obname) output=output+" ("+obname+")";
514 if (previous_object()&&object_name(previous_object())=="/obj/shut"){
515 output+=" faehrt das Spiel via Armageddon herunter.\n";
516 output=dtime(time())+": "+output;
517 log_file("GAME_LOG",output+"\n",-1);
518 efun::shutdown();
519 return 1;
520 }
521 output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
522 output+=" Grund: "+reason;
523 log_file("GAME_LOG",output+"\n",-1);
524 efun::shutdown();
525 return 1;
526}
527
528// * lowerchar
529
530int lowerchar(int char) {
531 if (char<'A' || char>'Z') return char;
532 return char+32;
533}
534
535// * upperstring
536
537string upperstring(string s)
538{
539#if __EFUN_DEFINED__(upper_case)
540 return(upper_case(s));
541#else
542 int i;
543 if (!stringp(s)) return 0;
544 for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
545 return s;
546#endif
547}
548
549// * lowerstring
550
551string lowerstring(string s)
552{
553 if (!stringp(s)) return 0;
554 return lower_case(s);
555}
556
557
558// * GD version
559string version()
560{
561 return __VERSION__;
562}
563
564// * break_string
565// stretch() -- stretch a line to fill a given width
566private string stretch(string s, int width) {
567 int len=sizeof(s);
568 if (len==width) return s;
569
570 // reine Leerzeilen, direkt zurueckgeben
571 string trimmed=trim(s,TRIM_LEFT," ");
572 if (trimmed=="") return s;
573 int start_spaces = len - sizeof(trimmed);
574
575 string* words = explode(trimmed, " ");
576 // der letzte kriegt keine Spaces
577 int word_count=sizeof(words) - 1;
578 // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
579 if (!word_count)
580 return " "*start_spaces + words[0];
581
582 int space_count = width - len;
583
584 int space_per_word=(word_count+space_count) / word_count;
585 // Anz.Woerter mit Zusatz-Space
586 int rest=(word_count+space_count) % word_count;
587 // Rest-Spaces Verteilen
588 foreach (int pos : rest) words[pos]+=" ";
589 return (" "*start_spaces) + implode( words, " "*space_per_word );
590}
591
592// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
593// eigene Funktion bekommen:
594private varargs string block_string(string s, int width, int flags) {
595 // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
596 // einem Leerzeichen begonnen.
597 // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
598 // ersetzt.
599 if ( (flags & BS_LEAVE_MY_LFS)
600 && !(flags & BS_NO_PARINDENT))
601 {
602 s = " "+regreplace(s,"\n","\n ",1);
603 }
604
605 // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
606 // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
607 // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
608 // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
609 // wieder rueckgaengig zu machen.
610 s = sprintf( "%-*=s", width, s);
611
612 string *tmp=explode(s, "\n");
613 // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
614 // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
615 // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
616 // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
617 // Ende der letzten Zeile hat), das macht der Aufrufer...
618 if (sizeof(tmp) > 1)
619 return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" )
620 + "\n" + tmp[<1];
621
622 return s;
623}
624
625public varargs string break_string(string s, int w, mixed indent, int flags)
626{
627 if ( !s || s == "" ) return "";
628
629 if ( !w ) w=78;
630
631 if( intp(indent) )
632 indent = indent ? " "*indent : "";
633
634 int indentlen=stringp(indent) ? sizeof(indent) : 0;
635
636 if (indentlen>w) {
637 set_this_object(previous_object());
638 raise_error(sprintf("break_string: indent longer %d than width %d\n",
639 indentlen,w));
640 // w=((indentlen/w)+1)*w;
641 }
642
643 if (!(flags & BS_LEAVE_MY_LFS))
644 s=regreplace( s, "\n", " ", 1 );
645
646 if ( flags & BS_SINGLE_SPACE )
647 s = regreplace( s, "(^|\n| ) *", "\\1", 1 );
648
649 string prefix="";
650 if (indentlen && flags & BS_PREPEND_INDENT) {
651 if (indentlen+sizeof(s) > w ||
652 (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
653 prefix=indent+"\n";
654 indent=(flags & BS_NO_PARINDENT) ? "" : " ";
655 indentlen=sizeof(indent);
656 }
657 }
658
659 if ( flags & BS_BLOCK ) {
660 /*
661 s = implode( map( explode( s, "\n" ),
662 #'block_string, w, indentlen, flags),
663 "" );
664 */
665 s = block_string( s , w - indentlen, flags );
666 }
667 else {
668 s = sprintf("%-1.*=s",w-indentlen,s);
669 }
670 if ( s[<1] != '\n' ) s += "\n";
671
672 if ( !indentlen ) return prefix + s;
673
674 string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
675
676 return prefix + indent +
677 regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
678 /*
679 string *buf;
680
681 buf = explode( s, "\n" );
682 return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
683 */
684}
685
686// * Elemente aus mapping loeschen - mapping vorher kopieren
687
688mapping m_copy_delete(mapping m, mixed key) {
689 return m_delete(copy(m), key);
690}
691
692// * times
693int last_reboot_time()
694{
695 return __BOOT_TIME__;
696}
697
698int first_boot_time()
699{
700 return 701517600;
701}
702
703int exist_days()
704{
705 return (((time()-first_boot_time())/8640)+5)/10;
706}
707
708// * uptime :)
709string uptime()
710{
711 int t;
712 int tmp;
713 string s;
714
715 t=time()-__BOOT_TIME__;
716 s="";
717 if (t>=86400)
718 s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
719 if (t>=3600)
720 s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
721 if (t>60)
722 s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
723 return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
724}
725
726// * Was tun bei 'dangling' lfun-closures ?
727void dangling_lfun_closure() {
728 raise_error("dangling lfun closure\n");
729}
730
731// * Sperren ausser fuer master/simul_efun
732
733#if __EFUN_DEFINED__(set_environment)
734nomask void set_environment(object o1, object o2) {
735 raise_error("Available only for root\n");
736}
737#endif
738
739nomask void set_this_player(object pl) {
740 raise_error("Available only for root\n");
741}
742
743#if __EFUN_DEFINED__(export_uid)
744nomask void export_uid(object ob) {
745 raise_error("Available only for root\n");
746}
747#endif
748
749// * Jetzt auch closures
750int process_flag;
751
752public nomask int process_call()
753{
754 if (process_flag>0)
755 return process_flag;
756 else return(0);
757}
758
759private nomask string _process_string(string str,object po) {
760 set_this_object(po);
761 return(efun::process_string(str));
762}
763
764nomask string process_string( mixed str )
765{
766 string tmp, err;
767 int flag;
768
769 if ( closurep(str) ) {
770 set_this_object( previous_object() );
771 return funcall(str);
772 }
773 else if (str==0)
Zesstra4dbb9882019-11-26 21:26:36 +0100774 return(str);
MG Mud User88f12472016-06-24 23:31:02 +0200775 else if ( !stringp(str) ) {
776 return to_string(str);
777 }
778
779 if ( !(flag = process_flag > time() - 60))
780 process_flag=time();
781
782 err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
783
784 if ( !flag )
785 process_flag=0;
786
787 if (err) {
788 // Verarbeitung abbrechen
789 set_this_object(previous_object());
790 raise_error(err);
791 }
792 return tmp;
793}
794
795// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
796// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
797// zurueckgeliefert, sonst 0.
798// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
799public int mkdirp(string dir) {
800 // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
801 if (strstr(dir, "/") != 0)
802 raise_error("mkdirp(): Pfad ist nicht absolute.\n");
803 // cut off trailing /...
804 if (dir[<1]=='/')
805 dir = dir[0..<2];
806
807 int fstat = file_size(dir);
808 // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
809 if (fstat == FSIZE_DIR)
810 return 1;
811 // wenn schon ne Datei existiert, geht es nicht.
812 if (fstat != FSIZE_NOFILE)
813 return 0;
814 // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
815 // toplevel-verzeichnis, was direkt angelegt wird.
816 if (strrstr(dir,"/")==0) {
817 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
818 }
819
820 // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
821 // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
822 // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
823
824 // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
825 // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
826 if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
827 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
Zesstraa25c9c12018-11-17 00:09:25 +0100828 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200829}
830
831
832// * Properties ggfs. mitspeichern
833mixed save_object(mixed name)
834{
835 mapping properties;
836 mapping save;
837 mixed index, res;
838 int i;
Zesstraf84b6272013-01-26 00:14:12 +0100839 string oldpath;
MG Mud User88f12472016-06-24 23:31:02 +0200840
841 // nur Strings und 0 zulassen
842 if ((!stringp(name) || !sizeof(name)) &&
843 (!intp(name) || name!=0)) {
844 set_this_object(previous_object());
845 raise_error(sprintf(
846 "Only non-empty strings and 0 may be used as filename in "
847 "sefun::save_object()! Argument was %O\n",name));
848 }
849
Zesstraf84b6272013-01-26 00:14:12 +0100850 if (stringp(name)) {
851 // abs. Pfad erzeugen. *seufz*
852 if (name[0]!='/')
853 name = "/" + name;
Zesstraf84b6272013-01-26 00:14:12 +0100854 // automatisch in LIBDATADIR speichern
855 if (strstr(name,"/"LIBDATADIR"/") != 0) {
Zesstrad43edf72017-01-31 15:43:35 +0100856 oldpath = name;
Zesstraf84b6272013-01-26 00:14:12 +0100857 name = "/"LIBDATADIR + name;
858 // wenn das Verzeichnis nicht existiert, ggf. anlegen
859 string dir = name[0..strrstr(name,"/")-1];
860 if (file_size(dir) != FSIZE_DIR) {
861 if (mkdirp(dir) != 1)
862 raise_error("save_object(): kann Verzeichnis " + dir
863 + " nicht anlegen!");
864 }
865 }
866 }
867
MG Mud User88f12472016-06-24 23:31:02 +0200868 save = m_allocate(0, 2);
Zesstra4dbb9882019-11-26 21:26:36 +0100869 properties = previous_object()->QueryProperties();
MG Mud User88f12472016-06-24 23:31:02 +0200870
871 if(mappingp(properties))
872 {
873 // delete all entries in mapping properties without SAVE flag!
874 index = m_indices(properties);
875 for(i = sizeof(index)-1; i>=0;i--)
876 {
877 if(properties[index[i], F_MODE] & SAVE)
878 {
879 save[index[i]] = properties[index[i]];
880 save[index[i], F_MODE] =
881 properties[index[i], F_MODE] &
882 (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
883 }
884 }
885 }
886 else save = ([]);
887
888 // save object!
889 previous_object()->_set_save_data(save);
890 // format: wie definiert in config.h
Zesstraf84b6272013-01-26 00:14:12 +0100891 if (stringp(name)) {
MG Mud User88f12472016-06-24 23:31:02 +0200892 res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
893 __LIB__SAVE_FORMAT_VERSION__);
Zesstraf84b6272013-01-26 00:14:12 +0100894 // wenn erfolgreich und noch nen Savefile existiert, was nicht unter
895 // /data/ liegt, wird das geloescht.
896 if (!res && oldpath
897 && file_size(oldpath+".o") >= 0)
898 rm(oldpath+".o");
899 }
MG Mud User88f12472016-06-24 23:31:02 +0200900 else
901 res = funcall(bind_lambda(#'efun::save_object, previous_object()),
902 __LIB__SAVE_FORMAT_VERSION__);
Zesstraf84b6272013-01-26 00:14:12 +0100903
MG Mud User88f12472016-06-24 23:31:02 +0200904 previous_object()->_set_save_data(0);
905
906#ifdef IOSTATS
907 // Stats...
908 struct iostat_s stat = (<iostat_s>);
909 stat->oname = object_name(previous_object());
910 stat->time = time();
911 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
912 // OIM_TOTAL_DATA_SIZE);
913 if (stringp(name))
914 stat->size = file_size(name + ".o");
915 else
916 stat->sizeof(res);
917 //debug_message("saveo: "+saveo_stat[0]+"\n");
918 saveo_stat[1][saveo_stat[0]] = stat;
919 saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
920 //debug_message("saveo 2: "+saveo_stat[0]+"\n");
921#endif
922
923 return res;
924}
925
926// * Auch Properties laden
927int restore_object(string name)
928{
929 int result;
930 mixed index;
931 mixed save;
932 mapping properties;
933 int i;
934 closure cl;
935
Zesstra94381a42017-01-29 22:37:52 +0100936 if (sizeof(name) < 1)
937 {
938 set_this_object(previous_object());
939 raise_error("Bad arg 1 to restore_object(): expected non-empty "
940 "'string'.\n");
941 }
942
Zesstra87166e12017-01-29 20:12:37 +0100943 // Wenn name vermutlich ein Pfad (also nicht mit #x:y anfaengt)
944 if (name[0] != '#')
945 {
946 // abs. Pfad erzeugen *seufz*
Zesstra0fbf95d2017-01-29 21:48:02 +0100947 if (name[0]!='/')
Zesstra87166e12017-01-29 20:12:37 +0100948 name = "/" + name;
Zesstraf84b6272013-01-26 00:14:12 +0100949
Bugfixb9fa6e82017-04-28 14:42:27 +0200950 // .c am Ende loeschen, sonst wird das File ggf. nicht gefunden.
951 if(name[<2..]==".c")
952 name=name[..<3];
953
Zesstra87166e12017-01-29 20:12:37 +0100954 // wenn kein /data/ vorn steht, erstmal gucken, ob das Savefile unter
955 // /data/ existiert. Wenn ja, wird das geladen.
Zesstra0fbf95d2017-01-29 21:48:02 +0100956 if (strstr(name,"/"LIBDATADIR"/") != 0)
Zesstra87166e12017-01-29 20:12:37 +0100957 {
958 string path = "/"LIBDATADIR + name;
959 if (file_size(path + ".o") >= 0)
960 name = path;
961 }
Zesstraf84b6272013-01-26 00:14:12 +0100962 }
963
MG Mud User88f12472016-06-24 23:31:02 +0200964 // get actual property settings (by create())
Zesstra4dbb9882019-11-26 21:26:36 +0100965 properties = previous_object()->QueryProperties();
MG Mud User88f12472016-06-24 23:31:02 +0200966
967// DEBUG(sprintf("RESTORE %O\n",name));
968 // restore object
969 result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
970 //'))
971 //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein
972 //mapping sein sollte.
Zesstra4dbb9882019-11-26 21:26:36 +0100973 save = previous_object()->_get_save_data();
974 if((save))
MG Mud User88f12472016-06-24 23:31:02 +0200975 {
976 index = m_indices(save);
977 for(i = sizeof(index)-1; i>=0; i--)
978 {
979 properties[index[i]] = save[index[i]];
980 properties[index[i], F_MODE] = save[index[i], F_MODE]
981 &~(SETCACHED|QUERYCACHED);
982 }
983 }
984 else properties = ([]);
985
986 // restore properties
987 funcall(
988 bind_lambda(
989 unbound_lambda(({'arg}), //'})
990 ({#'call_other,({#'this_object}),
991 "SetProperties",'arg})),//')
992 previous_object()),properties);
993 previous_object()->_set_save_data(0);
994
995#ifdef IOSTATS
996 // Stats...
997 //debug_message("restoreo: "+restoreo_stat[0]+"\n");
998 struct iostat_s stat = (<iostat_s>);
999 stat->oname = object_name(previous_object());
1000 stat->time = time();
1001 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
1002 // OIM_TOTAL_DATA_SIZE);
1003 stat->size = file_size(name + ".o");
1004 restoreo_stat[1][restoreo_stat[0]] = stat;
1005
1006 restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
1007#endif
1008
1009 return result;
1010}
1011
1012// * HB eines Objektes ein/ausschalten
1013int set_object_heart_beat(object ob, int flag)
1014{
1015 if (objectp(ob))
1016 return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
1017}
1018
1019// * Magierlevelgruppen ermitteln
1020int query_wiz_grp(mixed wiz)
1021{
1022 int lev;
1023
1024 lev=query_wiz_level(wiz);
1025 if (lev<SEER_LVL) return 0;
1026 if (lev>=GOD_LVL) return lev;
1027 if (lev>=ARCH_LVL) return ARCH_GRP;
1028 if (lev>=ELDER_LVL) return ELDER_GRP;
1029 if (lev>=LORD_LVL) return LORD_GRP;
1030 if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
1031 if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
1032 if (lev>=WIZARD_LVL) return WIZARD_GRP;
1033 if (lev>=LEARNER_LVL) return LEARNER_GRP;
1034 return SEER_GRP;
1035}
1036
1037mixed *wizlist_info()
1038{
1039 if (ARCH_SECURITY || !extern_call())
1040 return efun::wizlist_info();
1041 return 0;
1042}
1043
1044// * wizlist ausgeben
1045varargs void wizlist(string name, int sortkey ) {
1046
1047 if (!name)
1048 {
1049 if (this_player())
1050 name = getuid(this_player());
1051 if (!name)
1052 return;
1053 }
1054
1055 // Schluessel darf nur in einem gueltigen Bereich sein
1056 if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
1057
1058 mixed** wl = efun::wizlist_info();
1059 // nach <sortkey> sortieren
1060 wl = sort_array(wl, function int (mixed a, mixed b)
1061 {return a[sortkey] < b[sortkey]; } );
1062
1063 // Summe ueber alle Kommandos ermitteln.
1064 int total_cmd, i;
1065 int pos=-1;
1066 foreach(mixed entry : wl)
1067 {
1068 total_cmd += entry[WL_COMMANDS];
1069 if (entry[WL_NAME] == name)
1070 pos = i;
1071 ++i;
1072 }
1073
1074 if (pos < 0 && name != "ALL" && name != "TOP100")
1075 return;
1076
1077 if (name == "TOP100")
1078 {
1079 if (sizeof(wl) > 100)
1080 wl = wl[0..100];
1081 else
1082 wl = wl;
1083 }
1084 // um name herum schneiden
1085 else if (name != "ALL")
1086 {
1087 if (sizeof(wl) <= 21)
1088 wl = wl;
1089 else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
1090 wl = wl[pos-10..pos+10];
1091 else if (pos <=21)
1092 wl = wl[0..20];
1093 else if (pos >= sizeof(wl) - 21)
1094 wl = wl[<21..];
1095 else
1096 wl = wl;
1097 }
1098
1099 write("\nWizard top score list\n\n");
1100 if (total_cmd == 0)
1101 total_cmd = 1;
1102 printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
1103 "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
1104 foreach(mixed e: wl)
1105 {
1106 printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
1107 e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
1108 e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
1109 e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
1110 );
1111 }
1112 printf("\nTotal %7d (%d)\n\n", total_cmd, sizeof(wl));
1113}
1114
1115
1116// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
1117
1118// ermittelt das Objekt des Callouts.
1119private object _call_out_obj( mixed call_out ) {
1120 return pointerp(call_out) ? call_out[0] : 0;
1121}
1122
1123private void _same_object( object ob, mapping m ) {
1124 // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
1125 if ( m[ob] )
1126 m[ob] += ({ ob });
1127 else
1128 m[ob] = ({ ob });
1129}
1130
1131// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
1132// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
1133// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
1134private void _same_path( object key, object *obs, mapping m ) {
1135 string path;
1136 if (!objectp(key) || !pointerp(obs)) return;
1137
1138 path = load_name(key);
1139
1140 if ( m[path] )
1141 m[path] += obs;
1142 else
1143 m[path] = obs;
1144}
1145
1146// key kann object oder string sein.
1147private int _too_many( mixed key, mapping m, int i ) {
1148 return sizeof(m[key]) >= i;
1149}
1150
1151// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
1152// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
1153// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
1154private void _destroy( mixed key, object *obs, string text, int uid ) {
1155 if (!pointerp(obs)) return;
1156 // Array mit unique Eintraege erzeugen.
1157 obs = m_indices( mkmapping(obs) );
1158 // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
1159 // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
1160 // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
1161 // laeuft.
1162 catch( efun::raise_error(
1163 sprintf( text,
Zesstra4dbb9882019-11-26 21:26:36 +01001164 uid ? master()->creator_file(key) : key,
MG Mud User88f12472016-06-24 23:31:02 +02001165 sizeof(obs), object_name(obs[<1]) ) );publish);
1166 // Und weg mit dem Kram...
1167 filter( obs, #'efun::destruct/*'*/ );
1168}
1169
1170// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
1171// sind dabei nicht unique.
1172private void _same_uid( string key, object *obs, mapping m, closure cf ) {
1173 string uid;
1174
1175 if ( !pointerp(obs) || !sizeof(obs) )
1176 return;
1177
1178 uid = funcall( cf, key );
1179
1180 if ( m[uid] )
1181 m[uid] += obs; // obs ist nen Array
1182 else
1183 m[uid] = obs;
1184}
1185
1186private int _log_call_out(mixed co)
1187{
1188 log_file("TOO_MANY_CALLOUTS",
1189 sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
1190 200000);
1191 return 0;
1192}
1193
1194private int last_callout_log=0;
1195
1196nomask varargs void call_out( varargs mixed *args )
1197{
1198 mixed tmp, *call_outs;
1199
1200 // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
1201 // haben.
1202 if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
1203 && geteuid(previous_object()) != ROOTID )
1204 {
1205 // Log erzeugen...
1206 if (last_callout_log <
1207 efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
1208 && get_eval_cost() > 200000)
1209 {
1210 last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1211 log_file("TOO_MANY_CALLOUTS",
1212 sprintf("\n%s: ############ Too many callouts: %d ##############\n",
1213 strftime("%y%m%d-%H%M%S"),
1214 efun::driver_info(DI_NUM_CALLOUTS)));
1215 filter(efun::call_out_info(), #'_log_call_out);
1216 }
1217 // Objekte aller Callouts ermitteln
1218 call_outs = map( efun::call_out_info(), #'_call_out_obj );
1219 mapping objectmap = ([]);
1220 filter( call_outs, #'_same_object, &objectmap );
1221 // Master nicht grillen...
1222 efun::m_delete( objectmap, master(1) );
1223 // alle Objekte raussuchen, die zuviele haben...
1224 mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
1225 // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
1226 // Objekten uebergeben (in diesem Fall sind Keys und Array mit
1227 // Objekten jeweils das gleiche Objekt).
1228 if ( sizeof(res) )
1229 walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "
1230 "object [%O]. Destructed %d objects. [%s]\n", 0 );
1231
1232 // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
1233 // Objekte killen, die mehr als 50 Callouts laufen haben - und
1234 // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
1235 // Clones einer BP gekillt, die Callouts laufen haben, falls alle
1236 // diese Objekte _zusammen_ mehr als 50 Callouts haben!
1237 if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
1238 // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
1239 // mehr drin, da sie dort als Keys verwendet wurden.
1240 mapping pathmap=([]);
1241 // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
1242 // von Objekten als Werte.
1243 walk_mapping( objectmap, #'_same_path, &pathmap);
1244 // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
1245 res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
1246 // und ueber alle Keys gehen, an _destroy() werden die Clones
1247 // uebergeben, die Callouts haben.
1248 if ( sizeof(res) )
1249 walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
1250 "'%s'. Destructed %d objects. [%s]\n", 0 );
1251
1252 // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
1253 // noch >1000 Callouts laufen, werden diesmal alle Callouts
1254 // einer UID zusammengezaehlt.
1255 // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
1256 // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
1257 if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
1258 {
1259 // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
1260 // zusammensortieren. Zerstoerte Clones filter _same_uid()
1261 // raus.
1262 mapping uidmap=([]);
1263 walk_mapping( pathmap, #'_same_uid, &uidmap,
1264 symbol_function( "creator_file",
1265 "/secure/master" ) );
1266 // In res nun UIDs als Keys und Arrays von Objekten als Werte.
1267 // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
1268 // 100 Callouts!) haben.
1269 res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
1270 // und erneut ueber die Keys laufen und jeweils die Arrays mit
1271 // den Objekten zur Zerstoerung an _destroy()...
1272 if ( sizeof(res) )
1273 walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
1274 "UID '%s'. Destructed %d objects. [%s]\n",
1275 1 );
1276 }
1277 }
1278 }
1279
1280 // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
1281 if ( !previous_object() )
1282 return;
1283
1284 set_this_object( previous_object() );
1285 apply( #'efun::call_out, args );
1286 return;
1287}
1288
1289mixed call_out_info() {
1290
1291 object po = previous_object();
1292 mixed coi = efun::call_out_info();
1293
1294 // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
1295 // /obj haben die BackboneID.
1296 if (query_wiz_level(getuid(po)) >= ARCH_LVL
Zesstra4dbb9882019-11-26 21:26:36 +01001297 || master()->creator_file(load_name(po)) == BACKBONEID ) {
MG Mud User88f12472016-06-24 23:31:02 +02001298 return coi;
1299 }
1300 else {
1301 return filter(coi, function mixed (mixed arr) {
1302 if (pointerp(arr) && arr[0]==po)
1303 return 1;
1304 else return 0; });
1305 }
1306}
1307
1308// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
1309// NICHT das Objekt, was ggf. die lfun definiert!
1310mixed query_closure_object(closure c) {
1311 return
1312 CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
1313 0 :
1314 (to_object(c) || -1);
1315}
1316
1317// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
1318varargs void move_object(mixed what, mixed where)
1319{
1320 object po,tmp;
1321
1322 po=previous_object();
1323 if (!where)
1324 {
1325 where=what;
1326 what=po;
1327 }
1328 if (((stringp(where) && where==NETDEAD_ROOM ) ||
1329 (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
1330 objectp(what) && object_name(what)!="/obj/sperrer")
1331 {
1332 if (!query_once_interactive(what))
1333 {
1334 what->remove();
1335 if (what) destruct(what);
1336 return;
1337 }
1338 if (living(what) || interactive(what))
1339 {
1340 log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
1341 return;
1342 }
1343 set_object_heart_beat(what,0);
1344 }
1345 tmp=what;
1346 while (tmp=environment(tmp))
1347 // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
1348 // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
1349 // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
1350 // da um jedes bisschen Rechenzeit geht.
1351 // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
1352 //
1353 // Tiamak
1354 tmp->_set_last_content_change();
1355 funcall(bind_lambda(#'efun::move_object,po),what,where);
1356 if (tmp=what)
1357 while (tmp=environment(tmp))
1358 tmp->_set_last_content_change();
1359}
1360
1361
1362void start_simul_efun() {
1363 mixed *info;
1364
1365 // Falls noch nicht getan, extra_wizinfo initialisieren
1366 if ( !pointerp(info = get_extra_wizinfo(0)) )
1367 set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
1368
1369 InitLivingData(info);
1370
1371 set_next_reset(10); // direkt mal aufraeumen
1372}
1373
1374protected void reset() {
1375 set_next_reset(7200);
1376 CleanLivingData();
1377}
1378
1379#if !__EFUN_DEFINED__(absolute_hb_count)
1380int absolute_hb_count() {
1381 return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1382}
1383#endif
1384
1385void __set_environment(object ob, mixed target)
1386{
1387 string path;
1388 object obj;
1389
1390 if (!objectp(ob))
1391 return;
1392 if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
1393 return;
1394 if (objectp(target))
1395 {
1396 efun::set_environment(ob,target);
1397 return;
1398 }
Zesstra4dbb9882019-11-26 21:26:36 +01001399 path=master()->make_path_absolute(target);
MG Mud User88f12472016-06-24 23:31:02 +02001400 if (stringp(path) && file_size(path+".c")>=0 &&
1401 !catch(load_object(path);publish) )
1402 {
1403 obj=find_object(path);
1404 efun::set_environment(ob,obj);
1405 return;
1406 }
1407}
1408
1409void _dump_wizlist(string file, int sortby) {
1410 int i;
1411 mixed *a;
1412
1413 if (!LORD_SECURITY)
1414 return;
1415 if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
1416 {
1417 write("NO WRITE PERMISSION\n");
1418 return;
1419 }
1420 a = wizlist_info();
1421 a = sort_array(a, lambda( ({'a,'b}),
1422 ({#'<,
1423 ({#'[,'a,sortby}),
1424 ({#'[,'b,sortby})
1425 })));
1426 rm(file);
1427 for (i=sizeof(a)-1;i>=0;i--)
1428 write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
1429 a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
1430 a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
1431}
1432
1433public varargs object deep_present(mixed what, object ob) {
1434
1435 if(!objectp(ob))
1436 ob=previous_object();
1437 // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
1438 // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
1439 if(objectp(what)) {
1440 object *envs=all_environment(what);
1441 // wenn ob kein Environment hat, ist es offensichtlich nicht in what
1442 // enthalten.
1443 if (!pointerp(envs)) return 0;
1444 if (member(envs, ob) != -1) return what;
1445 }
1446 // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
1447 // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
1448 // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
1449 else if (stringp(what)) {
1450 int cnt;
1451 string newwhat;
1452 if(sscanf(what,"%s %d",newwhat,cnt)!=2)
1453 cnt=1;
1454 else
1455 what=newwhat;
1456 foreach(object invob: deep_inventory(ob)) {
1457 if (invob->id(what) && !--cnt)
1458 return invob;
1459 }
1460 }
1461 else {
1462 set_this_object(previous_object());
1463 raise_error(sprintf("Wrong argument 1 to deep_present(). "
1464 "Expected \"object\" or \"string\", got %.50O.\n",
1465 what));
1466 }
1467 return 0;
1468}
1469
1470mapping dump_ip_mapping()
1471{
1472 return 0;
1473}
1474
1475nomask void swap(object obj)
1476{
1477 write("Your are not allowed to swap objects by hand!\n");
1478 return;
1479}
1480
1481nomask varargs void garbage_collection(string str)
1482{
1483 if(previous_object()==0 || !IS_ARCH(geteuid(previous_object()))
1484 || !ARCH_SECURITY)
1485 {
1486 write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
1487 return;
1488 }
1489 else if (stringp(str))
1490 {
1491 return efun::garbage_collection(str);
1492 }
1493 else
1494 return efun::garbage_collection();
1495}
1496
1497varargs void notify_fail(mixed nf, int prio) {
1498 object po,oldo;
1499 int oldprio;
1500
1501 if (!PL || !objectp(po=previous_object())) return;
1502 if (!stringp(nf) && !closurep(nf)) {
1503 set_this_object(po);
1504 raise_error(sprintf(
1505 "Only strings and closures allowed for notify_fail! "
1506 "Argument was: %.50O...\n",nf));
1507 }
1508
1509 // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
1510 // und vergleichen.
1511 if (objectp(oldo=query_notify_fail(1)) && po!=oldo) {
1512 if (!prio) {
1513 //Prioritaet dieses notify_fail() 'abschaetzen'
1514 if (po==PL) // Spieler-interne (soul-cmds)
1515 prio=NF_NL_OWN;
1516 else if (living(po))
1517 prio=NF_NL_LIVING;
Zesstra4dbb9882019-11-26 21:26:36 +01001518 else if (po->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001519 prio=NF_NL_ROOM;
1520 else
1521 prio=NF_NL_THING;
1522 }
1523 //Prioritaet des alten Setzers abschaetzen
1524 if (oldo==PL)
1525 oldprio=NF_NL_OWN;
1526 else if (living(oldo))
1527 oldprio=NF_NL_LIVING;
Zesstra4dbb9882019-11-26 21:26:36 +01001528 else if (oldo->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001529 oldprio=NF_NL_ROOM;
1530 else
1531 oldprio=NF_NL_THING;
1532 }
1533 else // wenn es noch kein Notify_fail gibt:
1534 oldprio=NF_NL_NONE;
1535
1536 //vergleichen und ggf. setzen
1537 if (prio >= oldprio) {
1538 set_this_object(po);
1539 efun::notify_fail(nf);
1540 }
1541
1542 return;
1543}
1544
1545void _notify_fail(string str)
1546{
1547 //query_notify_fail() benutzen, um das Objekt
1548 //des letzten notify_fail() zu ermitteln
1549 object o;
1550 if ((o=query_notify_fail(1)) && o!=previous_object())
1551 return;
1552 //noch kein notify_fail() fuer dieses Kommando gesetzt, auf gehts.
1553 set_this_object(previous_object());
1554 efun::notify_fail(str);
1555 return;
1556}
1557
1558string time2string( string format, int zeit )
1559{
1560 int i,ch,maxunit,dummy;
1561 string *parts, fmt;
1562
1563 int secs = zeit;
1564 int mins = (zeit/60);
1565 int hours = (zeit/3600);
1566 int days = (zeit/86400);
1567 int weeks = (zeit/604800);
1568 int months = (zeit/2419200);
1569 int abbr = 0;
1570
1571 parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
1572
1573 for( i=1; i<sizeof(parts); i+=2 )
1574 {
1575 ch = parts[i][<1];
1576 switch( parts[i][<1] )
1577 {
1578 case 'x': case 'X':
1579 abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
1580 // NO break !
1581 case 'n': case 'N':
1582 maxunit |= 31;
1583 break;
1584 case 'w': case 'W':
1585 maxunit |= 15;
1586 break;
1587 case 'd': case 'D':
1588 maxunit |= 7;
1589 break;
1590 case 'h': case 'H':
1591 maxunit |= 3;
1592 break;
1593 case 'm': case 'M':
1594 maxunit |= 1;
1595 break;
1596 }
1597 }
1598 if( maxunit & 16 ) weeks %= 4;
1599 if( maxunit & 8 ) days %= 7;
1600 if( maxunit & 4 ) hours %= 24;
1601 if( maxunit & 2 ) mins %= 60;
1602 if( maxunit ) secs %= 60;
1603
1604 for( i=1; i<sizeof(parts); i+=2 )
1605 {
1606 fmt = parts[i][0..<2];
1607 ch = parts[i][<1];
1608 if( ch=='x' )
1609 {
1610 if (months > 0) ch='n';
1611 else if( weeks>0 ) ch='w';
1612 else if( days>0 ) ch='d';
1613 else if( hours>0 ) ch='h';
1614 else if(mins > 0) ch = 'm';
1615 else ch = 's';
1616 }
1617 else if( ch=='X' )
1618 {
1619 if (months > 0) ch='N';
1620 else if( weeks>0 ) ch='W';
1621 else if( days>0 ) ch='D';
1622 else if( hours>0 ) ch='H';
1623 else if(mins > 0) ch = 'M';
1624 else ch = 'S';
1625 }
1626
1627 switch( ch )
1628 {
1629 case 'n': parts[i] = sprintf( fmt+"d", months ); break;
1630 case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
1631 case 'd': parts[i] = sprintf( fmt+"d", days ); break;
1632 case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
1633 case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
1634 case 's': parts[i] = sprintf( fmt+"d", secs ); break;
1635 case 'N':
1636 if(abbr) parts[i] = "M";
1637 else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
1638 break;
1639 case 'W':
1640 if(abbr) parts[i] = "w"; else
1641 parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
1642 break;
1643 case 'D':
1644 if(abbr) parts[i] = "d"; else
1645 parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
1646 break;
1647 case 'H':
1648 if(abbr) parts[i] = "h"; else
1649 parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
1650 break;
1651 case 'M':
1652 if(abbr) parts[i] = "m"; else
1653 parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
1654 break;
1655 case 'S':
1656 if(abbr) parts[i] = "s"; else
1657 parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
1658 break;
1659 case '%':
1660 parts[i] = "%";
1661 break;
1662 }
1663 }
1664 return implode( parts, "" );
1665}
1666
1667nomask mixed __create_player_dummy(string name)
1668{
1669 string err;
1670 object ob;
1671 mixed m;
1672 //hat nen Scherzkeks die Blueprint bewegt?
1673 if ((ob=find_object("/secure/login")) && environment(ob))
1674 catch(destruct(ob);publish);
1675 err = catch(ob = clone_object("secure/login");publish);
1676 if (err)
1677 {
1678 write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
1679 return 0;
1680 }
Zesstra4dbb9882019-11-26 21:26:36 +01001681 if (objectp(m=ob->new_logon(name))) netdead[name]=m;
MG Mud User88f12472016-06-24 23:31:02 +02001682 return m;
1683}
1684
1685nomask int secure_level()
1686{
1687 int *level;
1688 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
1689 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
1690 //von 0.
1691 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
1692 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
1693 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
1694 //INteractive geben muss.
1695 level=map(caller_stack(1),function int (object caller)
1696 {if (objectp(caller))
1697 return(query_wiz_level(geteuid(caller)));
1698 return(0); // kein Objekt da, 0.
1699 } );
1700 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
1701}
1702
1703nomask string secure_euid()
1704{
1705 string euid;
1706
1707 if (!this_interactive()) // Es muss einen interactive geben
1708 return 0;
1709 euid=geteuid(this_interactive());
1710 // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
1711 // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
1712 // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
1713 foreach(object caller: caller_stack()) {
1714 if (!objectp(caller) ||
1715 (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
1716 return 0;
1717 }
1718 return euid; // 'sichere' euid zurueckgeben
1719}
1720
1721// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
1722// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
1723//#if __BOOT_TIME__ < 1360017213
1724varargs void input_to( mixed fun, int flags, varargs mixed *args )
1725{
1726 mixed *arr;
1727 int i;
1728
1729 if ( !this_player() || !previous_object() )
1730 return;
1731
1732 // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
1733 // vorkommt...
1734 if ( flags&INPUT_PROMPT ) {
1735 arr = ({ fun, flags }) + args;
1736 }
1737 else {
1738 // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
1739 flags |= INPUT_PROMPT;
1740 arr = ({ fun, flags, "" }) + args;
1741 }
1742
1743 // Arrays gegen flatten quoten.
1744 for ( i = sizeof(arr) - 1; i > 1; i-- )
1745 if ( pointerp(arr[i]) )
1746 arr[i] = quote(arr[i]);
1747
1748 apply( bind_lambda( unbound_lambda( ({}),
1749 ({ #'efun::input_to/*'*/ }) + arr ),
1750 previous_object() ) );
1751}
1752//#endif
1753
1754nomask int set_light(int i)
1755// erhoeht das Lichtlevel eines Objekts um i
1756// result: das Lichtlevel innerhalb des Objekts
1757{
1758 object ob, *inv;
1759 int lall, light, dark, tmp;
1760
1761 if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
1762
1763 // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
1764 if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
1765
1766 // Lichtberechnung findet eigentlich in der Mudlib statt.
Zesstra4dbb9882019-11-26 21:26:36 +01001767 return ob->QueryProp(P_INT_LIGHT);
MG Mud User88f12472016-06-24 23:31:02 +02001768}
1769
MG Mud User88f12472016-06-24 23:31:02 +02001770public varargs string CountUp( string *s, string sep, string lastsep )
1771{
1772 string ret;
1773
1774 if ( !pointerp(s) )
1775 return "";
1776
1777 if (!sep) sep = ", ";
1778 if (!lastsep) lastsep = " und ";
1779
1780 switch (sizeof(s)) {
1781 case 0: ret=""; break;
1782 case 1: ret=s[0]; break;
1783 default:
1784 ret = implode(s[0..<2], sep);
1785 ret += lastsep + s[<1];
1786 }
1787 return ret;
1788}
1789
1790nomask varargs int query_next_reset(object ob) {
1791
1792 // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
1793 if (ob && !objectp(ob))
1794 raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
1795 "expected object.\n",ob));
1796
1797 // Defaultobjekt PO, wenn 0 uebergeben.
1798 if ( !objectp(ob) )
1799 ob = previous_object();
1800
1801 return efun::object_info(ob, OI_NEXT_RESET_TIME);
1802}
1803
1804
MG Mud User88f12472016-06-24 23:31:02 +02001805
1806// ### Ersatzaufloesung in Strings ###
Arathorn066820b2019-11-27 19:47:19 +01001807varargs string replace_personal(string str, <string|object>* obs, int caps) {
1808 string* parts = regexplode(str, "@WE[A-SU]*[0-9]");
1809 int i = sizeof(parts);
MG Mud User88f12472016-06-24 23:31:02 +02001810
Zesstra19102132016-09-01 22:50:36 +02001811 if (i>1)
1812 {
MG Mud User88f12472016-06-24 23:31:02 +02001813 int j, t;
MG Mud User88f12472016-06-24 23:31:02 +02001814 t = j = sizeof(obs);
1815
Arathorn066820b2019-11-27 19:47:19 +01001816 <string|closure>* name_cls = allocate(j);
1817 while (j--) {
MG Mud User88f12472016-06-24 23:31:02 +02001818 if (objectp(obs[j]))
1819 name_cls[j] = symbol_function("name", obs[j]);
1820 else if (stringp(obs[j]))
1821 name_cls[j] = obs[j];
Arathorn066820b2019-11-27 19:47:19 +01001822 }
MG Mud User88f12472016-06-24 23:31:02 +02001823
Zesstra19102132016-09-01 22:50:36 +02001824 while ((i-= 2)>0)
1825 {
MG Mud User88f12472016-06-24 23:31:02 +02001826 // zu ersetzendes Token in Fall und Objektindex aufspalten
Arathorn066820b2019-11-27 19:47:19 +01001827 int ob_nr = parts[i][<1]-'1';
MG Mud User88f12472016-06-24 23:31:02 +02001828 if (ob_nr<0 || ob_nr>=t) {
1829 set_this_object(previous_object());
1830 raise_error(sprintf("replace_personal: using wrong object index %d\n",
1831 ob_nr));
1832 return implode(parts, "");
1833 }
1834
1835 // casus kann man schon hier entscheiden
1836 int casus;
1837 string part = parts[i];
1838 switch (part[3]) {
1839 case 'R': casus = WER; break;
1840 case 'S': casus = WESSEN; break;
1841 case 'M': casus = WEM; break;
1842 case 'N': casus = WEN; break;
1843 default: continue; // passt schon jetzt nicht in das Hauptmuster
1844 }
1845
1846 // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
1847 mixed tmp;
1848 switch (part[3..<2]) {
1849 case "R": case "SSEN": case "M": case "N": // Name
1850 parts[i] = funcall(name_cls[ob_nr], casus, 1); break;
1851 case "RU": case "SSENU": case "MU": case "NU": // unbestimmt
1852 parts[i] = funcall(name_cls[ob_nr], casus); break;
1853 case "RQP": case "SSENQP": case "MQP": case "NQP": // Pronoun
1854 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001855 parts[i] = tmp->QueryPronoun(casus);
MG Mud User88f12472016-06-24 23:31:02 +02001856 break;
1857 case "RQA": case "SSENQA": case "MQA": case "NQA": // Article
1858 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001859 tmp = tmp->QueryArticle(casus, 1, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001860 if (stringp(tmp) && !(tmp[<1]^' '))
1861 tmp = tmp[0..<2]; // Extra-Space wieder loeschen
1862 break;
1863 case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
1864 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001865 parts[i] = tmp->QueryPossPronoun(MALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001866 break;
1867 case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
1868 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001869 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001870 break;
1871 case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
1872 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001873 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001874 break;
1875 case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
1876 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001877 parts[i] = tmp->QueryPossPronoun(MALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001878 break;
1879 case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
1880 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001881 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001882 break;
1883 case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
1884 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001885 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001886 break;
1887 default:
1888 continue;
1889 }
Zesstra19102132016-09-01 22:50:36 +02001890
MG Mud User88f12472016-06-24 23:31:02 +02001891 // wenn tmp ein String war, weisen wir es hier pauschal zu
1892 if (stringp(tmp))
1893 parts[i] = tmp;
1894
1895 // auf Wunsch wird nach Satzenden gross geschrieben
1896 if (caps)
Zesstra19102132016-09-01 22:50:36 +02001897 {
1898 // Wenn das vorhergehende parts[i] == "" ist, sind wir am Anfang vom
1899 // String und dies wird wie ein Satzende vorher behandelt.
1900 if (parts[i-1] == "")
MG Mud User88f12472016-06-24 23:31:02 +02001901 parts[i] = capitalize(parts[i]);
Zesstra19102132016-09-01 22:50:36 +02001902 else
1903 {
1904 switch (parts[i-1][<2..])
1905 {
1906 case ". ": case "! ": case "? ":
1907 case ".": case "!": case "?":
1908 case ".\n": case "!\n": case "?\n":
1909 case "\" ": case "\"\n":
1910 parts[i] = capitalize(parts[i]);
1911 break;
1912 }
MG Mud User88f12472016-06-24 23:31:02 +02001913 }
Zesstra19102132016-09-01 22:50:36 +02001914 }
MG Mud User88f12472016-06-24 23:31:02 +02001915 }
1916 return implode(parts, "");
1917 }
1918 return str;
1919}
1920
MG Mud User88f12472016-06-24 23:31:02 +02001921//replacements for dropped efuns in LD
1922#if !__EFUN_DEFINED__(extract)
1923deprecated varargs string extract(string str, int from, int to) {
1924
1925 if(!stringp(str)) {
1926 set_this_object(previous_object());
1927 raise_error(sprintf("Bad argument 1 to extract(): %O",str));
1928 }
1929 if (intp(from) && intp(to)) {
1930 if (from>=0 && to>=0)
1931 return(str[from .. to]);
1932 else if (from>=0 && to<0)
1933 return(str[from .. <abs(to)]);
1934 else if (from<0 && to>=0)
1935 return(str[<abs(from) .. to]);
1936 else
1937 return(str[<abs(from) .. <abs(to)]);
1938 }
1939 else if (intp(from)) {
1940 if (from>=0)
1941 return(str[from .. ]);
1942 else
1943 return(str[<abs(from) .. ]);
1944 }
1945 else {
1946 return(str);
1947 }
1948}
1949#endif // !__EFUN_DEFINED__(extract)
1950
1951#if !__EFUN_DEFINED__(slice_array)
1952deprecated varargs mixed slice_array(mixed array, int from, int to) {
1953
1954 if(!pointerp(array)) {
1955 set_this_object(previous_object());
1956 raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
1957 }
1958 if (intp(from) && intp(to)) {
1959 if (from>=0 && to>=0)
1960 return(array[from .. to]);
1961 else if (from>=0 && to<0)
1962 return(array[from .. <abs(to)]);
1963 else if (from<0 && to>=0)
1964 return(array[<abs(from) .. to]);
1965 else
1966 return(array[<abs(from) .. <abs(to)]);
1967 }
1968 else if (intp(from)) {
1969 if (from>=0)
1970 return(array[from .. ]);
1971 else
1972 return(array[<abs(from) .. ]);
1973 }
1974 else {
1975 return(array);
1976 }
1977}
1978#endif // !__EFUN_DEFINED__(slice_array)
1979
1980#if !__EFUN_DEFINED__(member_array)
1981deprecated int member_array(mixed item, mixed arraystring) {
1982
1983 if (pointerp(arraystring)) {
1984 return(efun::member(arraystring,item));
1985 }
1986 else if (stringp(arraystring)) {
1987 return(efun::member(arraystring,to_int(item)));
1988 }
1989 else {
1990 set_this_object(previous_object());
1991 raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
1992 }
1993}
1994#endif // !__EFUN_DEFINED__(member_array)
1995
1996// The digit at the i'th position is the number of bits set in 'i'.
1997string count_table =
1998 "0112122312232334122323342334344512232334233434452334344534454556";
1999int broken_count_bits( string s ) {
2000 int i, res;
2001 if( !stringp(s) || !(i=sizeof(s)) ) return 0;
2002 for( ; i-->0; ) {
2003 // We are counting 6 bits at a time using a precompiled table.
2004 res += count_table[(s[i]-' ')&63]-'0';
2005 }
2006 return res;
2007}
2008
2009#if !__EFUN_DEFINED__(count_bits)
2010int count_bits( string s ) {
2011 return(broken_count_bits(s));
2012}
2013#endif
2014
2015
2016// * Teile aus einem Array entfernen *** OBSOLETE
2017deprecated mixed *exclude_array(mixed *arr,int from,int to)
2018{
2019 if (to<from)
2020 to = from;
2021 return arr[0..from-1]+arr[to+1..];
2022}
2023