blob: d4dc52199c637694f213191a2f5e3587fc9ad281 [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 +020051nomask int secure_level();
52nomask string secure_euid();
53public nomask int process_call();
54nomask mixed __create_player_dummy(string name);
Zesstra953b7ea2019-11-28 19:29:37 +010055varargs string replace_personal(string str, <string|object>* obs, int caps);
MG Mud User88f12472016-06-24 23:31:02 +020056
57//replacements for dropped efuns in LD
58#if !__EFUN_DEFINED__(extract)
59varargs string extract(string str, int from, int to);
60#endif
61#if !__EFUN_DEFINED__(slice_array)
62varargs mixed slice_array(mixed array, int from, int to);
63#endif
64#if !__EFUN_DEFINED__(member_array)
65int member_array(mixed item, mixed arraystring);
66#endif
67
68// Include the different sub 'modules' of the simul_efun
69#include __DIR__"debug_info.c"
70#include __DIR__"enable_commands.c"
71#include __DIR__"hash.c"
72#include __DIR__"object_info.c"
73#include __DIR__"query_editing.c"
74#include __DIR__"query_idle.c"
75#include __DIR__"query_input_pending.c"
76#include __DIR__"query_ip_name.c"
77#include __DIR__"query_limits.c"
78#include __DIR__"query_load_average.c"
79#include __DIR__"query_mud_port.c"
80#include __DIR__"query_once_interactive.c"
81#include __DIR__"query_snoop.c"
82#include __DIR__"set_heart_beat.c"
83#if __BOOT_TIME__ < 1456261859
84#include __DIR__"set_prompt.c"
85#endif
86#include __DIR__"shadow.c"
87#include __DIR__"livings.c"
88#include __DIR__"comm.c"
Zesstra984bde92017-01-29 22:19:52 +010089#include __DIR__"files.c"
Zesstra2ea5d3e2017-06-20 22:03:32 +020090#include __DIR__"seteuid.c"
MG Mud User88f12472016-06-24 23:31:02 +020091
92#define TO efun::this_object()
93#define TI efun::this_interactive()
94#define TP efun::this_player()
95#define PO efun::previous_object(0)
96#define LEVEL(x) query_wiz_level(x)
97#define NAME(x) capitalize(getuid(x))
98
99#define DEBUG(x) if (find_player("zesstra")) \
100 tell_object(find_player("zesstra"),x)
101
102mixed dtime_cache = ({-1,""});
103
104#ifdef IOSTATS
105struct iostat_s {
106 string oname;
107 int time;
108 int size;
109};
110mixed saveo_stat = ({ 0,allocate(200, 0) });
111mixed restoreo_stat = ({ 0,allocate(200,0) });
112//mixed writefile_stat = ({ 0,allocate(100,(<iostat_s>)) });
113//mixed readfile_stat = ({ 0,allocate(100,(<iostat_s>)) });
114//mixed log_stat = ({ 0,allocate(100,(<iostat_s>)) });
115
116mixed ___iostats(int type) {
117 switch(type) {
118 case 1:
119 return saveo_stat;
120 case 2:
121 return restoreo_stat;
122/* case 3:
123 return writefile_stat;
124 case 4:
125 return readfile_stat;
126 case 5:
127 return log_stat;
128 */
129 }
130 return 0;
131}
132#endif
133
134// Nicht jeder Magier muss die simul_efun entsorgen koennen.
135string NotifyDestruct(object caller) {
136 if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
137 return "Du darfst das simul_efun Objekt nicht zerstoeren!\n";
138 }
139 return 0;
140}
141
142public nomask void remove_interactive( object ob )
143{
144 if ( objectp(ob) && previous_object()
145 && object_name(previous_object())[0..7] != "/secure/"
146 && ((previous_object() != ob
147 && (ob != this_player() || ob != this_interactive()))
148 || (previous_object() == ob
149 && (this_player() && this_player() != ob
150 || this_interactive() && this_interactive() != ob)) ) )
151
152 log_file( "PLAYERDEST",
153 sprintf( "%s: %O ausgeloggt von PO %O, TI %O, TP %O\n",
154 dtime(time()), ob, previous_object(),
155 this_interactive(), this_player() ) );
156
157 efun::remove_interactive(ob);
158}
159
160
161void ___updmaster()
162{
163 object ob;
164
165 //sollte nicht jeder duerfen.
166 if (process_call() || !ARCH_SECURITY)
167 raise_error("Illegal use of ___updmaster()!");
168
169 write("Removing old master ... ");
170 foreach(string file:
171 get_dir("/secure/master/*.c",GETDIR_NAMES|GETDIR_UNSORTED|GETDIR_PATH)) {
172 if (ob = find_object(file))
173 efun::destruct(ob);
174 }
175 efun::destruct(efun::find_object("/secure/master"));
176 write("done.\nLoading again ... ");
177 load_object("/secure/master");
178
179 write("done.\n");
180}
181
182varargs string country(mixed ip, string num) {
183 mixed ret;
184
Zesstra4dbb9882019-11-26 21:26:36 +0100185 if(ret = "/p/daemon/iplookup"->country(num || ip)) {
MG Mud User88f12472016-06-24 23:31:02 +0200186 return ret;
187 } else return "???";
188}
189
190
191// * Snoopen und was dazugehoert
MG Mud User88f12472016-06-24 23:31:02 +0200192
MG Mud User88f12472016-06-24 23:31:02 +0200193private string Lcut(string str) {
194 return str[5..11]+str[18..];
195}
196
Zesstra8d245512020-04-28 20:35:34 +0200197nomask varargs int snoop( object snooper, object snoopee )
MG Mud User88f12472016-06-24 23:31:02 +0200198{
199 int ret;
MG Mud User88f12472016-06-24 23:31:02 +0200200
Zesstra8d245512020-04-28 20:35:34 +0200201 if( !objectp(snooper) || snooper == snoopee || !PO )
MG Mud User88f12472016-06-24 23:31:02 +0200202 return 0;
203
Zesstra2e499d62020-04-28 22:13:53 +0200204 // Evtl. gibt es bereits einen snoopee, der von snopper gesnoopt wird?
Zesstra69174b42020-04-28 22:48:45 +0200205 object existing_snoopee = efun::interactive_info(snooper, II_SNOOP_PREV);
MG Mud User88f12472016-06-24 23:31:02 +0200206
Zesstra69174b42020-04-28 22:48:45 +0200207 // soll jemand neues gesnoopt werden?
Zesstra8d245512020-04-28 20:35:34 +0200208 if(snoopee)
209 {
Zesstra69174b42020-04-28 22:48:45 +0200210 // Jemand mit niedrigerem Level kann keinen hoeherleveligen snoopen
211 // lassen.
Zesstra8d245512020-04-28 20:35:34 +0200212 if ( PO != snooper
213 && query_wiz_grp(snooper) >= query_wiz_grp(geteuid(PO)) )
MG Mud User88f12472016-06-24 23:31:02 +0200214 return 0;
215
Zesstra69174b42020-04-28 22:48:45 +0200216 // Niedriglevelige User koennen nur mit Einverstaendnis hoeherlevelige
217 // snoopen.
218 if ( query_wiz_grp(snooper) <= query_wiz_grp(snoopee)
219 && !(snoopee->QueryAllowSnoop(snooper)) )
Zesstra6c333cc2020-04-28 20:18:45 +0200220 {
Zesstra69174b42020-04-28 22:48:45 +0200221 // es sei denn der snooper ist Sheriff und der snoopee ist kein
222 // EM+
Zesstra8d245512020-04-28 20:35:34 +0200223 if ( !IS_DEPUTY(snooper) || IS_ARCH(snoopee) )
MG Mud User88f12472016-06-24 23:31:02 +0200224 return 0;
Zesstra6c333cc2020-04-28 20:18:45 +0200225 }
Zesstra69174b42020-04-28 22:48:45 +0200226 // Wird der snoopee bereits gesnoopt? Dann darf sich der neue snooper
227 // nur unter Umstaenden in die Snoop-Kette einreihen...
228 object existing_snooper;
229 if ( (existing_snooper = efun::interactive_info(snoopee, II_SNOOP_NEXT))
230 && query_wiz_grp(existing_snooper) >= query_wiz_grp(snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200231 {
Zesstra69174b42020-04-28 22:48:45 +0200232 // ... naemlich nur dann, wenn der bestehende Snooper kein
233 // SF_LOCKED gesetzt hat.
234 if ( existing_snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
MG Mud User88f12472016-06-24 23:31:02 +0200235 return 0;
236
Zesstra69174b42020-04-28 22:48:45 +0200237 tell_object( existing_snooper, sprintf( "%s snooped jetzt %s.\n",
Zesstra8d245512020-04-28 20:35:34 +0200238 snooper->name(WER), snoopee->name(WER) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200239
Zesstra69174b42020-04-28 22:48:45 +0200240 // Evtl. wird der neue snooper selber gesnoopt. Dafuer wird jetzt
241 // ggf. die Kette von *ihren* snoopern verfolgt.
242 object snooper_of_new_snooper = snooper;
243 object snooper_rover;
244 while ( snooper_rover = interactive_info(snooper_of_new_snooper, II_SNOOP_NEXT) )
Zesstra6c333cc2020-04-28 20:18:45 +0200245 {
Zesstra69174b42020-04-28 22:48:45 +0200246 tell_object( existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200247 sprintf( "%s wird seinerseits von %s gesnooped.\n"
Zesstra69174b42020-04-28 22:48:45 +0200248 ,snooper_of_new_snooper->name(WER),
249 snooper_rover->name(WEM) ) );
250 snooper_of_new_snooper = snooper_rover;
MG Mud User88f12472016-06-24 23:31:02 +0200251 }
252
Zesstra69174b42020-04-28 22:48:45 +0200253 // Der letzt snooper des hier anzumeldenden snoopers wird nun vom
254 // bestehenden snooper gesnoopt, falls moeglich.
255 efun::snoop( existing_snooper, snooper_of_new_snooper );
MG Mud User88f12472016-06-24 23:31:02 +0200256
Zesstra69174b42020-04-28 22:48:45 +0200257 if ( efun::interactive_info(snooper_of_new_snooper, II_SNOOP_NEXT)
258 != existing_snooper )
259 tell_object( existing_snooper, sprintf( "Du kannst %s nicht snoopen.\n",
260 snooper_of_new_snooper->name(WEN) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200261 else
262 {
Zesstra69174b42020-04-28 22:48:45 +0200263 tell_object( existing_snooper, sprintf( "Du snoopst jetzt %s.\n",
264 snooper_of_new_snooper->name(WEN) ) );
265 if ( !IS_DEPUTY(existing_snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200266 {
MG Mud User88f12472016-06-24 23:31:02 +0200267 log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
268 dtime(time()),
Zesstra69174b42020-04-28 22:48:45 +0200269 existing_snooper,
270 snooper_of_new_snooper,
271 environment(snooper_of_new_snooper) ),
MG Mud User88f12472016-06-24 23:31:02 +0200272 100000 );
Zesstra69174b42020-04-28 22:48:45 +0200273 if (existing_snoopee)
274 CHMASTER->send( "Snoop", existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200275 sprintf( "%s *OFF* %s (%O)",
Zesstra69174b42020-04-28 22:48:45 +0200276 capitalize(getuid(existing_snooper)),
277 capitalize(getuid(existing_snoopee)),
278 environment(existing_snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200279
Zesstra69174b42020-04-28 22:48:45 +0200280 CHMASTER->send( "Snoop", existing_snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200281 sprintf("%s -> %s (%O)",
Zesstra69174b42020-04-28 22:48:45 +0200282 capitalize(getuid(existing_snooper)),
283 capitalize(getuid(snooper_of_new_snooper)),
284 environment(snooper_of_new_snooper)));
MG Mud User88f12472016-06-24 23:31:02 +0200285 }
Zesstra6c333cc2020-04-28 20:18:45 +0200286 else
287 {
MG Mud User88f12472016-06-24 23:31:02 +0200288 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
289 dtime(time()),
Zesstra69174b42020-04-28 22:48:45 +0200290 existing_snooper,
291 snooper_of_new_snooper,
292 environment(snooper_of_new_snooper) )
MG Mud User88f12472016-06-24 23:31:02 +0200293 ,100000 );
294 }
295 }
296 }
297 else
Zesstra6c333cc2020-04-28 20:18:45 +0200298 {
Zesstra69174b42020-04-28 22:48:45 +0200299 if (existing_snooper)
Zesstra6c333cc2020-04-28 20:18:45 +0200300 {
Zesstra8d245512020-04-28 20:35:34 +0200301 if ( !snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
Zesstra6c333cc2020-04-28 20:18:45 +0200302 {
MG Mud User88f12472016-06-24 23:31:02 +0200303 printf( "%s wird bereits von %s gesnooped. Benutze das "
304 "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
Zesstra69174b42020-04-28 22:48:45 +0200305 snoopee->name(WER), existing_snooper->name(WEM) );
MG Mud User88f12472016-06-24 23:31:02 +0200306 return 0;
307 }
Zesstra6c333cc2020-04-28 20:18:45 +0200308 }
309 }
Zesstra8d245512020-04-28 20:35:34 +0200310 ret = efun::snoop( snooper, snoopee );
MG Mud User88f12472016-06-24 23:31:02 +0200311
Zesstra8d245512020-04-28 20:35:34 +0200312 if ( !IS_DEPUTY(snooper)
313 && efun::interactive_info(snoopee, II_SNOOP_NEXT) == snooper)
Zesstra6c333cc2020-04-28 20:18:45 +0200314 {
MG Mud User88f12472016-06-24 23:31:02 +0200315 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
316 Lcut(dtime(time())),
Zesstra8d245512020-04-28 20:35:34 +0200317 snooper, snoopee, environment(snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200318 100000 );
319
Zesstra69174b42020-04-28 22:48:45 +0200320 if (existing_snoopee)
Zesstra6c333cc2020-04-28 20:18:45 +0200321 {
Zesstra8d245512020-04-28 20:35:34 +0200322 CHMASTER->send( "Snoop", snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200323 sprintf( "%s *OFF* %s (%O).",
Zesstra8d245512020-04-28 20:35:34 +0200324 capitalize(getuid(snooper)),
Zesstra69174b42020-04-28 22:48:45 +0200325 capitalize(getuid(existing_snoopee)),
326 environment(existing_snoopee) ) );
Zesstra6c333cc2020-04-28 20:18:45 +0200327 }
MG Mud User88f12472016-06-24 23:31:02 +0200328
Zesstra8d245512020-04-28 20:35:34 +0200329 CHMASTER->send( "Snoop", snooper, sprintf( "%s -> %s (%O).",
330 capitalize(getuid(snooper)),
331 capitalize(getuid(snoopee)),
332 environment(snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200333 }
Zesstra6c333cc2020-04-28 20:18:45 +0200334 else
335 {
Zesstra8d245512020-04-28 20:35:34 +0200336 if ( efun::interactive_info(snoopee, II_SNOOP_NEXT) == snooper )
Zesstra6c333cc2020-04-28 20:18:45 +0200337 {
MG Mud User88f12472016-06-24 23:31:02 +0200338 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
339 Lcut(dtime(time())),
Zesstra8d245512020-04-28 20:35:34 +0200340 snooper, snoopee, environment(snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200341 100000 );
342 }
343 }
344
Zesstra8d245512020-04-28 20:35:34 +0200345 if ( ret && query_wiz_grp(snooper) <= query_wiz_grp(snoopee) &&
346 !IS_DEPUTY(snooper) )
347 tell_object( snoopee, "*** " + NAME(snooper) + " snoopt Dich!\n" );
MG Mud User88f12472016-06-24 23:31:02 +0200348
349 return ret;
350 }
Zesstra69174b42020-04-28 22:48:45 +0200351 // Ansonsten soll ein bestehender snoop beendet werden.
Zesstra6c333cc2020-04-28 20:18:45 +0200352 else
353 {
Zesstra69174b42020-04-28 22:48:45 +0200354 // Das beenden duerfen aber nur Aufrufer selber oder hoeherlevelige
355 // ausloesen oder gleichen levels, wenn sie selber gerade vom snooper
356 // gesnoopt werden.
Zesstra8d245512020-04-28 20:35:34 +0200357 if ( (snooper == PO ||
358 query_wiz_grp(geteuid(PO)) > query_wiz_grp(snooper) ||
359 (query_wiz_grp(geteuid(PO)) == query_wiz_grp(snooper) &&
Zesstra69174b42020-04-28 22:48:45 +0200360 efun::interactive_info(PO, II_SNOOP_NEXT) == snooper) )
361 && existing_snoopee )
Zesstra6c333cc2020-04-28 20:18:45 +0200362 {
Zesstra8d245512020-04-28 20:35:34 +0200363 if ( !IS_DEPUTY(snooper) )
Zesstra6c333cc2020-04-28 20:18:45 +0200364 {
MG Mud User88f12472016-06-24 23:31:02 +0200365 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
Zesstra8d245512020-04-28 20:35:34 +0200366 Lcut(dtime(time())), snooper,
Zesstra69174b42020-04-28 22:48:45 +0200367 existing_snoopee,
368 environment(existing_snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200369 100000 );
370
Zesstra8d245512020-04-28 20:35:34 +0200371 CHMASTER->send( "Snoop", snooper,
MG Mud User88f12472016-06-24 23:31:02 +0200372 sprintf( "%s *OFF* %s (%O).",
Zesstra8d245512020-04-28 20:35:34 +0200373 capitalize(getuid(snooper)),
Zesstra69174b42020-04-28 22:48:45 +0200374 capitalize(getuid(existing_snoopee)),
375 environment(existing_snoopee) ) );
MG Mud User88f12472016-06-24 23:31:02 +0200376 }
Zesstra6c333cc2020-04-28 20:18:45 +0200377 else
378 {
MG Mud User88f12472016-06-24 23:31:02 +0200379 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
Zesstra8d245512020-04-28 20:35:34 +0200380 Lcut(dtime(time())), snooper,
Zesstra69174b42020-04-28 22:48:45 +0200381 existing_snoopee,
382 environment(existing_snoopee) ),
MG Mud User88f12472016-06-24 23:31:02 +0200383 100000 );
384 }
Zesstra8d245512020-04-28 20:35:34 +0200385 return efun::snoop(snooper);
MG Mud User88f12472016-06-24 23:31:02 +0200386 }
387 }
388 return 0;
389}
390
391
392
393// * Emulation des 'alten' explode durch das neue
394string *old_explode(string str, string del) {
395 int s, t;
396 string *strs;
397
398 if (!stringp(str)) {
399 set_this_object(previous_object());
400 raise_error(sprintf(
401 "Invalid argument 1 to old_explode()! Expected <string>, got: "
402 "%.30O\n",str));
403 }
404 if (!stringp(del)) {
405 set_this_object(previous_object());
406 raise_error(sprintf(
407 "Invalid argument 2 to old_explode()! Expected <string>, got: "
408 "%.30O\n",del));
409 }
410 if(del == "")
411 return ({str});
412 strs=efun::explode(str, del);
413 t=sizeof(strs)-1;
414 while(s<=t && strs[s++] == "");s--;
415 while(t>=0 && strs[t--] == "");t++;
416 if(s<=t)
417 return strs[s..t];
418 return ({});
419}
420
421int file_time(string path) {
422 mixed *v;
423
424 set_this_object(previous_object());
425 if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
426 return(0); //sonst
427}
428
MG Mud User88f12472016-06-24 23:31:02 +0200429// * Magier-Level abfragen
Zesstra39d284a2020-08-30 23:55:47 +0200430int query_wiz_level(object|string player) {
Zesstra4dbb9882019-11-26 21:26:36 +0100431 return "/secure/master"->query_wiz_level(player);
MG Mud User88f12472016-06-24 23:31:02 +0200432}
433
MG Mud User88f12472016-06-24 23:31:02 +0200434// * German version of ctime()
435#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
436#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
437 "Sep","Okt","Nov","Dez"})
438string dtime(int wann) {
439
440 if (wann == dtime_cache[0])
441 return(dtime_cache[1]);
442
443 int *lt = localtime(wann);
444 return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
445 TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]],
446 lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
447}
448
449// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
450// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
451// ein ordentliches Datum/Uhrzeit.
452#if !__EFUN_DEFINED__(strftime)
453varargs string strftime(mixed fmt, int clock, int localized) {
454 if (intp(clock) && clock >= 0)
455 return dtime(clock);
456 else if (intp(fmt) && fmt >= 0)
457 return dtime(fmt);
458
459 return dtime(time());
460}
461#endif //!__EFUN_DEFINED__(strftime)
462
463// * Shutdown mit zusaetzlichem logging
464nomask int shutdown(string reason)
465{
466 string name;
467 string obname;
468 string output;
469
470 if (!reason)
471 return 0;
472 if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
473 object_name(previous_object())!="/obj/shut" )
474 {
475 write("You have no permission to shut down the gamedriver!\n");
476 return 0;
477 }
478 if ((this_interactive())&&(name=getuid(this_interactive())))
479 {
480 name=capitalize(name);
481 filter(users(),#'tell_object,//'
482 capitalize(name)+" faehrt das Spiel herunter!\n");
483 }
484 else
485 name="ANONYMOUS";
486 if (previous_object()) obname=capitalize(getuid(previous_object()));
487 output=name;
488 if (obname && name!=obname) output=output+" ("+obname+")";
489 if (previous_object()&&object_name(previous_object())=="/obj/shut"){
490 output+=" faehrt das Spiel via Armageddon herunter.\n";
491 output=dtime(time())+": "+output;
492 log_file("GAME_LOG",output+"\n",-1);
493 efun::shutdown();
494 return 1;
495 }
496 output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
497 output+=" Grund: "+reason;
498 log_file("GAME_LOG",output+"\n",-1);
499 efun::shutdown();
500 return 1;
501}
502
503// * lowerchar
504
505int lowerchar(int char) {
506 if (char<'A' || char>'Z') return char;
507 return char+32;
508}
509
510// * upperstring
511
512string upperstring(string s)
513{
514#if __EFUN_DEFINED__(upper_case)
515 return(upper_case(s));
516#else
517 int i;
518 if (!stringp(s)) return 0;
519 for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
520 return s;
521#endif
522}
523
524// * lowerstring
525
526string lowerstring(string s)
527{
528 if (!stringp(s)) return 0;
529 return lower_case(s);
530}
531
532
533// * GD version
534string version()
535{
536 return __VERSION__;
537}
538
539// * break_string
540// stretch() -- stretch a line to fill a given width
541private string stretch(string s, int width) {
542 int len=sizeof(s);
543 if (len==width) return s;
544
545 // reine Leerzeilen, direkt zurueckgeben
546 string trimmed=trim(s,TRIM_LEFT," ");
547 if (trimmed=="") return s;
548 int start_spaces = len - sizeof(trimmed);
549
550 string* words = explode(trimmed, " ");
551 // der letzte kriegt keine Spaces
552 int word_count=sizeof(words) - 1;
553 // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
554 if (!word_count)
555 return " "*start_spaces + words[0];
556
557 int space_count = width - len;
558
559 int space_per_word=(word_count+space_count) / word_count;
560 // Anz.Woerter mit Zusatz-Space
561 int rest=(word_count+space_count) % word_count;
562 // Rest-Spaces Verteilen
563 foreach (int pos : rest) words[pos]+=" ";
564 return (" "*start_spaces) + implode( words, " "*space_per_word );
565}
566
567// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
568// eigene Funktion bekommen:
569private varargs string block_string(string s, int width, int flags) {
570 // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
571 // einem Leerzeichen begonnen.
572 // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
573 // ersetzt.
574 if ( (flags & BS_LEAVE_MY_LFS)
575 && !(flags & BS_NO_PARINDENT))
576 {
577 s = " "+regreplace(s,"\n","\n ",1);
578 }
579
580 // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
581 // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
582 // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
583 // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
584 // wieder rueckgaengig zu machen.
585 s = sprintf( "%-*=s", width, s);
586
587 string *tmp=explode(s, "\n");
588 // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
589 // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
590 // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
591 // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
592 // Ende der letzten Zeile hat), das macht der Aufrufer...
593 if (sizeof(tmp) > 1)
594 return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" )
595 + "\n" + tmp[<1];
596
597 return s;
598}
599
600public varargs string break_string(string s, int w, mixed indent, int flags)
601{
602 if ( !s || s == "" ) return "";
603
604 if ( !w ) w=78;
605
606 if( intp(indent) )
607 indent = indent ? " "*indent : "";
608
609 int indentlen=stringp(indent) ? sizeof(indent) : 0;
610
611 if (indentlen>w) {
612 set_this_object(previous_object());
613 raise_error(sprintf("break_string: indent longer %d than width %d\n",
614 indentlen,w));
615 // w=((indentlen/w)+1)*w;
616 }
617
618 if (!(flags & BS_LEAVE_MY_LFS))
619 s=regreplace( s, "\n", " ", 1 );
620
621 if ( flags & BS_SINGLE_SPACE )
622 s = regreplace( s, "(^|\n| ) *", "\\1", 1 );
623
624 string prefix="";
625 if (indentlen && flags & BS_PREPEND_INDENT) {
626 if (indentlen+sizeof(s) > w ||
627 (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
628 prefix=indent+"\n";
629 indent=(flags & BS_NO_PARINDENT) ? "" : " ";
630 indentlen=sizeof(indent);
631 }
632 }
633
634 if ( flags & BS_BLOCK ) {
635 /*
636 s = implode( map( explode( s, "\n" ),
637 #'block_string, w, indentlen, flags),
638 "" );
639 */
640 s = block_string( s , w - indentlen, flags );
641 }
642 else {
643 s = sprintf("%-1.*=s",w-indentlen,s);
644 }
645 if ( s[<1] != '\n' ) s += "\n";
646
647 if ( !indentlen ) return prefix + s;
648
649 string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
650
651 return prefix + indent +
652 regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
653 /*
654 string *buf;
655
656 buf = explode( s, "\n" );
657 return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
658 */
659}
660
661// * Elemente aus mapping loeschen - mapping vorher kopieren
662
663mapping m_copy_delete(mapping m, mixed key) {
664 return m_delete(copy(m), key);
665}
666
667// * times
668int last_reboot_time()
669{
670 return __BOOT_TIME__;
671}
672
673int first_boot_time()
674{
675 return 701517600;
676}
677
678int exist_days()
679{
680 return (((time()-first_boot_time())/8640)+5)/10;
681}
682
683// * uptime :)
684string uptime()
685{
686 int t;
687 int tmp;
688 string s;
689
690 t=time()-__BOOT_TIME__;
691 s="";
692 if (t>=86400)
693 s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
694 if (t>=3600)
695 s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
696 if (t>60)
697 s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
698 return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
699}
700
701// * Was tun bei 'dangling' lfun-closures ?
702void dangling_lfun_closure() {
703 raise_error("dangling lfun closure\n");
704}
705
706// * Sperren ausser fuer master/simul_efun
707
708#if __EFUN_DEFINED__(set_environment)
709nomask void set_environment(object o1, object o2) {
710 raise_error("Available only for root\n");
711}
712#endif
713
714nomask void set_this_player(object pl) {
715 raise_error("Available only for root\n");
716}
717
MG Mud User88f12472016-06-24 23:31:02 +0200718// * Jetzt auch closures
719int process_flag;
720
721public nomask int process_call()
722{
723 if (process_flag>0)
724 return process_flag;
725 else return(0);
726}
727
Zesstraddddbf72021-05-14 16:52:16 +0200728private nomask string _process_string(string str, object|lwobject po)
729{
730 set_this_object(po);
731 return(efun::process_string(str));
MG Mud User88f12472016-06-24 23:31:02 +0200732}
733
Zesstra16158af2020-09-01 00:07:59 +0200734private nomask int _illegal_ps_call(string s)
735{
736 int i_arg = strstr(s,"|"); // erst arg-trenner suchen
737 // dann ggf. von dort rueckwaerts den obj-trenner suchen
738 int i_ob = i_arg!=-1 ? strrstr(s, ":", i_arg)
739 : strstr(s,":");
740 // Wenn kein ":" vorkommt, ists max. ein objektinterner Call und erlaubt
741 if (i_ob == -1)
742 return 0;
743 string obname = (i_arg != -1) ? s[i_ob+1..i_arg-1]
744 : s[i_ob+1..];
745 // Wenn es das nicht gibt, ist es auch OK - process_string laedt keine
746 // nicht-geladenen Objekte. Das hier ist auch der Fall, wenn in
747 // dem Substring nen : vorkommt, es aber gar kein process_call ist...
748 object ob = find_object(obname);
749 if (!ob)
750 return 0;
751 // Es gibt ein Objekt. Jetzt wird es spannend. Erlaubt sind calls zwischen
Zesstra2ad183a2020-09-03 20:05:18 +0200752 // Objekten, welche dieselbe UID haben oder vom gleichen Magier stammen.
753 if (getuid(ob) == getuid(previous_object()) ||
754 REAL_UID(ob) == REAL_UID(previous_object()))
Zesstra16158af2020-09-01 00:07:59 +0200755 return 0;
756 // Alles andere ist nicht erlaubt
757 return 1;
758}
759
Zesstra739c58a2020-08-30 23:45:26 +0200760nomask string process_string( string|closure str )
MG Mud User88f12472016-06-24 23:31:02 +0200761{
762 string tmp, err;
Zesstrac5af6a72020-09-03 20:03:06 +0200763 int flag;
764
765 // Hmpf, es wird tatsaechlich reihenweise mit 0 gerufen.
766 if (!str)
767 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200768
Zesstra739c58a2020-08-30 23:45:26 +0200769 // process_string() wird nur noch ausgewertet, wenn der Aufrufer einen
Zesstra16158af2020-09-01 00:07:59 +0200770 // Level von maximal 30 hat. Das schliesst alle Objekten in /d/, /p/ und den
Zesstra739c58a2020-08-30 23:45:26 +0200771 // Gilden ein, aber verhindert es fuer alle hochstufigen Magier und ihre
772 // Objekte. Ausserdem erlauben wir keine Auswertung mehr fuer
773 // Spielerobjekte, wenn sie mehr als Seher sind.
Zesstra16158af2020-09-01 00:07:59 +0200774 // TODO: aus Spielershells ausbauen
Zesstra739c58a2020-08-30 23:45:26 +0200775 // TODO 2: ganz ausbauen.
776 if ( (query_once_interactive(previous_object())
777 && query_wiz_level(previous_object()) > SEER_LVL
778 )
Zesstrac8030682020-08-31 20:50:08 +0200779 || query_wiz_level(getuid(previous_object())) > SPECIAL_LVL)
Zesstra739c58a2020-08-30 23:45:26 +0200780 {
Zesstra86e26da2020-08-31 21:13:17 +0200781 // Ein Fehler wird aber nur ausgeloest, falls der String ein @@ enthaelt,
Zesstra284a35f2020-08-31 21:11:04 +0200782 // ansonsten koennen wir den ohne Fehler returnieren.
783 if (stringp(str) && strstr(str, "@@") == -1)
784 return str;
785 else
786 {
787 set_this_object(previous_object());
788 raise_error("Illegale Benutzung von process_string(). Aufrufer "
789 "ist Magiershell oder Objekt mit Level > 30.\n");
790 }
Zesstra739c58a2020-08-30 23:45:26 +0200791 }
Zesstra16158af2020-09-01 00:07:59 +0200792 // Kein Aufruf von Funktionen in Objekten anderer Magier erlaubt.
Zesstra98c5d582020-09-03 21:36:19 +0200793 if (stringp(str))
Zesstra16158af2020-09-01 00:07:59 +0200794 {
Zesstra98c5d582020-09-03 21:36:19 +0200795 foreach(string s: explode(str, "@@"))
Zesstra16158af2020-09-01 00:07:59 +0200796 {
Zesstra98c5d582020-09-03 21:36:19 +0200797 if (sizeof(s) && _illegal_ps_call(s))
798 {
799 set_this_object(previous_object());
800 raise_error("Illegale Benutzung von process_string(). Aufruf in "
801 "in fremder UID nicht erlaubt.\n");
802 }
Zesstra16158af2020-09-01 00:07:59 +0200803 }
804 }
Zesstra98c5d582020-09-03 21:36:19 +0200805 else if ( closurep(str) ) {
MG Mud User88f12472016-06-24 23:31:02 +0200806 set_this_object( previous_object() );
807 return funcall(str);
808 }
MG Mud User88f12472016-06-24 23:31:02 +0200809
810 if ( !(flag = process_flag > time() - 60))
811 process_flag=time();
812
813 err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
814
815 if ( !flag )
816 process_flag=0;
817
818 if (err) {
819 // Verarbeitung abbrechen
820 set_this_object(previous_object());
821 raise_error(err);
822 }
823 return tmp;
824}
825
826// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
827// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
828// zurueckgeliefert, sonst 0.
829// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
830public int mkdirp(string dir) {
831 // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
832 if (strstr(dir, "/") != 0)
833 raise_error("mkdirp(): Pfad ist nicht absolute.\n");
834 // cut off trailing /...
835 if (dir[<1]=='/')
836 dir = dir[0..<2];
837
838 int fstat = file_size(dir);
839 // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
840 if (fstat == FSIZE_DIR)
841 return 1;
842 // wenn schon ne Datei existiert, geht es nicht.
843 if (fstat != FSIZE_NOFILE)
844 return 0;
845 // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
846 // toplevel-verzeichnis, was direkt angelegt wird.
847 if (strrstr(dir,"/")==0) {
848 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
849 }
850
851 // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
852 // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
853 // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
854
855 // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
856 // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
857 if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
858 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
Zesstraa25c9c12018-11-17 00:09:25 +0100859 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200860}
861
862
863// * Properties ggfs. mitspeichern
Zesstraddddbf72021-05-14 16:52:16 +0200864mixed save_object(string|int name)
MG Mud User88f12472016-06-24 23:31:02 +0200865{
866 mapping properties;
867 mapping save;
868 mixed index, res;
869 int i;
Zesstraa012d622017-01-29 20:38:48 +0100870 string oldpath;
MG Mud User88f12472016-06-24 23:31:02 +0200871
872 // nur Strings und 0 zulassen
Zesstraddddbf72021-05-14 16:52:16 +0200873 if ((!stringp(name) || !sizeof(name)) &&
874 (!intp(name) || name!=0))
875 {
MG Mud User88f12472016-06-24 23:31:02 +0200876 set_this_object(previous_object());
877 raise_error(sprintf(
878 "Only non-empty strings and 0 may be used as filename in "
879 "sefun::save_object()! Argument was %O\n",name));
880 }
Zesstraddddbf72021-05-14 16:52:16 +0200881 if(!objectp(previous_object()))
882 {
883 set_this_object(previous_object());
884 raise_error(sprintf("save_object() only calleable by objects!\n"));
885 }
MG Mud User88f12472016-06-24 23:31:02 +0200886
Zesstraa012d622017-01-29 20:38:48 +0100887 if (stringp(name)) {
888 // abs. Pfad erzeugen. *seufz*
889 if (name[0]!='/')
890 name = "/" + name;
Zesstraa012d622017-01-29 20:38:48 +0100891 // automatisch in LIBDATADIR speichern
892 if (strstr(name,"/"LIBDATADIR"/") != 0) {
Zesstra60aed3e2017-01-31 16:03:51 +0100893 oldpath = name;
Zesstraa012d622017-01-29 20:38:48 +0100894 name = "/"LIBDATADIR + name;
895 // wenn das Verzeichnis nicht existiert, ggf. anlegen
896 string dir = name[0..strrstr(name,"/")-1];
897 if (file_size(dir) != FSIZE_DIR) {
898 if (mkdirp(dir) != 1)
899 raise_error("save_object(): kann Verzeichnis " + dir
900 + " nicht anlegen!");
901 }
902 }
903 }
904
MG Mud User88f12472016-06-24 23:31:02 +0200905 save = m_allocate(0, 2);
Zesstra4dbb9882019-11-26 21:26:36 +0100906 properties = previous_object()->QueryProperties();
MG Mud User88f12472016-06-24 23:31:02 +0200907
908 if(mappingp(properties))
909 {
910 // delete all entries in mapping properties without SAVE flag!
911 index = m_indices(properties);
912 for(i = sizeof(index)-1; i>=0;i--)
913 {
914 if(properties[index[i], F_MODE] & SAVE)
915 {
916 save[index[i]] = properties[index[i]];
917 save[index[i], F_MODE] =
918 properties[index[i], F_MODE] &
919 (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
920 }
921 }
922 }
923 else save = ([]);
924
925 // save object!
926 previous_object()->_set_save_data(save);
927 // format: wie definiert in config.h
Zesstraa012d622017-01-29 20:38:48 +0100928 if (stringp(name)) {
MG Mud User88f12472016-06-24 23:31:02 +0200929 res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
930 __LIB__SAVE_FORMAT_VERSION__);
Zesstraa012d622017-01-29 20:38:48 +0100931 // wenn erfolgreich und noch nen Savefile existiert, was nicht unter
932 // /data/ liegt, wird das geloescht.
933 if (!res && oldpath
934 && file_size(oldpath+".o") >= 0)
935 rm(oldpath+".o");
936 }
MG Mud User88f12472016-06-24 23:31:02 +0200937 else
938 res = funcall(bind_lambda(#'efun::save_object, previous_object()),
939 __LIB__SAVE_FORMAT_VERSION__);
Zesstraa012d622017-01-29 20:38:48 +0100940
MG Mud User88f12472016-06-24 23:31:02 +0200941 previous_object()->_set_save_data(0);
942
943#ifdef IOSTATS
944 // Stats...
945 struct iostat_s stat = (<iostat_s>);
946 stat->oname = object_name(previous_object());
947 stat->time = time();
948 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
949 // OIM_TOTAL_DATA_SIZE);
950 if (stringp(name))
951 stat->size = file_size(name + ".o");
952 else
953 stat->sizeof(res);
954 //debug_message("saveo: "+saveo_stat[0]+"\n");
955 saveo_stat[1][saveo_stat[0]] = stat;
956 saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
957 //debug_message("saveo 2: "+saveo_stat[0]+"\n");
958#endif
959
960 return res;
961}
962
963// * Auch Properties laden
964int restore_object(string name)
965{
966 int result;
967 mixed index;
968 mixed save;
969 mapping properties;
970 int i;
MG Mud User88f12472016-06-24 23:31:02 +0200971
Zesstra94381a42017-01-29 22:37:52 +0100972 if (sizeof(name) < 1)
973 {
974 set_this_object(previous_object());
975 raise_error("Bad arg 1 to restore_object(): expected non-empty "
976 "'string'.\n");
977 }
Zesstraddddbf72021-05-14 16:52:16 +0200978 if(!objectp(previous_object()))
979 {
980 set_this_object(previous_object());
981 raise_error(sprintf("restore_object() only calleable by objects!\n"));
982 }
Zesstra94381a42017-01-29 22:37:52 +0100983
Zesstraa012d622017-01-29 20:38:48 +0100984 // Wenn name vermutlich ein Pfad (also nicht mit #x:y anfaengt)
985 if (name[0] != '#')
986 {
987 // abs. Pfad erzeugen *seufz*
Zesstra984bde92017-01-29 22:19:52 +0100988 if (name[0]!='/')
Zesstraa012d622017-01-29 20:38:48 +0100989 name = "/" + name;
990
Zesstraf2a0d492017-04-29 11:05:46 +0200991 // .c am Ende loeschen, sonst wird das File ggf. nicht gefunden.
992 if(name[<2..]==".c")
993 name=name[..<3];
994
Zesstraa012d622017-01-29 20:38:48 +0100995 // wenn kein /data/ vorn steht, erstmal gucken, ob das Savefile unter
996 // /data/ existiert. Wenn ja, wird das geladen.
Zesstra984bde92017-01-29 22:19:52 +0100997 if (strstr(name,"/"LIBDATADIR"/") != 0)
Zesstraa012d622017-01-29 20:38:48 +0100998 {
999 string path = "/"LIBDATADIR + name;
1000 if (file_size(path + ".o") >= 0)
1001 name = path;
1002 }
1003 }
1004
MG Mud User88f12472016-06-24 23:31:02 +02001005 // get actual property settings (by create())
Zesstra4dbb9882019-11-26 21:26:36 +01001006 properties = previous_object()->QueryProperties();
MG Mud User88f12472016-06-24 23:31:02 +02001007
1008// DEBUG(sprintf("RESTORE %O\n",name));
1009 // restore object
1010 result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
1011 //'))
1012 //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein
1013 //mapping sein sollte.
Zesstra4dbb9882019-11-26 21:26:36 +01001014 save = previous_object()->_get_save_data();
1015 if((save))
MG Mud User88f12472016-06-24 23:31:02 +02001016 {
1017 index = m_indices(save);
1018 for(i = sizeof(index)-1; i>=0; i--)
1019 {
1020 properties[index[i]] = save[index[i]];
1021 properties[index[i], F_MODE] = save[index[i], F_MODE]
1022 &~(SETCACHED|QUERYCACHED);
1023 }
1024 }
1025 else properties = ([]);
1026
1027 // restore properties
1028 funcall(
1029 bind_lambda(
1030 unbound_lambda(({'arg}), //'})
1031 ({#'call_other,({#'this_object}),
1032 "SetProperties",'arg})),//')
1033 previous_object()),properties);
1034 previous_object()->_set_save_data(0);
1035
1036#ifdef IOSTATS
1037 // Stats...
1038 //debug_message("restoreo: "+restoreo_stat[0]+"\n");
1039 struct iostat_s stat = (<iostat_s>);
1040 stat->oname = object_name(previous_object());
1041 stat->time = time();
1042 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
1043 // OIM_TOTAL_DATA_SIZE);
1044 stat->size = file_size(name + ".o");
1045 restoreo_stat[1][restoreo_stat[0]] = stat;
1046
1047 restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
1048#endif
1049
1050 return result;
1051}
1052
1053// * HB eines Objektes ein/ausschalten
1054int set_object_heart_beat(object ob, int flag)
1055{
1056 if (objectp(ob))
1057 return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
Zesstra738a1ef2021-05-14 17:01:19 +02001058 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02001059}
1060
1061// * Magierlevelgruppen ermitteln
Zesstra39d284a2020-08-30 23:55:47 +02001062int query_wiz_grp(object|string wiz)
MG Mud User88f12472016-06-24 23:31:02 +02001063{
1064 int lev;
1065
1066 lev=query_wiz_level(wiz);
1067 if (lev<SEER_LVL) return 0;
1068 if (lev>=GOD_LVL) return lev;
1069 if (lev>=ARCH_LVL) return ARCH_GRP;
1070 if (lev>=ELDER_LVL) return ELDER_GRP;
1071 if (lev>=LORD_LVL) return LORD_GRP;
1072 if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
1073 if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
1074 if (lev>=WIZARD_LVL) return WIZARD_GRP;
1075 if (lev>=LEARNER_LVL) return LEARNER_GRP;
1076 return SEER_GRP;
1077}
1078
1079mixed *wizlist_info()
1080{
1081 if (ARCH_SECURITY || !extern_call())
1082 return efun::wizlist_info();
1083 return 0;
1084}
1085
1086// * wizlist ausgeben
1087varargs void wizlist(string name, int sortkey ) {
1088
1089 if (!name)
1090 {
1091 if (this_player())
1092 name = getuid(this_player());
1093 if (!name)
1094 return;
1095 }
1096
1097 // Schluessel darf nur in einem gueltigen Bereich sein
1098 if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
1099
1100 mixed** wl = efun::wizlist_info();
1101 // nach <sortkey> sortieren
1102 wl = sort_array(wl, function int (mixed a, mixed b)
1103 {return a[sortkey] < b[sortkey]; } );
1104
1105 // Summe ueber alle Kommandos ermitteln.
1106 int total_cmd, i;
1107 int pos=-1;
1108 foreach(mixed entry : wl)
1109 {
1110 total_cmd += entry[WL_COMMANDS];
1111 if (entry[WL_NAME] == name)
1112 pos = i;
1113 ++i;
1114 }
1115
1116 if (pos < 0 && name != "ALL" && name != "TOP100")
1117 return;
1118
1119 if (name == "TOP100")
1120 {
1121 if (sizeof(wl) > 100)
1122 wl = wl[0..100];
1123 else
1124 wl = wl;
1125 }
1126 // um name herum schneiden
1127 else if (name != "ALL")
1128 {
1129 if (sizeof(wl) <= 21)
1130 wl = wl;
1131 else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
1132 wl = wl[pos-10..pos+10];
1133 else if (pos <=21)
1134 wl = wl[0..20];
1135 else if (pos >= sizeof(wl) - 21)
1136 wl = wl[<21..];
1137 else
1138 wl = wl;
1139 }
1140
1141 write("\nWizard top score list\n\n");
1142 if (total_cmd == 0)
1143 total_cmd = 1;
1144 printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
1145 "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
1146 foreach(mixed e: wl)
1147 {
1148 printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
1149 e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
1150 e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
1151 e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
1152 );
1153 }
1154 printf("\nTotal %7d (%d)\n\n", total_cmd, sizeof(wl));
1155}
1156
1157
1158// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
1159
1160// ermittelt das Objekt des Callouts.
1161private object _call_out_obj( mixed call_out ) {
1162 return pointerp(call_out) ? call_out[0] : 0;
1163}
1164
1165private void _same_object( object ob, mapping m ) {
1166 // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
1167 if ( m[ob] )
1168 m[ob] += ({ ob });
1169 else
1170 m[ob] = ({ ob });
1171}
1172
1173// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
1174// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
1175// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
1176private void _same_path( object key, object *obs, mapping m ) {
1177 string path;
1178 if (!objectp(key) || !pointerp(obs)) return;
1179
1180 path = load_name(key);
1181
1182 if ( m[path] )
1183 m[path] += obs;
1184 else
1185 m[path] = obs;
1186}
1187
1188// key kann object oder string sein.
1189private int _too_many( mixed key, mapping m, int i ) {
1190 return sizeof(m[key]) >= i;
1191}
1192
1193// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
1194// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
1195// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
1196private void _destroy( mixed key, object *obs, string text, int uid ) {
1197 if (!pointerp(obs)) return;
1198 // Array mit unique Eintraege erzeugen.
1199 obs = m_indices( mkmapping(obs) );
1200 // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
1201 // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
1202 // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
1203 // laeuft.
1204 catch( efun::raise_error(
1205 sprintf( text,
Zesstra4dbb9882019-11-26 21:26:36 +01001206 uid ? master()->creator_file(key) : key,
MG Mud User88f12472016-06-24 23:31:02 +02001207 sizeof(obs), object_name(obs[<1]) ) );publish);
1208 // Und weg mit dem Kram...
1209 filter( obs, #'efun::destruct/*'*/ );
1210}
1211
1212// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
1213// sind dabei nicht unique.
1214private void _same_uid( string key, object *obs, mapping m, closure cf ) {
1215 string uid;
1216
1217 if ( !pointerp(obs) || !sizeof(obs) )
1218 return;
1219
1220 uid = funcall( cf, key );
1221
1222 if ( m[uid] )
1223 m[uid] += obs; // obs ist nen Array
1224 else
1225 m[uid] = obs;
1226}
1227
1228private int _log_call_out(mixed co)
1229{
1230 log_file("TOO_MANY_CALLOUTS",
1231 sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
1232 200000);
1233 return 0;
1234}
1235
1236private int last_callout_log=0;
1237
1238nomask varargs void call_out( varargs mixed *args )
1239{
Zesstra738a1ef2021-05-14 17:01:19 +02001240 mixed *call_outs;
MG Mud User88f12472016-06-24 23:31:02 +02001241
1242 // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
1243 // haben.
1244 if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
1245 && geteuid(previous_object()) != ROOTID )
1246 {
1247 // Log erzeugen...
1248 if (last_callout_log <
1249 efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
1250 && get_eval_cost() > 200000)
1251 {
1252 last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1253 log_file("TOO_MANY_CALLOUTS",
1254 sprintf("\n%s: ############ Too many callouts: %d ##############\n",
1255 strftime("%y%m%d-%H%M%S"),
1256 efun::driver_info(DI_NUM_CALLOUTS)));
1257 filter(efun::call_out_info(), #'_log_call_out);
1258 }
1259 // Objekte aller Callouts ermitteln
1260 call_outs = map( efun::call_out_info(), #'_call_out_obj );
1261 mapping objectmap = ([]);
1262 filter( call_outs, #'_same_object, &objectmap );
1263 // Master nicht grillen...
1264 efun::m_delete( objectmap, master(1) );
1265 // alle Objekte raussuchen, die zuviele haben...
1266 mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
1267 // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
1268 // Objekten uebergeben (in diesem Fall sind Keys und Array mit
1269 // Objekten jeweils das gleiche Objekt).
1270 if ( sizeof(res) )
1271 walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "
1272 "object [%O]. Destructed %d objects. [%s]\n", 0 );
1273
1274 // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
1275 // Objekte killen, die mehr als 50 Callouts laufen haben - und
1276 // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
1277 // Clones einer BP gekillt, die Callouts laufen haben, falls alle
1278 // diese Objekte _zusammen_ mehr als 50 Callouts haben!
1279 if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
1280 // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
1281 // mehr drin, da sie dort als Keys verwendet wurden.
1282 mapping pathmap=([]);
1283 // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
1284 // von Objekten als Werte.
1285 walk_mapping( objectmap, #'_same_path, &pathmap);
1286 // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
1287 res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
1288 // und ueber alle Keys gehen, an _destroy() werden die Clones
1289 // uebergeben, die Callouts haben.
1290 if ( sizeof(res) )
1291 walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
1292 "'%s'. Destructed %d objects. [%s]\n", 0 );
1293
1294 // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
1295 // noch >1000 Callouts laufen, werden diesmal alle Callouts
1296 // einer UID zusammengezaehlt.
1297 // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
1298 // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
1299 if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
1300 {
1301 // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
1302 // zusammensortieren. Zerstoerte Clones filter _same_uid()
1303 // raus.
1304 mapping uidmap=([]);
1305 walk_mapping( pathmap, #'_same_uid, &uidmap,
1306 symbol_function( "creator_file",
1307 "/secure/master" ) );
1308 // In res nun UIDs als Keys und Arrays von Objekten als Werte.
1309 // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
1310 // 100 Callouts!) haben.
1311 res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
1312 // und erneut ueber die Keys laufen und jeweils die Arrays mit
1313 // den Objekten zur Zerstoerung an _destroy()...
1314 if ( sizeof(res) )
1315 walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
1316 "UID '%s'. Destructed %d objects. [%s]\n",
1317 1 );
1318 }
1319 }
1320 }
1321
1322 // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
1323 if ( !previous_object() )
1324 return;
1325
1326 set_this_object( previous_object() );
1327 apply( #'efun::call_out, args );
1328 return;
1329}
1330
1331mixed call_out_info() {
1332
Zesstraddddbf72021-05-14 16:52:16 +02001333 object|lwobject po = previous_object();
MG Mud User88f12472016-06-24 23:31:02 +02001334 mixed coi = efun::call_out_info();
1335
1336 // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
1337 // /obj haben die BackboneID.
1338 if (query_wiz_level(getuid(po)) >= ARCH_LVL
Zesstra4dbb9882019-11-26 21:26:36 +01001339 || master()->creator_file(load_name(po)) == BACKBONEID ) {
MG Mud User88f12472016-06-24 23:31:02 +02001340 return coi;
1341 }
1342 else {
1343 return filter(coi, function mixed (mixed arr) {
1344 if (pointerp(arr) && arr[0]==po)
1345 return 1;
1346 else return 0; });
1347 }
1348}
1349
1350// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
1351// NICHT das Objekt, was ggf. die lfun definiert!
1352mixed query_closure_object(closure c) {
1353 return
1354 CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
1355 0 :
1356 (to_object(c) || -1);
1357}
1358
1359// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
1360varargs void move_object(mixed what, mixed where)
1361{
Zesstraddddbf72021-05-14 16:52:16 +02001362 // Wenn nur ein Argument angegeben wird, ist es das Ziel und wir nehmen
1363 // previous_object() als zu bewegendes Objekt.
MG Mud User88f12472016-06-24 23:31:02 +02001364 if (!where)
1365 {
1366 where=what;
Zesstraddddbf72021-05-14 16:52:16 +02001367 what=previous_object();
MG Mud User88f12472016-06-24 23:31:02 +02001368 }
1369 if (((stringp(where) && where==NETDEAD_ROOM ) ||
1370 (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
1371 objectp(what) && object_name(what)!="/obj/sperrer")
1372 {
1373 if (!query_once_interactive(what))
1374 {
1375 what->remove();
1376 if (what) destruct(what);
1377 return;
1378 }
1379 if (living(what) || interactive(what))
1380 {
1381 log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
1382 return;
1383 }
1384 set_object_heart_beat(what,0);
1385 }
Zesstraddddbf72021-05-14 16:52:16 +02001386 object tmp=what;
MG Mud User88f12472016-06-24 23:31:02 +02001387 while (tmp=environment(tmp))
1388 // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
1389 // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
1390 // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
1391 // da um jedes bisschen Rechenzeit geht.
1392 // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
1393 //
1394 // Tiamak
1395 tmp->_set_last_content_change();
Zesstraddddbf72021-05-14 16:52:16 +02001396 funcall(bind_lambda(#'efun::move_object,previous_object()),what,where);
MG Mud User88f12472016-06-24 23:31:02 +02001397 if (tmp=what)
1398 while (tmp=environment(tmp))
1399 tmp->_set_last_content_change();
1400}
1401
1402
1403void start_simul_efun() {
1404 mixed *info;
1405
1406 // Falls noch nicht getan, extra_wizinfo initialisieren
1407 if ( !pointerp(info = get_extra_wizinfo(0)) )
1408 set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
1409
1410 InitLivingData(info);
1411
1412 set_next_reset(10); // direkt mal aufraeumen
1413}
1414
1415protected void reset() {
1416 set_next_reset(7200);
1417 CleanLivingData();
1418}
1419
1420#if !__EFUN_DEFINED__(absolute_hb_count)
1421int absolute_hb_count() {
1422 return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1423}
1424#endif
1425
1426void __set_environment(object ob, mixed target)
1427{
1428 string path;
1429 object obj;
1430
1431 if (!objectp(ob))
1432 return;
1433 if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
1434 return;
1435 if (objectp(target))
1436 {
1437 efun::set_environment(ob,target);
1438 return;
1439 }
Zesstra4dbb9882019-11-26 21:26:36 +01001440 path=master()->make_path_absolute(target);
MG Mud User88f12472016-06-24 23:31:02 +02001441 if (stringp(path) && file_size(path+".c")>=0 &&
1442 !catch(load_object(path);publish) )
1443 {
1444 obj=find_object(path);
1445 efun::set_environment(ob,obj);
1446 return;
1447 }
1448}
1449
1450void _dump_wizlist(string file, int sortby) {
1451 int i;
1452 mixed *a;
1453
1454 if (!LORD_SECURITY)
1455 return;
1456 if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
1457 {
1458 write("NO WRITE PERMISSION\n");
1459 return;
1460 }
1461 a = wizlist_info();
1462 a = sort_array(a, lambda( ({'a,'b}),
1463 ({#'<,
1464 ({#'[,'a,sortby}),
1465 ({#'[,'b,sortby})
1466 })));
1467 rm(file);
1468 for (i=sizeof(a)-1;i>=0;i--)
1469 write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
1470 a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
1471 a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
1472}
1473
Zesstraddddbf72021-05-14 16:52:16 +02001474public object deep_present(string|object what, object ob=previous_object())
1475{
MG Mud User88f12472016-06-24 23:31:02 +02001476 // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
1477 // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
1478 if(objectp(what)) {
1479 object *envs=all_environment(what);
1480 // wenn ob kein Environment hat, ist es offensichtlich nicht in what
1481 // enthalten.
1482 if (!pointerp(envs)) return 0;
Zesstraddddbf72021-05-14 16:52:16 +02001483 if (ob in envs) return what;
MG Mud User88f12472016-06-24 23:31:02 +02001484 }
1485 // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
1486 // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
1487 // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
1488 else if (stringp(what)) {
1489 int cnt;
1490 string newwhat;
1491 if(sscanf(what,"%s %d",newwhat,cnt)!=2)
1492 cnt=1;
1493 else
1494 what=newwhat;
1495 foreach(object invob: deep_inventory(ob)) {
1496 if (invob->id(what) && !--cnt)
1497 return invob;
1498 }
1499 }
1500 else {
1501 set_this_object(previous_object());
1502 raise_error(sprintf("Wrong argument 1 to deep_present(). "
1503 "Expected \"object\" or \"string\", got %.50O.\n",
1504 what));
1505 }
1506 return 0;
1507}
1508
1509mapping dump_ip_mapping()
1510{
1511 return 0;
1512}
1513
1514nomask void swap(object obj)
1515{
1516 write("Your are not allowed to swap objects by hand!\n");
1517 return;
1518}
1519
1520nomask varargs void garbage_collection(string str)
1521{
1522 if(previous_object()==0 || !IS_ARCH(geteuid(previous_object()))
1523 || !ARCH_SECURITY)
1524 {
Zesstraddddbf72021-05-14 16:52:16 +02001525 // historical info, but amusing message. ;-)
MG Mud User88f12472016-06-24 23:31:02 +02001526 write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
1527 return;
1528 }
1529 else if (stringp(str))
1530 {
1531 return efun::garbage_collection(str);
1532 }
1533 else
1534 return efun::garbage_collection();
1535}
1536
Zesstrae490a532020-09-22 22:07:54 +02001537varargs void notify_fail(string|closure nf, int prio)
1538{
MG Mud User88f12472016-06-24 23:31:02 +02001539 int oldprio;
Zesstrae490a532020-09-22 22:07:54 +02001540
Zesstrad7795012020-09-22 22:13:31 +02001541 if (!PL || !previous_object())
Zesstrae490a532020-09-22 22:07:54 +02001542 return;
MG Mud User88f12472016-06-24 23:31:02 +02001543
1544 // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
1545 // und vergleichen.
Zesstrad7795012020-09-22 22:13:31 +02001546 object oldo = query_notify_fail(1);
1547 if (oldo && previous_object()!=oldo)
1548 {
1549 if (!prio)
1550 {
MG Mud User88f12472016-06-24 23:31:02 +02001551 //Prioritaet dieses notify_fail() 'abschaetzen'
Zesstrad7795012020-09-22 22:13:31 +02001552 if (previous_object()==PL) // Spieler-interne (soul-cmds)
MG Mud User88f12472016-06-24 23:31:02 +02001553 prio=NF_NL_OWN;
Zesstrad7795012020-09-22 22:13:31 +02001554 else if (living(previous_object()))
MG Mud User88f12472016-06-24 23:31:02 +02001555 prio=NF_NL_LIVING;
Zesstrad7795012020-09-22 22:13:31 +02001556 else if (previous_object()->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001557 prio=NF_NL_ROOM;
1558 else
1559 prio=NF_NL_THING;
1560 }
1561 //Prioritaet des alten Setzers abschaetzen
1562 if (oldo==PL)
1563 oldprio=NF_NL_OWN;
1564 else if (living(oldo))
1565 oldprio=NF_NL_LIVING;
Zesstra4dbb9882019-11-26 21:26:36 +01001566 else if (oldo->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001567 oldprio=NF_NL_ROOM;
1568 else
1569 oldprio=NF_NL_THING;
1570 }
1571 else // wenn es noch kein Notify_fail gibt:
1572 oldprio=NF_NL_NONE;
1573
Zesstrad7795012020-09-22 22:13:31 +02001574 // vergleichen und ggf. setzen
1575 if (prio >= oldprio)
1576 {
1577 set_this_object(previous_object());
MG Mud User88f12472016-06-24 23:31:02 +02001578 efun::notify_fail(nf);
1579 }
1580
1581 return;
1582}
1583
Zesstrae490a532020-09-22 22:07:54 +02001584void _notify_fail(string|closure str)
MG Mud User88f12472016-06-24 23:31:02 +02001585{
Zesstra351ca4d2020-09-22 22:17:24 +02001586 notify_fail(str, NF_NL_NONE);
MG Mud User88f12472016-06-24 23:31:02 +02001587}
1588
1589string time2string( string format, int zeit )
1590{
1591 int i,ch,maxunit,dummy;
1592 string *parts, fmt;
1593
1594 int secs = zeit;
1595 int mins = (zeit/60);
1596 int hours = (zeit/3600);
1597 int days = (zeit/86400);
1598 int weeks = (zeit/604800);
1599 int months = (zeit/2419200);
1600 int abbr = 0;
1601
1602 parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
1603
1604 for( i=1; i<sizeof(parts); i+=2 )
1605 {
1606 ch = parts[i][<1];
1607 switch( parts[i][<1] )
1608 {
1609 case 'x': case 'X':
1610 abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
1611 // NO break !
1612 case 'n': case 'N':
1613 maxunit |= 31;
1614 break;
1615 case 'w': case 'W':
1616 maxunit |= 15;
1617 break;
1618 case 'd': case 'D':
1619 maxunit |= 7;
1620 break;
1621 case 'h': case 'H':
1622 maxunit |= 3;
1623 break;
1624 case 'm': case 'M':
1625 maxunit |= 1;
1626 break;
1627 }
1628 }
1629 if( maxunit & 16 ) weeks %= 4;
1630 if( maxunit & 8 ) days %= 7;
1631 if( maxunit & 4 ) hours %= 24;
1632 if( maxunit & 2 ) mins %= 60;
1633 if( maxunit ) secs %= 60;
1634
1635 for( i=1; i<sizeof(parts); i+=2 )
1636 {
1637 fmt = parts[i][0..<2];
1638 ch = parts[i][<1];
1639 if( ch=='x' )
1640 {
1641 if (months > 0) ch='n';
1642 else if( weeks>0 ) ch='w';
1643 else if( days>0 ) ch='d';
1644 else if( hours>0 ) ch='h';
1645 else if(mins > 0) ch = 'm';
1646 else ch = 's';
1647 }
1648 else if( ch=='X' )
1649 {
1650 if (months > 0) ch='N';
1651 else if( weeks>0 ) ch='W';
1652 else if( days>0 ) ch='D';
1653 else if( hours>0 ) ch='H';
1654 else if(mins > 0) ch = 'M';
1655 else ch = 'S';
1656 }
1657
1658 switch( ch )
1659 {
1660 case 'n': parts[i] = sprintf( fmt+"d", months ); break;
1661 case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
1662 case 'd': parts[i] = sprintf( fmt+"d", days ); break;
1663 case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
1664 case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
1665 case 's': parts[i] = sprintf( fmt+"d", secs ); break;
1666 case 'N':
1667 if(abbr) parts[i] = "M";
1668 else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
1669 break;
1670 case 'W':
1671 if(abbr) parts[i] = "w"; else
1672 parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
1673 break;
1674 case 'D':
1675 if(abbr) parts[i] = "d"; else
1676 parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
1677 break;
1678 case 'H':
1679 if(abbr) parts[i] = "h"; else
1680 parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
1681 break;
1682 case 'M':
1683 if(abbr) parts[i] = "m"; else
1684 parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
1685 break;
1686 case 'S':
1687 if(abbr) parts[i] = "s"; else
1688 parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
1689 break;
1690 case '%':
1691 parts[i] = "%";
1692 break;
1693 }
1694 }
1695 return implode( parts, "" );
1696}
1697
1698nomask mixed __create_player_dummy(string name)
1699{
1700 string err;
1701 object ob;
1702 mixed m;
1703 //hat nen Scherzkeks die Blueprint bewegt?
1704 if ((ob=find_object("/secure/login")) && environment(ob))
1705 catch(destruct(ob);publish);
1706 err = catch(ob = clone_object("secure/login");publish);
1707 if (err)
1708 {
1709 write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
1710 return 0;
1711 }
Zesstra4dbb9882019-11-26 21:26:36 +01001712 if (objectp(m=ob->new_logon(name))) netdead[name]=m;
MG Mud User88f12472016-06-24 23:31:02 +02001713 return m;
1714}
1715
1716nomask int secure_level()
1717{
1718 int *level;
1719 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
1720 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
1721 //von 0.
1722 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
1723 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
1724 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
1725 //INteractive geben muss.
1726 level=map(caller_stack(1),function int (object caller)
1727 {if (objectp(caller))
1728 return(query_wiz_level(geteuid(caller)));
1729 return(0); // kein Objekt da, 0.
1730 } );
1731 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
1732}
1733
1734nomask string secure_euid()
1735{
1736 string euid;
1737
1738 if (!this_interactive()) // Es muss einen interactive geben
1739 return 0;
1740 euid=geteuid(this_interactive());
1741 // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
1742 // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
1743 // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
1744 foreach(object caller: caller_stack()) {
1745 if (!objectp(caller) ||
1746 (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
1747 return 0;
1748 }
1749 return euid; // 'sichere' euid zurueckgeben
1750}
1751
Zesstra1bd79662021-05-14 19:23:20 +02001752// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
1753// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
1754//#if __BOOT_TIME__ < 1360017213
1755varargs void input_to( mixed fun, int flags, varargs mixed *args )
1756{
1757 mixed *arr;
1758 int i;
1759
1760 if ( !this_player() || !previous_object() )
1761 return;
1762
1763 // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
1764 // vorkommt...
1765 if ( flags&INPUT_PROMPT ) {
1766 arr = ({ fun, flags }) + args;
1767 }
1768 else {
1769 // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
1770 flags |= INPUT_PROMPT;
1771 arr = ({ fun, flags, "" }) + args;
1772 }
1773
1774 // Arrays gegen flatten quoten.
1775 for ( i = sizeof(arr) - 1; i > 1; i-- )
1776 if ( pointerp(arr[i]) )
1777 arr[i] = quote(arr[i]);
1778
1779 apply( bind_lambda( unbound_lambda( ({}),
1780 ({ #'efun::input_to/*'*/ }) + arr ),
1781 previous_object() ) );
1782}
1783//#endif
1784
Zesstra42324bf2021-05-14 15:25:20 +02001785deprecated nomask int set_light(int i)
MG Mud User88f12472016-06-24 23:31:02 +02001786// erhoeht das Lichtlevel eines Objekts um i
1787// result: das Lichtlevel innerhalb des Objekts
1788{
Zesstra738a1ef2021-05-14 17:01:19 +02001789 object ob;
MG Mud User88f12472016-06-24 23:31:02 +02001790
1791 if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
1792
1793 // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
1794 if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
1795
1796 // Lichtberechnung findet eigentlich in der Mudlib statt.
Zesstra4dbb9882019-11-26 21:26:36 +01001797 return ob->QueryProp(P_INT_LIGHT);
MG Mud User88f12472016-06-24 23:31:02 +02001798}
1799
MG Mud User88f12472016-06-24 23:31:02 +02001800public varargs string CountUp( string *s, string sep, string lastsep )
1801{
1802 string ret;
1803
1804 if ( !pointerp(s) )
1805 return "";
1806
1807 if (!sep) sep = ", ";
1808 if (!lastsep) lastsep = " und ";
1809
1810 switch (sizeof(s)) {
1811 case 0: ret=""; break;
1812 case 1: ret=s[0]; break;
1813 default:
1814 ret = implode(s[0..<2], sep);
1815 ret += lastsep + s[<1];
1816 }
1817 return ret;
1818}
1819
Zesstraddddbf72021-05-14 16:52:16 +02001820nomask int query_next_reset(object ob=previous_object())
1821{
MG Mud User88f12472016-06-24 23:31:02 +02001822 // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
1823 if (ob && !objectp(ob))
1824 raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
1825 "expected object.\n",ob));
1826
MG Mud User88f12472016-06-24 23:31:02 +02001827 return efun::object_info(ob, OI_NEXT_RESET_TIME);
1828}
1829
1830
MG Mud User88f12472016-06-24 23:31:02 +02001831// ### Ersatzaufloesung in Strings ###
Arathorn066820b2019-11-27 19:47:19 +01001832varargs string replace_personal(string str, <string|object>* obs, int caps) {
1833 string* parts = regexplode(str, "@WE[A-SU]*[0-9]");
1834 int i = sizeof(parts);
MG Mud User88f12472016-06-24 23:31:02 +02001835
Zesstra19102132016-09-01 22:50:36 +02001836 if (i>1)
1837 {
MG Mud User88f12472016-06-24 23:31:02 +02001838 int j, t;
MG Mud User88f12472016-06-24 23:31:02 +02001839 t = j = sizeof(obs);
1840
Arathorn066820b2019-11-27 19:47:19 +01001841 <string|closure>* name_cls = allocate(j);
1842 while (j--) {
MG Mud User88f12472016-06-24 23:31:02 +02001843 if (objectp(obs[j]))
1844 name_cls[j] = symbol_function("name", obs[j]);
1845 else if (stringp(obs[j]))
1846 name_cls[j] = obs[j];
Arathorn066820b2019-11-27 19:47:19 +01001847 }
MG Mud User88f12472016-06-24 23:31:02 +02001848
Zesstra19102132016-09-01 22:50:36 +02001849 while ((i-= 2)>0)
1850 {
MG Mud User88f12472016-06-24 23:31:02 +02001851 // zu ersetzendes Token in Fall und Objektindex aufspalten
Arathorn066820b2019-11-27 19:47:19 +01001852 int ob_nr = parts[i][<1]-'1';
MG Mud User88f12472016-06-24 23:31:02 +02001853 if (ob_nr<0 || ob_nr>=t) {
1854 set_this_object(previous_object());
Zesstra738a1ef2021-05-14 17:01:19 +02001855 raise_error(sprintf(
1856 "replace_personal: using wrong object index %d\n", ob_nr));
MG Mud User88f12472016-06-24 23:31:02 +02001857 }
1858
1859 // casus kann man schon hier entscheiden
1860 int casus;
1861 string part = parts[i];
1862 switch (part[3]) {
1863 case 'R': casus = WER; break;
1864 case 'S': casus = WESSEN; break;
1865 case 'M': casus = WEM; break;
1866 case 'N': casus = WEN; break;
1867 default: continue; // passt schon jetzt nicht in das Hauptmuster
1868 }
1869
1870 // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
1871 mixed tmp;
1872 switch (part[3..<2]) {
1873 case "R": case "SSEN": case "M": case "N": // Name
1874 parts[i] = funcall(name_cls[ob_nr], casus, 1); break;
1875 case "RU": case "SSENU": case "MU": case "NU": // unbestimmt
1876 parts[i] = funcall(name_cls[ob_nr], casus); break;
1877 case "RQP": case "SSENQP": case "MQP": case "NQP": // Pronoun
1878 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001879 parts[i] = tmp->QueryPronoun(casus);
MG Mud User88f12472016-06-24 23:31:02 +02001880 break;
1881 case "RQA": case "SSENQA": case "MQA": case "NQA": // Article
1882 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001883 tmp = tmp->QueryArticle(casus, 1, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001884 if (stringp(tmp) && !(tmp[<1]^' '))
1885 tmp = tmp[0..<2]; // Extra-Space wieder loeschen
1886 break;
1887 case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
1888 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001889 parts[i] = tmp->QueryPossPronoun(MALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001890 break;
1891 case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
1892 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001893 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001894 break;
1895 case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
1896 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001897 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001898 break;
1899 case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
1900 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001901 parts[i] = tmp->QueryPossPronoun(MALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001902 break;
1903 case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
1904 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001905 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001906 break;
1907 case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
1908 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001909 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001910 break;
1911 default:
1912 continue;
1913 }
Zesstra19102132016-09-01 22:50:36 +02001914
MG Mud User88f12472016-06-24 23:31:02 +02001915 // wenn tmp ein String war, weisen wir es hier pauschal zu
1916 if (stringp(tmp))
1917 parts[i] = tmp;
1918
1919 // auf Wunsch wird nach Satzenden gross geschrieben
1920 if (caps)
Zesstra19102132016-09-01 22:50:36 +02001921 {
1922 // Wenn das vorhergehende parts[i] == "" ist, sind wir am Anfang vom
1923 // String und dies wird wie ein Satzende vorher behandelt.
1924 if (parts[i-1] == "")
MG Mud User88f12472016-06-24 23:31:02 +02001925 parts[i] = capitalize(parts[i]);
Zesstra19102132016-09-01 22:50:36 +02001926 else
1927 {
1928 switch (parts[i-1][<2..])
1929 {
1930 case ". ": case "! ": case "? ":
1931 case ".": case "!": case "?":
1932 case ".\n": case "!\n": case "?\n":
1933 case "\" ": case "\"\n":
1934 parts[i] = capitalize(parts[i]);
1935 break;
1936 }
MG Mud User88f12472016-06-24 23:31:02 +02001937 }
Zesstra19102132016-09-01 22:50:36 +02001938 }
MG Mud User88f12472016-06-24 23:31:02 +02001939 }
1940 return implode(parts, "");
1941 }
1942 return str;
1943}
1944
MG Mud User88f12472016-06-24 23:31:02 +02001945//replacements for dropped efuns in LD
1946#if !__EFUN_DEFINED__(extract)
1947deprecated varargs string extract(string str, int from, int to) {
1948
1949 if(!stringp(str)) {
1950 set_this_object(previous_object());
1951 raise_error(sprintf("Bad argument 1 to extract(): %O",str));
1952 }
1953 if (intp(from) && intp(to)) {
1954 if (from>=0 && to>=0)
1955 return(str[from .. to]);
1956 else if (from>=0 && to<0)
1957 return(str[from .. <abs(to)]);
1958 else if (from<0 && to>=0)
1959 return(str[<abs(from) .. to]);
1960 else
1961 return(str[<abs(from) .. <abs(to)]);
1962 }
1963 else if (intp(from)) {
1964 if (from>=0)
1965 return(str[from .. ]);
1966 else
1967 return(str[<abs(from) .. ]);
1968 }
1969 else {
1970 return(str);
1971 }
1972}
1973#endif // !__EFUN_DEFINED__(extract)
1974
1975#if !__EFUN_DEFINED__(slice_array)
1976deprecated varargs mixed slice_array(mixed array, int from, int to) {
1977
1978 if(!pointerp(array)) {
1979 set_this_object(previous_object());
1980 raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
1981 }
1982 if (intp(from) && intp(to)) {
1983 if (from>=0 && to>=0)
1984 return(array[from .. to]);
1985 else if (from>=0 && to<0)
1986 return(array[from .. <abs(to)]);
1987 else if (from<0 && to>=0)
1988 return(array[<abs(from) .. to]);
1989 else
1990 return(array[<abs(from) .. <abs(to)]);
1991 }
1992 else if (intp(from)) {
1993 if (from>=0)
1994 return(array[from .. ]);
1995 else
1996 return(array[<abs(from) .. ]);
1997 }
1998 else {
1999 return(array);
2000 }
2001}
2002#endif // !__EFUN_DEFINED__(slice_array)
2003
2004#if !__EFUN_DEFINED__(member_array)
2005deprecated int member_array(mixed item, mixed arraystring) {
2006
2007 if (pointerp(arraystring)) {
2008 return(efun::member(arraystring,item));
2009 }
2010 else if (stringp(arraystring)) {
2011 return(efun::member(arraystring,to_int(item)));
2012 }
2013 else {
2014 set_this_object(previous_object());
2015 raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
2016 }
2017}
2018#endif // !__EFUN_DEFINED__(member_array)
2019
2020// The digit at the i'th position is the number of bits set in 'i'.
2021string count_table =
2022 "0112122312232334122323342334344512232334233434452334344534454556";
2023int broken_count_bits( string s ) {
2024 int i, res;
2025 if( !stringp(s) || !(i=sizeof(s)) ) return 0;
2026 for( ; i-->0; ) {
2027 // We are counting 6 bits at a time using a precompiled table.
2028 res += count_table[(s[i]-' ')&63]-'0';
2029 }
2030 return res;
2031}
2032
2033#if !__EFUN_DEFINED__(count_bits)
2034int count_bits( string s ) {
2035 return(broken_count_bits(s));
2036}
2037#endif
2038
2039
2040// * Teile aus einem Array entfernen *** OBSOLETE
2041deprecated mixed *exclude_array(mixed *arr,int from,int to)
2042{
2043 if (to<from)
2044 to = from;
2045 return arr[0..from-1]+arr[to+1..];
2046}
2047