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