blob: d8a1f39c5e0bb7c773a6d5cf6d3bb289729d666e [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// master/misc.c -- Diverses: (T)Banish, Projektverwaltung, Levelaufstieg, ...
4//
5// $Id: misc.c 9467 2016-02-19 19:48:24Z Zesstra $
6
7#pragma strict_types,rtt_checks
8
9#include "/sys/functionlist.h"
10#include "/sys/lpctypes.h"
11#include "/sys/object_info.h"
12#include "/sys/interactive_info.h"
13
14#include "/secure/master.h"
15#include "/mail/post.h"
16#include "/sys/thing/language.h"
17#include "/sys/thing/description.h"
Zesstra86607bb2017-06-17 19:02:40 +020018#include "/sys/configuration.h"
MG Mud User88f12472016-06-24 23:31:02 +020019
20// Fuer CIDR-Notatio im sbanish
21#include "/secure/master/cidr.c"
22
23static mixed *banished;
24static mapping tbanished, sbanished;
25static string *deputies;
26
27// TODO: muss ggf. Fakeobjekt erzeugen+uebergeben, wenn sender kein object
28protected void send_channel_msg(string channel,mixed sendername,string msg)
29{
30 object sender;
31 if (objectp(sendername))
32 sender=sendername;
33 else
34 {
35 // wenn kein Objekt uebergeben, erstmal schauen, ob ein Spielerobject
36 // existiert... Wenn ja, das nehmen.
37 sender = call_sefun("find_player", sendername)
38 || call_sefun("find_netdead", sendername);
39 if (!objectp(sender))
40 {
41 // sonst faken wir eins. *seufz*
42 sender=clone_object("/p/daemon/namefake");
43 sender->SetProp(P_NAME, sendername);
44 sender->SetProp(P_ARTICLE,0);
45 // Dieses Objekt zerstoert sich nach 3s automatisch.
46 }
47 }
48 CHMASTER->send(channel, sender, msg);
49}
50
51static string *explode_files(string file) {
52 string data;
53 mixed *exploded;
54 int i;
55
56 data=read_file(file);
57 if (!data || !stringp(data) || data == "") return ({});
58 exploded = efun::explode(data,"\n");
59 for (i=sizeof(exploded);i--;)
60 if (!stringp(exploded[i]) || exploded[i]=="" || exploded[i][0]=='#')
61 exploded[i]=0;
62 exploded-=({0});
63 printf("%-30s: %3d Objekt%s\n",file,i=sizeof(exploded),(i==1?"":"e"));
64 return exploded;
65}
66
67void UpdateTBanish();
68
69mixed QueryBanished(string str){
70 int i;
71
72 if (!str) return 0;
73 if (!pointerp(banished)) return 0;
74 for (i=sizeof(banished)-1;i>=0;i--)
75 if (sizeof(regexp(({str}),"^"+banished[i][0]+"$")))
76 {
77 if (!banished[i][1] || banished[i][1]=="")
78 return "Dieser Name ist gesperrt.";
79 else
80 return banished[i][1];
81 }
82 return 0;
83}
84
85mixed QueryTBanished(string str) {
86 int i;
87
88 if (!str || !mappingp(tbanished) || !(i=tbanished[str]))
89 return 0;
90
91 if (i == -1 || i > time())
92 return sprintf("Es gibt schon einen Spieler diesen Namens.\n"
93 +"Allerdings kann er/sie erst am %s wieder ins Mud kommen.\n",
94 (i == -1 ? "St. Nimmerleinstag" :
95 call_sefun("dtime",i)[0..16]));
96
97// Ansonsten: die Zeit ist abgelaufen, Spieler darf wieder...
98 m_delete(tbanished, str);
99 UpdateTBanish();
100 return 0;
101}
102
103void ReloadBanishFile(){
104 int i, t;
105 string s1, s2, *s;
106
107 banished = efun::explode( read_file("/secure/BANISH") || "", "\n" );
108 banished = banished - ({""});
109 for ( i = sizeof(banished); i--; ){
110 s = efun::explode( banished[i], " " );
111 s1 = s[0];
112 s2 = implode( s[1..], " " );
113 banished[i] = ({ s1, s2 });
114 }
115
116 if ( !mappingp(tbanished) ){
117 tbanished = ([]);
118
119 s = efun::explode( read_file("/secure/TBANISH") || "", "\n" );
120 s -= ({""});
121
122 for ( i = sizeof(s); i--; ){
123 sscanf( s[i], "%s:%d", s1, t );
124 tbanished += ([ s1 : t ]);
125 }
126 }
127
128 if ( !mappingp(sbanished) ){
129 sbanished = m_allocate(3, 2);
130
131 s = efun::explode( read_file("/secure/SITEBANISH") || "", "\n" );
132 s -= ({""});
133
134 for ( i = sizeof(s); i--; ) {
135 int int_ip;
136 sscanf( s[i], "%s:%d:%s", s1, t, s2 );
137 int_ip = IPv4_addr2int(s1);
138 m_add(sbanished, int_ip, t, s2);
139 }
140 }
141}
142
143int IsDeputy(mixed name)
144{
145 if ( IS_ARCH(name) )
146 return 1;
147
148 if ( objectp(name) )
149 name = getuid(name);
150
151 if ( member( deputies || ({}), name ) >= 0 )
152 return 1;
153
154 return 0;
155}
156
157
158varargs void BanishName( string name, string reason, int force )
159{
160 string *names;
161 int i;
162
163 if ( PO != TO && call_sefun("secure_level") < LORD_LVL
164 && !IsDeputy( call_sefun("secure_euid") ) )
165 return;
166
167 if ( !stringp(name) || !sizeof(name) )
168 return;
169
170 name = lower_case(name);
171
172 if ( !reason || !stringp(reason) )
173 reason = "";
174
175 if ( QueryBanished(name) && lower_case(reason) != "loeschen" ){
176 write("Der Name ist schon gebannt.\n");
177 return;
178 }
179
Zesstra85576452017-01-30 15:43:21 +0100180 if ( !force && find_userinfo(name))
181 {
MG Mud User88f12472016-06-24 23:31:02 +0200182 write("Es existiert bereits ein Spieler dieses Namens.\n");
183 return;
184 }
185
186/* if (!("/secure/login"->valid_name(name))) return;*/
187 if ( lower_case(reason) != "loeschen" ){
188 names = ({ name + " " + reason });
189
190 for ( i = sizeof(banished); i--; )
191 names += ({ banished[i][0] + " " + banished[i][1] });
192 }
193 else{
194 names = ({});
195
196 for ( i = sizeof(banished); i--; )
197 if ( banished[i][0] != name )
198 names += ({ banished[i][0] + " " + banished[i][1] });
199 }
200
201 names = sort_array( names, #'> );
202 rm("/secure/BANISH");
203 write_file( "/secure/BANISH", implode(names, "\n") );
204 write( "Okay, '"+capitalize(name)+"' ist jetzt "+
205 (lower_case(reason) == "loeschen" ? "nicht mehr " : "")+"gebanisht.\n" );
206 ReloadBanishFile();
207}
208
209void UpdateTBanish()
210{
211 int i;
212 string *names;
213
214 for (i=sizeof(names = sort_array(m_indices(tbanished), #'</*'*/))-1;i>=0;i--)
215 names[i] = sprintf("%s:%d", names[i], tbanished[names[i]]);
216
217 rm("/secure/TBANISH");
218 write_file("/secure/TBANISH", implode(names, "\n"));
219}
220
221void UpdateSBanish()
222{
223 int i;
224 mapping lines = m_allocate(sizeof(sbanished),0);
225
226 foreach(int ip, int tstamp, string user : sbanished) {
227 m_add(lines, sprintf("%s:%d:%s",
228 IPv4_int2addr(ip), tstamp, user));
229 }
230
231 write_file( "/secure/SITEBANISH",
232 implode( sort_array (m_indices(lines), #'<), "\n" ), 1);
233}
234
235int TBanishName(string name, int days)
236{
237 int t;
238
239 if ( (getuid(TI) != name) &&
240 !IsDeputy( call_sefun("secure_euid") ) )
241 return 0;
242
243 if (days && QueryTBanished(name)){
244 write("Der Name ist schon gebannt.\n");
245 return 0;
246 }
247
Zesstra85576452017-01-30 15:43:21 +0100248 if (!find_userinfo(name))
249 {
MG Mud User88f12472016-06-24 23:31:02 +0200250 write("Es existiert kein Spieler dieses Namens!\n");
251 return 0;
252 }
253
254 if (!days && member(tbanished, name))
255 m_delete(tbanished, name);
256 else {
257 if (!mappingp(tbanished))
258 tbanished = ([]);
259 if (days <= -1)
260 t = -1;
261 else
262 t = (time()/86400)*86400 + days*86400;
263 tbanished += ([ name : t ]);
264 }
265
266 UpdateTBanish();
267 return 1;
268}
269
270
271mixed QuerySBanished( string ip )
272{
273 int save_me, site;
274 string banished_meldung =
275 "\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
276 "ausschliesslich\nAerger machen wollten. Deshalb haben wir "
277 "die Moeglichkeit,\neinfach neue Charaktere "
278 "anzulegen, kurzzeitig fuer diese Adresse gesperrt.\n\n"
279 "Falls Du bei uns spielen moechtest, schicke bitte eine Email "
280 "an\n\n mud@mg.mud.de\n\n"
281 "mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
282
283 if ( !ip || !stringp(ip) || !mappingp(sbanished) || !sizeof(sbanished) )
284 return 0;
285
286 foreach(site, int tstamp: sbanished) {
287 if ( tstamp > 0 && tstamp < time() ) {
288 m_delete( sbanished, site );
289 save_me=1;
290 }
291 }
292 if (save_me)
293 UpdateSBanish();
294
295 if ( !sizeof(sbanished) )
296 return 0;
297
298 site = IPv4_addr2int(ip);
299 if (!site)
300 return 0;
301 // direkt drin?
302 if (member(sbanished, site))
303 return banished_meldung;
304 // oder Netz dieser IP gesperrt?
305 foreach(int locked_site : sbanished) {
306 if ((locked_site & site) == locked_site)
307 return banished_meldung;
308 }
309
310 return 0;
311}
312
313
314private int _sbanished_by( int key, string name )
315{
316 return sbanished[key, 1] == name;
317}
318
319
320mixed SiteBanish( string ip, int days )
321{
322 string *s, tmp, euid;
323 int t, level;
324
325 euid = call_sefun("secure_euid");
326 level = call_sefun("secure_level");
327
328 // Unter L26 gibt's gar nix.
329 if ( level <= DOMAINMEMBER_LVL )
330 return -1;
331
332 // Die Liste der gesperrten IPs anschauen darf jeder ab L26.
333 if ( !ip && !days )
334 return copy(sbanished);
335
336
337 if ( !stringp(ip) || !intp(days) )
338 return 0;
339
340 if ( days && QuerySBanished(ip) ){
341 write( "Diese Adresse ist schon gebannt.\n" );
342 return 0;
343 }
344
345 if ( !days ) {
346 int int_ip = IPv4_addr2int(ip);
347
348 if( member(sbanished, int_ip) ){
349 // Fremde Sitebanishs duerfen nur Deputies loeschen.
350 if ( sbanished[int_ip, 1] != euid && !IsDeputy(euid) )
351 return -1;
352
353 call_sefun("log_file", "ARCH/SBANISH",
354 sprintf( "%s: %s hebt die Sperrung der Adresse %s von %s "
355 + "auf.\n",
356 ctime(time()), capitalize(euid), ip,
357 capitalize(sbanished[int_ip, 1]) ) );
358
359 m_delete( sbanished, int_ip );
360 }
361 else
362 return 0;
363 }
364 else {
365 // Alles, was nicht Deputy ist, darf nur fuer einen Tag sbanishen.
366 if ( days != 1 && !IsDeputy(euid) )
367 return -1;
368
369 // Nur Deputies duerfen mehr als 10 Sperrungen vornehmen.
370 if ( sizeof(filter_indices(sbanished, #'_sbanished_by, euid)) >= 10
371 && !IsDeputy(euid) )
372 return -2;
373
374 int int_ip = IPv4_addr2int(ip);
375
376 if(!int_ip) {
377 write( "Ungueltige Adresse!\n" );
378 return 0;
379 }
380
381 // L26 duerfen exakt eine IP sperren, RMs ganze Class C-Subnetze
382 // und Deputies auch Class B-Subnetze.
383 int nsize=IPv4_net_size(ip);
384 if ( nsize > 1 && level < LORD_LVL
385 || nsize > 255 && !IsDeputy(euid) )
386 return -1;
387
388 if ( !mappingp(sbanished) )
389 sbanished = m_allocate(1, 2);
390
391 if ( days < 0 )
392 t = -1;
393 else
394 t = (time() / 86400) * 86400 + days * 86400;
395
396 m_add(sbanished, int_ip, t, euid);
397
398 call_sefun("log_file", "ARCH/SBANISH",
399 sprintf( "%s: %s sperrt die Adresse %s %s.\n",
400 ctime(time()), capitalize(euid),
401 ip,
402 days > 0 ? (days > 1 ? "fuer " + days + " Tage"
403 : "fuer einen Tag")
404 : "bis zum St. Nimmerleinstag" ) );
405 }
406
407 UpdateSBanish();
408 return 1;
409}
410
411static void CheckDeputyRights()
412{
413 object ob;
414 mixed *ginfo;
415
416 // Lese- und Schreibberechtigungen fuer die Rubrik 'polizei' setzen
417 call_other( "/secure/news", "???" );
418 ob = find_object("secure/news");
419 ginfo = ((mixed)ob->GetGroup("polizei"))[5..6];
420 ob->RemoveAllowed( "polizei", 0, ginfo[0], ginfo[1] );
421 ob->AddAllowed( "polizei", 0, deputies, deputies );
422 LoadDeputyFileList();
423}
424
425int ReloadDeputyFile()
426{
427 deputies = efun::explode( read_file("/secure/DEPUTIES") || "", "\n" );
428 deputies -= ({""});
429 deputies = map( deputies, #'lower_case/*'*/ );
430 call_out( "CheckDeputyRights", 2 );
431 return(1);
432}
433
434
435static int create_home(string owner, int level)
436{
437 string def_castle;
438 string dest, castle, wizard;
439 object player;
440 string *domains;
441 int i;
442
443 player = call_sefun("find_player",owner);
444 if (!player)
445 return -5;
446 domains=get_domain_homes(owner);
447 if (!sizeof(domains) && level >= DOMAINMEMBER_LVL)
448 {
449 tell_object(player,"Du gehoerst noch keiner Region an !\n");
450 return -6;
451 }
452 tell_object(player,"Du bist Mitarbeiter der folgenden Regionen:\n");
453 for (i=0;i<sizeof(domains);i++)
454 {
455 if (i) tell_object(player,", ");
456 tell_object(player,domains[i]);
457 }
458 tell_object(player,".\n");
459 update_wiz_level(owner, level);
460 wizard = "/players/" + owner;
461 castle = "/players/" + owner + "/workroom.c";
462 if (file_size(wizard) == -1) {
463 tell_object(player, "Verzeichnis " + wizard + " angelegt\n");
464 mkdir(wizard);
465 }
466 dest = object_name(environment(player));
467 def_castle = read_file("/std/def_workroom.c");
468 if (file_size(castle) > 0) {
469 tell_object(player, "Du HATTEST ja schon ein Arbeitszimmer !\n");
470 } else {
471 if (write_file(castle, def_castle))
472 {
473 tell_object(player, "Arbeitszimmer " + castle + " erzeugt.\n");
474 // Arbeitszimmer als Home setzen
475 player->SetProp(P_START_HOME,castle[0..<3]);
476 }
477 else
478 tell_object(player, "Arbeitszimmer konnte nicht erzeugt werden !\n");
479 }
480 return 1;
481}
482
483// Sendet dem befoerderten Magier eine Hilfemail zu.
484protected void SendWizardHelpMail(string name, int level) {
485
486 object pl=call_sefun("find_player",name);
487 if (!objectp(pl)) return;
488
489 string file=sprintf("%sinfo_ml%d", WIZ_HELP_MAIL_DIR, level);
490 // wenn kein Hilfetext fuer den Level da ist: raus
491 if (file_size(file) <= 0)
492 return;
493
494 string subject = read_file(file,1,1);
495 string text = call_sefun("replace_personal",
496 read_file(file,2), ({pl}));
497
498 mixed mail = ({"Merlin", "<Master>", name, 0, 0, subject,
499 call_sefun("dtime",time()),
500 MUDNAME+time(), text });
501 MAILDEMON->DeliverMail(mail, 0);
502}
503
504int allowed_advance_wizlevel(mixed ob)
505{
506 string what;
507
508 if (objectp(ob) && geteuid(ob)==ROOTID) return 1;
509
510 if (!stringp(ob))
511 what=efun::explode(object_name(ob),"#")[0];
512 else
513 what=ob;
514
515 if (what=="/secure/merlin") return 1;
516
517 return 0;
518}
519
520int advance_wizlevel(string name, int level)
521{
522 int answer;
523 mixed *user;
524
525 if (!allowed_advance_wizlevel(PO))
526 return -1;
527
528 if (level>80) return -2;
529
530 if (!find_userinfo(name)) return -3;
531
532 user=get_full_userinfo(name);
533
534 if (user[USER_LEVEL+1]>level) return -4;
535
536 if (user[USER_LEVEL+1]==level) return 1;
537
538 if (level>=10 && level<20)
539 {
540 update_wiz_level(name, level);
541 SendWizardHelpMail(name, level);
542 return 1;
543 }
544 if (level>=20 && user[USER_LEVEL+1]<21)
545 {
546 answer = create_home(name, level);
547 if ( answer > 0 ) {
548 answer = update_wiz_level(name, level);
549 SendWizardHelpMail(name, level);
550 }
551 return answer;
552 }
553
554 update_wiz_level(name, level);
555 SendWizardHelpMail(name, level);
556
557 return 1;
558}
559
560void restart_heart_beat(object heart_beat)
561{
562 if (heart_beat) heart_beat->_restart_beat();
563}
564
565int renew_player_object(mixed who)
566{
567 object newob;
568 object *obs, *obs2;
569 mixed err;
570 string ob_name;
571 object *armours, weapon;
572 object tp;
573 int i,active,j;
574
575 if (stringp(who))
576 {
577 who=call_sefun("find_player",who);
578 if (!who)
579 {
580 who=call_sefun("find_netdead",who);
581 if (!who)
582 return -1;
583 }
584 }
585 if (!objectp(who))
586 return -2;
587 if (!object_info(who, OI_ONCE_INTERACTIVE))
588 return -3;
589 if (who->QueryGuest())
590 {
591 printf("Can't renew guests!\n");
592 return -6;
593 }
594 active=interactive(who);
595 printf("OK, renewing %O\n",who);
Zesstra86607bb2017-06-17 19:02:40 +0200596 efun::configure_object(this_object(), OC_EUID, geteuid(who));
MG Mud User88f12472016-06-24 23:31:02 +0200597 err=catch(newob=clone_object(query_player_object(getuid(who))); publish);
Zesstra86607bb2017-06-17 19:02:40 +0200598 efun::configure_object(this_object(), OC_EUID, getuid(TO));
MG Mud User88f12472016-06-24 23:31:02 +0200599 if (err)
600 {
601 printf("%O\n",err);
602 return -4;
603 }
604 if (!newob)
605 return -5;
606 /* Ok, the object is here now ... lets go for it ... */
607 who->save_me(0);
608 /* SSL ip weiterreichen */
609 if( call_sefun("query_ip_number", who) != efun::interactive_info(who,II_IP_NUMBER) )
610 {
611 newob->set_realip( call_sefun("query_ip_number",who) );
612 }
zesstraf06f57a2016-07-06 20:36:05 +0200613 efun::configure_object(who, OC_COMMANDS_ENABLED, 0);
MG Mud User88f12472016-06-24 23:31:02 +0200614 efun::set_this_player(0);
615 armours=(object *)who->QueryProp(P_ARMOURS);
616 weapon=(object)who->QueryProp(P_WEAPON);
617
618 if ( previous_object() && object_name(previous_object()) == "/secure/merlin" )
619 send_channel_msg("Debug",
620 previous_object(),
621 sprintf("RENEWING: %O %O\n",newob,who));
622 else
623 send_channel_msg("Entwicklung",
624 previous_object(),
625 sprintf("RENEWING: %O %O\n",newob,who));
626
627 ob_name=explode(object_name(newob),"#")[0];
628 if (sizeof(ob_name)>11 && ob_name[0..11]=="/std/shells/")
629 ob_name=ob_name[11..];
630 ob_name=ob_name+":"+getuid((object)who);
631 if (active)
632 exec(newob,who);
633 if (active && (interactive(who)||!interactive(newob)))
634 {
635 send_channel_msg("Debug",previous_object(),
636 "ERROR: still active !\n");
637 newob->remove();
638 return 0;
639 }
640// newob->start_player(capitalize(getuid(who)),who->_query_my_ip());
641 funcall(
642 bind_lambda(
643 unbound_lambda( ({'x, 'y}),
644 ({#'call_other/*'*/,
645 newob,
646 "start_player",
647 'x, 'y
648 })
649 ), who
650 ),
651 capitalize(getuid(who)), who->_query_my_ip() );
652
653 newob->move(environment(who),M_TPORT|M_NOCHECK|M_NO_SHOW|M_SILENT
654 |M_NO_ATTACK);
655 obs=all_inventory(who);
656 foreach(object tob: all_inventory(who)) {
657 if (!tob->QueryProp(P_AUTOLOADOBJ))
658 {
659 // kein Autoloader...
660 foreach(object ob: deep_inventory(tob))
661 {
662 // aber enthaltene Autoloader entsorgen...
663 if (ob->QueryProp(P_AUTOLOADOBJ))
664 {
665 catch(ob->remove();publish);
666 if (ob) destruct(ob);
667 }
668 }
669 // objekt ohne die AL bewegen.
670 catch(tob->move(newob,M_NOCHECK);publish);
671 }
672 else {
673 // Inhalt von Autoloadern retten.
674 // neue instanz des ALs im neuen Objekt.
675 object new_al_instance = present_clone(tob, newob);
676 foreach(object ob: deep_inventory(tob)) {
677 if (ob->QueryProp(P_AUTOLOADOBJ)) {
678 // autoloader in Autoloadern zerstoeren...
679 catch(ob->remove(1);publish);
680 if (ob) destruct(ob);
681 }
682 // alle nicht autoloader in die AL-Instanz im neuen Objekt oder
683 // notfalls ins Inv.
684 else {
685 if (objectp(new_al_instance))
686 catch(ob->move(new_al_instance, M_NOCHECK);publish);
687 else
688 catch(ob->move(newob, M_NOCHECK);publish);
689 }
690 }
691 // Autoloader zerstoeren. Wird nicht vom Spielerobjekt im remove()
692 // gemacht, wenn nicht NODROP.
693 catch(tob->remove(1);publish);
694 if (tob) destruct(tob);
695 }
696 }
697 who->remove();
698 if ( objectp(who) )
699 destruct(who);
700 rename_object(newob,ob_name);
701 newob->__reload_explore();
702 tp=this_player();
703 efun::set_this_player(newob);
704 if (objectp(weapon))
705 weapon->DoWield();
706 if (pointerp(armours))
707 for (i=sizeof(armours)-1;i>=0;i--)
708 if (objectp(armours[i]))
709 armours[i]->do_wear("alles");
710 efun::set_this_player(tp);
711 //Rueckgabewert noetig, weil Funktion vom Typ 'int'
712 return(1);
713}
714
715mixed __query_variable(object ob, string var)
716{
717 if (!PO || !IS_ARCH(geteuid(PO)) || !this_interactive() ||
718 !IS_ARCH(this_interactive()) || getuid(ob)==ROOTID )
719 {
720 write("Du bist kein EM oder Gott!\n");
721 return 0;
722 }
723 if (efun::object_info(ob, OI_ONCE_INTERACTIVE) && (PO!=ob) &&
724 (var=="history" || var=="hist2"))
725 send_channel_msg("Snoop", previous_object(),
726 sprintf("%s -> %s (history).",
727 capitalize(getuid(PO)),capitalize(getuid(ob))));
728
729 call_sefun("log_file", "ARCH/QV",
730 sprintf("%s: %O inquires var %s in %O\n",
731 ctime(time()),this_interactive(),var,ob) );
732 mixed res = variable_list(ob, RETURN_FUNCTION_NAME|RETURN_FUNCTION_FLAGS|
733 RETURN_FUNCTION_TYPE|RETURN_VARIABLE_VALUE);
734 int index = member(res,var);
735 if (index > -1)
736 {
737 return ({res[index],res[index+1],res[index+2],res[index+3]});
738 }
739 return 0;
740}
741
742void RestartBeats()
743{
744 int i,size,counter;
745 object ob;
746 mixed *list;
747 string file,obname,fun;
748
749 "/secure/simul_efun"->StopCallOut(0);
750 write("CALL_OUT-Restart in progress !\n");
751 filter(users(),#'tell_object,"CALL_OUT-Restart in progress !\n");
752 size=file_size("log/call_out_stop");
753 if (size<=0)
754 return;
755 file="";
756 counter=0;
757 while (counter<size)
758 {
759 file+=read_bytes("log/call_out_stop",counter,
760 (size-(counter+=40000)>0?counter:size));
761 }
762 list=explode(file,"__(CUT HERE)__\n");
763 list=list[<1..];
764 list=explode(list[0],"\n")-({""});
765 for (i=sizeof(list)-1;i>=0;i--)
766 if (sscanf(list[i],"%s \"%s\"",obname,fun)==2 && ob=find_object(obname))
767 {
768 write(sprintf("%O -> %s\n",ob,fun));
769 catch(ob->__restart(fun);publish);
770 }
771 write("CALL_OUT-Restart completed !\n");
772 filter(users(),#'tell_object,"CALL_OUT-Restart completed !\n");
773 rename("log/call_out_stop","log/call_out_stop.old");
774}
775