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