blob: 2b1c18c04fe3b88f7a588cbd5af5d4276e288ed4 [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
Zesstra12fde662022-09-13 22:16:28 +020010#pragma no_simul_efuns
MG Mud User88f12472016-06-24 23:31:02 +020011
12// Absolute Pfade erforderlich - zum Zeitpunkt, wo der Master geladen
13// wird, sind noch keine Include-Pfade da ...
14
15#define SNOOPLOGFILE "SNOOP"
16#define ASNOOPLOGFILE "ARCH/SNOOP"
17
Zesstradf157462018-07-31 21:49:59 +020018public int mkdirp(string dir);
19
MG Mud User88f12472016-06-24 23:31:02 +020020#include "/secure/config.h"
21#include "/secure/wizlevels.h"
22#include "/sys/snooping.h"
23#include "/sys/language.h"
24#include "/sys/thing/properties.h"
25#include "/sys/wizlist.h"
26#include "/sys/erq.h"
27#include "/sys/lpctypes.h"
28#include "/sys/daemon.h"
29#include "/sys/player/base.h"
30#include "/sys/thing/description.h"
31#include "/sys/container.h"
32#include "/sys/defines.h"
33#include "/sys/telnet.h"
34#include "/sys/objectinfo.h"
35#include "/sys/files.h"
36#include "/sys/strings.h"
37#include "/sys/time.h"
38#include "/sys/lpctypes.h"
39#include "/sys/notify_fail.h"
40#include "/sys/tls.h"
41#include "/sys/input_to.h"
42#include "/sys/object_info.h"
43
44/* function prototypes
45 */
46string dtime(int wann);
47varargs int log_file(string file, string txt, int size_to_break);
Zesstra39d284a2020-08-30 23:55:47 +020048int query_wiz_level(object|string player);
Zesstra8d245512020-04-28 20:35:34 +020049nomask varargs int snoop(object snooper, object snoopee);
MG Mud User88f12472016-06-24 23:31:02 +020050varargs string country(mixed ip, string num);
Zesstra39d284a2020-08-30 23:55:47 +020051int query_wiz_grp(object|string wiz);
MG Mud User88f12472016-06-24 23:31:02 +020052nomask int secure_level();
MG Mud User88f12472016-06-24 23:31:02 +020053public 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"
Zesstra19d657d2017-01-29 21:40:38 +010089#include __DIR__"files.c"
Zesstra7b2fbe72017-06-17 19:05:06 +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
Zesstrad36ce392020-08-30 23:42:04 +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;
Zesstraf84b6272013-01-26 00:14:12 +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
Zesstraf84b6272013-01-26 00:14:12 +0100887 if (stringp(name)) {
888 // abs. Pfad erzeugen. *seufz*
889 if (name[0]!='/')
890 name = "/" + name;
Zesstraf84b6272013-01-26 00:14:12 +0100891 // automatisch in LIBDATADIR speichern
892 if (strstr(name,"/"LIBDATADIR"/") != 0) {
Zesstrad43edf72017-01-31 15:43:35 +0100893 oldpath = name;
Zesstraf84b6272013-01-26 00:14:12 +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
Zesstra7426b7f2022-01-17 13:04:27 +0100905 // prepare properties
906 previous_object()->_prop_prepare_save();
MG Mud User88f12472016-06-24 23:31:02 +0200907
Zesstra7426b7f2022-01-17 13:04:27 +0100908 // actual save object (format version: wie definiert in config.h)
Zesstraf84b6272013-01-26 00:14:12 +0100909 if (stringp(name)) {
MG Mud User88f12472016-06-24 23:31:02 +0200910 res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
911 __LIB__SAVE_FORMAT_VERSION__);
Zesstraf84b6272013-01-26 00:14:12 +0100912 // wenn erfolgreich und noch nen Savefile existiert, was nicht unter
913 // /data/ liegt, wird das geloescht.
914 if (!res && oldpath
915 && file_size(oldpath+".o") >= 0)
916 rm(oldpath+".o");
917 }
MG Mud User88f12472016-06-24 23:31:02 +0200918 else
919 res = funcall(bind_lambda(#'efun::save_object, previous_object()),
920 __LIB__SAVE_FORMAT_VERSION__);
Zesstraf84b6272013-01-26 00:14:12 +0100921
Zesstra7426b7f2022-01-17 13:04:27 +0100922 previous_object()->_prop_finalize_save();
MG Mud User88f12472016-06-24 23:31:02 +0200923
924#ifdef IOSTATS
925 // Stats...
926 struct iostat_s stat = (<iostat_s>);
927 stat->oname = object_name(previous_object());
928 stat->time = time();
929 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
930 // OIM_TOTAL_DATA_SIZE);
931 if (stringp(name))
932 stat->size = file_size(name + ".o");
933 else
934 stat->sizeof(res);
935 //debug_message("saveo: "+saveo_stat[0]+"\n");
936 saveo_stat[1][saveo_stat[0]] = stat;
937 saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
938 //debug_message("saveo 2: "+saveo_stat[0]+"\n");
939#endif
940
941 return res;
942}
943
944// * Auch Properties laden
945int restore_object(string name)
946{
947 int result;
948 mixed index;
949 mixed save;
950 mapping properties;
951 int i;
MG Mud User88f12472016-06-24 23:31:02 +0200952
Zesstra94381a42017-01-29 22:37:52 +0100953 if (sizeof(name) < 1)
954 {
955 set_this_object(previous_object());
956 raise_error("Bad arg 1 to restore_object(): expected non-empty "
957 "'string'.\n");
958 }
Zesstraddddbf72021-05-14 16:52:16 +0200959 if(!objectp(previous_object()))
960 {
961 set_this_object(previous_object());
962 raise_error(sprintf("restore_object() only calleable by objects!\n"));
963 }
Zesstra94381a42017-01-29 22:37:52 +0100964
Zesstra87166e12017-01-29 20:12:37 +0100965 // Wenn name vermutlich ein Pfad (also nicht mit #x:y anfaengt)
966 if (name[0] != '#')
967 {
968 // abs. Pfad erzeugen *seufz*
Zesstra0fbf95d2017-01-29 21:48:02 +0100969 if (name[0]!='/')
Zesstra87166e12017-01-29 20:12:37 +0100970 name = "/" + name;
Zesstraf84b6272013-01-26 00:14:12 +0100971
Bugfixb9fa6e82017-04-28 14:42:27 +0200972 // .c am Ende loeschen, sonst wird das File ggf. nicht gefunden.
973 if(name[<2..]==".c")
974 name=name[..<3];
975
Zesstra87166e12017-01-29 20:12:37 +0100976 // wenn kein /data/ vorn steht, erstmal gucken, ob das Savefile unter
977 // /data/ existiert. Wenn ja, wird das geladen.
Zesstra0fbf95d2017-01-29 21:48:02 +0100978 if (strstr(name,"/"LIBDATADIR"/") != 0)
Zesstra87166e12017-01-29 20:12:37 +0100979 {
980 string path = "/"LIBDATADIR + name;
981 if (file_size(path + ".o") >= 0)
982 name = path;
983 }
Zesstraf84b6272013-01-26 00:14:12 +0100984 }
985
Zesstra7426b7f2022-01-17 13:04:27 +0100986 // DEBUG(sprintf("RESTORE %O\n",name));
MG Mud User88f12472016-06-24 23:31:02 +0200987 // restore object
988 result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
989 //'))
MG Mud User88f12472016-06-24 23:31:02 +0200990
Zesstra7426b7f2022-01-17 13:04:27 +0100991 previous_object()->_prop_finalize_restore();
MG Mud User88f12472016-06-24 23:31:02 +0200992
993#ifdef IOSTATS
994 // Stats...
995 //debug_message("restoreo: "+restoreo_stat[0]+"\n");
996 struct iostat_s stat = (<iostat_s>);
997 stat->oname = object_name(previous_object());
998 stat->time = time();
999 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
1000 // OIM_TOTAL_DATA_SIZE);
1001 stat->size = file_size(name + ".o");
1002 restoreo_stat[1][restoreo_stat[0]] = stat;
1003
1004 restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
1005#endif
1006
1007 return result;
1008}
1009
1010// * HB eines Objektes ein/ausschalten
1011int set_object_heart_beat(object ob, int flag)
1012{
1013 if (objectp(ob))
1014 return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
Zesstrab8a823b2021-05-14 16:59:32 +02001015 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02001016}
1017
1018// * Magierlevelgruppen ermitteln
Zesstrad36ce392020-08-30 23:42:04 +02001019int query_wiz_grp(object|string wiz)
MG Mud User88f12472016-06-24 23:31:02 +02001020{
1021 int lev;
1022
1023 lev=query_wiz_level(wiz);
1024 if (lev<SEER_LVL) return 0;
1025 if (lev>=GOD_LVL) return lev;
1026 if (lev>=ARCH_LVL) return ARCH_GRP;
1027 if (lev>=ELDER_LVL) return ELDER_GRP;
1028 if (lev>=LORD_LVL) return LORD_GRP;
1029 if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
1030 if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
1031 if (lev>=WIZARD_LVL) return WIZARD_GRP;
1032 if (lev>=LEARNER_LVL) return LEARNER_GRP;
1033 return SEER_GRP;
1034}
1035
1036mixed *wizlist_info()
1037{
1038 if (ARCH_SECURITY || !extern_call())
1039 return efun::wizlist_info();
1040 return 0;
1041}
1042
1043// * wizlist ausgeben
1044varargs void wizlist(string name, int sortkey ) {
1045
1046 if (!name)
1047 {
1048 if (this_player())
1049 name = getuid(this_player());
1050 if (!name)
1051 return;
1052 }
1053
1054 // Schluessel darf nur in einem gueltigen Bereich sein
1055 if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
1056
1057 mixed** wl = efun::wizlist_info();
1058 // nach <sortkey> sortieren
1059 wl = sort_array(wl, function int (mixed a, mixed b)
1060 {return a[sortkey] < b[sortkey]; } );
1061
1062 // Summe ueber alle Kommandos ermitteln.
1063 int total_cmd, i;
1064 int pos=-1;
1065 foreach(mixed entry : wl)
1066 {
1067 total_cmd += entry[WL_COMMANDS];
1068 if (entry[WL_NAME] == name)
1069 pos = i;
1070 ++i;
1071 }
1072
1073 if (pos < 0 && name != "ALL" && name != "TOP100")
1074 return;
1075
1076 if (name == "TOP100")
1077 {
1078 if (sizeof(wl) > 100)
1079 wl = wl[0..100];
1080 else
1081 wl = wl;
1082 }
1083 // um name herum schneiden
1084 else if (name != "ALL")
1085 {
1086 if (sizeof(wl) <= 21)
1087 wl = wl;
1088 else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
1089 wl = wl[pos-10..pos+10];
1090 else if (pos <=21)
1091 wl = wl[0..20];
1092 else if (pos >= sizeof(wl) - 21)
1093 wl = wl[<21..];
1094 else
1095 wl = wl;
1096 }
1097
1098 write("\nWizard top score list\n\n");
1099 if (total_cmd == 0)
1100 total_cmd = 1;
1101 printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
1102 "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
1103 foreach(mixed e: wl)
1104 {
1105 printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
1106 e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
1107 e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
1108 e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
1109 );
1110 }
1111 printf("\nTotal %7d (%d)\n\n", total_cmd, sizeof(wl));
1112}
1113
1114
1115// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
1116
1117// ermittelt das Objekt des Callouts.
1118private object _call_out_obj( mixed call_out ) {
1119 return pointerp(call_out) ? call_out[0] : 0;
1120}
1121
1122private void _same_object( object ob, mapping m ) {
1123 // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
1124 if ( m[ob] )
1125 m[ob] += ({ ob });
1126 else
1127 m[ob] = ({ ob });
1128}
1129
1130// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
1131// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
1132// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
1133private void _same_path( object key, object *obs, mapping m ) {
1134 string path;
1135 if (!objectp(key) || !pointerp(obs)) return;
1136
1137 path = load_name(key);
1138
1139 if ( m[path] )
1140 m[path] += obs;
1141 else
1142 m[path] = obs;
1143}
1144
1145// key kann object oder string sein.
1146private int _too_many( mixed key, mapping m, int i ) {
1147 return sizeof(m[key]) >= i;
1148}
1149
1150// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
1151// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
1152// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
1153private void _destroy( mixed key, object *obs, string text, int uid ) {
1154 if (!pointerp(obs)) return;
1155 // Array mit unique Eintraege erzeugen.
1156 obs = m_indices( mkmapping(obs) );
1157 // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
1158 // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
1159 // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
1160 // laeuft.
1161 catch( efun::raise_error(
1162 sprintf( text,
Zesstra4dbb9882019-11-26 21:26:36 +01001163 uid ? master()->creator_file(key) : key,
MG Mud User88f12472016-06-24 23:31:02 +02001164 sizeof(obs), object_name(obs[<1]) ) );publish);
1165 // Und weg mit dem Kram...
1166 filter( obs, #'efun::destruct/*'*/ );
1167}
1168
1169// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
1170// sind dabei nicht unique.
1171private void _same_uid( string key, object *obs, mapping m, closure cf ) {
1172 string uid;
1173
1174 if ( !pointerp(obs) || !sizeof(obs) )
1175 return;
1176
1177 uid = funcall( cf, key );
1178
1179 if ( m[uid] )
1180 m[uid] += obs; // obs ist nen Array
1181 else
1182 m[uid] = obs;
1183}
1184
1185private int _log_call_out(mixed co)
1186{
1187 log_file("TOO_MANY_CALLOUTS",
1188 sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
1189 200000);
1190 return 0;
1191}
1192
1193private int last_callout_log=0;
1194
1195nomask varargs void call_out( varargs mixed *args )
1196{
Zesstrab8a823b2021-05-14 16:59:32 +02001197 mixed *call_outs;
MG Mud User88f12472016-06-24 23:31:02 +02001198
1199 // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
1200 // haben.
1201 if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
1202 && geteuid(previous_object()) != ROOTID )
1203 {
1204 // Log erzeugen...
1205 if (last_callout_log <
1206 efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
1207 && get_eval_cost() > 200000)
1208 {
1209 last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1210 log_file("TOO_MANY_CALLOUTS",
1211 sprintf("\n%s: ############ Too many callouts: %d ##############\n",
1212 strftime("%y%m%d-%H%M%S"),
1213 efun::driver_info(DI_NUM_CALLOUTS)));
1214 filter(efun::call_out_info(), #'_log_call_out);
1215 }
1216 // Objekte aller Callouts ermitteln
1217 call_outs = map( efun::call_out_info(), #'_call_out_obj );
1218 mapping objectmap = ([]);
1219 filter( call_outs, #'_same_object, &objectmap );
1220 // Master nicht grillen...
1221 efun::m_delete( objectmap, master(1) );
1222 // alle Objekte raussuchen, die zuviele haben...
1223 mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
1224 // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
1225 // Objekten uebergeben (in diesem Fall sind Keys und Array mit
1226 // Objekten jeweils das gleiche Objekt).
1227 if ( sizeof(res) )
1228 walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "
1229 "object [%O]. Destructed %d objects. [%s]\n", 0 );
1230
1231 // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
1232 // Objekte killen, die mehr als 50 Callouts laufen haben - und
1233 // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
1234 // Clones einer BP gekillt, die Callouts laufen haben, falls alle
1235 // diese Objekte _zusammen_ mehr als 50 Callouts haben!
1236 if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
1237 // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
1238 // mehr drin, da sie dort als Keys verwendet wurden.
1239 mapping pathmap=([]);
1240 // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
1241 // von Objekten als Werte.
1242 walk_mapping( objectmap, #'_same_path, &pathmap);
1243 // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
1244 res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
1245 // und ueber alle Keys gehen, an _destroy() werden die Clones
1246 // uebergeben, die Callouts haben.
1247 if ( sizeof(res) )
1248 walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
1249 "'%s'. Destructed %d objects. [%s]\n", 0 );
1250
1251 // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
1252 // noch >1000 Callouts laufen, werden diesmal alle Callouts
1253 // einer UID zusammengezaehlt.
1254 // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
1255 // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
1256 if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
1257 {
1258 // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
1259 // zusammensortieren. Zerstoerte Clones filter _same_uid()
1260 // raus.
1261 mapping uidmap=([]);
1262 walk_mapping( pathmap, #'_same_uid, &uidmap,
1263 symbol_function( "creator_file",
1264 "/secure/master" ) );
1265 // In res nun UIDs als Keys und Arrays von Objekten als Werte.
1266 // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
1267 // 100 Callouts!) haben.
1268 res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
1269 // und erneut ueber die Keys laufen und jeweils die Arrays mit
1270 // den Objekten zur Zerstoerung an _destroy()...
1271 if ( sizeof(res) )
1272 walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
1273 "UID '%s'. Destructed %d objects. [%s]\n",
1274 1 );
1275 }
1276 }
1277 }
1278
1279 // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
1280 if ( !previous_object() )
1281 return;
1282
1283 set_this_object( previous_object() );
1284 apply( #'efun::call_out, args );
1285 return;
1286}
1287
1288mixed call_out_info() {
1289
Zesstraddddbf72021-05-14 16:52:16 +02001290 object|lwobject po = previous_object();
MG Mud User88f12472016-06-24 23:31:02 +02001291 mixed coi = efun::call_out_info();
1292
1293 // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
1294 // /obj haben die BackboneID.
1295 if (query_wiz_level(getuid(po)) >= ARCH_LVL
Zesstra4dbb9882019-11-26 21:26:36 +01001296 || master()->creator_file(load_name(po)) == BACKBONEID ) {
MG Mud User88f12472016-06-24 23:31:02 +02001297 return coi;
1298 }
1299 else {
1300 return filter(coi, function mixed (mixed arr) {
1301 if (pointerp(arr) && arr[0]==po)
1302 return 1;
1303 else return 0; });
1304 }
1305}
1306
1307// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
1308// NICHT das Objekt, was ggf. die lfun definiert!
1309mixed query_closure_object(closure c) {
1310 return
1311 CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
1312 0 :
1313 (to_object(c) || -1);
1314}
1315
1316// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
1317varargs void move_object(mixed what, mixed where)
1318{
Zesstraddddbf72021-05-14 16:52:16 +02001319 // Wenn nur ein Argument angegeben wird, ist es das Ziel und wir nehmen
1320 // previous_object() als zu bewegendes Objekt.
MG Mud User88f12472016-06-24 23:31:02 +02001321 if (!where)
1322 {
1323 where=what;
Zesstraddddbf72021-05-14 16:52:16 +02001324 what=previous_object();
MG Mud User88f12472016-06-24 23:31:02 +02001325 }
1326 if (((stringp(where) && where==NETDEAD_ROOM ) ||
1327 (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
1328 objectp(what) && object_name(what)!="/obj/sperrer")
1329 {
1330 if (!query_once_interactive(what))
1331 {
1332 what->remove();
1333 if (what) destruct(what);
1334 return;
1335 }
1336 if (living(what) || interactive(what))
1337 {
1338 log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
1339 return;
1340 }
1341 set_object_heart_beat(what,0);
1342 }
Zesstraddddbf72021-05-14 16:52:16 +02001343 object tmp=what;
MG Mud User88f12472016-06-24 23:31:02 +02001344 while (tmp=environment(tmp))
1345 // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
1346 // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
1347 // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
1348 // da um jedes bisschen Rechenzeit geht.
1349 // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
1350 //
1351 // Tiamak
1352 tmp->_set_last_content_change();
Zesstraddddbf72021-05-14 16:52:16 +02001353 funcall(bind_lambda(#'efun::move_object,previous_object()),what,where);
MG Mud User88f12472016-06-24 23:31:02 +02001354 if (tmp=what)
1355 while (tmp=environment(tmp))
1356 tmp->_set_last_content_change();
1357}
1358
1359
1360void start_simul_efun() {
1361 mixed *info;
1362
1363 // Falls noch nicht getan, extra_wizinfo initialisieren
1364 if ( !pointerp(info = get_extra_wizinfo(0)) )
1365 set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
1366
1367 InitLivingData(info);
1368
1369 set_next_reset(10); // direkt mal aufraeumen
1370}
1371
1372protected void reset() {
1373 set_next_reset(7200);
1374 CleanLivingData();
1375}
1376
1377#if !__EFUN_DEFINED__(absolute_hb_count)
1378int absolute_hb_count() {
1379 return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1380}
1381#endif
1382
1383void __set_environment(object ob, mixed target)
1384{
1385 string path;
1386 object obj;
1387
1388 if (!objectp(ob))
1389 return;
1390 if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
1391 return;
1392 if (objectp(target))
1393 {
1394 efun::set_environment(ob,target);
1395 return;
1396 }
Zesstra4dbb9882019-11-26 21:26:36 +01001397 path=master()->make_path_absolute(target);
MG Mud User88f12472016-06-24 23:31:02 +02001398 if (stringp(path) && file_size(path+".c")>=0 &&
1399 !catch(load_object(path);publish) )
1400 {
1401 obj=find_object(path);
1402 efun::set_environment(ob,obj);
1403 return;
1404 }
1405}
1406
1407void _dump_wizlist(string file, int sortby) {
1408 int i;
1409 mixed *a;
1410
1411 if (!LORD_SECURITY)
1412 return;
1413 if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
1414 {
1415 write("NO WRITE PERMISSION\n");
1416 return;
1417 }
1418 a = wizlist_info();
1419 a = sort_array(a, lambda( ({'a,'b}),
1420 ({#'<,
1421 ({#'[,'a,sortby}),
1422 ({#'[,'b,sortby})
1423 })));
1424 rm(file);
1425 for (i=sizeof(a)-1;i>=0;i--)
1426 write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
1427 a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
1428 a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
1429}
1430
Zesstraddddbf72021-05-14 16:52:16 +02001431public object deep_present(string|object what, object ob=previous_object())
1432{
MG Mud User88f12472016-06-24 23:31:02 +02001433 // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
1434 // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
1435 if(objectp(what)) {
1436 object *envs=all_environment(what);
1437 // wenn ob kein Environment hat, ist es offensichtlich nicht in what
1438 // enthalten.
1439 if (!pointerp(envs)) return 0;
Zesstraddddbf72021-05-14 16:52:16 +02001440 if (ob in envs) return what;
MG Mud User88f12472016-06-24 23:31:02 +02001441 }
1442 // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
1443 // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
1444 // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
1445 else if (stringp(what)) {
1446 int cnt;
1447 string newwhat;
1448 if(sscanf(what,"%s %d",newwhat,cnt)!=2)
1449 cnt=1;
1450 else
1451 what=newwhat;
1452 foreach(object invob: deep_inventory(ob)) {
1453 if (invob->id(what) && !--cnt)
1454 return invob;
1455 }
1456 }
1457 else {
1458 set_this_object(previous_object());
1459 raise_error(sprintf("Wrong argument 1 to deep_present(). "
1460 "Expected \"object\" or \"string\", got %.50O.\n",
1461 what));
1462 }
1463 return 0;
1464}
1465
1466mapping dump_ip_mapping()
1467{
1468 return 0;
1469}
1470
1471nomask void swap(object obj)
1472{
1473 write("Your are not allowed to swap objects by hand!\n");
1474 return;
1475}
1476
1477nomask varargs void garbage_collection(string str)
1478{
1479 if(previous_object()==0 || !IS_ARCH(geteuid(previous_object()))
1480 || !ARCH_SECURITY)
1481 {
Zesstraddddbf72021-05-14 16:52:16 +02001482 // historical info, but amusing message. ;-)
MG Mud User88f12472016-06-24 23:31:02 +02001483 write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
1484 return;
1485 }
1486 else if (stringp(str))
1487 {
1488 return efun::garbage_collection(str);
1489 }
1490 else
1491 return efun::garbage_collection();
1492}
1493
Zesstrae490a532020-09-22 22:07:54 +02001494varargs void notify_fail(string|closure nf, int prio)
1495{
MG Mud User88f12472016-06-24 23:31:02 +02001496 int oldprio;
Zesstrae490a532020-09-22 22:07:54 +02001497
Zesstrad7795012020-09-22 22:13:31 +02001498 if (!PL || !previous_object())
Zesstrae490a532020-09-22 22:07:54 +02001499 return;
MG Mud User88f12472016-06-24 23:31:02 +02001500
1501 // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
1502 // und vergleichen.
Zesstrad7795012020-09-22 22:13:31 +02001503 object oldo = query_notify_fail(1);
1504 if (oldo && previous_object()!=oldo)
1505 {
1506 if (!prio)
1507 {
MG Mud User88f12472016-06-24 23:31:02 +02001508 //Prioritaet dieses notify_fail() 'abschaetzen'
Zesstrad7795012020-09-22 22:13:31 +02001509 if (previous_object()==PL) // Spieler-interne (soul-cmds)
MG Mud User88f12472016-06-24 23:31:02 +02001510 prio=NF_NL_OWN;
Zesstrad7795012020-09-22 22:13:31 +02001511 else if (living(previous_object()))
MG Mud User88f12472016-06-24 23:31:02 +02001512 prio=NF_NL_LIVING;
Zesstrad7795012020-09-22 22:13:31 +02001513 else if (previous_object()->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001514 prio=NF_NL_ROOM;
1515 else
1516 prio=NF_NL_THING;
1517 }
1518 //Prioritaet des alten Setzers abschaetzen
1519 if (oldo==PL)
1520 oldprio=NF_NL_OWN;
1521 else if (living(oldo))
1522 oldprio=NF_NL_LIVING;
Zesstra4dbb9882019-11-26 21:26:36 +01001523 else if (oldo->IsRoom())
MG Mud User88f12472016-06-24 23:31:02 +02001524 oldprio=NF_NL_ROOM;
1525 else
1526 oldprio=NF_NL_THING;
1527 }
1528 else // wenn es noch kein Notify_fail gibt:
1529 oldprio=NF_NL_NONE;
1530
Zesstrad7795012020-09-22 22:13:31 +02001531 // vergleichen und ggf. setzen
1532 if (prio >= oldprio)
1533 {
1534 set_this_object(previous_object());
MG Mud User88f12472016-06-24 23:31:02 +02001535 efun::notify_fail(nf);
1536 }
1537
1538 return;
1539}
1540
Zesstrae490a532020-09-22 22:07:54 +02001541void _notify_fail(string|closure str)
MG Mud User88f12472016-06-24 23:31:02 +02001542{
Zesstra351ca4d2020-09-22 22:17:24 +02001543 notify_fail(str, NF_NL_NONE);
MG Mud User88f12472016-06-24 23:31:02 +02001544}
1545
1546string time2string( string format, int zeit )
1547{
1548 int i,ch,maxunit,dummy;
1549 string *parts, fmt;
1550
1551 int secs = zeit;
1552 int mins = (zeit/60);
1553 int hours = (zeit/3600);
1554 int days = (zeit/86400);
1555 int weeks = (zeit/604800);
1556 int months = (zeit/2419200);
1557 int abbr = 0;
1558
1559 parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
1560
1561 for( i=1; i<sizeof(parts); i+=2 )
1562 {
1563 ch = parts[i][<1];
1564 switch( parts[i][<1] )
1565 {
1566 case 'x': case 'X':
1567 abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
1568 // NO break !
1569 case 'n': case 'N':
1570 maxunit |= 31;
1571 break;
1572 case 'w': case 'W':
1573 maxunit |= 15;
1574 break;
1575 case 'd': case 'D':
1576 maxunit |= 7;
1577 break;
1578 case 'h': case 'H':
1579 maxunit |= 3;
1580 break;
1581 case 'm': case 'M':
1582 maxunit |= 1;
1583 break;
1584 }
1585 }
1586 if( maxunit & 16 ) weeks %= 4;
1587 if( maxunit & 8 ) days %= 7;
1588 if( maxunit & 4 ) hours %= 24;
1589 if( maxunit & 2 ) mins %= 60;
1590 if( maxunit ) secs %= 60;
1591
1592 for( i=1; i<sizeof(parts); i+=2 )
1593 {
1594 fmt = parts[i][0..<2];
1595 ch = parts[i][<1];
1596 if( ch=='x' )
1597 {
1598 if (months > 0) ch='n';
1599 else if( weeks>0 ) ch='w';
1600 else if( days>0 ) ch='d';
1601 else if( hours>0 ) ch='h';
1602 else if(mins > 0) ch = 'm';
1603 else ch = 's';
1604 }
1605 else if( ch=='X' )
1606 {
1607 if (months > 0) ch='N';
1608 else if( weeks>0 ) ch='W';
1609 else if( days>0 ) ch='D';
1610 else if( hours>0 ) ch='H';
1611 else if(mins > 0) ch = 'M';
1612 else ch = 'S';
1613 }
1614
1615 switch( ch )
1616 {
1617 case 'n': parts[i] = sprintf( fmt+"d", months ); break;
1618 case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
1619 case 'd': parts[i] = sprintf( fmt+"d", days ); break;
1620 case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
1621 case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
1622 case 's': parts[i] = sprintf( fmt+"d", secs ); break;
1623 case 'N':
1624 if(abbr) parts[i] = "M";
1625 else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
1626 break;
1627 case 'W':
1628 if(abbr) parts[i] = "w"; else
1629 parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
1630 break;
1631 case 'D':
1632 if(abbr) parts[i] = "d"; else
1633 parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
1634 break;
1635 case 'H':
1636 if(abbr) parts[i] = "h"; else
1637 parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
1638 break;
1639 case 'M':
1640 if(abbr) parts[i] = "m"; else
1641 parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
1642 break;
1643 case 'S':
1644 if(abbr) parts[i] = "s"; else
1645 parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
1646 break;
1647 case '%':
1648 parts[i] = "%";
1649 break;
1650 }
1651 }
1652 return implode( parts, "" );
1653}
1654
1655nomask mixed __create_player_dummy(string name)
1656{
1657 string err;
1658 object ob;
1659 mixed m;
1660 //hat nen Scherzkeks die Blueprint bewegt?
1661 if ((ob=find_object("/secure/login")) && environment(ob))
1662 catch(destruct(ob);publish);
1663 err = catch(ob = clone_object("secure/login");publish);
1664 if (err)
1665 {
1666 write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
1667 return 0;
1668 }
Zesstra4dbb9882019-11-26 21:26:36 +01001669 if (objectp(m=ob->new_logon(name))) netdead[name]=m;
MG Mud User88f12472016-06-24 23:31:02 +02001670 return m;
1671}
1672
1673nomask int secure_level()
1674{
1675 int *level;
1676 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
1677 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
1678 //von 0.
1679 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
1680 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
1681 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
1682 //INteractive geben muss.
1683 level=map(caller_stack(1),function int (object caller)
1684 {if (objectp(caller))
1685 return(query_wiz_level(geteuid(caller)));
1686 return(0); // kein Objekt da, 0.
1687 } );
1688 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
1689}
1690
1691nomask string secure_euid()
1692{
1693 string euid;
1694
1695 if (!this_interactive()) // Es muss einen interactive geben
1696 return 0;
1697 euid=geteuid(this_interactive());
1698 // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
1699 // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
1700 // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
1701 foreach(object caller: caller_stack()) {
1702 if (!objectp(caller) ||
1703 (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
1704 return 0;
1705 }
1706 return euid; // 'sichere' euid zurueckgeben
1707}
1708
Zesstra1bd79662021-05-14 19:23:20 +02001709// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
1710// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
1711//#if __BOOT_TIME__ < 1360017213
1712varargs void input_to( mixed fun, int flags, varargs mixed *args )
1713{
1714 mixed *arr;
1715 int i;
1716
1717 if ( !this_player() || !previous_object() )
1718 return;
1719
1720 // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
1721 // vorkommt...
1722 if ( flags&INPUT_PROMPT ) {
1723 arr = ({ fun, flags }) + args;
1724 }
1725 else {
1726 // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
1727 flags |= INPUT_PROMPT;
1728 arr = ({ fun, flags, "" }) + args;
1729 }
1730
1731 // Arrays gegen flatten quoten.
1732 for ( i = sizeof(arr) - 1; i > 1; i-- )
1733 if ( pointerp(arr[i]) )
1734 arr[i] = quote(arr[i]);
1735
1736 apply( bind_lambda( unbound_lambda( ({}),
1737 ({ #'efun::input_to/*'*/ }) + arr ),
1738 previous_object() ) );
1739}
1740//#endif
1741
Zesstra42324bf2021-05-14 15:25:20 +02001742deprecated nomask int set_light(int i)
MG Mud User88f12472016-06-24 23:31:02 +02001743// erhoeht das Lichtlevel eines Objekts um i
1744// result: das Lichtlevel innerhalb des Objekts
1745{
Zesstrab8a823b2021-05-14 16:59:32 +02001746 object ob;
MG Mud User88f12472016-06-24 23:31:02 +02001747
1748 if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
1749
1750 // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
1751 if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
1752
1753 // Lichtberechnung findet eigentlich in der Mudlib statt.
Zesstra4dbb9882019-11-26 21:26:36 +01001754 return ob->QueryProp(P_INT_LIGHT);
MG Mud User88f12472016-06-24 23:31:02 +02001755}
1756
MG Mud User88f12472016-06-24 23:31:02 +02001757public varargs string CountUp( string *s, string sep, string lastsep )
1758{
1759 string ret;
1760
1761 if ( !pointerp(s) )
1762 return "";
1763
1764 if (!sep) sep = ", ";
1765 if (!lastsep) lastsep = " und ";
1766
1767 switch (sizeof(s)) {
1768 case 0: ret=""; break;
1769 case 1: ret=s[0]; break;
1770 default:
1771 ret = implode(s[0..<2], sep);
1772 ret += lastsep + s[<1];
1773 }
1774 return ret;
1775}
1776
Zesstraddddbf72021-05-14 16:52:16 +02001777nomask int query_next_reset(object ob=previous_object())
1778{
MG Mud User88f12472016-06-24 23:31:02 +02001779 // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
1780 if (ob && !objectp(ob))
1781 raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
1782 "expected object.\n",ob));
1783
MG Mud User88f12472016-06-24 23:31:02 +02001784 return efun::object_info(ob, OI_NEXT_RESET_TIME);
1785}
1786
1787
MG Mud User88f12472016-06-24 23:31:02 +02001788// ### Ersatzaufloesung in Strings ###
Arathorn066820b2019-11-27 19:47:19 +01001789varargs string replace_personal(string str, <string|object>* obs, int caps) {
1790 string* parts = regexplode(str, "@WE[A-SU]*[0-9]");
1791 int i = sizeof(parts);
MG Mud User88f12472016-06-24 23:31:02 +02001792
Zesstra19102132016-09-01 22:50:36 +02001793 if (i>1)
1794 {
MG Mud User88f12472016-06-24 23:31:02 +02001795 int j, t;
MG Mud User88f12472016-06-24 23:31:02 +02001796 t = j = sizeof(obs);
1797
Arathorn066820b2019-11-27 19:47:19 +01001798 <string|closure>* name_cls = allocate(j);
1799 while (j--) {
MG Mud User88f12472016-06-24 23:31:02 +02001800 if (objectp(obs[j]))
1801 name_cls[j] = symbol_function("name", obs[j]);
1802 else if (stringp(obs[j]))
1803 name_cls[j] = obs[j];
Arathorn066820b2019-11-27 19:47:19 +01001804 }
MG Mud User88f12472016-06-24 23:31:02 +02001805
Zesstra19102132016-09-01 22:50:36 +02001806 while ((i-= 2)>0)
1807 {
MG Mud User88f12472016-06-24 23:31:02 +02001808 // zu ersetzendes Token in Fall und Objektindex aufspalten
Arathorn066820b2019-11-27 19:47:19 +01001809 int ob_nr = parts[i][<1]-'1';
MG Mud User88f12472016-06-24 23:31:02 +02001810 if (ob_nr<0 || ob_nr>=t) {
1811 set_this_object(previous_object());
Zesstrab8a823b2021-05-14 16:59:32 +02001812 raise_error(sprintf(
1813 "replace_personal: using wrong object index %d\n", ob_nr));
MG Mud User88f12472016-06-24 23:31:02 +02001814 }
1815
1816 // casus kann man schon hier entscheiden
1817 int casus;
1818 string part = parts[i];
1819 switch (part[3]) {
1820 case 'R': casus = WER; break;
1821 case 'S': casus = WESSEN; break;
1822 case 'M': casus = WEM; break;
1823 case 'N': casus = WEN; break;
1824 default: continue; // passt schon jetzt nicht in das Hauptmuster
1825 }
1826
1827 // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
1828 mixed tmp;
1829 switch (part[3..<2]) {
1830 case "R": case "SSEN": case "M": case "N": // Name
1831 parts[i] = funcall(name_cls[ob_nr], casus, 1); break;
1832 case "RU": case "SSENU": case "MU": case "NU": // unbestimmt
1833 parts[i] = funcall(name_cls[ob_nr], casus); break;
1834 case "RQP": case "SSENQP": case "MQP": case "NQP": // Pronoun
1835 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001836 parts[i] = tmp->QueryPronoun(casus);
MG Mud User88f12472016-06-24 23:31:02 +02001837 break;
1838 case "RQA": case "SSENQA": case "MQA": case "NQA": // Article
1839 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001840 tmp = tmp->QueryArticle(casus, 1, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001841 if (stringp(tmp) && !(tmp[<1]^' '))
1842 tmp = tmp[0..<2]; // Extra-Space wieder loeschen
1843 break;
1844 case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
1845 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001846 parts[i] = tmp->QueryPossPronoun(MALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001847 break;
1848 case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
1849 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001850 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001851 break;
1852 case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
1853 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001854 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
MG Mud User88f12472016-06-24 23:31:02 +02001855 break;
1856 case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
1857 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001858 parts[i] = tmp->QueryPossPronoun(MALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001859 break;
1860 case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
1861 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001862 parts[i] = tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001863 break;
1864 case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
1865 if (objectp(tmp = obs[ob_nr]))
Zesstra4dbb9882019-11-26 21:26:36 +01001866 parts[i] = tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
MG Mud User88f12472016-06-24 23:31:02 +02001867 break;
1868 default:
1869 continue;
1870 }
Zesstra19102132016-09-01 22:50:36 +02001871
MG Mud User88f12472016-06-24 23:31:02 +02001872 // wenn tmp ein String war, weisen wir es hier pauschal zu
1873 if (stringp(tmp))
1874 parts[i] = tmp;
1875
1876 // auf Wunsch wird nach Satzenden gross geschrieben
1877 if (caps)
Zesstra19102132016-09-01 22:50:36 +02001878 {
1879 // Wenn das vorhergehende parts[i] == "" ist, sind wir am Anfang vom
1880 // String und dies wird wie ein Satzende vorher behandelt.
1881 if (parts[i-1] == "")
MG Mud User88f12472016-06-24 23:31:02 +02001882 parts[i] = capitalize(parts[i]);
Zesstra19102132016-09-01 22:50:36 +02001883 else
1884 {
1885 switch (parts[i-1][<2..])
1886 {
1887 case ". ": case "! ": case "? ":
1888 case ".": case "!": case "?":
1889 case ".\n": case "!\n": case "?\n":
1890 case "\" ": case "\"\n":
1891 parts[i] = capitalize(parts[i]);
1892 break;
1893 }
MG Mud User88f12472016-06-24 23:31:02 +02001894 }
Zesstra19102132016-09-01 22:50:36 +02001895 }
MG Mud User88f12472016-06-24 23:31:02 +02001896 }
1897 return implode(parts, "");
1898 }
1899 return str;
1900}
1901
MG Mud User88f12472016-06-24 23:31:02 +02001902//replacements for dropped efuns in LD
1903#if !__EFUN_DEFINED__(extract)
1904deprecated varargs string extract(string str, int from, int to) {
1905
1906 if(!stringp(str)) {
1907 set_this_object(previous_object());
1908 raise_error(sprintf("Bad argument 1 to extract(): %O",str));
1909 }
1910 if (intp(from) && intp(to)) {
1911 if (from>=0 && to>=0)
1912 return(str[from .. to]);
1913 else if (from>=0 && to<0)
1914 return(str[from .. <abs(to)]);
1915 else if (from<0 && to>=0)
1916 return(str[<abs(from) .. to]);
1917 else
1918 return(str[<abs(from) .. <abs(to)]);
1919 }
1920 else if (intp(from)) {
1921 if (from>=0)
1922 return(str[from .. ]);
1923 else
1924 return(str[<abs(from) .. ]);
1925 }
1926 else {
1927 return(str);
1928 }
1929}
1930#endif // !__EFUN_DEFINED__(extract)
1931
1932#if !__EFUN_DEFINED__(slice_array)
1933deprecated varargs mixed slice_array(mixed array, int from, int to) {
1934
1935 if(!pointerp(array)) {
1936 set_this_object(previous_object());
1937 raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
1938 }
1939 if (intp(from) && intp(to)) {
1940 if (from>=0 && to>=0)
1941 return(array[from .. to]);
1942 else if (from>=0 && to<0)
1943 return(array[from .. <abs(to)]);
1944 else if (from<0 && to>=0)
1945 return(array[<abs(from) .. to]);
1946 else
1947 return(array[<abs(from) .. <abs(to)]);
1948 }
1949 else if (intp(from)) {
1950 if (from>=0)
1951 return(array[from .. ]);
1952 else
1953 return(array[<abs(from) .. ]);
1954 }
1955 else {
1956 return(array);
1957 }
1958}
1959#endif // !__EFUN_DEFINED__(slice_array)
1960
1961#if !__EFUN_DEFINED__(member_array)
1962deprecated int member_array(mixed item, mixed arraystring) {
1963
1964 if (pointerp(arraystring)) {
1965 return(efun::member(arraystring,item));
1966 }
1967 else if (stringp(arraystring)) {
1968 return(efun::member(arraystring,to_int(item)));
1969 }
1970 else {
1971 set_this_object(previous_object());
1972 raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
1973 }
1974}
1975#endif // !__EFUN_DEFINED__(member_array)
1976
1977// The digit at the i'th position is the number of bits set in 'i'.
1978string count_table =
1979 "0112122312232334122323342334344512232334233434452334344534454556";
1980int broken_count_bits( string s ) {
1981 int i, res;
1982 if( !stringp(s) || !(i=sizeof(s)) ) return 0;
1983 for( ; i-->0; ) {
1984 // We are counting 6 bits at a time using a precompiled table.
1985 res += count_table[(s[i]-' ')&63]-'0';
1986 }
1987 return res;
1988}
1989
1990#if !__EFUN_DEFINED__(count_bits)
1991int count_bits( string s ) {
1992 return(broken_count_bits(s));
1993}
1994#endif
1995
1996
1997// * Teile aus einem Array entfernen *** OBSOLETE
1998deprecated mixed *exclude_array(mixed *arr,int from,int to)
1999{
2000 if (to<from)
2001 to = from;
2002 return arr[0..from-1]+arr[to+1..];
2003}
2004