Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/secure/master/misc.c b/secure/master/misc.c
new file mode 100644
index 0000000..02b0fcc
--- /dev/null
+++ b/secure/master/misc.c
@@ -0,0 +1,772 @@
+// MorgenGrauen MUDlib
+//
+// master/misc.c -- Diverses: (T)Banish, Projektverwaltung, Levelaufstieg, ...
+//
+// $Id: misc.c 9467 2016-02-19 19:48:24Z Zesstra $
+
+#pragma strict_types,rtt_checks
+
+#include "/sys/functionlist.h"
+#include "/sys/lpctypes.h"
+#include "/sys/object_info.h"
+#include "/sys/interactive_info.h"
+
+#include "/secure/master.h"
+#include "/mail/post.h"
+#include "/sys/thing/language.h"
+#include "/sys/thing/description.h"
+
+// Fuer CIDR-Notatio im sbanish
+#include "/secure/master/cidr.c"
+
+static mixed *banished;
+static mapping tbanished, sbanished;
+static string *deputies;
+
+// TODO: muss ggf. Fakeobjekt erzeugen+uebergeben, wenn sender kein object
+protected void send_channel_msg(string channel,mixed sendername,string msg)
+{
+ object sender;
+ if (objectp(sendername))
+ sender=sendername;
+ else
+ {
+ // wenn kein Objekt uebergeben, erstmal schauen, ob ein Spielerobject
+ // existiert... Wenn ja, das nehmen.
+ sender = call_sefun("find_player", sendername)
+ || call_sefun("find_netdead", sendername);
+ if (!objectp(sender))
+ {
+ // sonst faken wir eins. *seufz*
+ sender=clone_object("/p/daemon/namefake");
+ sender->SetProp(P_NAME, sendername);
+ sender->SetProp(P_ARTICLE,0);
+ // Dieses Objekt zerstoert sich nach 3s automatisch.
+ }
+ }
+ CHMASTER->send(channel, sender, msg);
+}
+
+static string *explode_files(string file) {
+ string data;
+ mixed *exploded;
+ int i;
+
+ data=read_file(file);
+ if (!data || !stringp(data) || data == "") return ({});
+ exploded = efun::explode(data,"\n");
+ for (i=sizeof(exploded);i--;)
+ if (!stringp(exploded[i]) || exploded[i]=="" || exploded[i][0]=='#')
+ exploded[i]=0;
+ exploded-=({0});
+ printf("%-30s: %3d Objekt%s\n",file,i=sizeof(exploded),(i==1?"":"e"));
+ return exploded;
+}
+
+void UpdateTBanish();
+
+mixed QueryBanished(string str){
+ int i;
+
+ if (!str) return 0;
+ if (!pointerp(banished)) return 0;
+ for (i=sizeof(banished)-1;i>=0;i--)
+ if (sizeof(regexp(({str}),"^"+banished[i][0]+"$")))
+ {
+ if (!banished[i][1] || banished[i][1]=="")
+ return "Dieser Name ist gesperrt.";
+ else
+ return banished[i][1];
+ }
+ return 0;
+}
+
+mixed QueryTBanished(string str) {
+ int i;
+
+ if (!str || !mappingp(tbanished) || !(i=tbanished[str]))
+ return 0;
+
+ if (i == -1 || i > time())
+ return sprintf("Es gibt schon einen Spieler diesen Namens.\n"
+ +"Allerdings kann er/sie erst am %s wieder ins Mud kommen.\n",
+ (i == -1 ? "St. Nimmerleinstag" :
+ call_sefun("dtime",i)[0..16]));
+
+// Ansonsten: die Zeit ist abgelaufen, Spieler darf wieder...
+ m_delete(tbanished, str);
+ UpdateTBanish();
+ return 0;
+}
+
+void ReloadBanishFile(){
+ int i, t;
+ string s1, s2, *s;
+
+ banished = efun::explode( read_file("/secure/BANISH") || "", "\n" );
+ banished = banished - ({""});
+ for ( i = sizeof(banished); i--; ){
+ s = efun::explode( banished[i], " " );
+ s1 = s[0];
+ s2 = implode( s[1..], " " );
+ banished[i] = ({ s1, s2 });
+ }
+
+ if ( !mappingp(tbanished) ){
+ tbanished = ([]);
+
+ s = efun::explode( read_file("/secure/TBANISH") || "", "\n" );
+ s -= ({""});
+
+ for ( i = sizeof(s); i--; ){
+ sscanf( s[i], "%s:%d", s1, t );
+ tbanished += ([ s1 : t ]);
+ }
+ }
+
+ if ( !mappingp(sbanished) ){
+ sbanished = m_allocate(3, 2);
+
+ s = efun::explode( read_file("/secure/SITEBANISH") || "", "\n" );
+ s -= ({""});
+
+ for ( i = sizeof(s); i--; ) {
+ int int_ip;
+ sscanf( s[i], "%s:%d:%s", s1, t, s2 );
+ int_ip = IPv4_addr2int(s1);
+ m_add(sbanished, int_ip, t, s2);
+ }
+ }
+}
+
+int IsDeputy(mixed name)
+{
+ if ( IS_ARCH(name) )
+ return 1;
+
+ if ( objectp(name) )
+ name = getuid(name);
+
+ if ( member( deputies || ({}), name ) >= 0 )
+ return 1;
+
+ return 0;
+}
+
+
+varargs void BanishName( string name, string reason, int force )
+{
+ string *names;
+ int i;
+
+ if ( PO != TO && call_sefun("secure_level") < LORD_LVL
+ && !IsDeputy( call_sefun("secure_euid") ) )
+ return;
+
+ if ( !stringp(name) || !sizeof(name) )
+ return;
+
+ name = lower_case(name);
+
+ if ( !reason || !stringp(reason) )
+ reason = "";
+
+ if ( QueryBanished(name) && lower_case(reason) != "loeschen" ){
+ write("Der Name ist schon gebannt.\n");
+ return;
+ }
+
+ if ( !force && file_size(SAVEPATH+name[0..0]+"/"+name+".o") > 0 ){
+ write("Es existiert bereits ein Spieler dieses Namens.\n");
+ return;
+ }
+
+/* if (!("/secure/login"->valid_name(name))) return;*/
+ if ( lower_case(reason) != "loeschen" ){
+ names = ({ name + " " + reason });
+
+ for ( i = sizeof(banished); i--; )
+ names += ({ banished[i][0] + " " + banished[i][1] });
+ }
+ else{
+ names = ({});
+
+ for ( i = sizeof(banished); i--; )
+ if ( banished[i][0] != name )
+ names += ({ banished[i][0] + " " + banished[i][1] });
+ }
+
+ names = sort_array( names, #'> );
+ rm("/secure/BANISH");
+ write_file( "/secure/BANISH", implode(names, "\n") );
+ write( "Okay, '"+capitalize(name)+"' ist jetzt "+
+ (lower_case(reason) == "loeschen" ? "nicht mehr " : "")+"gebanisht.\n" );
+ ReloadBanishFile();
+}
+
+void UpdateTBanish()
+{
+ int i;
+ string *names;
+
+ for (i=sizeof(names = sort_array(m_indices(tbanished), #'</*'*/))-1;i>=0;i--)
+ names[i] = sprintf("%s:%d", names[i], tbanished[names[i]]);
+
+ rm("/secure/TBANISH");
+ write_file("/secure/TBANISH", implode(names, "\n"));
+}
+
+void UpdateSBanish()
+{
+ int i;
+ mapping lines = m_allocate(sizeof(sbanished),0);
+
+ foreach(int ip, int tstamp, string user : sbanished) {
+ m_add(lines, sprintf("%s:%d:%s",
+ IPv4_int2addr(ip), tstamp, user));
+ }
+
+ write_file( "/secure/SITEBANISH",
+ implode( sort_array (m_indices(lines), #'<), "\n" ), 1);
+}
+
+int TBanishName(string name, int days)
+{
+ int t;
+
+ if ( (getuid(TI) != name) &&
+ !IsDeputy( call_sefun("secure_euid") ) )
+ return 0;
+
+ if (days && QueryTBanished(name)){
+ write("Der Name ist schon gebannt.\n");
+ return 0;
+ }
+
+ if (file_size(SAVEPATH+name[0..0]+"/"+name+".o")<=0){
+ write("Es existiert kein Spieler dieses Namens!\n");
+ return 0;
+ }
+
+ if (!days && member(tbanished, name))
+ m_delete(tbanished, name);
+ else {
+ if (!mappingp(tbanished))
+ tbanished = ([]);
+ if (days <= -1)
+ t = -1;
+ else
+ t = (time()/86400)*86400 + days*86400;
+ tbanished += ([ name : t ]);
+ }
+
+ UpdateTBanish();
+ return 1;
+}
+
+
+mixed QuerySBanished( string ip )
+{
+ int save_me, site;
+ string banished_meldung =
+ "\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
+ "ausschliesslich\nAerger machen wollten. Deshalb haben wir "
+ "die Moeglichkeit,\neinfach neue Charaktere "
+ "anzulegen, kurzzeitig fuer diese Adresse gesperrt.\n\n"
+ "Falls Du bei uns spielen moechtest, schicke bitte eine Email "
+ "an\n\n mud@mg.mud.de\n\n"
+ "mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
+
+ if ( !ip || !stringp(ip) || !mappingp(sbanished) || !sizeof(sbanished) )
+ return 0;
+
+ foreach(site, int tstamp: sbanished) {
+ if ( tstamp > 0 && tstamp < time() ) {
+ m_delete( sbanished, site );
+ save_me=1;
+ }
+ }
+ if (save_me)
+ UpdateSBanish();
+
+ if ( !sizeof(sbanished) )
+ return 0;
+
+ site = IPv4_addr2int(ip);
+ if (!site)
+ return 0;
+ // direkt drin?
+ if (member(sbanished, site))
+ return banished_meldung;
+ // oder Netz dieser IP gesperrt?
+ foreach(int locked_site : sbanished) {
+ if ((locked_site & site) == locked_site)
+ return banished_meldung;
+ }
+
+ return 0;
+}
+
+
+private int _sbanished_by( int key, string name )
+{
+ return sbanished[key, 1] == name;
+}
+
+
+mixed SiteBanish( string ip, int days )
+{
+ string *s, tmp, euid;
+ int t, level;
+
+ euid = call_sefun("secure_euid");
+ level = call_sefun("secure_level");
+
+ // Unter L26 gibt's gar nix.
+ if ( level <= DOMAINMEMBER_LVL )
+ return -1;
+
+ // Die Liste der gesperrten IPs anschauen darf jeder ab L26.
+ if ( !ip && !days )
+ return copy(sbanished);
+
+
+ if ( !stringp(ip) || !intp(days) )
+ return 0;
+
+ if ( days && QuerySBanished(ip) ){
+ write( "Diese Adresse ist schon gebannt.\n" );
+ return 0;
+ }
+
+ if ( !days ) {
+ int int_ip = IPv4_addr2int(ip);
+
+ if( member(sbanished, int_ip) ){
+ // Fremde Sitebanishs duerfen nur Deputies loeschen.
+ if ( sbanished[int_ip, 1] != euid && !IsDeputy(euid) )
+ return -1;
+
+ call_sefun("log_file", "ARCH/SBANISH",
+ sprintf( "%s: %s hebt die Sperrung der Adresse %s von %s "
+ + "auf.\n",
+ ctime(time()), capitalize(euid), ip,
+ capitalize(sbanished[int_ip, 1]) ) );
+
+ m_delete( sbanished, int_ip );
+ }
+ else
+ return 0;
+ }
+ else {
+ // Alles, was nicht Deputy ist, darf nur fuer einen Tag sbanishen.
+ if ( days != 1 && !IsDeputy(euid) )
+ return -1;
+
+ // Nur Deputies duerfen mehr als 10 Sperrungen vornehmen.
+ if ( sizeof(filter_indices(sbanished, #'_sbanished_by, euid)) >= 10
+ && !IsDeputy(euid) )
+ return -2;
+
+ int int_ip = IPv4_addr2int(ip);
+
+ if(!int_ip) {
+ write( "Ungueltige Adresse!\n" );
+ return 0;
+ }
+
+ // L26 duerfen exakt eine IP sperren, RMs ganze Class C-Subnetze
+ // und Deputies auch Class B-Subnetze.
+ int nsize=IPv4_net_size(ip);
+ if ( nsize > 1 && level < LORD_LVL
+ || nsize > 255 && !IsDeputy(euid) )
+ return -1;
+
+ if ( !mappingp(sbanished) )
+ sbanished = m_allocate(1, 2);
+
+ if ( days < 0 )
+ t = -1;
+ else
+ t = (time() / 86400) * 86400 + days * 86400;
+
+ m_add(sbanished, int_ip, t, euid);
+
+ call_sefun("log_file", "ARCH/SBANISH",
+ sprintf( "%s: %s sperrt die Adresse %s %s.\n",
+ ctime(time()), capitalize(euid),
+ ip,
+ days > 0 ? (days > 1 ? "fuer " + days + " Tage"
+ : "fuer einen Tag")
+ : "bis zum St. Nimmerleinstag" ) );
+ }
+
+ UpdateSBanish();
+ return 1;
+}
+
+static void CheckDeputyRights()
+{
+ object ob;
+ mixed *ginfo;
+
+ // Lese- und Schreibberechtigungen fuer die Rubrik 'polizei' setzen
+ call_other( "/secure/news", "???" );
+ ob = find_object("secure/news");
+ ginfo = ((mixed)ob->GetGroup("polizei"))[5..6];
+ ob->RemoveAllowed( "polizei", 0, ginfo[0], ginfo[1] );
+ ob->AddAllowed( "polizei", 0, deputies, deputies );
+ LoadDeputyFileList();
+}
+
+int ReloadDeputyFile()
+{
+ deputies = efun::explode( read_file("/secure/DEPUTIES") || "", "\n" );
+ deputies -= ({""});
+ deputies = map( deputies, #'lower_case/*'*/ );
+ call_out( "CheckDeputyRights", 2 );
+ return(1);
+}
+
+
+static int create_home(string owner, int level)
+{
+ string def_castle;
+ string dest, castle, wizard;
+ object player;
+ string *domains;
+ int i;
+
+ player = call_sefun("find_player",owner);
+ if (!player)
+ return -5;
+ domains=get_domain_homes(owner);
+ if (!sizeof(domains) && level >= DOMAINMEMBER_LVL)
+ {
+ tell_object(player,"Du gehoerst noch keiner Region an !\n");
+ return -6;
+ }
+ tell_object(player,"Du bist Mitarbeiter der folgenden Regionen:\n");
+ for (i=0;i<sizeof(domains);i++)
+ {
+ if (i) tell_object(player,", ");
+ tell_object(player,domains[i]);
+ }
+ tell_object(player,".\n");
+ update_wiz_level(owner, level);
+ wizard = "/players/" + owner;
+ castle = "/players/" + owner + "/workroom.c";
+ if (file_size(wizard) == -1) {
+ tell_object(player, "Verzeichnis " + wizard + " angelegt\n");
+ mkdir(wizard);
+ }
+ dest = object_name(environment(player));
+ def_castle = read_file("/std/def_workroom.c");
+ if (file_size(castle) > 0) {
+ tell_object(player, "Du HATTEST ja schon ein Arbeitszimmer !\n");
+ } else {
+ if (write_file(castle, def_castle))
+ {
+ tell_object(player, "Arbeitszimmer " + castle + " erzeugt.\n");
+ // Arbeitszimmer als Home setzen
+ player->SetProp(P_START_HOME,castle[0..<3]);
+ }
+ else
+ tell_object(player, "Arbeitszimmer konnte nicht erzeugt werden !\n");
+ }
+ return 1;
+}
+
+// Sendet dem befoerderten Magier eine Hilfemail zu.
+protected void SendWizardHelpMail(string name, int level) {
+
+ object pl=call_sefun("find_player",name);
+ if (!objectp(pl)) return;
+
+ string file=sprintf("%sinfo_ml%d", WIZ_HELP_MAIL_DIR, level);
+ // wenn kein Hilfetext fuer den Level da ist: raus
+ if (file_size(file) <= 0)
+ return;
+
+ string subject = read_file(file,1,1);
+ string text = call_sefun("replace_personal",
+ read_file(file,2), ({pl}));
+
+ mixed mail = ({"Merlin", "<Master>", name, 0, 0, subject,
+ call_sefun("dtime",time()),
+ MUDNAME+time(), text });
+ MAILDEMON->DeliverMail(mail, 0);
+}
+
+int allowed_advance_wizlevel(mixed ob)
+{
+ string what;
+
+ if (objectp(ob) && geteuid(ob)==ROOTID) return 1;
+
+ if (!stringp(ob))
+ what=efun::explode(object_name(ob),"#")[0];
+ else
+ what=ob;
+
+ if (what=="/secure/merlin") return 1;
+
+ return 0;
+}
+
+int advance_wizlevel(string name, int level)
+{
+ int answer;
+ mixed *user;
+
+ if (!allowed_advance_wizlevel(PO))
+ return -1;
+
+ if (level>80) return -2;
+
+ if (!find_userinfo(name)) return -3;
+
+ user=get_full_userinfo(name);
+
+ if (user[USER_LEVEL+1]>level) return -4;
+
+ if (user[USER_LEVEL+1]==level) return 1;
+
+ if (level>=10 && level<20)
+ {
+ update_wiz_level(name, level);
+ SendWizardHelpMail(name, level);
+ return 1;
+ }
+ if (level>=20 && user[USER_LEVEL+1]<21)
+ {
+ answer = create_home(name, level);
+ if ( answer > 0 ) {
+ answer = update_wiz_level(name, level);
+ SendWizardHelpMail(name, level);
+ }
+ return answer;
+ }
+
+ update_wiz_level(name, level);
+ SendWizardHelpMail(name, level);
+
+ return 1;
+}
+
+void restart_heart_beat(object heart_beat)
+{
+ if (heart_beat) heart_beat->_restart_beat();
+}
+
+int renew_player_object(mixed who)
+{
+ object newob;
+ object *obs, *obs2;
+ mixed err;
+ string ob_name;
+ object *armours, weapon;
+ object tp;
+ int i,active,j;
+
+ if (stringp(who))
+ {
+ who=call_sefun("find_player",who);
+ if (!who)
+ {
+ who=call_sefun("find_netdead",who);
+ if (!who)
+ return -1;
+ }
+ }
+ if (!objectp(who))
+ return -2;
+ if (!object_info(who, OI_ONCE_INTERACTIVE))
+ return -3;
+ if (who->QueryGuest())
+ {
+ printf("Can't renew guests!\n");
+ return -6;
+ }
+ active=interactive(who);
+ printf("OK, renewing %O\n",who);
+ seteuid(geteuid(who));
+ err=catch(newob=clone_object(query_player_object(getuid(who))); publish);
+ seteuid(getuid(TO));
+ if (err)
+ {
+ printf("%O\n",err);
+ return -4;
+ }
+ if (!newob)
+ return -5;
+ /* Ok, the object is here now ... lets go for it ... */
+ who->save_me(0);
+ /* SSL ip weiterreichen */
+ if( call_sefun("query_ip_number", who) != efun::interactive_info(who,II_IP_NUMBER) )
+ {
+ newob->set_realip( call_sefun("query_ip_number",who) );
+ }
+ efun::configure_object(previous_object(), OC_COMMANDS_ENABLED, 0);
+ efun::set_this_player(0);
+ armours=(object *)who->QueryProp(P_ARMOURS);
+ weapon=(object)who->QueryProp(P_WEAPON);
+
+ if ( previous_object() && object_name(previous_object()) == "/secure/merlin" )
+ send_channel_msg("Debug",
+ previous_object(),
+ sprintf("RENEWING: %O %O\n",newob,who));
+ else
+ send_channel_msg("Entwicklung",
+ previous_object(),
+ sprintf("RENEWING: %O %O\n",newob,who));
+
+ ob_name=explode(object_name(newob),"#")[0];
+ if (sizeof(ob_name)>11 && ob_name[0..11]=="/std/shells/")
+ ob_name=ob_name[11..];
+ ob_name=ob_name+":"+getuid((object)who);
+ if (active)
+ exec(newob,who);
+ if (active && (interactive(who)||!interactive(newob)))
+ {
+ send_channel_msg("Debug",previous_object(),
+ "ERROR: still active !\n");
+ newob->remove();
+ return 0;
+ }
+// newob->start_player(capitalize(getuid(who)),who->_query_my_ip());
+ funcall(
+ bind_lambda(
+ unbound_lambda( ({'x, 'y}),
+ ({#'call_other/*'*/,
+ newob,
+ "start_player",
+ 'x, 'y
+ })
+ ), who
+ ),
+ capitalize(getuid(who)), who->_query_my_ip() );
+
+ newob->move(environment(who),M_TPORT|M_NOCHECK|M_NO_SHOW|M_SILENT
+ |M_NO_ATTACK);
+ obs=all_inventory(who);
+ foreach(object tob: all_inventory(who)) {
+ if (!tob->QueryProp(P_AUTOLOADOBJ))
+ {
+ // kein Autoloader...
+ foreach(object ob: deep_inventory(tob))
+ {
+ // aber enthaltene Autoloader entsorgen...
+ if (ob->QueryProp(P_AUTOLOADOBJ))
+ {
+ catch(ob->remove();publish);
+ if (ob) destruct(ob);
+ }
+ }
+ // objekt ohne die AL bewegen.
+ catch(tob->move(newob,M_NOCHECK);publish);
+ }
+ else {
+ // Inhalt von Autoloadern retten.
+ // neue instanz des ALs im neuen Objekt.
+ object new_al_instance = present_clone(tob, newob);
+ foreach(object ob: deep_inventory(tob)) {
+ if (ob->QueryProp(P_AUTOLOADOBJ)) {
+ // autoloader in Autoloadern zerstoeren...
+ catch(ob->remove(1);publish);
+ if (ob) destruct(ob);
+ }
+ // alle nicht autoloader in die AL-Instanz im neuen Objekt oder
+ // notfalls ins Inv.
+ else {
+ if (objectp(new_al_instance))
+ catch(ob->move(new_al_instance, M_NOCHECK);publish);
+ else
+ catch(ob->move(newob, M_NOCHECK);publish);
+ }
+ }
+ // Autoloader zerstoeren. Wird nicht vom Spielerobjekt im remove()
+ // gemacht, wenn nicht NODROP.
+ catch(tob->remove(1);publish);
+ if (tob) destruct(tob);
+ }
+ }
+ who->remove();
+ if ( objectp(who) )
+ destruct(who);
+ rename_object(newob,ob_name);
+ newob->__reload_explore();
+ tp=this_player();
+ efun::set_this_player(newob);
+ if (objectp(weapon))
+ weapon->DoWield();
+ if (pointerp(armours))
+ for (i=sizeof(armours)-1;i>=0;i--)
+ if (objectp(armours[i]))
+ armours[i]->do_wear("alles");
+ efun::set_this_player(tp);
+ //Rueckgabewert noetig, weil Funktion vom Typ 'int'
+ return(1);
+}
+
+mixed __query_variable(object ob, string var)
+{
+ if (!PO || !IS_ARCH(geteuid(PO)) || !this_interactive() ||
+ !IS_ARCH(this_interactive()) || getuid(ob)==ROOTID )
+ {
+ write("Du bist kein EM oder Gott!\n");
+ return 0;
+ }
+ if (efun::object_info(ob, OI_ONCE_INTERACTIVE) && (PO!=ob) &&
+ (var=="history" || var=="hist2"))
+ send_channel_msg("Snoop", previous_object(),
+ sprintf("%s -> %s (history).",
+ capitalize(getuid(PO)),capitalize(getuid(ob))));
+
+ call_sefun("log_file", "ARCH/QV",
+ sprintf("%s: %O inquires var %s in %O\n",
+ ctime(time()),this_interactive(),var,ob) );
+ mixed res = variable_list(ob, RETURN_FUNCTION_NAME|RETURN_FUNCTION_FLAGS|
+ RETURN_FUNCTION_TYPE|RETURN_VARIABLE_VALUE);
+ int index = member(res,var);
+ if (index > -1)
+ {
+ return ({res[index],res[index+1],res[index+2],res[index+3]});
+ }
+ return 0;
+}
+
+void RestartBeats()
+{
+ int i,size,counter;
+ object ob;
+ mixed *list;
+ string file,obname,fun;
+
+ "/secure/simul_efun"->StopCallOut(0);
+ write("CALL_OUT-Restart in progress !\n");
+ filter(users(),#'tell_object,"CALL_OUT-Restart in progress !\n");
+ size=file_size("log/call_out_stop");
+ if (size<=0)
+ return;
+ file="";
+ counter=0;
+ while (counter<size)
+ {
+ file+=read_bytes("log/call_out_stop",counter,
+ (size-(counter+=40000)>0?counter:size));
+ }
+ list=explode(file,"__(CUT HERE)__\n");
+ list=list[<1..];
+ list=explode(list[0],"\n")-({""});
+ for (i=sizeof(list)-1;i>=0;i--)
+ if (sscanf(list[i],"%s \"%s\"",obname,fun)==2 && ob=find_object(obname))
+ {
+ write(sprintf("%O -> %s\n",ob,fun));
+ catch(ob->__restart(fun);publish);
+ }
+ write("CALL_OUT-Restart completed !\n");
+ filter(users(),#'tell_object,"CALL_OUT-Restart completed !\n");
+ rename("log/call_out_stop","log/call_out_stop.old");
+}
+