blob: acb73db66d107bcbc12485aa93aeb2e59de6c07c [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);
Zesstra39d284a2020-08-30 23:55:47 +020047int query_wiz_level(object|string player);
Zesstra8d245512020-04-28 20:35:34 +020048nomask varargs int snoop(object snooper, object snoopee);
MG Mud User88f12472016-06-24 23:31:02 +020049varargs string country(mixed ip, string num);
Zesstra39d284a2020-08-30 23:55:47 +020050int query_wiz_grp(object|string wiz);
MG Mud User88f12472016-06-24 23:31:02 +020051public 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
MG Mud User88f12472016-06-24 23:31:02 +0200194private string Lcut(string str) {
195 return str[5..11]+str[18..];
196}
197
Zesstra8d245512020-04-28 20:35:34 +0200198nomask varargs int snoop( object snooper, object snoopee )
MG Mud User88f12472016-06-24 23:31:02 +0200199{
200 int ret;
MG Mud User88f12472016-06-24 23:31:02 +0200201
Zesstra8d245512020-04-28 20:35:34 +0200202 if( !objectp(snooper) || snooper == snoopee || !PO )
MG Mud User88f12472016-06-24 23:31:02 +0200203 return 0;
204
Zesstra2e499d62020-04-28 22:13:53 +0200205 // Evtl. gibt es bereits einen snoopee, der von snopper gesnoopt wird?
Zesstra69174b42020-04-28 22:48:45 +0200206 object existing_snoopee = efun::interactive_info(snooper, II_SNOOP_PREV);
MG Mud User88f12472016-06-24 23:31:02 +0200207
Zesstra69174b42020-04-28 22:48:45 +0200208 // soll jemand neues gesnoopt werden?
Zesstra8d245512020-04-28 20:35:34 +0200209 if(snoopee)
210 {
Zesstraee2182f2020-04-28 22:54:51 +0200211#if __BOOT_TIME__ < 1588106971
212 // Diese Uptime duerfen nur EM+ und Sheriff snoopen.
213 if (!ARCH_SECURITY && !IS_DEPUTY(snooper))
214 return 0;
215#endif
Zesstra69174b42020-04-28 22:48:45 +0200216 // Jemand mit niedrigerem Level kann keinen hoeherleveligen snoopen
217 // lassen.
Zesstra8d245512020-04-28 20:35:34 +0200218 if ( PO != snooper
219 && query_wiz_grp(snooper) >= query_wiz_grp(geteuid(PO)) )
MG Mud User88f12472016-06-24 23:31:02 +0200220 return 0;
221
Zesstra69174b42020-04-28 22:48:45 +0200222 // Niedriglevelige User koennen nur mit Einverstaendnis hoeherlevelige
223 // snoopen.
224 if ( query_wiz_grp(snooper) <= query_wiz_grp(snoopee)
225 && !(snoopee->QueryAllowSnoop(snooper)) )
Zesstra6c333cc2020-04-28 20:18:45 +0200226 {
Zesstra69174b42020-04-28 22:48:45 +0200227 // es sei denn der snooper ist Sheriff und der snoopee ist kein
228 // EM+
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 }
Zesstra69174b42020-04-28 22:48:45 +0200232 // Wird der snoopee bereits gesnoopt? Dann darf sich der neue snooper
233 // nur unter Umstaenden in die Snoop-Kette einreihen...
234 object existing_snooper;
235 if ( (existing_snooper = efun::interactive_info(snoopee, II_SNOOP_NEXT))
236 && query_wiz_grp(existing_snooper) >= query_wiz_grp(snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200237 {
Zesstra69174b42020-04-28 22:48:45 +0200238 // ... naemlich nur dann, wenn der bestehende Snooper kein
239 // SF_LOCKED gesetzt hat.
240 if ( existing_snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
MG Mud User88f12472016-06-24 23:31:02 +0200241 return 0;
242
Zesstra69174b42020-04-28 22:48:45 +0200243 tell_object( existing_snooper, sprintf( "%s snooped jetzt %s.\n",
Zesstra8d245512020-04-28 20:35:34 +0200244 snooper->name(WER), snoopee->name(WER) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200245
Zesstra69174b42020-04-28 22:48:45 +0200246 // Evtl. wird der neue snooper selber gesnoopt. Dafuer wird jetzt
247 // ggf. die Kette von *ihren* snoopern verfolgt.
248 object snooper_of_new_snooper = snooper;
249 object snooper_rover;
250 while ( snooper_rover = interactive_info(snooper_of_new_snooper, II_SNOOP_NEXT) )
Zesstra6c333cc2020-04-28 20:18:45 +0200251 {
Zesstra69174b42020-04-28 22:48:45 +0200252 tell_object( existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200253 sprintf( "%s wird seinerseits von %s gesnooped.\n"
Zesstra69174b42020-04-28 22:48:45 +0200254 ,snooper_of_new_snooper->name(WER),
255 snooper_rover->name(WEM) ) );
256 snooper_of_new_snooper = snooper_rover;
MG Mud User88f12472016-06-24 23:31:02 +0200257 }
258
Zesstra69174b42020-04-28 22:48:45 +0200259 // Der letzt snooper des hier anzumeldenden snoopers wird nun vom
260 // bestehenden snooper gesnoopt, falls moeglich.
261 efun::snoop( existing_snooper, snooper_of_new_snooper );
MG Mud User88f12472016-06-24 23:31:02 +0200262
Zesstra69174b42020-04-28 22:48:45 +0200263 if ( efun::interactive_info(snooper_of_new_snooper, II_SNOOP_NEXT)
264 != existing_snooper )
265 tell_object( existing_snooper, sprintf( "Du kannst %s nicht snoopen.\n",
266 snooper_of_new_snooper->name(WEN) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200267 else
268 {
Zesstra69174b42020-04-28 22:48:45 +0200269 tell_object( existing_snooper, sprintf( "Du snoopst jetzt %s.\n",
270 snooper_of_new_snooper->name(WEN) ) );
271 if ( !IS_DEPUTY(existing_snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200272 {
MG Mud User88f12472016-06-24 23:31:02 +0200273 log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
274 dtime(time()),
Zesstra69174b42020-04-28 22:48:45 +0200275 existing_snooper,
276 snooper_of_new_snooper,
277 environment(snooper_of_new_snooper) ),
MG Mud User88f12472016-06-24 23:31:02 +0200278 100000 );
Zesstra69174b42020-04-28 22:48:45 +0200279 if (existing_snoopee)
280 CHMASTER->send( "Snoop", existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200281 sprintf( "%s *OFF* %s (%O)",
Zesstra69174b42020-04-28 22:48:45 +0200282 capitalize(getuid(existing_snooper)),
283 capitalize(getuid(existing_snoopee)),
284 environment(existing_snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200285
Zesstra69174b42020-04-28 22:48:45 +0200286 CHMASTER->send( "Snoop", existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200287 sprintf("%s -> %s (%O)",
Zesstra69174b42020-04-28 22:48:45 +0200288 capitalize(getuid(existing_snooper)),
289 capitalize(getuid(snooper_of_new_snooper)),
290 environment(snooper_of_new_snooper)));
MG Mud User88f12472016-06-24 23:31:02 +0200291 }
Zesstra6c333cc2020-04-28 20:18:45 +0200292 else
293 {
MG Mud User88f12472016-06-24 23:31:02 +0200294 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
295 dtime(time()),
Zesstra69174b42020-04-28 22:48:45 +0200296 existing_snooper,
297 snooper_of_new_snooper,
298 environment(snooper_of_new_snooper) )
MG Mud User88f12472016-06-24 23:31:02 +0200299 ,100000 );
300 }
301 }
302 }
303 else
Zesstra6c333cc2020-04-28 20:18:45 +0200304 {
Zesstra69174b42020-04-28 22:48:45 +0200305 if (existing_snooper)
Zesstra6c333cc2020-04-28 20:18:45 +0200306 {
Zesstra8d245512020-04-28 20:35:34 +0200307 if ( !snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
Zesstra6c333cc2020-04-28 20:18:45 +0200308 {
MG Mud User88f12472016-06-24 23:31:02 +0200309 printf( "%s wird bereits von %s gesnooped. Benutze das "
310 "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
Zesstra69174b42020-04-28 22:48:45 +0200311 snoopee->name(WER), existing_snooper->name(WEM) );
MG Mud User88f12472016-06-24 23:31:02 +0200312 return 0;
313 }
Zesstra6c333cc2020-04-28 20:18:45 +0200314 }
315 }
Zesstra8d245512020-04-28 20:35:34 +0200316 ret = efun::snoop( snooper, snoopee );
MG Mud User88f12472016-06-24 23:31:02 +0200317
Zesstra8d245512020-04-28 20:35:34 +0200318 if ( !IS_DEPUTY(snooper)
319 && efun::interactive_info(snoopee, II_SNOOP_NEXT) == snooper)
Zesstra6c333cc2020-04-28 20:18:45 +0200320 {
MG Mud User88f12472016-06-24 23:31:02 +0200321 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
322 Lcut(dtime(time())),
Zesstra8d245512020-04-28 20:35:34 +0200323 snooper, snoopee, environment(snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200324 100000 );
325
Zesstra69174b42020-04-28 22:48:45 +0200326 if (existing_snoopee)
Zesstra6c333cc2020-04-28 20:18:45 +0200327 {
Zesstra8d245512020-04-28 20:35:34 +0200328 CHMASTER->send( "Snoop", snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200329 sprintf( "%s *OFF* %s (%O).",
Zesstra8d245512020-04-28 20:35:34 +0200330 capitalize(getuid(snooper)),
Zesstra69174b42020-04-28 22:48:45 +0200331 capitalize(getuid(existing_snoopee)),
332 environment(existing_snoopee) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200333 }
MG Mud User88f12472016-06-24 23:31:02 +0200334
Zesstra8d245512020-04-28 20:35:34 +0200335 CHMASTER->send( "Snoop", snooper, sprintf( "%s -> %s (%O).",
336 capitalize(getuid(snooper)),
337 capitalize(getuid(snoopee)),
338 environment(snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200339 }
Zesstra6c333cc2020-04-28 20:18:45 +0200340 else
341 {
Zesstra8d245512020-04-28 20:35:34 +0200342 if ( efun::interactive_info(snoopee, II_SNOOP_NEXT) == snooper )
Zesstra6c333cc2020-04-28 20:18:45 +0200343 {
MG Mud User88f12472016-06-24 23:31:02 +0200344 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
345 Lcut(dtime(time())),
Zesstra8d245512020-04-28 20:35:34 +0200346 snooper, snoopee, environment(snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200347 100000 );
348 }
349 }
350
Zesstra8d245512020-04-28 20:35:34 +0200351 if ( ret && query_wiz_grp(snooper) <= query_wiz_grp(snoopee) &&
352 !IS_DEPUTY(snooper) )
353 tell_object( snoopee, "*** " + NAME(snooper) + " snoopt Dich!\n" );
MG Mud User88f12472016-06-24 23:31:02 +0200354
355 return ret;
356 }
Zesstra69174b42020-04-28 22:48:45 +0200357 // Ansonsten soll ein bestehender snoop beendet werden.
Zesstra6c333cc2020-04-28 20:18:45 +0200358 else
359 {
Zesstra69174b42020-04-28 22:48:45 +0200360 // Das beenden duerfen aber nur Aufrufer selber oder hoeherlevelige
361 // ausloesen oder gleichen levels, wenn sie selber gerade vom snooper
362 // gesnoopt werden.
Zesstra8d245512020-04-28 20:35:34 +0200363 if ( (snooper == PO ||
364 query_wiz_grp(geteuid(PO)) > query_wiz_grp(snooper) ||
365 (query_wiz_grp(geteuid(PO)) == query_wiz_grp(snooper) &&
Zesstra69174b42020-04-28 22:48:45 +0200366 efun::interactive_info(PO, II_SNOOP_NEXT) == snooper) )
367 && existing_snoopee )
Zesstra6c333cc2020-04-28 20:18:45 +0200368 {
Zesstra8d245512020-04-28 20:35:34 +0200369 if ( !IS_DEPUTY(snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200370 {
MG Mud User88f12472016-06-24 23:31:02 +0200371 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
Zesstra8d245512020-04-28 20:35:34 +0200372 Lcut(dtime(time())), snooper,
Zesstra69174b42020-04-28 22:48:45 +0200373 existing_snoopee,
374 environment(existing_snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200375 100000 );
376
Zesstra8d245512020-04-28 20:35:34 +0200377 CHMASTER->send( "Snoop", snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200378 sprintf( "%s *OFF* %s (%O).",
Zesstra8d245512020-04-28 20:35:34 +0200379 capitalize(getuid(snooper)),
Zesstra69174b42020-04-28 22:48:45 +0200380 capitalize(getuid(existing_snoopee)),
381 environment(existing_snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200382 }
Zesstra6c333cc2020-04-28 20:18:45 +0200383 else
384 {
MG Mud User88f12472016-06-24 23:31:02 +0200385 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
Zesstra8d245512020-04-28 20:35:34 +0200386 Lcut(dtime(time())), snooper,
Zesstra69174b42020-04-28 22:48:45 +0200387 existing_snoopee,
388 environment(existing_snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200389 100000 );
390 }
Zesstra8d245512020-04-28 20:35:34 +0200391 return efun::snoop(snooper);
MG Mud User88f12472016-06-24 23:31:02 +0200392 }
393 }
394 return 0;
395}
396
397
398
399// * Emulation des 'alten' explode durch das neue
400string *old_explode(string str, string del) {
401 int s, t;
402 string *strs;
403
404 if (!stringp(str)) {
405 set_this_object(previous_object());
406 raise_error(sprintf(
407 "Invalid argument 1 to old_explode()! Expected <string>, got: "
408 "%.30O\n",str));
409 }
410 if (!stringp(del)) {
411 set_this_object(previous_object());
412 raise_error(sprintf(
413 "Invalid argument 2 to old_explode()! Expected <string>, got: "
414 "%.30O\n",del));
415 }
416 if(del == "")
417 return ({str});
418 strs=efun::explode(str, del);
419 t=sizeof(strs)-1;
420 while(s<=t && strs[s++] == "");s--;
421 while(t>=0 && strs[t--] == "");t++;
422 if(s<=t)
423 return strs[s..t];
424 return ({});
425}
426
427int file_time(string path) {
428 mixed *v;
429
430 set_this_object(previous_object());
431 if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
432 return(0); //sonst
433}
434
MG Mud User88f12472016-06-24 23:31:02 +0200435// * Magier-Level abfragen
Zesstra39d284a2020-08-30 23:55:47 +0200436int query_wiz_level(object|string player) {
Zesstra4dbb9882019-11-26 21:26:36 +0100437 return "/secure/master"->query_wiz_level(player);
MG Mud User88f12472016-06-24 23:31:02 +0200438}
439
440#ifdef __ALISTS__
441// * Element aus Alist loeschen (by key)
442mixed *remove_alist(mixed key,mixed *alist)
443{
444 int i,j;
445
446 if (!pointerp(alist) || !sizeof(alist))
447 return alist;
448 if (!pointerp(alist[0]))
449 {
450 if ((i=assoc(key,alist))<0)
451 return alist;
452 return alist[0..i-1]+alist[i+1..];
453 }
454 i = assoc(key,alist[0]);
455 if ((i=assoc(key,alist[0]))<0)
456 return alist;
457 alist=alist[0..];
458 for (j=sizeof(alist)-1;j>=0;j--)
459 alist[j]=alist[j][0..i-1]+alist[j][i+1..];
460 return alist;
461}
462
463// * Element aus Alist loeschen (by pos)
464mixed *exclude_alist(int i,mixed *alist)
465{
466 int j;
467 if (!pointerp(alist) || !sizeof(alist) || i<0)
468 return alist;
469 if (!pointerp(alist[0]))
470 return alist[0..i-1]+alist[i+1..];
471 alist=alist[0..]; /* Create PHYSICAL copy of alist */
472 for (j=sizeof(alist)-1;j>=0;j--)
473 alist[j]=alist[j][0..i-1]+alist[j][i+1..];
474 return alist; /* order_alist is NOT necessary - see /doc/LPC/alist */
475}
476#endif // __ALISTS__
477
478// * German version of ctime()
479#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
480#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
481 "Sep","Okt","Nov","Dez"})
482string dtime(int wann) {
483
484 if (wann == dtime_cache[0])
485 return(dtime_cache[1]);
486
487 int *lt = localtime(wann);
488 return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
489 TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]],
490 lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
491}
492
493// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
494// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
495// ein ordentliches Datum/Uhrzeit.
496#if !__EFUN_DEFINED__(strftime)
497varargs string strftime(mixed fmt, int clock, int localized) {
498 if (intp(clock) && clock >= 0)
499 return dtime(clock);
500 else if (intp(fmt) && fmt >= 0)
501 return dtime(fmt);
502
503 return dtime(time());
504}
505#endif //!__EFUN_DEFINED__(strftime)
506
507// * Shutdown mit zusaetzlichem logging
508nomask int shutdown(string reason)
509{
510 string name;
511 string obname;
512 string output;
513
514 if (!reason)
515 return 0;
516 if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
517 object_name(previous_object())!="/obj/shut" )
518 {
519 write("You have no permission to shut down the gamedriver!\n");
520 return 0;
521 }
522 if ((this_interactive())&&(name=getuid(this_interactive())))
523 {
524 name=capitalize(name);
525 filter(users(),#'tell_object,//'
526 capitalize(name)+" faehrt das Spiel herunter!\n");
527 }
528 else
529 name="ANONYMOUS";
530 if (previous_object()) obname=capitalize(getuid(previous_object()));
531 output=name;
532 if (obname && name!=obname) output=output+" ("+obname+")";
533 if (previous_object()&&object_name(previous_object())=="/obj/shut"){
534 output+=" faehrt das Spiel via Armageddon herunter.\n";
535 output=dtime(time())+": "+output;
536 log_file("GAME_LOG",output+"\n",-1);
537 efun::shutdown();
538 return 1;
539 }
540 output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
541 output+=" Grund: "+reason;
542 log_file("GAME_LOG",output+"\n",-1);
543 efun::shutdown();
544 return 1;
545}
546
547// * lowerchar
548
549int lowerchar(int char) {
550 if (char<'A' || char>'Z') return char;
551 return char+32;
552}
553
554// * upperstring
555
556string upperstring(string s)
557{
558#if __EFUN_DEFINED__(upper_case)
559 return(upper_case(s));
560#else
561 int i;
562 if (!stringp(s)) return 0;
563 for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
564 return s;
565#endif
566}
567
568// * lowerstring
569
570string lowerstring(string s)
571{
572 if (!stringp(s)) return 0;
573 return lower_case(s);
574}
575
576
577// * GD version
578string version()
579{
580 return __VERSION__;
581}
582
583// * break_string
584// stretch() -- stretch a line to fill a given width
585private string stretch(string s, int width) {
586 int len=sizeof(s);
587 if (len==width) return s;
588
589 // reine Leerzeilen, direkt zurueckgeben
590 string trimmed=trim(s,TRIM_LEFT," ");
591 if (trimmed=="") return s;
592 int start_spaces = len - sizeof(trimmed);
593
594 string* words = explode(trimmed, " ");
595 // der letzte kriegt keine Spaces
596 int word_count=sizeof(words) - 1;
597 // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
598 if (!word_count)
599 return " "*start_spaces + words[0];
600
601 int space_count = width - len;
602
603 int space_per_word=(word_count+space_count) / word_count;
604 // Anz.Woerter mit Zusatz-Space
605 int rest=(word_count+space_count) % word_count;
606 // Rest-Spaces Verteilen
607 foreach (int pos : rest) words[pos]+=" ";
608 return (" "*start_spaces) + implode( words, " "*space_per_word );
609}
610
611// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
612// eigene Funktion bekommen:
613private varargs string block_string(string s, int width, int flags) {
614 // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
615 // einem Leerzeichen begonnen.
616 // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
617 // ersetzt.
618 if ( (flags & BS_LEAVE_MY_LFS)
619 && !(flags & BS_NO_PARINDENT))
620 {
621 s = " "+regreplace(s,"\n","\n ",1);
622 }
623
624 // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
625 // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
626 // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
627 // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
628 // wieder rueckgaengig zu machen.
629 s = sprintf( "%-*=s", width, s);
630
631 string *tmp=explode(s, "\n");
632 // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
633 // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
634 // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
635 // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
636 // Ende der letzten Zeile hat), das macht der Aufrufer...
637 if (sizeof(tmp) > 1)
638 return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" )
639 + "\n" + tmp[<1];
640
641 return s;
642}
643
644public varargs string break_string(string s, int w, mixed indent, int flags)
645{
646 if ( !s || s == "" ) return "";
647
648 if ( !w ) w=78;
649
650 if( intp(indent) )
651 indent = indent ? " "*indent : "";
652
653 int indentlen=stringp(indent) ? sizeof(indent) : 0;
654
655 if (indentlen>w) {
656 set_this_object(previous_object());
657 raise_error(sprintf("break_string: indent longer %d than width %d\n",
658 indentlen,w));
659 // w=((indentlen/w)+1)*w;
660 }
661
662 if (!(flags & BS_LEAVE_MY_LFS))
663 s=regreplace( s, "\n", " ", 1 );
664
665 if ( flags & BS_SINGLE_SPACE )
666 s = regreplace( s, "(^|\n| ) *", "\\1", 1 );
667
668 string prefix="";
669 if (indentlen && flags & BS_PREPEND_INDENT) {
670 if (indentlen+sizeof(s) > w ||
671 (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
672 prefix=indent+"\n";
673 indent=(flags & BS_NO_PARINDENT) ? "" : " ";
674 indentlen=sizeof(indent);
675 }
676 }
677
678 if ( flags & BS_BLOCK ) {
679 /*
680 s = implode( map( explode( s, "\n" ),
681 #'block_string, w, indentlen, flags),
682 "" );
683 */
684 s = block_string( s , w - indentlen, flags );
685 }
686 else {
687 s = sprintf("%-1.*=s",w-indentlen,s);
688 }
689 if ( s[<1] != '\n' ) s += "\n";
690
691 if ( !indentlen ) return prefix + s;
692
693 string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
694
695 return prefix + indent +
696 regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
697 /*
698 string *buf;
699
700 buf = explode( s, "\n" );
701 return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
702 */
703}
704
705// * Elemente aus mapping loeschen - mapping vorher kopieren
706
707mapping m_copy_delete(mapping m, mixed key) {
708 return m_delete(copy(m), key);
709}
710
711// * times
712int last_reboot_time()
713{
714 return __BOOT_TIME__;
715}
716
717int first_boot_time()
718{
719 return 701517600;
720}
721
722int exist_days()
723{
724 return (((time()-first_boot_time())/8640)+5)/10;
725}
726
727// * uptime :)
728string uptime()
729{
730 int t;
731 int tmp;
732 string s;
733
734 t=time()-__BOOT_TIME__;
735 s="";
736 if (t>=86400)
737 s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
738 if (t>=3600)
739 s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
740 if (t>60)
741 s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
742 return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
743}
744
745// * Was tun bei 'dangling' lfun-closures ?
746void dangling_lfun_closure() {
747 raise_error("dangling lfun closure\n");
748}
749
750// * Sperren ausser fuer master/simul_efun
751
752#if __EFUN_DEFINED__(set_environment)
753nomask void set_environment(object o1, object o2) {
754 raise_error("Available only for root\n");
755}
756#endif
757
758nomask void set_this_player(object pl) {
759 raise_error("Available only for root\n");
760}
761
762#if __EFUN_DEFINED__(export_uid)
763nomask void export_uid(object ob) {
764 raise_error("Available only for root\n");
765}
766#endif
767
768// * Jetzt auch closures
769int process_flag;
770
771public nomask int process_call()
772{
773 if (process_flag>0)
774 return process_flag;
775 else return(0);
776}
777
778private nomask string _process_string(string str,object po) {
779 set_this_object(po);
780 return(efun::process_string(str));
781}
782
Zesstra16158af2020-09-01 00:07:59 +0200783private nomask int _illegal_ps_call(string s)
784{
785 int i_arg = strstr(s,"|"); // erst arg-trenner suchen
786 // dann ggf. von dort rueckwaerts den obj-trenner suchen
787 int i_ob = i_arg!=-1 ? strrstr(s, ":", i_arg)
788 : strstr(s,":");
789 // Wenn kein ":" vorkommt, ists max. ein objektinterner Call und erlaubt
790 if (i_ob == -1)
791 return 0;
792 string obname = (i_arg != -1) ? s[i_ob+1..i_arg-1]
793 : s[i_ob+1..];
794 // Wenn es das nicht gibt, ist es auch OK - process_string laedt keine
795 // nicht-geladenen Objekte. Das hier ist auch der Fall, wenn in
796 // dem Substring nen : vorkommt, es aber gar kein process_call ist...
797 object ob = find_object(obname);
798 if (!ob)
799 return 0;
800 // Es gibt ein Objekt. Jetzt wird es spannend. Erlaubt sind calls zwischen
Zesstra2ad183a2020-09-03 20:05:18 +0200801 // Objekten, welche dieselbe UID haben oder vom gleichen Magier stammen.
802 if (getuid(ob) == getuid(previous_object()) ||
803 REAL_UID(ob) == REAL_UID(previous_object()))
Zesstra16158af2020-09-01 00:07:59 +0200804 return 0;
805 // Alles andere ist nicht erlaubt
806 return 1;
807}
808
Zesstra739c58a2020-08-30 23:45:26 +0200809nomask string process_string( string|closure str )
MG Mud User88f12472016-06-24 23:31:02 +0200810{
811 string tmp, err;
Zesstrac5af6a72020-09-03 20:03:06 +0200812 int flag;
813
814 // Hmpf, es wird tatsaechlich reihenweise mit 0 gerufen.
815 if (!str)
816 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200817
Zesstra739c58a2020-08-30 23:45:26 +0200818 // process_string() wird nur noch ausgewertet, wenn der Aufrufer einen
Zesstra16158af2020-09-01 00:07:59 +0200819 // Level von maximal 30 hat. Das schliesst alle Objekten in /d/, /p/ und den
Zesstra739c58a2020-08-30 23:45:26 +0200820 // Gilden ein, aber verhindert es fuer alle hochstufigen Magier und ihre
821 // Objekte. Ausserdem erlauben wir keine Auswertung mehr fuer
822 // Spielerobjekte, wenn sie mehr als Seher sind.
Zesstra16158af2020-09-01 00:07:59 +0200823 // TODO: aus Spielershells ausbauen
Zesstra739c58a2020-08-30 23:45:26 +0200824 // TODO 2: ganz ausbauen.
825 if ( (query_once_interactive(previous_object())
826 && query_wiz_level(previous_object()) > SEER_LVL
827 )
Zesstrac8030682020-08-31 20:50:08 +0200828 || query_wiz_level(getuid(previous_object())) > SPECIAL_LVL)
Zesstra739c58a2020-08-30 23:45:26 +0200829 {
Zesstra86e26da2020-08-31 21:13:17 +0200830 // Ein Fehler wird aber nur ausgeloest, falls der String ein @@ enthaelt,
Zesstra284a35f2020-08-31 21:11:04 +0200831 // ansonsten koennen wir den ohne Fehler returnieren.
832 if (stringp(str) && strstr(str, "@@") == -1)
833 return str;
834 else
835 {
836 set_this_object(previous_object());
837 raise_error("Illegale Benutzung von process_string(). Aufrufer "
838 "ist Magiershell oder Objekt mit Level > 30.\n");
839 }
Zesstra739c58a2020-08-30 23:45:26 +0200840 }
Zesstra16158af2020-09-01 00:07:59 +0200841 // Kein Aufruf von Funktionen in Objekten anderer Magier erlaubt.
842 foreach(string s: explode(str, "@@"))
843 {
844 if (sizeof(s) && _illegal_ps_call(s))
845 {
846 set_this_object(previous_object());
847 raise_error("Illegale Benutzung von process_string(). Aufruf in "
848 "in fremder UID nicht erlaubt.\n");
849 }
850 }
Zesstra739c58a2020-08-30 23:45:26 +0200851
MG Mud User88f12472016-06-24 23:31:02 +0200852 if ( closurep(str) ) {
853 set_this_object( previous_object() );
854 return funcall(str);
855 }
856 else if (str==0)
Zesstra4dbb9882019-11-26 21:26:36 +0100857 return(str);
MG Mud User88f12472016-06-24 23:31:02 +0200858 else if ( !stringp(str) ) {
859 return to_string(str);
860 }
861
862 if ( !(flag = process_flag > time() - 60))
863 process_flag=time();
864
865 err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
866
867 if ( !flag )
868 process_flag=0;
869
870 if (err) {
871 // Verarbeitung abbrechen
872 set_this_object(previous_object());
873 raise_error(err);
874 }
875 return tmp;
876}
877
878// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
879// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
880// zurueckgeliefert, sonst 0.
881// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
882public int mkdirp(string dir) {
883 // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
884 if (strstr(dir, "/") != 0)
885 raise_error("mkdirp(): Pfad ist nicht absolute.\n");
886 // cut off trailing /...
887 if (dir[<1]=='/')
888 dir = dir[0..<2];
889
890 int fstat = file_size(dir);
891 // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
892 if (fstat == FSIZE_DIR)
893 return 1;
894 // wenn schon ne Datei existiert, geht es nicht.
895 if (fstat != FSIZE_NOFILE)
896 return 0;
897 // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
898 // toplevel-verzeichnis, was direkt angelegt wird.
899 if (strrstr(dir,"/")==0) {
900 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
901 }
902
903 // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
904 // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
905 // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
906
907 // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
908 // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
909 if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
910 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
Zesstraa25c9c12018-11-17 00:09:25 +0100911 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200912}
913
914
915// * Properties ggfs. mitspeichern
916mixed save_object(mixed name)
917{
918 mapping properties;
919 mapping save;
920 mixed index, res;
921 int i;
Zesstraa012d622017-01-29 20:38:48 +0100922 string oldpath;
MG Mud User88f12472016-06-24 23:31:02 +0200923
924 // nur Strings und 0 zulassen
925 if ((!stringp(name) || !sizeof(name)) &&
926 (!intp(name) || name!=0)) {
927 set_this_object(previous_object());
928 raise_error(sprintf(
929 "Only non-empty strings and 0 may be used as filename in "
930 "sefun::save_object()! Argument was %O\n",name));
931 }
932
Zesstraa012d622017-01-29 20:38:48 +0100933 if (stringp(name)) {
934 // abs. Pfad erzeugen. *seufz*
935 if (name[0]!='/')
936 name = "/" + name;
Zesstraa012d622017-01-29 20:38:48 +0100937 // automatisch in LIBDATADIR speichern
938 if (strstr(name,"/"LIBDATADIR"/") != 0) {
Zesstra60aed3e2017-01-31 16:03:51 +0100939 oldpath = name;
Zesstraa012d622017-01-29 20:38:48 +0100940 name = "/"LIBDATADIR + name;
941 // wenn das Verzeichnis nicht existiert, ggf. anlegen
942 string dir = name[0..strrstr(name,"/")-1];
943 if (file_size(dir) != FSIZE_DIR) {
944 if (mkdirp(dir) != 1)
945 raise_error("save_object(): kann Verzeichnis " + dir
946 + " nicht anlegen!");
947 }
948 }
949 }
950
MG Mud User88f12472016-06-24 23:31:02 +0200951 save = m_allocate(0, 2);
Zesstra4dbb9882019-11-26 21:26:36 +0100952 properties = previous_object()->QueryProperties();
MG Mud User88f12472016-06-24 23:31:02 +0200953
954 if(mappingp(properties))
955 {
956 // delete all entries in mapping properties without SAVE flag!
957 index = m_indices(properties);
958 for(i = sizeof(index)-1; i>=0;i--)
959 {
960 if(properties[index[i], F_MODE] & SAVE)
961 {
962 save[index[i]] = properties[index[i]];
963 save[index[i], F_MODE] =
964 properties[index[i], F_MODE] &
965 (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
966 }
967 }
968 }
969 else save = ([]);
970
971 // save object!
972 previous_object()->_set_save_data(save);
973 // format: wie definiert in config.h
Zesstraa012d622017-01-29 20:38:48 +0100974 if (stringp(name)) {
MG Mud User88f12472016-06-24 23:31:02 +0200975 res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
976 __LIB__SAVE_FORMAT_VERSION__);
Zesstraa012d622017-01-29 20:38:48 +0100977 // wenn erfolgreich und noch nen Savefile existiert, was nicht unter
978 // /data/ liegt, wird das geloescht.
979 if (!res && oldpath
980 && file_size(oldpath+".o") >= 0)
981 rm(oldpath+".o");
982 }
MG Mud User88f12472016-06-24 23:31:02 +0200983 else
984 res = funcall(bind_lambda(#'efun::save_object, previous_object()),
985 __LIB__SAVE_FORMAT_VERSION__);
Zesstraa012d622017-01-29 20:38:48 +0100986
MG Mud User88f12472016-06-24 23:31:02 +0200987 previous_object()->_set_save_data(0);
988
989#ifdef IOSTATS
990 // Stats...
991 struct iostat_s stat = (<iostat_s>);
992 stat->oname = object_name(previous_object());
993 stat->time = time();
994 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
995 // OIM_TOTAL_DATA_SIZE);
996 if (stringp(name))
997 stat->size = file_size(name + ".o");
998 else
999 stat->sizeof(res);
1000 //debug_message("saveo: "+saveo_stat[0]+"\n");
1001 saveo_stat[1][saveo_stat[0]] = stat;
1002 saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
1003 //debug_message("saveo 2: "+saveo_stat[0]+"\n");
1004#endif
1005
1006 return res;
1007}
1008
1009// * Auch Properties laden
1010int restore_object(string name)
1011{
1012 int result;
1013 mixed index;
1014 mixed save;
1015 mapping properties;
1016 int i;
1017 closure cl;
1018
Zesstra94381a42017-01-29 22:37:52 +01001019 if (sizeof(name) < 1)
1020 {
1021 set_this_object(previous_object());
1022 raise_error("Bad arg 1 to restore_object(): expected non-empty "
1023 "'string'.\n");
1024 }
1025
Zesstraa012d622017-01-29 20:38:48 +01001026 // Wenn name vermutlich ein Pfad (also nicht mit #x:y anfaengt)
1027 if (name[0] != '#')
1028 {
1029 // abs. Pfad erzeugen *seufz*
Zesstra984bde92017-01-29 22:19:52 +01001030 if (name[0]!='/')
Zesstraa012d622017-01-29 20:38:48 +01001031 name = "/" + name;
1032
Zesstraf2a0d492017-04-29 11:05:46 +02001033 // .c am Ende loeschen, sonst wird das File ggf. nicht gefunden.
1034 if(name[<2..]==".c")
1035 name=name[..<3];
1036
Zesstraa012d622017-01-29 20:38:48 +01001037 // wenn kein /data/ vorn steht, erstmal gucken, ob das Savefile unter
1038 // /data/ existiert. Wenn ja, wird das geladen.
Zesstra984bde92017-01-29 22:19:52 +01001039 if (strstr(name,"/"LIBDATADIR"/") != 0)
Zesstraa012d622017-01-29 20:38:48 +01001040 {
1041 string path = "/"LIBDATADIR + name;
1042 if (file_size(path + ".o") >= 0)
1043 name = path;
1044 }
1045 }
1046
MG Mud User88f12472016-06-24 23:31:02 +02001047 // get actual property settings (by create())
Zesstra4dbb9882019-11-26 21:26:36 +01001048 properties = previous_object()->QueryProperties();
MG Mud User88f12472016-06-24 23:31:02 +02001049
1050// DEBUG(sprintf("RESTORE %O\n",name));
1051 // restore object
1052 result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
1053 //'))
1054 //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein
1055 //mapping sein sollte.
Zesstra4dbb9882019-11-26 21:26:36 +01001056 save = previous_object()->_get_save_data();
1057 if((save))
MG Mud User88f12472016-06-24 23:31:02 +02001058 {
1059 index = m_indices(save);
1060 for(i = sizeof(index)-1; i>=0; i--)
1061 {
1062 properties[index[i]] = save[index[i]];
1063 properties[index[i], F_MODE] = save[index[i], F_MODE]
1064 &~(SETCACHED|QUERYCACHED);
1065 }
1066 }
1067 else properties = ([]);
1068
1069 // restore properties
1070 funcall(
1071 bind_lambda(
1072 unbound_lambda(({'arg}), //'})
1073 ({#'call_other,({#'this_object}),
1074 "SetProperties",'arg})),//')
1075 previous_object()),properties);
1076 previous_object()->_set_save_data(0);
1077
1078#ifdef IOSTATS
1079 // Stats...
1080 //debug_message("restoreo: "+restoreo_stat[0]+"\n");
1081 struct iostat_s stat = (<iostat_s>);
1082 stat->oname = object_name(previous_object());
1083 stat->time = time();
1084 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
1085 // OIM_TOTAL_DATA_SIZE);
1086 stat->size = file_size(name + ".o");
1087 restoreo_stat[1][restoreo_stat[0]] = stat;
1088
1089 restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
1090#endif
1091
1092 return result;
1093}
1094
1095// * HB eines Objektes ein/ausschalten
1096int set_object_heart_beat(object ob, int flag)
1097{
1098 if (objectp(ob))
1099 return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
1100}
1101
1102// * Magierlevelgruppen ermitteln
Zesstra39d284a2020-08-30 23:55:47 +02001103int query_wiz_grp(object|string wiz)
MG Mud User88f12472016-06-24 23:31:02 +02001104{
1105 int lev;
1106
1107 lev=query_wiz_level(wiz);
1108 if (lev<SEER_LVL) return 0;
1109 if (lev>=GOD_LVL) return lev;
1110 if (lev>=ARCH_LVL) return ARCH_GRP;
1111 if (lev>=ELDER_LVL) return ELDER_GRP;
1112 if (lev>=LORD_LVL) return LORD_GRP;
1113 if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
1114 if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
1115 if (lev>=WIZARD_LVL) return WIZARD_GRP;
1116 if (lev>=LEARNER_LVL) return LEARNER_GRP;
1117 return SEER_GRP;
1118}
1119
1120mixed *wizlist_info()
1121{
1122 if (ARCH_SECURITY || !extern_call())
1123 return efun::wizlist_info();
1124 return 0;
1125}
1126
1127// * wizlist ausgeben
1128varargs void wizlist(string name, int sortkey ) {
1129
1130 if (!name)
1131 {
1132 if (this_player())
1133 name = getuid(this_player());
1134 if (!name)
1135 return;
1136 }
1137
1138 // Schluessel darf nur in einem gueltigen Bereich sein
1139 if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
1140
1141 mixed** wl = efun::wizlist_info();
1142 // nach <sortkey> sortieren
1143 wl = sort_array(wl, function int (mixed a, mixed b)
1144 {return a[sortkey] < b[sortkey]; } );
1145
1146 // Summe ueber alle Kommandos ermitteln.
1147 int total_cmd, i;
1148 int pos=-1;
1149 foreach(mixed entry : wl)
1150 {
1151 total_cmd += entry[WL_COMMANDS];
1152 if (entry[WL_NAME] == name)
1153 pos = i;
1154 ++i;
1155 }
1156
1157 if (pos < 0 && name != "ALL" && name != "TOP100")
1158 return;
1159
1160 if (name == "TOP100")
1161 {
1162 if (sizeof(wl) > 100)
1163 wl = wl[0..100];
1164 else
1165 wl = wl;
1166 }
1167 // um name herum schneiden
1168 else if (name != "ALL")
1169 {
1170 if (sizeof(wl) <= 21)
1171 wl = wl;
1172 else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
1173 wl = wl[pos-10..pos+10];
1174 else if (pos <=21)
1175 wl = wl[0..20];
1176 else if (pos >= sizeof(wl) - 21)
1177 wl = wl[<21..];
1178 else
1179 wl = wl;
1180 }
1181
1182 write("\nWizard top score list\n\n");
1183 if (total_cmd == 0)
1184 total_cmd = 1;
1185 printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
1186 "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
1187 foreach(mixed e: wl)
1188 {
1189 printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
1190 e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
1191 e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
1192 e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
1193 );
1194 }
1195 printf("\nTotal %7d (%d)\n\n", total_cmd, sizeof(wl));
1196}
1197
1198
1199// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
1200
1201// ermittelt das Objekt des Callouts.
1202private object _call_out_obj( mixed call_out ) {
1203 return pointerp(call_out) ? call_out[0] : 0;
1204}
1205
1206private void _same_object( object ob, mapping m ) {
1207 // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
1208 if ( m[ob] )
1209 m[ob] += ({ ob });
1210 else
1211 m[ob] = ({ ob });
1212}
1213
1214// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
1215// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
1216// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
1217private void _same_path( object key, object *obs, mapping m ) {
1218 string path;
1219 if (!objectp(key) || !pointerp(obs)) return;
1220
1221 path = load_name(key);
1222
1223 if ( m[path] )
1224 m[path] += obs;
1225 else
1226 m[path] = obs;
1227}
1228
1229// key kann object oder string sein.
1230private int _too_many( mixed key, mapping m, int i ) {
1231 return sizeof(m[key]) >= i;
1232}
1233
1234// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
1235// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
1236// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
1237private void _destroy( mixed key, object *obs, string text, int uid ) {
1238 if (!pointerp(obs)) return;
1239 // Array mit unique Eintraege erzeugen.
1240 obs = m_indices( mkmapping(obs) );
1241 // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
1242 // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
1243 // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
1244 // laeuft.
1245 catch( efun::raise_error(
1246 sprintf( text,
Zesstra4dbb9882019-11-26 21:26:36 +01001247 uid ? master()->creator_file(key) : key,
MG Mud User88f12472016-06-24 23:31:02 +02001248 sizeof(obs), object_name(obs[<1]) ) );publish);
1249 // Und weg mit dem Kram...
1250 filter( obs, #'efun::destruct/*'*/ );
1251}
1252
1253// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
1254// sind dabei nicht unique.
1255private void _same_uid( string key, object *obs, mapping m, closure cf ) {
1256 string uid;
1257
1258 if ( !pointerp(obs) || !sizeof(obs) )
1259 return;
1260
1261 uid = funcall( cf, key );
1262
1263 if ( m[uid] )
1264 m[uid] += obs; // obs ist nen Array
1265 else
1266 m[uid] = obs;
1267}
1268
1269private int _log_call_out(mixed co)
1270{
1271 log_file("TOO_MANY_CALLOUTS",
1272 sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
1273 200000);
1274 return 0;
1275}
1276
1277private int last_callout_log=0;
1278
1279nomask varargs void call_out( varargs mixed *args )
1280{
1281 mixed tmp, *call_outs;
1282
1283 // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
1284 // haben.
1285 if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
1286 && geteuid(previous_object()) != ROOTID )
1287 {
1288 // Log erzeugen...
1289 if (last_callout_log <
1290 efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
1291 && get_eval_cost() > 200000)
1292 {
1293 last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1294 log_file("TOO_MANY_CALLOUTS",
1295 sprintf("\n%s: ############ Too many callouts: %d ##############\n",
1296 strftime("%y%m%d-%H%M%S"),
1297 efun::driver_info(DI_NUM_CALLOUTS)));
1298 filter(efun::call_out_info(), #'_log_call_out);
1299 }
1300 // Objekte aller Callouts ermitteln
1301 call_outs = map( efun::call_out_info(), #'_call_out_obj );
1302 mapping objectmap = ([]);
1303 filter( call_outs, #'_same_object, &objectmap );
1304 // Master nicht grillen...
1305 efun::m_delete( objectmap, master(1) );
1306 // alle Objekte raussuchen, die zuviele haben...
1307 mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
1308 // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
1309 // Objekten uebergeben (in diesem Fall sind Keys und Array mit
1310 // Objekten jeweils das gleiche Objekt).
1311 if ( sizeof(res) )
1312 walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "
1313 "object [%O]. Destructed %d objects. [%s]\n", 0 );
1314
1315 // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
1316 // Objekte killen, die mehr als 50 Callouts laufen haben - und
1317 // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
1318 // Clones einer BP gekillt, die Callouts laufen haben, falls alle
1319 // diese Objekte _zusammen_ mehr als 50 Callouts haben!
1320 if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
1321 // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
1322 // mehr drin, da sie dort als Keys verwendet wurden.
1323 mapping pathmap=([]);
1324 // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
1325 // von Objekten als Werte.
1326 walk_mapping( objectmap, #'_same_path, &pathmap);
1327 // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
1328 res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
1329 // und ueber alle Keys gehen, an _destroy() werden die Clones
1330 // uebergeben, die Callouts haben.
1331 if ( sizeof(res) )
1332 walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
1333 "'%s'. Destructed %d objects. [%s]\n", 0 );
1334
1335 // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
1336 // noch >1000 Callouts laufen, werden diesmal alle Callouts
1337 // einer UID zusammengezaehlt.
1338 // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
1339 // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
1340 if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
1341 {
1342 // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
1343 // zusammensortieren. Zerstoerte Clones filter _same_uid()
1344 // raus.
1345 mapping uidmap=([]);
1346 walk_mapping( pathmap, #'_same_uid, &uidmap,
1347 symbol_function( "creator_file",
1348 "/secure/master" ) );
1349 // In res nun UIDs als Keys und Arrays von Objekten als Werte.
1350 // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
1351 // 100 Callouts!) haben.
1352 res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
1353 // und erneut ueber die Keys laufen und jeweils die Arrays mit
1354 // den Objekten zur Zerstoerung an _destroy()...
1355 if ( sizeof(res) )
1356 walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
1357 "UID '%s'. Destructed %d objects. [%s]\n",
1358 1 );
1359 }
1360 }
1361 }
1362
1363 // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
1364 if ( !previous_object() )
1365 return;
1366
1367 set_this_object( previous_object() );
1368 apply( #'efun::call_out, args );
1369 return;
1370}
1371
1372mixed call_out_info() {
1373
1374 object po = previous_object();
1375 mixed coi = efun::call_out_info();
1376
1377 // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
1378 // /obj haben die BackboneID.
1379 if (query_wiz_level(getuid(po)) >= ARCH_LVL
Zesstra4dbb9882019-11-26 21:26:36 +01001380 || master()->creator_file(load_name(po)) == BACKBONEID ) {
MG Mud User88f12472016-06-24 23:31:02 +02001381 return coi;
1382 }
1383 else {
1384 return filter(coi, function mixed (mixed arr) {
1385 if (pointerp(arr) && arr[0]==po)
1386 return 1;
1387 else return 0; });
1388 }
1389}
1390
1391// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
1392// NICHT das Objekt, was ggf. die lfun definiert!
1393mixed query_closure_object(closure c) {
1394 return
1395 CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
1396 0 :
1397 (to_object(c) || -1);
1398}
1399
1400// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
1401varargs void move_object(mixed what, mixed where)
1402{
1403 object po,tmp;
1404
1405 po=previous_object();
1406 if (!where)
1407 {
1408 where=what;
1409 what=po;
1410 }
1411 if (((stringp(where) && where==NETDEAD_ROOM ) ||
1412 (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
1413 objectp(what) && object_name(what)!="/obj/sperrer")
1414 {
1415 if (!query_once_interactive(what))
1416 {
1417 what->remove();
1418 if (what) destruct(what);
1419 return;
1420 }
1421 if (living(what) || interactive(what))
1422 {
1423 log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
1424 return;
1425 }
1426 set_object_heart_beat(what,0);
1427 }
1428 tmp=what;
1429 while (tmp=environment(tmp))
1430 // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
1431 // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
1432 // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
1433 // da um jedes bisschen Rechenzeit geht.
1434 // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
1435 //
1436 // Tiamak
1437 tmp->_set_last_content_change();
1438 funcall(bind_lambda(#'efun::move_object,po),what,where);
1439 if (tmp=what)
1440 while (tmp=environment(tmp))
1441 tmp->_set_last_content_change();
1442}
1443
1444
1445void start_simul_efun() {
1446 mixed *info;
1447
1448 // Falls noch nicht getan, extra_wizinfo initialisieren
1449 if ( !pointerp(info = get_extra_wizinfo(0)) )
1450 set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
1451
1452 InitLivingData(info);
1453
1454 set_next_reset(10); // direkt mal aufraeumen
1455}
1456
1457protected void reset() {
1458 set_next_reset(7200);
1459 CleanLivingData();
1460}
1461
1462#if !__EFUN_DEFINED__(absolute_hb_count)
1463int absolute_hb_count() {
1464 return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1465}
1466#endif
1467
1468void __set_environment(object ob, mixed target)
1469{
1470 string path;
1471 object obj;
1472
1473 if (!objectp(ob))
1474 return;
1475 if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
1476 return;
1477 if (objectp(target))
1478 {
1479 efun::set_environment(ob,target);
1480 return;
1481 }
Zesstra4dbb9882019-11-26 21:26:36 +01001482 path=master()->make_path_absolute(target);
MG Mud User88f12472016-06-24 23:31:02 +02001483 if (stringp(path) && file_size(path+".c")>=0 &&
1484 !catch(load_object(path);publish) )
1485 {
1486 obj=find_object(path);
1487 efun::set_environment(ob,obj);
1488 return;
1489 }
1490}
1491
1492void _dump_wizlist(string file, int sortby) {
1493 int i;
1494 mixed *a;
1495
1496 if (!LORD_SECURITY)
1497 return;
1498 if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
1499 {
1500 write("NO WRITE PERMISSION\n");
1501 return;
1502 }
1503 a = wizlist_info();
1504 a = sort_array(a, lambda( ({'a,'b}),
1505 ({#'<,
1506 ({#'[,'a,sortby}),
1507 ({#'[,'b,sortby})
1508 })));
1509 rm(file);
1510 for (i=sizeof(a)-1;i>=0;i--)
1511 write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
1512 a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
1513 a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
1514}
1515
1516public varargs object deep_present(mixed what, object ob) {
1517
1518 if(!objectp(ob))
1519 ob=previous_object();
1520 // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
1521 // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
1522 if(objectp(what)) {
1523 object *envs=all_environment(what);
1524 // wenn ob kein Environment hat, ist es offensichtlich nicht in what
1525 // enthalten.
1526 if (!pointerp(envs)) return 0;
1527 if (member(envs, ob) != -1) return what;
1528 }
1529 // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
1530 // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
1531 // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
1532 else if (stringp(what)) {
1533 int cnt;
1534 string newwhat;
1535 if(sscanf(what,"%s %d",newwhat,cnt)!=2)
1536 cnt=1;
1537 else
1538 what=newwhat;
1539 foreach(object invob: deep_inventory(ob)) {
1540 if (invob->id(what) && !--cnt)
1541 return invob;
1542 }
1543 }
1544 else {
1545 set_this_object(previous_object());
1546 raise_error(sprintf("Wrong argument 1 to deep_present(). "
1547 "Expected \"object\" or \"string\", got %.50O.\n",
1548 what));
1549 }
1550 return 0;
1551}
1552
1553mapping dump_ip_mapping()
1554{
1555 return 0;
1556}
1557
1558nomask void swap(object obj)
1559{
1560 write("Your are not allowed to swap objects by hand!\n");
1561 return;
1562}
1563
1564nomask varargs void garbage_collection(string str)
1565{
1566 if(previous_object()==0 || !IS_ARCH(geteuid(previous_object()))
1567 || !ARCH_SECURITY)
1568 {
1569 write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
1570 return;
1571 }
1572 else if (stringp(str))
1573 {
1574 return efun::garbage_collection(str);
1575 }
1576 else
1577 return efun::garbage_collection();
1578}
1579
1580varargs void notify_fail(mixed nf, int prio) {
1581 object po,oldo;
1582 int oldprio;
1583
1584 if (!PL || !objectp(po=previous_object())) return;
1585 if (!stringp(nf) && !closurep(nf)) {
1586 set_this_object(po);
1587 raise_error(sprintf(
1588 "Only strings and closures allowed for notify_fail! "
1589 "Argument was: %.50O...\n",nf));
1590 }
1591
1592 // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
1593 // und vergleichen.
1594 if (objectp(oldo=query_notify_fail(1)) && po!=oldo) {
1595 if (!prio) {
1596 //Prioritaet dieses notify_fail() 'abschaetzen'
1597 if (po==PL) // Spieler-interne (soul-cmds)
1598 prio=NF_NL_OWN;
1599 else if (living(po))
1600 prio=NF_NL_LIVING;
Zesstra4dbb9882019-11-26 21:26:36 +01001601 else if (po->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001602 prio=NF_NL_ROOM;
1603 else
1604 prio=NF_NL_THING;
1605 }
1606 //Prioritaet des alten Setzers abschaetzen
1607 if (oldo==PL)
1608 oldprio=NF_NL_OWN;
1609 else if (living(oldo))
1610 oldprio=NF_NL_LIVING;
Zesstra4dbb9882019-11-26 21:26:36 +01001611 else if (oldo->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001612 oldprio=NF_NL_ROOM;
1613 else
1614 oldprio=NF_NL_THING;
1615 }
1616 else // wenn es noch kein Notify_fail gibt:
1617 oldprio=NF_NL_NONE;
1618
1619 //vergleichen und ggf. setzen
1620 if (prio >= oldprio) {
1621 set_this_object(po);
1622 efun::notify_fail(nf);
1623 }
1624
1625 return;
1626}
1627
1628void _notify_fail(string str)
1629{
1630 //query_notify_fail() benutzen, um das Objekt
1631 //des letzten notify_fail() zu ermitteln
1632 object o;
1633 if ((o=query_notify_fail(1)) && o!=previous_object())
1634 return;
1635 //noch kein notify_fail() fuer dieses Kommando gesetzt, auf gehts.
1636 set_this_object(previous_object());
1637 efun::notify_fail(str);
1638 return;
1639}
1640
1641string time2string( string format, int zeit )
1642{
1643 int i,ch,maxunit,dummy;
1644 string *parts, fmt;
1645
1646 int secs = zeit;
1647 int mins = (zeit/60);
1648 int hours = (zeit/3600);
1649 int days = (zeit/86400);
1650 int weeks = (zeit/604800);
1651 int months = (zeit/2419200);
1652 int abbr = 0;
1653
1654 parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
1655
1656 for( i=1; i<sizeof(parts); i+=2 )
1657 {
1658 ch = parts[i][<1];
1659 switch( parts[i][<1] )
1660 {
1661 case 'x': case 'X':
1662 abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
1663 // NO break !
1664 case 'n': case 'N':
1665 maxunit |= 31;
1666 break;
1667 case 'w': case 'W':
1668 maxunit |= 15;
1669 break;
1670 case 'd': case 'D':
1671 maxunit |= 7;
1672 break;
1673 case 'h': case 'H':
1674 maxunit |= 3;
1675 break;
1676 case 'm': case 'M':
1677 maxunit |= 1;
1678 break;
1679 }
1680 }
1681 if( maxunit & 16 ) weeks %= 4;
1682 if( maxunit & 8 ) days %= 7;
1683 if( maxunit & 4 ) hours %= 24;
1684 if( maxunit & 2 ) mins %= 60;
1685 if( maxunit ) secs %= 60;
1686
1687 for( i=1; i<sizeof(parts); i+=2 )
1688 {
1689 fmt = parts[i][0..<2];
1690 ch = parts[i][<1];
1691 if( ch=='x' )
1692 {
1693 if (months > 0) ch='n';
1694 else if( weeks>0 ) ch='w';
1695 else if( days>0 ) ch='d';
1696 else if( hours>0 ) ch='h';
1697 else if(mins > 0) ch = 'm';
1698 else ch = 's';
1699 }
1700 else if( ch=='X' )
1701 {
1702 if (months > 0) ch='N';
1703 else if( weeks>0 ) ch='W';
1704 else if( days>0 ) ch='D';
1705 else if( hours>0 ) ch='H';
1706 else if(mins > 0) ch = 'M';
1707 else ch = 'S';
1708 }
1709
1710 switch( ch )
1711 {
1712 case 'n': parts[i] = sprintf( fmt+"d", months ); break;
1713 case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
1714 case 'd': parts[i] = sprintf( fmt+"d", days ); break;
1715 case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
1716 case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
1717 case 's': parts[i] = sprintf( fmt+"d", secs ); break;
1718 case 'N':
1719 if(abbr) parts[i] = "M";
1720 else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
1721 break;
1722 case 'W':
1723 if(abbr) parts[i] = "w"; else
1724 parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
1725 break;
1726 case 'D':
1727 if(abbr) parts[i] = "d"; else
1728 parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
1729 break;
1730 case 'H':
1731 if(abbr) parts[i] = "h"; else
1732 parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
1733 break;
1734 case 'M':
1735 if(abbr) parts[i] = "m"; else
1736 parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
1737 break;
1738 case 'S':
1739 if(abbr) parts[i] = "s"; else
1740 parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
1741 break;
1742 case '%':
1743 parts[i] = "%";
1744 break;
1745 }
1746 }
1747 return implode( parts, "" );
1748}
1749
1750nomask mixed __create_player_dummy(string name)
1751{
1752 string err;
1753 object ob;
1754 mixed m;
1755 //hat nen Scherzkeks die Blueprint bewegt?
1756 if ((ob=find_object("/secure/login")) && environment(ob))
1757 catch(destruct(ob);publish);
1758 err = catch(ob = clone_object("secure/login");publish);
1759 if (err)
1760 {
1761 write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
1762 return 0;
1763 }
Zesstra4dbb9882019-11-26 21:26:36 +01001764 if (objectp(m=ob->new_logon(name))) netdead[name]=m;
MG Mud User88f12472016-06-24 23:31:02 +02001765 return m;
1766}
1767
1768nomask int secure_level()
1769{
1770 int *level;
1771 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
1772 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
1773 //von 0.
1774 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
1775 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
1776 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
1777 //INteractive geben muss.
1778 level=map(caller_stack(1),function int (object caller)
1779 {if (objectp(caller))
1780 return(query_wiz_level(geteuid(caller)));
1781 return(0); // kein Objekt da, 0.
1782 } );
1783 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
1784}
1785
1786nomask string secure_euid()
1787{
1788 string euid;
1789
1790 if (!this_interactive()) // Es muss einen interactive geben
1791 return 0;
1792 euid=geteuid(this_interactive());
1793 // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
1794 // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
1795 // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
1796 foreach(object caller: caller_stack()) {
1797 if (!objectp(caller) ||
1798 (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
1799 return 0;
1800 }
1801 return euid; // 'sichere' euid zurueckgeben
1802}
1803
1804// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
1805// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
1806//#if __BOOT_TIME__ < 1360017213
1807varargs void input_to( mixed fun, int flags, varargs mixed *args )
1808{
1809 mixed *arr;
1810 int i;
1811
1812 if ( !this_player() || !previous_object() )
1813 return;
1814
1815 // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
1816 // vorkommt...
1817 if ( flags&INPUT_PROMPT ) {
1818 arr = ({ fun, flags }) + args;
1819 }
1820 else {
1821 // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
1822 flags |= INPUT_PROMPT;
1823 arr = ({ fun, flags, "" }) + args;
1824 }
1825
1826 // Arrays gegen flatten quoten.
1827 for ( i = sizeof(arr) - 1; i > 1; i-- )
1828 if ( pointerp(arr[i]) )
1829 arr[i] = quote(arr[i]);
1830
1831 apply( bind_lambda( unbound_lambda( ({}),
1832 ({ #'efun::input_to/*'*/ }) + arr ),
1833 previous_object() ) );
1834}
1835//#endif
1836
1837nomask int set_light(int i)
1838// erhoeht das Lichtlevel eines Objekts um i
1839// result: das Lichtlevel innerhalb des Objekts
1840{
1841 object ob, *inv;
1842 int lall, light, dark, tmp;
1843
1844 if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
1845
1846 // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
1847 if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
1848
1849 // Lichtberechnung findet eigentlich in der Mudlib statt.
Zesstra4dbb9882019-11-26 21:26:36 +01001850 return ob->QueryProp(P_INT_LIGHT);
MG Mud User88f12472016-06-24 23:31:02 +02001851}
1852
MG Mud User88f12472016-06-24 23:31:02 +02001853public varargs string CountUp( string *s, string sep, string lastsep )
1854{
1855 string ret;
1856
1857 if ( !pointerp(s) )
1858 return "";
1859
1860 if (!sep) sep = ", ";
1861 if (!lastsep) lastsep = " und ";
1862
1863 switch (sizeof(s)) {
1864 case 0: ret=""; break;
1865 case 1: ret=s[0]; break;
1866 default:
1867 ret = implode(s[0..<2], sep);
1868 ret += lastsep + s[<1];
1869 }
1870 return ret;
1871}
1872
1873nomask varargs int query_next_reset(object ob) {
1874
1875 // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
1876 if (ob && !objectp(ob))
1877 raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
1878 "expected object.\n",ob));
1879
1880 // Defaultobjekt PO, wenn 0 uebergeben.
1881 if ( !objectp(ob) )
1882 ob = previous_object();
1883
1884 return efun::object_info(ob, OI_NEXT_RESET_TIME);
1885}
1886
1887
MG Mud User88f12472016-06-24 23:31:02 +02001888
1889// ### Ersatzaufloesung in Strings ###
Arathorn066820b2019-11-27 19:47:19 +01001890varargs string replace_personal(string str, <string|object>* obs, int caps) {
1891 string* parts = regexplode(str, "@WE[A-SU]*[0-9]");
1892 int i = sizeof(parts);
MG Mud User88f12472016-06-24 23:31:02 +02001893
Zesstra19102132016-09-01 22:50:36 +02001894 if (i>1)
1895 {
MG Mud User88f12472016-06-24 23:31:02 +02001896 int j, t;
MG Mud User88f12472016-06-24 23:31:02 +02001897 t = j = sizeof(obs);
1898
Arathorn066820b2019-11-27 19:47:19 +01001899 <string|closure>* name_cls = allocate(j);
1900 while (j--) {
MG Mud User88f12472016-06-24 23:31:02 +02001901 if (objectp(obs[j]))
1902 name_cls[j] = symbol_function("name", obs[j]);
1903 else if (stringp(obs[j]))
1904 name_cls[j] = obs[j];
Arathorn066820b2019-11-27 19:47:19 +01001905 }
MG Mud User88f12472016-06-24 23:31:02 +02001906
Zesstra19102132016-09-01 22:50:36 +02001907 while ((i-= 2)>0)
1908 {
MG Mud User88f12472016-06-24 23:31:02 +02001909 // zu ersetzendes Token in Fall und Objektindex aufspalten
Arathorn066820b2019-11-27 19:47:19 +01001910 int ob_nr = parts[i][<1]-'1';
MG Mud User88f12472016-06-24 23:31:02 +02001911 if (ob_nr<0 || ob_nr>=t) {
1912 set_this_object(previous_object());
1913 raise_error(sprintf("replace_personal: using wrong object index %d\n",
1914 ob_nr));
1915 return implode(parts, "");
1916 }
1917
1918 // casus kann man schon hier entscheiden
1919 int casus;
1920 string part = parts[i];
1921 switch (part[3]) {
1922 case 'R': casus = WER; break;
1923 case 'S': casus = WESSEN; break;
1924 case 'M': casus = WEM; break;
1925 case 'N': casus = WEN; break;
1926 default: continue; // passt schon jetzt nicht in das Hauptmuster
1927 }
1928
1929 // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
1930 mixed tmp;
1931 switch (part[3..<2]) {
1932 case "R": case "SSEN": case "M": case "N": // Name
1933 parts[i] = funcall(name_cls[ob_nr], casus, 1); break;
1934 case "RU": case "SSENU": case "MU": case "NU": // unbestimmt
1935 parts[i] = funcall(name_cls[ob_nr], casus); break;
1936 case "RQP": case "SSENQP": case "MQP": case "NQP": // Pronoun
1937 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001938 parts[i] = tmp->QueryPronoun(casus);
MG Mud User88f12472016-06-24 23:31:02 +02001939 break;
1940 case "RQA": case "SSENQA": case "MQA": case "NQA": // Article
1941 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001942 tmp = tmp->QueryArticle(casus, 1, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001943 if (stringp(tmp) && !(tmp[<1]^' '))
1944 tmp = tmp[0..<2]; // Extra-Space wieder loeschen
1945 break;
1946 case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
1947 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001948 parts[i] = tmp->QueryPossPronoun(MALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001949 break;
1950 case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
1951 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001952 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001953 break;
1954 case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
1955 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001956 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001957 break;
1958 case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
1959 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001960 parts[i] = tmp->QueryPossPronoun(MALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001961 break;
1962 case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
1963 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001964 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001965 break;
1966 case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
1967 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001968 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001969 break;
1970 default:
1971 continue;
1972 }
Zesstra19102132016-09-01 22:50:36 +02001973
MG Mud User88f12472016-06-24 23:31:02 +02001974 // wenn tmp ein String war, weisen wir es hier pauschal zu
1975 if (stringp(tmp))
1976 parts[i] = tmp;
1977
1978 // auf Wunsch wird nach Satzenden gross geschrieben
1979 if (caps)
Zesstra19102132016-09-01 22:50:36 +02001980 {
1981 // Wenn das vorhergehende parts[i] == "" ist, sind wir am Anfang vom
1982 // String und dies wird wie ein Satzende vorher behandelt.
1983 if (parts[i-1] == "")
MG Mud User88f12472016-06-24 23:31:02 +02001984 parts[i] = capitalize(parts[i]);
Zesstra19102132016-09-01 22:50:36 +02001985 else
1986 {
1987 switch (parts[i-1][<2..])
1988 {
1989 case ". ": case "! ": case "? ":
1990 case ".": case "!": case "?":
1991 case ".\n": case "!\n": case "?\n":
1992 case "\" ": case "\"\n":
1993 parts[i] = capitalize(parts[i]);
1994 break;
1995 }
MG Mud User88f12472016-06-24 23:31:02 +02001996 }
Zesstra19102132016-09-01 22:50:36 +02001997 }
MG Mud User88f12472016-06-24 23:31:02 +02001998 }
1999 return implode(parts, "");
2000 }
2001 return str;
2002}
2003
MG Mud User88f12472016-06-24 23:31:02 +02002004//replacements for dropped efuns in LD
2005#if !__EFUN_DEFINED__(extract)
2006deprecated varargs string extract(string str, int from, int to) {
2007
2008 if(!stringp(str)) {
2009 set_this_object(previous_object());
2010 raise_error(sprintf("Bad argument 1 to extract(): %O",str));
2011 }
2012 if (intp(from) && intp(to)) {
2013 if (from>=0 && to>=0)
2014 return(str[from .. to]);
2015 else if (from>=0 && to<0)
2016 return(str[from .. <abs(to)]);
2017 else if (from<0 && to>=0)
2018 return(str[<abs(from) .. to]);
2019 else
2020 return(str[<abs(from) .. <abs(to)]);
2021 }
2022 else if (intp(from)) {
2023 if (from>=0)
2024 return(str[from .. ]);
2025 else
2026 return(str[<abs(from) .. ]);
2027 }
2028 else {
2029 return(str);
2030 }
2031}
2032#endif // !__EFUN_DEFINED__(extract)
2033
2034#if !__EFUN_DEFINED__(slice_array)
2035deprecated varargs mixed slice_array(mixed array, int from, int to) {
2036
2037 if(!pointerp(array)) {
2038 set_this_object(previous_object());
2039 raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
2040 }
2041 if (intp(from) && intp(to)) {
2042 if (from>=0 && to>=0)
2043 return(array[from .. to]);
2044 else if (from>=0 && to<0)
2045 return(array[from .. <abs(to)]);
2046 else if (from<0 && to>=0)
2047 return(array[<abs(from) .. to]);
2048 else
2049 return(array[<abs(from) .. <abs(to)]);
2050 }
2051 else if (intp(from)) {
2052 if (from>=0)
2053 return(array[from .. ]);
2054 else
2055 return(array[<abs(from) .. ]);
2056 }
2057 else {
2058 return(array);
2059 }
2060}
2061#endif // !__EFUN_DEFINED__(slice_array)
2062
2063#if !__EFUN_DEFINED__(member_array)
2064deprecated int member_array(mixed item, mixed arraystring) {
2065
2066 if (pointerp(arraystring)) {
2067 return(efun::member(arraystring,item));
2068 }
2069 else if (stringp(arraystring)) {
2070 return(efun::member(arraystring,to_int(item)));
2071 }
2072 else {
2073 set_this_object(previous_object());
2074 raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
2075 }
2076}
2077#endif // !__EFUN_DEFINED__(member_array)
2078
2079// The digit at the i'th position is the number of bits set in 'i'.
2080string count_table =
2081 "0112122312232334122323342334344512232334233434452334344534454556";
2082int broken_count_bits( string s ) {
2083 int i, res;
2084 if( !stringp(s) || !(i=sizeof(s)) ) return 0;
2085 for( ; i-->0; ) {
2086 // We are counting 6 bits at a time using a precompiled table.
2087 res += count_table[(s[i]-' ')&63]-'0';
2088 }
2089 return res;
2090}
2091
2092#if !__EFUN_DEFINED__(count_bits)
2093int count_bits( string s ) {
2094 return(broken_count_bits(s));
2095}
2096#endif
2097
2098
2099// * Teile aus einem Array entfernen *** OBSOLETE
2100deprecated mixed *exclude_array(mixed *arr,int from,int to)
2101{
2102 if (to<from)
2103 to = from;
2104 return arr[0..from-1]+arr[to+1..];
2105}
2106