View Issue Details

IDProjectCategoryView StatusLast Update
0000683LDMud 3.3Runtimepublic2018-01-29 21:57
Reporterfavoretti Assigned ToGnomi  
PrioritynormalSeveritycrashReproducibilityrandom
Status resolvedResolutionfixed 
Target Version3.3.720Fixed in Version3.3.720 
Summary0000683: 2 crashes on 3.3.719 x64
DescriptionHi there,

Been running x64 version for about a week now, so far had 2 crashes that I can't really explain. Any thoughts? Backtraces follow:

(gdb) bt
#0 fatal (fmt=0x4cf720 "(free_svalue) Illegal svalue %p type %d\n") at simulate.c:593
0000001 0x000000000043d564 in int_free_svalue (v=0x7f961cf0bad0) at interpret.c:1136
0000002 0x000000000044a504 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:8865
0000003 0x00000000004579e7 in apply_low (fun=0x7f961cf0baf0, ob=0x7f9643ebe488, num_arg=0, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17442
0000004 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
0000005 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:16645
0000006 0x00000000004579e7 in apply_low (fun=0x7f961d039ba8, ob=0x7f9643ebe488, num_arg=0, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17442
0000007 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
0000008 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:16645
0000009 0x0000000000458a0b in int_call_lambda (lsvp=<value optimized out>, num_arg=1, allowRefs=false, external=true)
    at interpret.c:18447
0000010 0x0000000000458bc3 in v_funcall (sp=0x713100, num_arg=2) at interpret.c:21003
0000011 0x000000000044b621 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:8475
0000012 0x000000000045871c in int_call_lambda (lsvp=0x7e40e0, num_arg=<value optimized out>, allowRefs=false,
    external=true) at interpret.c:18589
0000013 0x000000000047aa12 in reset_object (ob=0x7f9643ebe488, arg=96) at object.c:875
#14 0x00000000004ae1d5 in clone_object (str1=<value optimized out>) at simulate.c:2356
#15 0x00000000004ae421 in f_clone_object (sp=0x7130c0) at simulate.c:4376
#16 0x000000000044ee88 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:8276
#17 0x00000000004ad373 in catch_instruction (flags=0, offset=<value optimized out>, i_sp=0x7e3d50,
    i_pc=0x7f961d4d1a4b "e\b\002\037\001??g?4", i_fp=<value optimized out>, reserve_cost=150000, i_context=0x0)
    at simulate.c:455
#18 0x000000000044a9a2 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:9730
#19 0x00000000004579e7 in apply_low (fun=0x7f961d041110, ob=0x10680, num_arg=1, b_ign_prot=false, allowRefs=false,
    b_ign_shadows=false) at interpret.c:17442
#20 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
#21 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:16645
#22 0x00000000004579e7 in apply_low (fun=0x7f961d039ba8, ob=0x2f280, num_arg=0, b_ign_prot=false, allowRefs=false,
    b_ign_shadows=false) at interpret.c:17442
#23 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
#24 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
#25 0x0000000000458a0b in int_call_lambda (lsvp=<value optimized out>, num_arg=1, allowRefs=false, external=true)
    at interpret.c:18447
#26 0x0000000000458bc3 in v_funcall (sp=0x712fb0, num_arg=2) at interpret.c:21003
#27 0x000000000044b621 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:8475
#28 0x000000000045871c in int_call_lambda (lsvp=0x7e40d0, num_arg=<value optimized out>, allowRefs=false,
    external=true) at interpret.c:18589
#29 0x000000000047aa12 in reset_object (ob=0x7f95fbc490e0, arg=80) at object.c:875
#30 0x00000000004aa17d in load_object (lname=<value optimized out>, create_super=false, depth=0, isMasterObj=false,
    chain=0x0) at simulate.c:2144
#31 0x00000000004aa454 in lookfor_object (str=<value optimized out>, bLoad=true) at simulate.c:2412
#32 0x0000000000452cb0 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:16618
#33 0x00000000004ad373 in catch_instruction (flags=0, offset=<value optimized out>, i_sp=0x7e3d50,
    i_pc=0x7f961d4e442c "e\037\006\n\"?f\003t\200\002*?f)\002\037\002n\020\n#\b\025\037\001??,\n$,\003~\020\030\037\006\003u\200\006+e?\n-eg?5", i_fp=<value optimized out>, reserve_cost=150000, i_context=0x0) at simulate.c:455
#34 0x000000000044a9a2 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:9730
#35 0x00000000004579e7 in apply_low (fun=0x7f961cf2d358, ob=0x7f963a9e06e0, num_arg=1, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17442
0000036 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
0000037 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>)
    at interpret.c:16645
0000038 0x00000000004579e7 in apply_low (fun=0x7f961cf91fb8, ob=0x7f9622f967e0, num_arg=0, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17442
0000039 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
0000040 0x00000000004480cb in sapply_int (fun=0x7f961cf91fb8, ob=0x7f9622f967e0, num_arg=0, b_find_static=120,
    b_use_default=true) at interpret.c:17707
0000041 0x000000000040718c in parse_command (buff=0x7fff4ef350c0 "west", from_efun=false) at actions.c:1162
0000042 0x0000000000408711 in execute_command (str=0x7fff4ef350c0 "west", ob=0x7f9622f967e0) at actions.c:1333
0000043 0x000000000040f2bf in backend () at backend.c:696
0000044 0x0000000000464f36 in main (argc=<value optimized out>, argv=<value optimized out>) at main.c:688





Second one:


(gdb) bt
#0 0x000000000041d8e6 in add_message (fmt=0x0) at comm.c:2118
0000001 0x00000000004a74d6 in print_svalue (arg=0xb7) at simulate.c:4328
0000002 0x00000000004a7652 in f_write (sp=0x713030) at simulate.c:4961
0000003 0x000000000044ee88 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:8276
0000004 0x00000000004579e7 in apply_low (fun=0x7fa1687b89c8, ob=0x7fa112564180, num_arg=0, b_ign_prot=false, allowRefs=false, b_ign_shadows=false)
    at interpret.c:17442
0000005 0x0000000000447584 in int_apply (fun=0x0, ob=0xb7, num_arg=183, b_ign_prot=<value optimized out>, b_use_default=true, b_ign_shadows=<value optimized out>)
    at interpret.c:17546
0000006 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:16645
0000007 0x00000000004579e7 in apply_low (fun=0x7fa1687b9060, ob=0x7fa1125640b8, num_arg=2, b_ign_prot=false, allowRefs=false, b_ign_shadows=false)
    at interpret.c:17442
0000008 0x0000000000447584 in int_apply (fun=0x0, ob=0xb7, num_arg=183, b_ign_prot=<value optimized out>, b_use_default=true, b_ign_shadows=<value optimized out>)
    at interpret.c:17546
0000009 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:16645
0000010 0x00000000004579e7 in apply_low (fun=0x7fa1687ba320, ob=0x7fa112564398, num_arg=1, b_ign_prot=false, allowRefs=false, b_ign_shadows=false)
    at interpret.c:17442
0000011 0x0000000000447584 in int_apply (fun=0x0, ob=0xb7, num_arg=183, b_ign_prot=<value optimized out>, b_use_default=true, b_ign_shadows=<value optimized out>)
    at interpret.c:17546
0000012 0x00000000004480cb in sapply_int (fun=0x7fa1687ba320, ob=0x7fa112564398, num_arg=1, b_find_static=128, b_use_default=true) at interpret.c:17707
0000013 0x00000000004a9a4c in execute_callback (cb=0x70b7c8, nargs=1, keep=false, toplevel=true) at simulate.c:4081
#14 0x000000000041cb9d in call_input_to (i=<value optimized out>, str=0x7fffaffe4670 "drop all corpse", it=0x7fa11e87b6c8) at comm.c:4127
#15 0x00000000004202ae in call_function_interactive (i=0x7fa119dd2148, str=0x7fffaffe4670 "drop all corpse") at comm.c:4212
#16 0x000000000040ee13 in backend () at backend.c:693
#17 0x0000000000464f36 in main (argc=<value optimized out>, argv=<value optimized out>) at main.c:688


Any ideas would be appreciated.
TagsNo tags attached.
Attached Files
bat.custom.diff (144,337 bytes)   
diff -ruN /home/favorit/ldmud-3.3.719/src/actions.c ./actions.c
--- /home/favorit/ldmud-3.3.719/src/actions.c	2009-06-15 01:18:51.000000000 +0300
+++ ./actions.c	2009-09-03 12:30:44.000000000 +0300
@@ -128,7 +128,8 @@
    * command verb as specified in the action definition.
    */
 
-static char *last_command = NULL;
+//BAT static
+char *last_command = NULL;
   /* During a command execution, this points to a (stack) buffer with
    * the full command.
    */
@@ -620,6 +621,58 @@
     return MY_FALSE;
 } /* call_modify_command() */
 
+//ifdef BAT
+/*-------------------------------------------------------------------------*/
+static Bool
+call_post_command ()
+
+     /* Call the post_command hook if it is set..
+      *
+      * Return FALSE if everything is ok, and TRUE if something happened
+      * (like the command_giver selfdestructed or the hook had something to
+      * complain).
+      */
+
+{
+  svalue_t *svp;
+
+  svp = NULL;
+
+  if (driver_hook[H_POST_COMMAND].type == T_CLOSURE)
+    {
+      lambda_t *l;
+
+      l = driver_hook[H_POST_COMMAND].u.lambda;
+      if (driver_hook[H_POST_COMMAND].x.closure_type == CLOSURE_LAMBDA)
+        {
+	  free_object(l->ob, "call_post_command");
+	  l->ob = ref_object(command_giver, "call_post_command");
+        }
+      push_ref_object(inter_sp, command_giver, "call_post_command");
+      call_lambda(&driver_hook[H_POST_COMMAND], 1);
+      transfer_svalue(svp = &apply_return_value, inter_sp--);
+      if (!command_giver)
+	return MY_TRUE;
+    }
+  else if (driver_hook[H_POST_COMMAND].type == T_STRING
+	   && !(O_DESTRUCTED & command_giver->flags))
+    {
+        svp =
+	  sapply(driver_hook[H_POST_COMMAND].u.str, command_giver, 0);
+        if (!command_giver)
+	  return MY_TRUE;
+    }
+
+  /* If svp is integer and not zero, return true
+   */
+  if (svp && svp->type == T_NUMBER && svp->u.number) {
+    return MY_TRUE;
+  }
+
+  return MY_FALSE;
+} /* call_post_command() */
+//endif BAT
+
 /*-------------------------------------------------------------------------*/
 static int
 special_parse (char *buff)
@@ -732,7 +785,7 @@
     if (svp->type == T_STRING)
     {
         if (!useHook)
-            tell_object(command_giver, svp->u.str);
+            tell_object(command_giver, svp->u.str, 0);
         else
             push_svalue(svp);
     }
@@ -745,7 +798,7 @@
         {
             if (!useHook)
             {
-                tell_object(command_giver, inter_sp->u.str);
+                tell_object(command_giver, inter_sp->u.str, 0);
                 pop_stack();
             }
         }
@@ -758,7 +811,7 @@
     else if (driver_hook[H_NOTIFY_FAIL].type == T_STRING)
     {
         if (!useHook)
-            tell_object(command_giver, driver_hook[H_NOTIFY_FAIL].u.str);
+            tell_object(command_giver, driver_hook[H_NOTIFY_FAIL].u.str, 0);
         else
             push_svalue(&driver_hook[H_NOTIFY_FAIL]);
     }
@@ -776,7 +829,7 @@
         {
             if (!useHook)
             {
-                tell_object(command_giver, inter_sp->u.str);
+                tell_object(command_giver, inter_sp->u.str, 0);
                 pop_stack();
             }
         }
@@ -1185,6 +1238,17 @@
     marker_sent->shadow_ob = NULL;
     command_marker = marker_sent;
 
+//#ifdef BAT
+    save_current_object = current_object;
+    save_command_giver  = command_giver;
+
+    if (!from_efun && call_post_command())
+      return MY_TRUE;
+
+    current_object = save_current_object;
+    command_giver  = save_command_giver;
+//#endif BAT
+
     /* If the command was not found, notify the failure */
     if (s == 0)
     {
diff -ruN /home/favorit/ldmud-3.3.719/src/actions.h ./actions.h
--- /home/favorit/ldmud-3.3.719/src/actions.h	2009-06-15 01:18:51.000000000 +0300
+++ ./actions.h	2009-09-03 11:49:52.000000000 +0300
@@ -10,6 +10,10 @@
 extern object_t *command_giver;
 extern p_int alloc_action_sent;
 
+//#ifdef BAT /* Blitzer */
+extern char *last_command;
+//#endif
+
 /* --- Prototypes --- */
 
 extern void free_action_temporaries(void);
diff -ruN /home/favorit/ldmud-3.3.719/src/backend.c ./backend.c
--- /home/favorit/ldmud-3.3.719/src/backend.c	2009-06-15 01:18:52.000000000 +0300
+++ ./backend.c	2009-10-02 23:56:44.000000000 +0300
@@ -77,6 +77,12 @@
 #include "../mudlib/sys/driver_hook.h"
 #include "../mudlib/sys/debug_message.h"
 
+//#ifdef BAT
+#include "sockets.h"
+#include "bat.h"
+
+//#endif BAT
+
 /*-------------------------------------------------------------------------*/
 
 mp_int current_time;
@@ -346,7 +352,8 @@
 #ifdef __MWERKS__
 #    pragma unused(sig)
 #endif
-    reopen_debug_log = MY_TRUE;
+    eval_cost = max_eval_cost; // BAT eval cost to max
+//    reopen_debug_log = MY_TRUE;
     (void)signal(SIGUSR2, handle_usr2);
 #ifndef RETSIGTYPE_VOID
     return 0;
@@ -507,7 +514,6 @@
         }
 
         if (extra_jobs_to_do) {
-
             current_interactive = NULL;
             if (game_is_being_shut_down)
             {
@@ -599,7 +605,6 @@
             }
 
             extra_jobs_to_do = MY_FALSE;
-
         } /* if (extra_jobs_to_do */
 
         do_state_check(2, "before get_message()");
@@ -691,7 +696,6 @@
                     print_prompt();
                 }
             }
-
             do_state_check(2, "after handling message");
         }
         else
@@ -721,9 +725,9 @@
             next_call_out_cycle();
 
             /* Do the timed events */
-	    if (!synch_heart_beats
-             || time_of_last_hb + heart_beat_interval <= current_time)
-	    {
+			if (!synch_heart_beats
+				|| time_of_last_hb + heart_beat_interval <= current_time)
+			{
                 do_state_check(2, "before heartbeat");
                 call_heart_beat();
                 time_of_last_hb = current_time;
@@ -747,6 +751,10 @@
             mem_consolidate(MY_FALSE);
         }
 
+//#ifdef BAT
+		socket_poll();
+//#endif
+
     } /* end of main loop */
 
     /* NOTREACHED */
diff -ruN /home/favorit/ldmud-3.3.719/src/bat.c ./bat.c
--- /home/favorit/ldmud-3.3.719/src/bat.c	1970-01-01 02:00:00.000000000 +0200
+++ ./bat.c	2009-10-02 23:51:18.000000000 +0300
@@ -0,0 +1,1541 @@
+
+#include <ctype.h>
+#include <stdio.h>
+
+#define BAT_C__
+
+#include "machine.h"
+
+#include "bat.h"
+#include "main.h"
+#include "object.h"
+#include "backend.h"
+#include "simulate.h"
+#include "interpret.h"
+#include "xalloc.h"
+#include "svalue.h"
+#include "array.h"
+#include "swap.h"
+#include "typedefs.h"
+#include "comm.h"
+#include "mstrings.h"
+#include "mapping.h"
+#include "stdstrings.h"
+#include "random.h"
+#include "dumpstat.h"
+#include "object.h"
+#include "exec.h"
+#include "closure.h"
+#include "ptrtable.h"
+#include "structs.h"
+#include "actions.h"
+#include "heartbeat.h"
+#include "../mudlib/sys/input_to.h"                                                                                                                                   
+
+
+#include <time.h>
+
+#if defined(__x86_64__) || defined(__i386__)
+#define USE_RDTSC
+#else
+#error too slow!
+#endif
+
+#define TYPE_TESTV1(arg1,type1) \
+  if ((arg1)->type != type1) \
+      efun_gen_arg_error(1, type1, arg1);
+
+#define TYPE_TESTV2(arg1,type1) \
+  if ((arg1)->type != type1) \
+      efun_gen_arg_error(2, type1, arg1);
+
+#define TYPE_TESTV3(arg1,type1) \
+  if ((arg1)->type != type1) \
+      efun_gen_arg_error(3, type1, arg1);
+
+#define TYPE_TESTV4(arg1,type1) \
+  if ((arg1)->type != type1) \
+      efun_gen_arg_error(4, type1, arg1);
+
+static int same_path(const char* p1, const char* p2, int len);
+static int count_objects(const char* name, int len);
+
+char catch_write_buf[CATCH_WRITE_MAX_RECURSION][CATCH_WRITE_BUF_LEN+1];
+int catching_write   = 0;
+int save_catching_write = 0;
+object_t* catch_write_cur_interactive = 0;
+int catch_write_len[CATCH_WRITE_MAX_RECURSION];
+typedef struct input_to_s input_to_t;
+struct input_to_s {                                                                                                                                                   
+    input_t     input;                                                                                                                                                
+    callback_t  fun;        /* The function to call, and its args */                                                                                                  
+    p_uint      eval_nr;    /* The thread number where this started. */                                                                                               
+};   
+
+void begin_catch_write()
+{
+	int i;
+
+    if(catching_write)
+    {
+		if (catching_write >= CATCH_WRITE_MAX_RECURSION)
+		{
+			catching_write = 0;
+
+			i = CATCH_WRITE_MAX_RECURSION;
+
+			while (i--)
+			{
+				catch_write_len [i] = 0;
+				catch_write_buf [i][0] = '\0';
+			}
+
+			catch_write_cur_interactive = 0;
+
+			errorf("Too long catch_write() recursion.\n");
+			return;
+		}
+    }
+
+    catch_write_len [catching_write] = 0;
+    catch_write_buf [catching_write][0] = '\0';
+    //    catch_write_cur_interactive = current_interactive;
+    catch_write_cur_interactive = command_giver;
+    catching_write++;
+}
+
+char* end_catch_write()
+{
+    char* tmp = 0;
+    int i;
+
+    if(catching_write == 0)
+		return 0;
+
+    i = catching_write - 1;
+    
+    catch_write_buf[i][CATCH_WRITE_BUF_LEN-1] = '\0';
+    catch_write_buf[i][catch_write_len [i]] = '\0';
+    tmp = string_copy(catch_write_buf [i]);
+
+    catch_write_buf [i][0] = '\0';
+    catch_write_len [i] = 0;
+    catching_write--;
+
+    if (catching_write == 0)
+		catch_write_cur_interactive = 0;
+
+    return tmp;
+}
+
+unsigned long long get_time ()
+{
+#ifdef USE_RDTSC
+	register unsigned long long ret;
+  
+	__asm__ __volatile__ ("rdtsc" : "=A"(ret));
+
+	return ret;
+#else
+  
+#if 1
+	struct timeval tv;
+	unsigned long long ret;
+  
+	gettimeofday (&tv, NULL);
+	ret= (unsigned long long) tv.tv_sec*1000*1000+ tv.tv_usec;
+  
+	return ret;
+  
+#else
+	struct timespec tp;
+	clock_gettime (CLOCK_REALTIME, &tp);
+	return tp.tv_sec*1000*1000+ tp.tv_nsec/1000;
+#endif
+  
+#endif
+}
+
+static double clocks_to_msec = 0;
+
+unsigned long long msec_to_time (double msec)
+{
+  time_to_msec (0);
+  
+  return msec* clocks_to_msec;
+}
+ 
+
+double time_to_msec(const unsigned long long proftime)
+{
+#ifdef USE_RDTSC
+  static unsigned int once= 1;
+
+  if (once)
+    {
+      FILE *f;
+      once= 0;
+      
+      if ( (f= fopen ("/proc/cpuinfo", "r")) )
+	{
+	  while (!feof (f))
+	    {
+	      char line [256];
+	      
+	      *line= 0;
+	      fgets (line, sizeof (line), f);
+	      
+	      if (!memcmp (line, "cpu MHz", strlen ("cpu MHz")) &&
+		  strchr (line, ':'))
+		{
+		  clocks_to_msec= strtod (strchr (line, ':')+1, NULL);
+		  clocks_to_msec *= 1000;
+		  
+		  fclose (f);
+		  
+		  return time_to_msec (proftime);
+		}
+	    }
+	  fclose (f);
+	}
+      
+#define CALIBRATE_TIME_SECS 1
+      
+      if (clocks_to_msec == 0)
+	{
+	  unsigned long long start, end;
+	  
+	  start= get_time ();
+	  sleep (CALIBRATE_TIME_SECS);
+	  end= get_time ();
+	  clocks_to_msec= (end-start)/(CALIBRATE_TIME_SECS* 1000);
+	  
+	  if (!clocks_to_msec)
+	    return 0;
+	}
+    }
+    
+  return proftime/clocks_to_msec;
+#else
+  return proftime/1000;
+#endif
+}
+
+double get_time_ms()
+{
+	return time_to_msec(get_time());
+}
+
+vector_t *all_objects(const char* name, int len)
+{
+    vector_t *v;
+    object_t *ob;
+    int count;
+    int i;
+    char *p, *cp;
+
+    if (len < 0)
+		return 0;
+    cp = string_copy(name);
+    if ((p = strrchr(cp, '#')) != 0)
+		*p = '\0';
+    p = cp;
+    if (*p == '/') {
+		p++;
+		if (len > 0)
+			len--;
+    }
+
+    count = count_objects(name, len);
+    
+    if (count == 0)
+    {
+		xfree(cp);
+		return allocate_array(0);
+    }
+    if (count >= MAX_ARRAY_SIZE)
+		count = MAX_ARRAY_SIZE;
+    
+    v = allocate_uninit_array(count);
+    
+    for (i = 0, ob = obj_list; ob && i < count; ob = ob->next_all) {
+		eval_cost++;
+		if ((ob->flags & O_DESTRUCTED) || !same_path(p, get_txt(ob->name), len))
+			continue;
+		if (ob->flags & O_SWAPPED)
+			load_ob_from_swap(ob);
+
+		v->item[i].type = T_OBJECT;
+		v->item[i].u.ob = ob;
+		put_ref_object(&v->item[i], ob, "all_objects");
+
+		i++;
+    }
+    
+    xfree(cp);
+    return v;
+}
+
+svalue_t *v_all_objects(svalue_t * sp, int n)
+{
+    svalue_t* arg;
+    char *str = 0;
+    vector_t* v;
+    
+    arg = sp - n + 1;
+    
+    switch(arg[0].type) 
+    {
+		case T_OBJECT: str = get_txt(arg[0].u.ob->name); break;
+		case T_STRING: str = get_txt(arg[0].u.str); break;
+		default: TYPE_TESTV1(&arg[0], T_STRING); break;
+    }
+    
+    if (n == 1)
+		v = all_objects(str, 0);
+    else {
+		TYPE_TESTV2(arg+1, T_NUMBER);
+		v = all_objects(str, arg[1].u.number);
+    }
+
+    sp = pop_n_elems(n, sp) + 1;
+    
+    if (v)
+		put_array(sp, v);
+    else
+		put_number(sp, 0);
+    
+    return sp;
+}
+
+static object_t* first_object(const char *name, int len)
+{
+    object_t *ob;
+    char *p;
+    char *cp;
+
+    if (len < 0)
+		return 0;
+    cp = string_copy(name);
+    if ((p = strrchr(cp, '#')) != 0)
+		*p = '\0';
+    p = cp;
+    if (*p == '/') {
+		p++;
+		if (len > 0)
+			len--;
+    }
+   
+    for (ob = obj_list; ob; ob = ob->next_all) {
+		eval_cost++;
+		if (!(ob->flags & O_DESTRUCTED) && same_path(p, get_txt(ob->name), len)) {
+			if (ob->flags & O_SWAPPED)
+				load_ob_from_swap(ob);
+      
+			xfree(cp);
+			return ob;
+		}
+    }
+  
+    xfree(cp);
+    return 0;
+}
+
+svalue_t *v_first_object(svalue_t * sp, int num_arg)
+{
+    object_t *ob;
+    svalue_t *arg;
+    char *str = 0;
+
+    arg = sp - num_arg + 1;
+
+    switch(arg[0].type) 
+    {
+		case T_OBJECT: str = get_txt(arg[0].u.ob->name); break;
+		case T_STRING: str = get_txt(arg[0].u.str); break;
+		default: TYPE_TESTV1(&arg[0], T_STRING); break;
+    }
+  
+    if (num_arg == 1)
+		ob = first_object(str, 0);
+    else {
+		TYPE_TESTV2(&arg[1], T_NUMBER);
+		ob = first_object(str, arg[1].u.number);
+    }
+
+    sp = pop_n_elems(num_arg, sp) + 1;
+  
+    if (ob)
+		put_ref_object(sp, ob, "first_object");
+    else
+		put_number(sp, 0);
+  
+    return sp;
+}
+
+static object_t* next_object(object_t* ob, int len)
+{
+    char *p, *cp;
+
+    if (len < 0)
+		return 0;
+    cp = string_copy(get_txt(ob->name));
+    if ((p = strrchr(cp, '#')) != 0)
+		*p = '\0';
+    p = cp;
+    if (*p == '/') {
+		p++;
+		if (len > 0)
+			len--;
+    }
+
+
+    for(ob = ob->next_all; ob; ob = ob->next_all) {
+		eval_cost++;
+		if (!(ob->flags & O_DESTRUCTED) &&
+			same_path(p, get_txt(ob->name), len)) {
+			if (ob->flags & O_SWAPPED)
+				load_ob_from_swap(ob);
+			xfree(cp);
+			return ob;
+		}
+    }
+    
+    xfree(cp);
+    return 0;
+}
+
+svalue_t *v_next_object(svalue_t * sp, int n)
+{
+    object_t *ob;
+    svalue_t *arg;
+    
+    arg = sp - n + 1;
+
+    TYPE_TESTV1(arg, T_OBJECT);
+    if (n == 1)
+		ob = next_object(arg[0].u.ob, 0);
+    else {
+		TYPE_TESTV2(arg+1, T_NUMBER);
+		ob = next_object(arg[0].u.ob, arg[1].u.number);
+    }
+    
+    sp = pop_n_elems(n, sp) + 1;
+
+    if (ob)
+		put_ref_object(sp, ob, "next_object");
+    else
+		put_number(sp, 0);
+
+    return sp;
+}
+
+svalue_t *f_all_shadow(svalue_t * sp)
+{
+    object_t *ob;
+    vector_t *v;
+    svalue_t *svp;
+    int i;
+
+    TYPE_TESTV1(sp, T_OBJECT);
+
+    ob = sp->u.ob;
+
+    if( !(ob->flags & O_SHADOW) )
+    {
+		i = 0;
+    }
+    else
+    {
+		while (O_GET_SHADOW(ob)->shadowed_by)
+			ob = O_GET_SHADOW(ob)->shadowed_by;
+  
+		for (i = 1; O_GET_SHADOW(ob)->shadowing; i++)
+			ob = O_GET_SHADOW(ob)->shadowing;
+    }
+  
+    v = allocate_uninit_array(i);
+    svp = v->item;
+  
+    while (i--)
+    {
+		put_ref_object(svp, ob, "all_shadow");
+		ob = O_GET_SHADOW(ob)->shadowed_by;
+		svp++;
+    }
+
+    free_svalue(sp);
+    put_array(sp, v);
+
+    return sp;
+}
+
+svalue_t *f_first_shadow(svalue_t * sp)
+{
+    object_t* ob;
+
+    TYPE_TESTV1(sp, T_OBJECT);
+    ob = sp->u.ob;
+
+    if( !(ob->flags & O_SHADOW) )
+    {
+		free_svalue(sp);
+		put_number(sp, 0);
+		return sp;
+    }
+    
+    while (O_GET_SHADOW(ob)->shadowed_by)
+    {
+		ob = O_GET_SHADOW(ob)->shadowed_by;
+		/*	
+		  fprintf(stderr, "FShadow: %s\n", get_txt(ob->name));
+		*/
+    }
+
+    free_svalue(sp);
+    put_ref_object(sp, ob, "first_shadow");
+
+    return sp;
+}
+
+svalue_t *f_last_shadow(svalue_t * sp)
+{
+    object_t* ob;
+
+    TYPE_TESTV1(sp, T_OBJECT);
+    ob = sp->u.ob;
+
+    if( !(ob->flags & O_SHADOW) )
+    {
+		free_svalue(sp);
+		put_number(sp, 0);
+		return sp;
+    }
+    
+    while (O_GET_SHADOW(ob)->shadowing)
+    {
+		ob = O_GET_SHADOW(ob)->shadowing;
+		/*
+		  fprintf(stderr, "LShadow: %s\n", get_txt(ob->name));
+		*/
+    }
+
+    free_svalue(sp);
+    put_ref_object(sp, ob, "last_shadow");
+
+    return sp;
+}
+
+svalue_t *f_next_shadow(svalue_t * sp)
+{
+    object_t* ob;
+    
+    TYPE_TESTV1(sp, T_OBJECT);
+    ob = sp->u.ob;
+
+    if( !(ob->flags & O_SHADOW) )
+    {
+		free_svalue(sp);
+		put_number(sp, 0);
+		return sp;
+    }
+    
+    if(O_GET_SHADOW(ob)->shadowing)
+    {
+		ob = O_GET_SHADOW(ob)->shadowing;
+    }
+    else
+    {
+		free_svalue(sp);
+		put_number(sp, 0);
+		return sp;
+    }
+
+    free_svalue(sp);
+    put_ref_object(sp, ob, "next_shadow");
+
+    return sp;
+}
+
+static int same_path(const char* p1, const char* p2, int len)
+{
+    int len1, len2;
+  
+    if (*p1 != *p2)
+		return 0;
+    len1 = strlen(p1);
+    len2 = strlen(p2);
+    if (len) {
+		if (len2 < len || len > len1)
+			return 0;
+		len1 = len;
+    } else {
+		if (len1 > len2)
+			return 0;
+    }
+    if (strncmp(p1, p2, len1))
+		return 0;
+    if (!len && p2[len1] != '#' && p2[len1] != '\0')
+		return 0;
+    return 1;
+}
+
+static int count_objects(const char* name, int len)
+{
+    int i;
+    object_t* ob;
+    char *p, *cp;
+  
+    if (len < 0)
+		return 0;
+    cp = string_copy(name);
+    if ((p = strrchr(cp, '#')) != 0)
+		*p = '\0';
+    p = cp;
+    if (*p == '/') {
+		p++;
+		if (len > 0)
+			len--;
+    }
+  
+    for (i = 0, ob = obj_list; ob; ob = ob->next_all)
+	{
+		eval_cost++;
+		if (!(ob->flags & O_DESTRUCTED) && same_path(p, get_txt(ob->name), len))
+			i++;
+    }
+	
+    xfree(cp);
+    return i;
+}
+
+
+svalue_t *v_count_objects(svalue_t * sp, int n)
+{
+    svalue_t* arg;
+    char *str = 0;
+    int ret;
+
+    arg = sp - n + 1;
+    
+    switch(arg[0].type) 
+    {
+		case T_OBJECT: str = get_txt(arg[0].u.ob->name); break;
+		case T_STRING: str = get_txt(arg[0].u.str); break;
+		default: TYPE_TESTV1(&arg[0], T_STRING); break;
+    }
+    
+    if (n == 1)
+		ret = count_objects(str, 0);
+    else {
+		TYPE_TESTV2(&arg[1], T_NUMBER);
+		ret = count_objects(str, arg[1].u.number);
+    }
+    
+    sp = pop_n_elems(n, sp) + 1;
+    put_number(sp, ret);
+    
+    return sp;
+}
+
+#define MAX_LINE_LEN    1024
+
+static int count_lines(string_t *file)
+{
+    FILE *f;
+    int ret;
+    static char buff[MAX_LINE_LEN];
+  
+    file = check_valid_path(file, current_object, STR_COUNT_LINES, MY_FALSE);
+    if (file == 0)
+		return 0;
+    f = fopen(get_txt(file), "r");
+    if (f == 0)
+		return -1;
+    ret = 0;
+    while (fgets(buff, MAX_LINE_LEN, f) != 0)
+		ret++;
+    fclose(f);
+    return ret;
+}
+
+svalue_t *f_count_lines(svalue_t *sp)
+{
+    int i;
+  
+    assign_eval_cost();
+    TYPE_TESTV1(sp, T_STRING);
+    i = count_lines(sp->u.str);
+
+    free_svalue(sp);
+   
+    put_number(sp, i);
+    
+    return sp;
+}
+
+
+
+#define MAX_SEND_LEN 16000
+
+static string_t *line_wrap_string(line, prompt, startline, linelen)
+    char *line, *prompt, *startline;
+    int linelen;
+{
+    char *tmp, buff[MAX_SEND_LEN+2];
+    char *srcpos;
+    enum {S_prompt, S_line} curstr;
+    int startlen, curlen, totlen, lastspace, wordlen;
+    int startpos;			/* tmp for startline-add */
+    string_t *new;
+
+    /* clear the first byte because it may be read in the end */
+    buff[0] = 0;
+
+    if (linelen < 21)
+		linelen = 21;
+    wordlen = linelen/4;
+    startlen = strlen(startline);
+    totlen = 0;
+    curlen = 0;
+    lastspace = -1;
+    tmp = buff+1;			/* space for lastspace */
+    srcpos = prompt;
+    curstr = S_prompt;
+
+    while(totlen < (MAX_SEND_LEN-4))
+    {
+		if(*srcpos == '\0')
+		{
+			if(curstr == S_prompt)
+			{
+				curstr = S_line;
+				srcpos = line;
+				continue;
+			}
+			else
+				break;		/* out from while */
+		}
+      
+		tmp[totlen] = *srcpos;
+		if (*srcpos == ' ') {
+			lastspace = totlen;
+		} else if (*srcpos == '\n') { /* reset to line start */
+			lastspace = totlen++;
+			srcpos++;
+			curlen = 0;
+			startpos =totlen;
+			goto startline_add;	/* jump, jump,... */
+		}
+		totlen++;
+		curlen++;
+		srcpos++;
+      
+		if(curlen >= linelen)	/* time to check wrap */
+		{
+			if(totlen-lastspace < wordlen) /* space in asseptable len */
+			{
+				tmp[lastspace] = '\n';
+				curlen = totlen - lastspace - 1;
+				startpos = lastspace + 1;
+			}
+			else			/* force wrap */
+				if(totlen < (MAX_SEND_LEN-4)) /* check there is space to insert */
+				{
+					tmp[totlen] = '\n';
+					lastspace = totlen; /* reset it to line end (\n) (safe place)*/
+					totlen++;
+					startpos = totlen;
+					curlen = 0;
+				}
+				else
+					break;		/* there wasn't space. out of while */
+
+			if(curstr == S_prompt || startlen == 0)
+				continue;
+	  
+			/*
+			 * Insert startline
+			 */
+		startline_add:
+			if(totlen+startlen < (MAX_SEND_LEN-4))
+			{
+				int i;
+				for(i = totlen-1 ; i>=startpos ; i--) /*move old to right place*/
+					tmp[i+startlen] = tmp[i];
+				for(i = 0 ; i<startlen ; i++, startpos++)	/* copy startline */
+					tmp[startpos] = startline[i];
+				curlen += startlen;
+				totlen += startlen;
+			}
+			else
+				break;		/* there wasn't space. out of while */
+		} /* if wrap */
+    } /* while totlen */
+
+    /*
+     * Insert possible last linefeed
+     */
+    if(tmp[totlen-1]!='\n')
+    {
+		tmp[totlen]='\n';
+		totlen++;
+    }
+    tmp[totlen]='\0';
+    totlen++;
+
+    /*
+     * Time to copy & return
+     */
+    
+    new = new_mstring(tmp);
+    return new;
+}
+
+#define DEFAULT_WRAP_LEN	79
+
+svalue_t *v_line_wrap (svalue_t *sp, int num_arg)
+{
+    svalue_t * args;
+    char *str, *first = "", *rest = "";
+    int len = DEFAULT_WRAP_LEN;
+    string_t *new;
+  
+    args = sp - num_arg + 1;
+
+    TYPE_TESTV1(args, T_STRING);
+    str = get_txt(args[0].u.str);
+
+    if(num_arg > 1 && !(args[1].type == T_NUMBER && args[1].u.number == 0))
+    {
+		TYPE_TESTV2(args+1, T_STRING);
+		first = get_txt(args[1].u.str);
+    }
+
+    if(num_arg > 2 && !(args[2].type == T_NUMBER && args[2].u.number == 0))
+    {
+		TYPE_TESTV3(args+2, T_STRING);
+		rest = get_txt(args[2].u.str);
+    }
+
+    if(num_arg > 3)
+    {
+		TYPE_TESTV4(args+3, T_NUMBER);
+		len = args[3].u.number;
+    }
+
+    new = line_wrap_string(str, first, rest, len);
+  
+    sp = pop_n_elems(num_arg, sp) + 1;
+
+    if (new)
+    {
+		put_string(sp, new);
+    } 
+    else
+    {
+		put_number(sp, 0);
+    }
+    
+    return sp;
+}
+
+
+svalue_t *f_nuke_controls (svalue_t *sp, int num_arg)
+{
+    char *s, *d;
+    unsigned char c;
+    string_t *new;
+    int len, i;
+
+    TYPE_TESTV1(sp, T_STRING);
+
+    len = 0;
+    s = get_txt(sp->u.str);
+
+    for(i = 0; i < mstrsize(sp->u.str); i++)
+    {
+		c = s[i];
+
+		if (isprint(c) && c != 0xff)
+			len++;
+    }
+
+    new = alloc_mstring(len);
+    d = get_txt(new);
+
+    for(i = 0; i < mstrsize(sp->u.str); i++)
+    {
+		c = s[i];
+
+		if (isprint(c) && c != 0xff)
+		{
+			*d = c;
+			d++;
+		}
+    }
+
+    free_mstring(sp->u.str);
+    sp->u.str = new;
+
+    return sp;
+}
+
+svalue_t *f_query_input_to(svalue_t * sp, int num_arg)
+{
+    object_t *ob;
+    interactive_t *ip;
+  
+    TYPE_TESTV1(sp, T_OBJECT);
+  
+    ob = sp->u.ob;
+    if (O_SET_INTERACTIVE(ip, ob) && 
+		ip->input_handler)
+    {
+		input_t *ih = ip->input_handler;
+		input_to_t *it = (input_to_t *) ih;
+
+		if(it->fun.is_lambda)
+		{
+			put_number(sp, 0);
+		}
+		else
+		{
+			sp->type = T_STRING;
+			sp->u.str = dup_mstring(it->fun.function.named.name);
+			//	  put_malloced_string(sp, string_copy(ip->input_to->fun.function.named.name));
+		}
+    }
+    else
+    {
+		put_number(sp, 0);
+    }
+  
+    deref_object(ob, "query_input_to");
+  
+    return sp;
+}
+
+svalue_t *
+f_get_backend_times (svalue_t *sp)
+{
+    vector_t* v = allocate_array(4);
+    
+    if (!v)
+		errorf("Out of memory: get_backend_times().\n");
+
+    put_number(v->item, 1);
+    put_number(v->item+1, 2);
+    put_number(v->item+2, 3);
+    put_number(v->item+3, 4);
+
+    sp++;
+    put_array(sp, v);
+
+    return sp;
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+svalue_t *
+f_random_integer (svalue_t *sp)
+    
+/* EFUN random_integer()
+ *
+ *   int random_integer()
+ *
+ * Returns a number in the random range [INT_MIN .. INT_MAX].
+ *
+ * The random number generator is proven to deliver an equal
+ * distribution of numbers over a big range, with no repetition of
+ * number sequences for a long time.
+ */
+
+{
+    push_number(sp, random_integer());
+    return sp;
+} /* f_random_integer() */
+
+/*-------------------------------------------------------------------------*/
+svalue_t *
+f_random_float (svalue_t *sp)
+    
+/* EFUN random_float()
+ *
+ *   float random_float()
+ *
+ * Returns a random float.
+ */
+
+{
+    STORE_DOUBLE_USED
+
+		sp++;
+    sp->type = T_FLOAT;
+    STORE_DOUBLE(sp, random_double());
+
+    return sp;
+} /* f_random_float() */
+
+/*-------------------------------------------------------------------------*/
+svalue_t *
+f_random_gaussian (svalue_t *sp)
+    
+/* EFUN random_gaussian()
+ *
+ *   float random_gaussian()
+ *
+ * Returns a random Gaussian float.
+ */
+
+{
+    STORE_DOUBLE_USED
+
+		sp++;
+    sp->type = T_FLOAT;
+    STORE_DOUBLE(sp, random_gaussian());
+
+    return sp;
+} /* f_random_gaussian() */
+
+
+
+
+svalue_t *f_get_variable_sizes(svalue_t *sp)
+{
+	object_t* ob;
+	vector_t* arr;
+	
+	ob = sp->u.ob;
+
+	arr = get_variable_sizes(ob);
+
+	free_svalue(sp);
+	put_array(sp, arr);
+
+	return sp;
+}
+
+
+
+FILE *g_ref_file;
+static struct pointer_table *ptable;
+
+struct find_ref_map_struct
+{
+	char* thisob;
+	char* thisvar;
+	int num_values;
+};
+
+void find_ref(svalue_t* v, char* thisob, char* thisvar);
+
+void find_ref_map_func (svalue_t *key, svalue_t *values, void *extra)
+{
+	int i;
+
+	struct find_ref_map_struct* str = (struct find_ref_map_struct*)extra;
+	
+	find_ref(key, str->thisob, str->thisvar);
+	
+	for(i = str->num_values; --i >= 0; )
+	{
+		find_ref(values, str->thisob, str->thisvar);
+		values++;
+	}
+}
+
+
+void find_ref(svalue_t* v, char* thisob, char* thisvar)
+{
+	switch(v->type)
+	{
+		case T_OBJECT:
+		{
+			if(v->u.ob->flags & O_DESTRUCTED)
+			{
+				fprintf(g_ref_file,
+						"%s: %s = %s\n", thisob, thisvar,
+						get_txt(v->u.ob->name));
+			}
+			break;
+		}
+
+		case T_MAPPING:
+		{
+			struct find_ref_map_struct str;
+
+			if (NULL == register_pointer(ptable, v->u.map) )
+				break;
+		
+			str.thisob = thisob;
+			str.thisvar = thisvar;
+			str.num_values = v->u.map->num_values;			
+			walk_mapping(v->u.map, find_ref_map_func, &str);
+
+			break;
+		}
+		
+		case T_POINTER:
+		case T_QUOTED_ARRAY:
+		{
+			int i;
+
+			if (v->u.vec == &null_vector)
+				break;
+			
+			if (NULL == register_pointer(ptable, v->u.vec) )
+				break;
+		
+			for (i=0; i < (mp_int)VEC_SIZE(v->u.vec); i++)
+			{
+				find_ref(&v->u.vec->item[i], thisob, thisvar);
+			}
+			
+			break;
+		}
+
+        case T_STRUCT:
+		{
+			struct_t *st = v->u.strct;
+			int i;
+			
+			if (NULL == register_pointer(ptable, st) )
+				break;
+			
+			for (i=0; i < (mp_int)struct_size(st); i++)
+			{
+				find_ref(&st->member[i], thisob, thisvar);
+			}
+			
+            break;
+		}
+		
+		case T_CLOSURE:
+		{
+			int num_values;
+			svalue_t *svp;
+			lambda_t *l;
+			int i;
+
+			if (!CLOSURE_MALLOCED(v->x.closure_type))
+				break;
+			
+			if (!CLOSURE_REFERENCES_CODE(v->x.closure_type))
+				break;
+
+			/* CLOSURE_LAMBDA */
+			l = v->u.lambda;
+
+			if(l->ob != 0)
+			{
+				if(l->ob->flags & O_DESTRUCTED)
+				{
+					fprintf(g_ref_file,
+							"%s: %s L> %s\n", thisob, thisvar,
+							get_txt(l->ob->name));
+				}
+			}
+			
+			if (v->x.closure_type == CLOSURE_BOUND_LAMBDA)
+				l = l->function.lambda;
+
+			num_values = EXTRACT_UCHAR(&l->function.code[0]);
+			if (num_values == 0xff)
+				num_values = ((svalue_t *)l)[-0xff].u.number;
+			
+			svp = (svalue_t *)l - num_values;
+
+			if (NULL == register_pointer(ptable, svp))
+				break;
+			
+			for(i=0; i<num_values; i++)
+			{
+				find_ref(svp++, thisob, thisvar);
+			}
+
+			break;
+		}
+	}
+}
+
+svalue_t *f_dump_destr_ob_refs(svalue_t *sp)
+{
+	object_t* ob;
+
+	add_message("Dumping to DEST_OBJ_REF_DUMP...\n");
+	
+    g_ref_file = fopen("DEST_OBJ_REF_DUMP", "w");
+    if (!g_ref_file)
+	{
+		add_message("...failed!\n");
+        return sp;
+	}
+
+    ptable = new_pointer_table();
+	
+	for (ob = obj_list; ob; ob = ob->next_all)
+	{
+		svalue_t *svp;
+		variable_t* names;
+		int i;
+
+		i = ob->prog->num_variables;
+		svp = ob->variables;
+		names = ob->prog->variables;
+		
+		for (; --i >= 0; svp++, names++)
+		{
+			find_ref(svp, get_txt(ob->name), get_txt(names->name));
+		}
+	}
+
+    free_pointer_table(ptable);
+	
+	fclose(g_ref_file);
+
+	add_message("...done!\n");
+	
+	return sp;
+}
+
+svalue_t* f_query_total_player_commands(svalue_t* sp)
+{
+	push_number(sp, total_player_commands);
+	return sp;
+}
+
+struct profil_line_entry *profil_line_array = 0;
+program_t* profil_line_prog = 0; // program to profile
+object_t* profil_line_ob = 0;    // profile only this ob, or all if 0
+
+svalue_t *v_profil_line_start(svalue_t *sp, int num_arg)
+{
+	program_t* prog;
+	int size;
+
+	if(profil_line_prog)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		
+		push_number(sp, 0);
+		return sp;
+	}
+
+	profil_line_ob = 0;
+
+	if(num_arg == 2)
+	{
+		if(sp->type == T_OBJECT)
+		{
+			/* no deref */
+			profil_line_ob = sp->u.ob;
+		}
+		
+		sp--;
+	}
+	
+    if (sp->type == T_STRING)
+    {
+		object_t* ob = find_object(sp->u.str);
+		if(ob)
+		{
+			prog = ob->prog;
+		}
+		else
+		{
+			errorf("profil_line_start: cant find %s.", get_txt(sp->u.str));
+		}
+    }
+    else
+    {
+		prog = sp->u.ob->prog;
+    }
+
+	free_svalue(sp);
+	sp--;
+	
+	profil_line_prog = prog;
+	reference_prog(prog, "profil_line_start");
+	
+	if(profil_line_array)
+	{
+		free(profil_line_array);
+		profil_line_array = 0;
+	}
+
+	size = PROGRAM_END(*prog) - prog->program;
+	
+	profil_line_array = malloc(size * sizeof(struct profil_line_entry));
+	memset(profil_line_array, 0, size * sizeof(struct profil_line_entry));
+
+	printf("Allocated %d items, %d bytes\n",
+		   size,
+		   (int)(size * sizeof(struct profil_line_entry)));
+
+	
+	push_number(sp, 1);
+	
+	return sp;
+}
+
+
+svalue_t *f_profil_line_dump(svalue_t *sp)
+{
+    STORE_DOUBLE_USED
+	int i, t;
+	int item_count = 0;
+	vector_t* v;
+	
+	if(profil_line_prog == 0)
+	{
+		push_number(sp, 0);
+		return sp;
+	}
+	
+	for(i = 0; i < PROGRAM_END(*profil_line_prog) - profil_line_prog->program; i++)
+	{
+		if(profil_line_array[i].count != 0)
+		{
+			item_count++;
+		}
+	}
+
+    v = allocate_uninit_array(item_count * 8);
+
+	t = 0;
+	for(i = 0; i < PROGRAM_END(*profil_line_prog) - profil_line_prog->program; i++)
+	{
+		if(profil_line_array[i].count != 0)
+		{
+			string_t* filename;
+			int line;
+			
+			line = get_line_number(profil_line_prog->program + i,
+								   profil_line_prog,
+								   &filename);
+			
+			v->item[t].type = T_STRING;
+			v->item[t].u.str = filename;
+			t++;
+
+			v->item[t].type = T_NUMBER;
+			v->item[t].u.number = line;
+			t++;
+
+			v->item[t].type = T_STRING;
+            memcpy(&v->item[t].u.str, FUNCTION_NAMEP(profil_line_array[i].funstart), sizeof(string_t*));
+			ref_mstring(v->item[t].u.str);
+			t++;
+			
+			v->item[t].type = T_FLOAT;
+			STORE_DOUBLE(&v->item[t], time_to_msec(profil_line_array[i].time));
+			t++;
+
+			v->item[t].type = T_FLOAT;
+			STORE_DOUBLE(&v->item[t], time_to_msec(profil_line_array[i].max_time));
+			t++;
+			
+			v->item[t].type = T_FLOAT;
+			STORE_DOUBLE(&v->item[t], time_to_msec(profil_line_array[i].self_time));
+			t++;
+
+			v->item[t].type = T_FLOAT;
+			STORE_DOUBLE(&v->item[t], time_to_msec(profil_line_array[i].max_self_time));
+			t++;
+			
+			v->item[t].type = T_NUMBER;
+			v->item[t].u.number = profil_line_array[i].count;
+			t++;
+		}
+	}
+
+	push_array(sp, v);
+
+	return sp;
+}
+
+svalue_t *f_profil_line_stop(svalue_t *sp)
+{
+	if(profil_line_prog == 0)
+	{
+		return sp;
+	}
+	
+	free_prog(profil_line_prog, MY_TRUE);
+	profil_line_prog = 0;
+
+	if(profil_line_ob)
+	{
+		free_object(profil_line_ob, "profil_line_stop");
+		profil_line_ob = 0;
+	}
+	
+	if(profil_line_array)
+	{
+		free(profil_line_array);
+		profil_line_array = 0;
+	}
+
+	return sp;
+}
+
+svalue_t* f_debug_sleep (svalue_t *sp)
+{
+  time_t from, to;
+
+  time (&from);
+  to= from+ sp->u.number;
+
+  if (from < to)
+    while (time (NULL) < to)
+      ;
+
+  free_svalue (sp);
+  put_number (sp, 0);
+  return sp;
+}
+
+svalue_t* f_get_time_ms (svalue_t *sp)
+{
+  STORE_DOUBLE_USED
+    
+    sp++;
+  sp->type = T_FLOAT;
+  STORE_DOUBLE(sp, get_time_ms ());
+
+  return sp;
+}
+
+svalue_t* f_true_time (svalue_t *sp)
+{
+  sp++;
+  put_number (sp, time (NULL));
+  return sp;
+}
+  
+svalue_t *
+f_recompile_object (svalue_t * sp)
+{
+    object_t *this_ob, *ob;
+    string_t *name;
+    long ret;
+
+    this_ob = sp->u.ob;
+
+    ob = get_object(this_ob->load_name);
+
+    ret = 0;
+
+    if (ob != NULL)
+      {
+	/* If ob is a clone, try finding the blueprint first via the object's
+	 * program, then via the load_name.
+	 */
+	if (ob->flags & O_CLONE)
+	  {
+	    object_t *bp = NULL;
+	    
+	    /* If the object's program hasn't been replaced, it most likely
+	     * contains a pointer to the blueprint we're looking for.
+	     */
+	    if (!(ob->flags & O_REPLACED))
+	      {
+		bp = ob->prog->blueprint;
+		if (bp && (bp->flags & O_DESTRUCTED))
+		  {
+		    free_object(bp, "recompile_object");
+		    bp = ob->prog->blueprint = NULL;
+		  }
+	      }
+	    
+	    /* Fallback: find/load the blueprint by the load_name */
+	    if (!bp)
+	      bp = get_object(ob->load_name);
+	    if (bp)
+	      ob = bp;
+	  }
+	
+	if (ob->super)
+	  errorf("Recompiling a bad object: '%s' is contained in '%s'.\n"
+		, get_txt(ob->name), get_txt(ob->super->name));
+	
+	name = ob->name;
+	
+	/* If the ob is a clone, we have to test if its name is something
+	 * illegal like 'foobar#34'. In that case, we have to use the
+	 * load_name as template.
+	 */
+	if (ob->flags & O_CLONE)
+	  {
+	    char c;
+	    char *p;
+	    mp_int name_length, i;
+	    
+	    name_length = mstrsize(name);
+	    i = name_length;
+	    p = get_txt(ob->name)+name_length;
+	    while (--i > 0) {
+	      /* isdigit would need to check isascii first... */
+	      if ( (c = *--p) < '0' || c > '9' )
+		{
+		  if (c == '#' && name_length - i > 1)
+		    {
+		      /* Well, unusable name format - use the load_name */
+		      name = ob->load_name;
+		    }
+		  break;
+		}
+	    }
+	  }
+	
+	if ((ob->flags & O_SWAPPED) && load_ob_from_swap(ob) < 0)
+	  errorf("Out of memory: unswap object '%s'\n", get_txt(ob->name));
+	
+	if (ob->prog->flags & P_NO_CLONE)
+	  errorf("Cloning a bad object: '%s' sets '#pragma no_clone'.\n"
+		, get_txt(ob->name));
+
+	ob->time_of_ref = current_time;
+	
+	/* We do not want the heart beat to be running for unused copied objects */
+	
+	if (!(ob->flags & O_CLONE) && ob->flags & O_HEART_BEAT)
+	  set_heart_beat(ob, MY_FALSE);
+	
+	/* Got the blueprint */
+
+	free_prog (this_ob->prog, MY_FALSE);
+
+	this_ob->prog = ob->prog;
+
+	reference_prog (this_ob->prog, "recompile_object");
+
+	ret = 1;
+      }
+
+    free_svalue(sp);
+
+    put_number(sp, ret);
+
+    return sp;
+} /* f_recompile_object() */
+
+
+unsigned long long warning_eval_cost_ticks_amt;
+
+/* in milliseconds. use 0 to disable (default). */
+
+svalue_t *
+f_set_warning_eval_cost (svalue_t *sp)
+{
+
+  warning_eval_cost_ticks_amt= msec_to_time (sp->u.number);
+
+  free_svalue (sp);
+  
+  put_number (sp, 0);
+  return sp;
+}
diff -ruN /home/favorit/ldmud-3.3.719/src/bat.h ./bat.h
--- /home/favorit/ldmud-3.3.719/src/bat.h	1970-01-01 02:00:00.000000000 +0200
+++ ./bat.h	2009-09-03 11:49:52.000000000 +0300
@@ -0,0 +1,77 @@
+#ifndef BAT_H__
+#define BAT_H__
+
+#include "svalue.h"
+#include "exec.h"
+#include "profil2.h"
+
+svalue_t *f_all_shadow(svalue_t * sp);
+svalue_t *f_first_shadow(svalue_t * sp);
+svalue_t *f_last_shadow(svalue_t * sp);
+svalue_t *f_next_shadow(svalue_t * sp);
+
+svalue_t *v_all_objects(svalue_t * sp, int n);
+svalue_t *v_first_object(svalue_t * sp, int n);
+svalue_t *v_next_object(svalue_t * sp, int n);
+
+svalue_t *v_count_objects(svalue_t * sp, int n);
+svalue_t *f_count_lines(svalue_t *sp);
+
+/* catch_write() stuff */
+
+void   begin_catch_write();
+char*  end_catch_write();
+
+#define CATCH_WRITE_BUF_LEN  (10*16*1024) // x10 by blitzer jan.15.2004
+#define CATCH_WRITE_MAX_RECURSION 5
+
+#ifndef BAT_C__
+extern char catch_write_buf[CATCH_WRITE_MAX_RECURSION][CATCH_WRITE_BUF_LEN];
+extern int catching_write;
+extern int save_catching_write;
+extern int catch_write_len[CATCH_WRITE_MAX_RECURSION];
+extern object_t* catch_write_cur_interactive;
+#endif
+
+/* end of catch_write() stuff */
+
+/* time stuff */
+
+unsigned long long get_time ();
+double time_to_msec(const unsigned long long proftime);
+unsigned long long msec_to_time (double msec);
+
+double get_time_ms();
+
+/* word stuff */
+
+void word_init();
+
+extern svalue_t *f_random_integer(svalue_t *);
+extern svalue_t *f_random_float(svalue_t *);
+extern svalue_t *f_random_gaussian(svalue_t *);
+
+extern svalue_t *f_dump_variables(svalue_t *);
+extern svalue_t *f_dump_destr_ob_refs(svalue_t *);
+
+struct profil_line_entry
+{
+	unsigned long long time;
+	unsigned long long max_time;
+	unsigned long long self_time;
+	unsigned long long max_self_time;
+	unsigned int count;
+	fun_hdr_p funstart;
+};
+
+extern struct profil_line_entry *profil_line_array;
+extern program_t* profil_line_prog;
+extern object_t* profil_line_ob;
+
+
+extern unsigned long long warning_eval_cost_ticks_amt;
+
+svalue_t *f_set_warning_eval_cost_ticks (svalue_t *sp);
+
+
+#endif
diff -ruN /home/favorit/ldmud-3.3.719/src/comm.c ./comm.c
--- /home/favorit/ldmud-3.3.719/src/comm.c	2009-06-15 01:18:52.000000000 +0300
+++ ./comm.c	2009-09-03 13:21:06.000000000 +0300
@@ -130,6 +130,9 @@
 #include "swap.h"
 #include "wiz_list.h"
 #include "xalloc.h"
+#include "bat.h"
+
+#include "i-eval_cost.h"
 
 #include "i-eval_cost.h"
 
@@ -1800,6 +1803,38 @@
             srcstr = NULL;
         }
 
+//#ifdef BAT
+        if(catching_write &&
+           catch_write_cur_interactive == command_giver)
+		{
+            if(srclen > CATCH_WRITE_BUF_LEN -
+               catch_write_len [catching_write - 1])
+			{
+				int i;
+                catching_write = 0;
+				
+                i = CATCH_WRITE_MAX_RECURSION;
+				
+                while (i--)
+				{
+                    catch_write_len [i] = 0;
+                    catch_write_buf [i][0] = '\0';
+				}
+				
+                catch_write_cur_interactive = 0;
+				
+                errorf("catch_write buffer full.\n");
+                return;
+			}
+			
+            strncpy(&catch_write_buf [catching_write - 1][catch_write_len [catching_write - 1]],
+                    source, srclen);
+            catch_write_len [catching_write - 1] += srclen;
+			
+            return;
+		}
+//#endif
+
         /* If we're not sending a telnet command with this message,
          * pass on the new data to any snooper and/or shadow
          */
@@ -2288,7 +2323,7 @@
                     continue;
                 }
 
-                if (ip->tn_state == TS_READY)
+                if (ip->tn_state == TS_READY || ip->push_end > 0) // BAT
                 {
                     /* If telnet is ready for commands, react quickly. */
                     twait = 0;
@@ -3145,28 +3180,47 @@
              * a command is complete, the tn_state is TS_READY.
              */
             DTN(("tn complete, telnet machine state: %hhd\n", ip->tn_state));
-            if (ip->tn_state == TS_READY)
+            if (ip->tn_state == TS_READY || ip->push_end > 0) // BAT
             {
                 /* We have a command: copy it into buff, handle a
                  * possible snooper and return.
                  */
 
-                DTN(("telnet machine ready\n"));
-                /* buffer overflows here are impossible even with strcpy(),
-                 * because buff is allocated in backend() as MAX_TEXT+4 and
-                 * ip->text is allocated as MAX_TEXT+2. Ok, as long as nobody
-                 * changes buff in backend() withour changing ip->text ... */
-                strcpy(buff, ip->text);
-                command_giver = ip->ob;
-                trace_level = ip->trace_level;
-                ip->chars_ready = 0; /* for escaped charmode */
-
-                /* Reinitialize the telnet machine, possibly already
-                 * producing the next command in .text[].
-                 */
-                ip->tn_state = TS_DATA;
-                telnet_neg(ip);
-
+//#ifdef BAT
+				if(ip->push_end > 0)
+                {
+					int pushlen;
+					
+					/* Check for max size only in copying, because
+					   otherwise the shit has already hit the fan */
+					strncpy(buff, ip->push_buf, MAX_TEXT);
+					buff[MAX_TEXT-1] = '\0';
+					command_giver = ip->ob;
+					trace_level = ip->trace_level;
+					
+					pushlen = strlen(ip->push_buf)+1;
+					ip->push_end -= pushlen;
+					if(ip->push_end > 0)
+						memmove(ip->push_buf, ip->push_buf+pushlen, ip->push_end);
+                } else {
+//#endif
+					DTN(("telnet machine ready\n"));
+					/* buffer overflows here are impossible even with strcpy(),
+					* because buff is allocated in backend() as MAX_TEXT+4 and
+					* ip->text is allocated as MAX_TEXT+2. Ok, as long as nobody
+					* changes buff in backend() withour changing ip->text ... */
+					strcpy(buff, ip->text);
+					command_giver = ip->ob;
+					trace_level = ip->trace_level;
+					ip->chars_ready = 0; /* for escaped charmode */
+
+					/* Reinitialize the telnet machine, possibly already
+					 * producing the next command in .text[].
+					 */
+					ip->tn_state = TS_DATA;
+					telnet_neg(ip);
+				}
+				
                 /* If the user is not in ed, don't let him issue another command
                  * before the poll comes again.
                  */
@@ -3677,6 +3731,7 @@
     new_interactive->quote_iac = MY_TRUE;
     set_default_conn_charset(new_interactive->charset);
     set_default_combine_charset(new_interactive->combine_cset);
+    new_interactive->push_end = 0; // BAT
     new_interactive->text[0] = '\0';
     memcpy(&new_interactive->addr, addr, addrlen);
     new_interactive->access_class = class;
@@ -4420,6 +4475,19 @@
         print_prompt_string(STR_DEFAULT_PROMPT);
     }
 
+//#ifdef BAT
+	char msg[2];
+	
+	msg[0] = IAC;
+	msg[1] = GA;
+	SEND_TELNET_COMMAND(
+		add_message(FMT_BINARY, msg, 2);
+		add_message(message_flush);
+		);
+
+	//add_message("%c%c", IAC, GA); // BAT
+//#endif
+	
     current_object = save_current;
     previous_ob = save_previous;
 } /* print_prompt() */
@@ -5422,6 +5490,21 @@
                 DTN(("t_n: got NOP\n")); break;
             case GA:
                 DTN(("t_n: got GA\n")); break;
+//#ifdef BAT
+			case AYT:
+				command_giver = ip->ob;
+                DTN(("t_n: got AYT\n"));
+				char msg[2];
+				
+				msg[0] = IAC;
+				msg[1] = AYT;
+				SEND_TELNET_COMMAND(
+					add_message(FMT_BINARY, msg, 2);
+					add_message(message_flush);
+					);
+				
+				break;
+//#endif	
             default:
                 DTN(("t_n: got %02hhx\n", ch)); break;
                 break;
@@ -9210,3 +9293,60 @@
 } /* f_configure_interactive() */
 
 /***************************************************************************/
+
+//#ifdef BAT
+int push_line_to_player(object_t *ob, char *line)
+{
+  interactive_t *ip;
+  int x;
+  int linelen = 0;
+
+  if (strchr(line, '\n'))
+    return 0;
+  if (strchr(line, '\r'))
+    return 0;
+
+  for (x=0; x< MAX_PLAYERS; x++)
+    if (all_players[x] && all_players[x]->ob == ob)
+      break;
+
+  if (x == MAX_PLAYERS)
+    return 0;
+
+  ip = all_players[x];
+
+  linelen = strlen(line)+1;
+
+  if (linelen + ip->push_end > MAX_PUSH)
+    return 0;
+
+  if(ip->push_end > 0)
+    memmove(ip->push_buf + linelen, ip->push_buf, ip->push_end);
+  strcpy(ip->push_buf, line);
+  ip->push_end += linelen;
+
+  return 1;
+}
+
+svalue_t *
+f_push_line (svalue_t *sp)
+{
+  if (current_object->flags & O_DESTRUCTED)
+    {
+      free_svalue(sp);
+      put_number(sp, 0);
+    }
+  else
+    {
+      int i;
+
+      i = push_line_to_player(current_object, get_txt(sp[0].u.str));
+
+      free_svalue(sp);
+      put_number(sp, i);
+    }
+
+  return sp;
+} /* f_push_line() */
+
+//#endif
diff -ruN /home/favorit/ldmud-3.3.719/src/comm.h ./comm.h
--- /home/favorit/ldmud-3.3.719/src/comm.h	2009-06-15 01:18:52.000000000 +0300
+++ ./comm.h	2009-09-03 12:30:44.000000000 +0300
@@ -40,6 +40,10 @@
 #    endif /* irix */
 #endif /* MAX_SOCKET_PACKET_SIXE */
 
+#ifdef BAT
+#undef MAX_SOCKET_PACKET_SIZE
+#define MAX_SOCKET_PACKET_SIZE (1024*16)
+#endif
 
 /* --- IPv6 --- */
 
@@ -79,6 +83,13 @@
 
 #define MAX_TEXT  2048
 
+//#ifdef BAT
+/* Size of a users push buffer for pushed data.
+ * This is also the maximum size for a push.
+ */
+#define MAX_PUSH 2048
+//#endif
+
 /* 'Format string' to use with add_message() when sending
  * a string_t* string to the player.
  */
@@ -235,6 +246,7 @@
     string_t *trace_prefix;     /* Trace only objects which have this string
                                    as name prefix. NULL traces everything. */
     int message_length;         /* Current length of message in message_buf[] */
+	int push_end;               /* first free char in push_buf[] */ // BAT
 
     object_t *next_player_for_flush;
     object_t *previous_player_for_flush;
@@ -262,6 +274,12 @@
     char gobble_char;           /* Char to ignore at the next telnet_neg() */
     char ts_data;               /* Telnet suboption? */
 
+//#ifdef BAT
+	char push_buf[MAX_PUSH];
+	/* The push buffer. It contains command pushed from the mud side
+	   to the input queue. The commands are separated by '\0'.*/
+//#endif
+
     char text[MAX_TEXT+2];
       /* The receive buffer. It can contain two extra characters:
        * a '\r' that is recognized only after another character is read,
diff -ruN /home/favorit/ldmud-3.3.719/src/config.h.in ./config.h.in
--- /home/favorit/ldmud-3.3.719/src/config.h.in	2009-06-15 01:18:52.000000000 +0300
+++ ./config.h.in	2009-09-03 12:30:44.000000000 +0300
@@ -619,14 +619,14 @@
  * TODO: Remove if it doesn't help with the fragmentation, as it uses up
  * TODO:: a small, but measurable amount of time in the allocator.
  */
-#define MALLOC_ORDER_LARGE_FREELISTS
+#undef MALLOC_ORDER_LARGE_FREELISTS
 
 /* Order slaballoc partial-slab freelists by number of free blocks.
  * Supported by: MALLOC_slaballoc.
  * TODO: Remove if it doesn't help with the fragmentation, as it uses up
  * TODO:: a small, but measurable amount of time in the allocator.
  */
-#define MALLOC_ORDER_SLAB_FREELISTS
+#undef MALLOC_ORDER_SLAB_FREELISTS
 
 /* Allow slaballoc to use multiples of DESIRED_SLAB_SIZE when creating
  * new slabs.
diff -ruN /home/favorit/ldmud-3.3.719/src/crc32.c ./crc32.c
--- /home/favorit/ldmud-3.3.719/src/crc32.c	1970-01-01 02:00:00.000000000 +0200
+++ ./crc32.c	2009-09-03 11:49:52.000000000 +0300
@@ -0,0 +1,111 @@
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be u_(bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ */
+
+
+#include "crc32.h"
+
+static unsigned int crc32_tab[] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+unsigned int
+ssh_crc32(const unsigned char *s, unsigned int len)
+{
+  unsigned int i;
+  unsigned int crc32val;
+  
+  crc32val = 0;
+  for (i = 0;  i < len;  i ++) {
+    crc32val = crc32_tab[(crc32val ^ s[i]) & 0xff] ^ (crc32val >> 8);
+  }
+  return crc32val;
+}
diff -ruN /home/favorit/ldmud-3.3.719/src/crc32.h ./crc32.h
--- /home/favorit/ldmud-3.3.719/src/crc32.h	1970-01-01 02:00:00.000000000 +0200
+++ ./crc32.h	2009-09-03 11:49:52.000000000 +0300
@@ -0,0 +1,25 @@
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1992 Tatu Ylonen, Espoo, Finland
+ *                    All rights reserved
+ * Functions for computing 32-bit CRC.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+/* RCSID("$OpenBSD: crc32.h,v 1.9 2000/12/19 23:17:56 markus Exp $"); */
+
+#ifndef CRC32_H
+#define CRC32_H
+
+/*
+ * This computes a 32 bit CRC of the data in the buffer, and returns the CRC.
+ * The polynomial used is 0xedb88320.
+ */
+unsigned int ssh_crc32(const unsigned char *buf, unsigned int len);
+
+#endif				/* CRC32_H */
diff -ruN /home/favorit/ldmud-3.3.719/src/dumpstat.c ./dumpstat.c
--- /home/favorit/ldmud-3.3.719/src/dumpstat.c	2009-06-15 01:18:52.000000000 +0300
+++ ./dumpstat.c	2009-09-03 12:20:24.000000000 +0300
@@ -429,3 +429,45 @@
 
 /***************************************************************************/
 
+//#ifdef BAT
+/*-------------------------------------------------------------------------*/
+vector_t*
+get_variable_sizes(object_t *ob)
+{
+  mp_int Total = 0;
+  mp_int total = sizeof(p_int); /* smalloc overhead */
+  int i;
+  svalue_t *svp;
+  variable_t* names;
+  vector_t* arr;
+
+  i = ob->prog->num_variables;
+
+  ptable = new_pointer_table();
+  if (!ptable)
+    errorf("(dumpstat) Out of memory for new pointer table.\n");
+
+  svp = ob->variables;
+  names = ob->prog->variables;
+
+  arr = allocate_array(i * 3);
+
+  for (; --i >= 0; svp++, names++)
+    {
+      mp_int tmp1, tmp2;
+
+      tmp1 = svalue_size(svp, &tmp2) + sizeof (svalue_t);
+      total += tmp1;
+      tmp2 += sizeof(svalue_t);
+      Total += tmp2;
+
+      put_ref_string(&(arr->item[(i-0)*3 + 0]), names->name);
+      put_number(&(arr->item[(i-0)*3 + 1]), tmp1);
+      put_number(&(arr->item[(i-0)*3 + 2]), tmp2);
+    }
+
+  free_pointer_table(ptable);
+
+  return arr;
+} /* get_variable_sizes() */
+//#endif
diff -ruN /home/favorit/ldmud-3.3.719/src/dumpstat.h ./dumpstat.h
--- /home/favorit/ldmud-3.3.719/src/dumpstat.h	2009-06-15 01:18:52.000000000 +0300
+++ ./dumpstat.h	2009-09-03 11:49:52.000000000 +0300
@@ -8,5 +8,6 @@
 extern mp_int program_string_size (program_t *prog, mp_int * pOverhead, mp_int * pData);
 extern Bool dumpstat(string_t *name);
 extern Bool dumpstat_dest(string_t *name);
+extern vector_t* get_variable_sizes (object_t *ob); // BAT
 
 #endif /* DUMPSTAT_H__ */
diff -ruN /home/favorit/ldmud-3.3.719/src/efuns.c ./efuns.c
--- /home/favorit/ldmud-3.3.719/src/efuns.c	2009-06-15 01:18:52.000000000 +0300
+++ ./efuns.c	2009-09-03 12:30:44.000000000 +0300
@@ -3420,6 +3420,7 @@
             *(nump = &info->min) = 0;
             continue;
 
+		case 'i': // BAT
         case 'd':
             /* Skip leading whitespace */
             while(isspace((unsigned char)*str))
@@ -5839,6 +5840,13 @@
     default:
         fatal("Bad arg 1 to to_array(): type %s\n", typename(sp->type));
         break;
+//#ifdef BAT
+    case T_NUMBER:
+      if(sp->type == T_NUMBER && sp->u.number == 0)
+        {
+          break;
+        }
+//#endif
     case T_STRING:
     case T_SYMBOL:
         /* Split the string into an array of ints */
@@ -6225,7 +6233,8 @@
         return sp;
 
     case T_STRING:
-        o = find_object(sp->u.str);
+        //o = find_object(sp->u.str);
+        o = get_object(sp->u.str);  // BAT
         free_svalue(sp);
         break;
     }
diff -ruN /home/favorit/ldmud-3.3.719/src/func_spec ./func_spec
--- /home/favorit/ldmud-3.3.719/src/func_spec	2009-06-15 01:18:52.000000000 +0300
+++ ./func_spec	2009-10-03 00:05:01.000000000 +0300
@@ -70,6 +70,7 @@
 #endif
         local
         catch
+        catch_write /* BAT */
 "++"    inc
 "--"    dec
 "x++"   post_inc
@@ -142,6 +143,7 @@
  * called from LPC.
  */
 
+        end_catch_write /* BAT */
         pop_value
         pop_second
         dup
@@ -296,6 +298,7 @@
 
         /* Functions and Closures */
 
+unknown call_shadow(object|string|object *|string *, string, ...); /* BAT */
 unknown call_other(object|string|object *|string *, string, ...);
 unknown call_direct(object|string|object *|string *, string, ...);
           /* The lexer switches the return type between 'unknown'
@@ -356,11 +359,11 @@
 string  to_string(mixed);
 #ifdef USE_STRUCTS
 int     baseof(struct, struct);
-mixed  *to_array(string|mixed*|symbol|quoted_array|struct);
+mixed  *to_array(string|mixed*|symbol|quoted_array|struct|null); /* BAT */
 mixed   to_struct(mapping|mixed*|struct, void|struct);
 mixed  *struct_info(struct, int);
 #else
-mixed  *to_array(string|mixed*|symbol|quoted_array);
+mixed  *to_array(string|mixed*|symbol|quoted_array|null); /* BAT */
 #endif /* USE_STRUCTS */
 object  to_object(null|object|string|closure);
 
@@ -542,9 +545,9 @@
 object  shadow(object, int);
 int     snoop(object, void|object);
 #ifdef USE_STRUCTS
-void    tell_object(object|string, string|mixed*|mapping|struct|object);
+void    tell_object(object|string, string|mixed*|mapping|struct|object, int default: F_CONST0); /* BAT */
 #else
-void    tell_object(object|string, string|mixed*|mapping|object);
+void    tell_object(object|string, string|mixed*|mapping|object, int default: F_CONST0); /* BAT */
 #endif /* USE_STRUCTS */
 void    unshadow();
 string  variable_exists(string, void|int|object, void|int);
@@ -796,5 +799,56 @@
 
 #endif /* USE_DEPRECATED */
 
+        /* BAT specific efuns */
+
+int     count_objects(object|string, void|int);
+int     count_lines(string);
+object  first_object(object|string, void|int);
+object  next_object(object, void|int);
+object  *all_objects(object|string, void|int);
+object  *all_shadow(object default: F_THIS_OBJECT);
+object  first_shadow(object default: F_THIS_OBJECT);
+object  last_shadow(object default: F_THIS_OBJECT);
+object  next_shadow(object default: F_THIS_OBJECT);
+string  line_wrap(string, void|null|string, void|null|string, void|int);
+string  nuke_controls(string);
+string  query_input_to(object default: F_THIS_OBJECT);
+int     push_line(string);
+
+int*    get_backend_times();
+
+
+/* mixed *erusage(int); */
+
+string  socket_strerror(int);
+void    socket_print_stats();
+int     socket_create(closure);
+int     socket_close(int);
+int     socket_connect(int, string, closure, closure);
+int     socket_bind(int, int);
+int     socket_listen(int, closure);
+int     socket_accept(int, closure, closure);
+int     socket_send(int, string);
+string  socket_address(int);
+
+int     random_integer();
+float   random_float();
+float   random_gaussian();
+
+mixed*  get_variable_sizes(object);
+void    dump_destr_ob_refs();
+
+int             query_total_player_commands();
+
+int     debug_sleep (int);
+float   get_time_ms ();
+int     true_time ();
+
+int     recompile_object (object);
+
+int     set_warning_eval_cost (int);
+
+/* end of BAT specific efuns */
+
 
 /***************************************************************************/
diff -ruN /home/favorit/ldmud-3.3.719/src/gcollect.c ./gcollect.c
--- /home/favorit/ldmud-3.3.719/src/gcollect.c	2009-06-15 01:18:52.000000000 +0300
+++ ./gcollect.c	2009-09-03 12:30:44.000000000 +0300
@@ -123,6 +123,9 @@
 #include "swap.h"
 #include "wiz_list.h"
 #include "xalloc.h"
+#include "sockets.h" // BAT
+
+#include "i-eval_cost.h"
 
 #include "i-eval_cost.h"
 
@@ -2053,6 +2056,8 @@
        * processing (and potentially swapping) the objects.
        */
 
+    clear_socket_refs(); // BAT
+	
     null_vector.ref = 0;
 
     /* Finally, walk the list of destructed objects and clear all references
@@ -2264,6 +2269,8 @@
 
     mb_note_refs();
 
+	clear_socket_refs(); // BAT
+
     if (reserved_user_area)
         note_ref(reserved_user_area);
     if (reserved_master_area)
Binary files /home/favorit/ldmud-3.3.719/src/hosts/amiga/ldmud.info and ./hosts/amiga/ldmud.info differ
Binary files /home/favorit/ldmud-3.3.719/src/hosts/amiga/README.Amiga.info and ./hosts/amiga/README.Amiga.info differ
Binary files /home/favorit/ldmud-3.3.719/src/hosts/amiga/StartMUD.info and ./hosts/amiga/StartMUD.info differ
Binary files /home/favorit/ldmud-3.3.719/src/hosts/be/driver.rsrc and ./hosts/be/driver.rsrc differ
Binary files /home/favorit/ldmud-3.3.719/src/hosts/be/icon-16x16.raw and ./hosts/be/icon-16x16.raw differ
Binary files /home/favorit/ldmud-3.3.719/src/hosts/be/icon-32x32.raw and ./hosts/be/icon-32x32.raw differ
Binary files /home/favorit/ldmud-3.3.719/src/hosts/win32/Readme.rtf and ./hosts/win32/Readme.rtf differ
Binary files /home/favorit/ldmud-3.3.719/src/hosts/win32/tubmud.bmp and ./hosts/win32/tubmud.bmp differ
diff -ruN /home/favorit/ldmud-3.3.719/src/i-eval_cost.h ./i-eval_cost.h
--- /home/favorit/ldmud-3.3.719/src/i-eval_cost.h	2009-06-15 01:18:52.000000000 +0300
+++ ./i-eval_cost.h	2009-09-03 13:02:18.000000000 +0300
@@ -64,7 +64,7 @@
 
 /* Reset the evaluation cost/time counter.
  */
-#define CLEAR_EVAL_COST (assigned_eval_cost = eval_cost = 0)
+//#define CLEAR_EVAL_COST (assigned_eval_cost = eval_cost = 0)
 
 /* Check if the current evaluation took too long
  */
diff -ruN /home/favorit/ldmud-3.3.719/src/interpret.c ./interpret.c
--- /home/favorit/ldmud-3.3.719/src/interpret.c	2009-06-15 01:18:52.000000000 +0300
+++ ./interpret.c	2009-10-02 23:50:30.000000000 +0300
@@ -307,6 +307,10 @@
     int variable_index_offset;
       /* Function and variable index offset.
        */
+
+//#ifdef BAT
+  char *class_name_for_this_function; /* true class in which this was defined */
+//#endif
 };
 
 /*-------------------------------------------------------------------------*/
@@ -559,6 +563,9 @@
    * assign_eval_cost().
    */
 
+unsigned long long eval_cost_warning_start_ticks;
+unsigned long long eval_cost_warning_total_ticks;
+
 svalue_t apply_return_value = { T_NUMBER };
   /* This variable holds the result from a call to apply(), transferred
    * properly from the interpreter stack where the called function
@@ -735,7 +742,7 @@
 /* Forward declarations */
 
 enum { APPLY_NOT_FOUND = 0, APPLY_FOUND, APPLY_DEFAULT_FOUND };
-static int int_apply(string_t *, object_t *, int, Bool, Bool);
+static int int_apply(string_t *, object_t *, int, Bool, Bool, Bool); // BAT
 static void call_simul_efun(unsigned int code, object_t *ob, int num_arg);
 #ifdef DEBUG
 static void check_extra_ref_in_vector(svalue_t *svp, size_t num);
@@ -3071,6 +3078,10 @@
     else
     {
         ind = i->u.number;
+//#ifdef BAT
+		if(ind < 0)
+			ind = VEC_SIZE(vec) + ind;
+//#endif
         if (ind < 0)
         {
             ERROR("Illegal index for []: not a positive number.\n");
@@ -3212,6 +3223,10 @@
     else
     {
         ind = i->u.number;
+//#ifdef BAT
+		if(ind < 0)
+			ind = mstrsize(svp->u.str) + ind;
+//#endif
         if (ind < 0)
         {
             ERROR("Illegal index for []: not a positive number.\n");
@@ -7715,6 +7730,31 @@
     }
 } /* put_default_argument() */
 
+//#ifdef BAT
+static void
+bat_check_eval_cost_warning ()
+{
+  unsigned long long this_ticks= get_time ();
+
+
+  if (warning_eval_cost_ticks_amt &&
+      this_ticks >= eval_cost_warning_start_ticks+ warning_eval_cost_ticks_amt)
+    {
+      debug_message("\n\n"
+                    "WARNING: Evaluation time %.1f seconds"
+                    "\n",
+                    time_to_msec (this_ticks- eval_cost_warning_total_ticks)/
+                    1000.0);
+      dump_trace(MY_FALSE, 0);
+
+      /* next warning after a second */
+
+      eval_cost_warning_start_ticks= this_ticks- warning_eval_cost_ticks_amt+
+        msec_to_time (1000);
+    }
+}
+//#endif
+
 /*-------------------------------------------------------------------------*/
 Bool
 eval_instruction (bytecode_p first_instruction
@@ -7773,7 +7813,6 @@
        * of eval_instruction().
        */
 
-
     /* Handy macros:
      *
      *   GET_NUM_ARG: Get the number of arguments, resp. check if the
@@ -7969,6 +8008,10 @@
     {
         rt_context_t * context;
 
+//#ifdef BAT
+        bat_check_eval_cost_warning ();
+//#endif
+	
         /* Evaluation too long. Restore some globals and throw
          * an error.
          */
@@ -8004,6 +8047,7 @@
     if (instrs[instruction].min_arg != instrs[instruction].max_arg
      && instruction != F_CALL_OTHER
      && instruction != F_CALL_DIRECT
+     && instruction != F_CALL_SHADOW // BAT
        )
     {
         num_arg = GET_UINT8(pc);
@@ -8737,6 +8781,9 @@
         svalue_t *pResult;  /* Return value on stack */
         svalue_t *efp = fp+csp->num_local_variables; /* Expected end of frame */
 
+//#ifdef BAT
+        bat_check_eval_cost_warning ();
+//#endif
         pResult = sp;
 
         /* Remove any intermediate error contexts */
@@ -9450,6 +9497,18 @@
         inter_sp = sp;
         inter_pc = pc;
         arg = sp - num_arg + 1;
+
+//#ifdef BAT
+		/* stupid zero check */
+		if(arg[0].type == T_NUMBER && arg[0].u.number == 0)
+		{
+			pop_n_elems(num_arg-1);
+			free_svalue(sp);
+			put_number(sp, 0);
+			break;
+		}
+//#endif
+		
         if (arg[0].type != T_STRING)
             BAD_ARG_ERROR(1, T_STRING, arg[0].type);
         if (arg[1].type != T_STRING)
@@ -9609,7 +9668,7 @@
 #endif /* CHECK_OBJECT_REF */
             return MY_FALSE; /* Guarded code terminated with 'return' itself */
         }
-
+		
         /* Restore the important variables */
         pc = inter_pc;
         sp = inter_sp;
@@ -9625,6 +9684,30 @@
 #endif
         break;
     }
+	
+//#ifdef BAT
+    CASE(F_CATCH_WRITE);
+    {
+		begin_catch_write();
+		break;
+    }
+    
+    CASE(F_END_CATCH_WRITE);
+    {
+		char *tmpStr = end_catch_write();
+		sp--;
+		
+		if(tmpStr)
+		{
+			string_t *new = new_mstring(tmpStr);
+			push_string(sp, new);
+		}
+		else
+			push_number(sp, 0);
+		
+		break;
+    }
+//#endif
 
     CASE(F_INC);                    /* --- inc                 --- */
     {
@@ -16334,6 +16417,7 @@
 
     CASE(F_CALL_DIRECT);            /* --- call_direct         --- */
     CASE(F_CALL_OTHER);             /* --- call_other          --- */
+    CASE(F_CALL_SHADOW); /* BAT */
     {
         /* EFUN call_other(), call_direct()
          *
@@ -16438,10 +16522,11 @@
                 }
                 traceing_recursion--;
             }
-
+			
             /* Call the function with the remaining args on the stack.
              */
-            if (!int_apply(arg[1].u.str, ob, num_arg-2, MY_FALSE, b_use_default))
+            if (!int_apply(arg[1].u.str, ob, num_arg-2, MY_FALSE, b_use_default,
+						   instruction == F_CALL_SHADOW ? MY_TRUE : MY_FALSE))  // BAT
             {
                 /* Function not found */
                 if (b_use_default) /* int_apply() removed the args */
@@ -16452,6 +16537,7 @@
                 push_number(sp, 0);
                 break;
             }
+
             sp -= num_arg - 3;
 
             /* The result of the function call is on the stack. But so
@@ -16562,7 +16648,8 @@
                 /* Call the function with the remaining args on the stack.
                  */
                 inter_sp = sp; /* update to new setting */
-                if (!int_apply(arg[1].u.str, ob, num_arg-2, MY_FALSE, b_use_default))
+                if (!int_apply(arg[1].u.str, ob, num_arg-2, MY_FALSE, b_use_default,
+							   instruction == F_CALL_SHADOW ? MY_TRUE : MY_FALSE)) // BAT
                 {
                     /* Function not found, Assign 0 as result.
                      */
@@ -16825,10 +16912,32 @@
 
 } /* eval_instruction() */
 
+//#ifdef BAT
+/* dura */
+char*
+find_function_name (unsigned int fx, program_t *progp)
+{
+	funflag_t flags;
+	
+	flags = progp->functions[fx];
+	
+	while (flags & NAME_INHERITED) {
+		inherit_t *inheritp;
+		
+		inheritp = &progp->inherit[flags & INHERIT_MASK];
+		fx -= inheritp->function_index_offset;
+		progp = inheritp->prog;
+		flags = progp->functions[fx];
+	}
+	
+	return progp->name->txt;
+}
+//#endif
+
 /*-------------------------------------------------------------------------*/
 static Bool
 apply_low ( string_t *fun, object_t *ob, int num_arg
-          , Bool b_ign_prot, Bool allowRefs)
+			, Bool b_ign_prot, Bool allowRefs, Bool b_ign_shadows) // BAT
 
 /* The low-level implementation of function calls.
  *
@@ -16858,7 +16967,7 @@
     program_t *progp;
     struct control_stack *save_csp;
     p_int ix;
-
+	
     /* This object will now be used, and is thus a target for
      * reset later on (when time due).
      */
@@ -16874,7 +16983,7 @@
     /* If there is a chain of objects shadowing, start with the first
      * of these.
      */
-    if (ob->flags & O_SHADOW)
+    if (!b_ign_shadows && (ob->flags & O_SHADOW)) // BAT (ob->flags & O_SHADOW)
     {
         object_t *shadow;
 
@@ -16980,7 +17089,9 @@
             previous_ob = current_object;
             current_object = ob;
             save_csp = csp;
+			
             eval_instruction(FUNCTION_CODE(funstart), inter_sp);
+
 #ifdef DEBUG
             if (save_csp-1 != csp)
                 fatal("Bad csp after execution in apply_low\n");
@@ -17061,7 +17172,6 @@
                 cache[ix].funstart = funstart;
                 cache[ix].flags = progp->functions[fx]
                                   & (TYPE_MOD_STATIC|TYPE_MOD_PROTECTED);
-
                 /* Static functions may not be called from outside,
                  * Protected functions not even from the inside.
                  */
@@ -17076,7 +17186,7 @@
                     previous_ob = csp->prev_ob;
                     current_object = csp->ob;
                     pop_control_stack();
-                    if (ob->flags & O_SHADOW && O_GET_SHADOW(ob)->shadowing)
+                    if (!b_ign_shadows && (ob->flags & O_SHADOW && O_GET_SHADOW(ob)->shadowing)) // BAT
                     {
                         /* This is an object shadowing another. The function
                          * was not found, but can maybe be found in the object
@@ -17093,6 +17203,7 @@
                 previous_ob = current_object;
                 current_object = ob;
                 save_csp = csp;
+
                 eval_instruction(FUNCTION_CODE(funstart), inter_sp);
 #ifdef DEBUG
                 if (save_csp-1 != csp)
@@ -17120,7 +17231,7 @@
      * shadowed object.
      */
 
-    if (ob->flags & O_SHADOW && O_GET_SHADOW(ob)->shadowing)
+    if (!b_ign_shadows && (ob->flags & O_SHADOW && O_GET_SHADOW(ob)->shadowing)) // BAT
     {
         ob = O_GET_SHADOW(ob)->shadowing;
         goto retry_for_shadow;
@@ -17138,7 +17249,7 @@
 /*-------------------------------------------------------------------------*/
 static int
 int_apply (string_t *fun, object_t *ob, int num_arg
-          , Bool b_ign_prot, Bool b_use_default
+		   , Bool b_ign_prot, Bool b_use_default, Bool b_ign_shadows // BAT
           )
 
 /* The wrapper around apply_low() to handle default methods.
@@ -17171,7 +17282,7 @@
  */
 
 {
-    if (apply_low(fun, ob, num_arg, b_ign_prot, MY_FALSE))
+    if (apply_low(fun, ob, num_arg, b_ign_prot, MY_FALSE, b_ign_shadows))  // BAT
         return APPLY_FOUND;
 
     if (b_use_default)
@@ -17213,7 +17324,7 @@
             /* Call the function */
             if (hook->type == T_STRING)
             {
-                rc = apply_low(hook->u.str, ob, num_arg+num_extra, b_ign_prot, MY_TRUE);
+                rc = apply_low(hook->u.str, ob, num_arg+num_extra, b_ign_prot, MY_TRUE, b_ign_shadows); // BAT
             }
             else /* hook->type == T_CLOSURE */
             {
@@ -17332,7 +17443,7 @@
 #endif
 
     /* Do the call */
-    if (!int_apply(fun, ob, num_arg, b_find_static, b_use_default))
+    if (!int_apply(fun, ob, num_arg, b_find_static, b_use_default, MY_FALSE)) // BAT
     {
         if (!b_use_default) /* int_apply() did not clean up the stack */
             inter_sp = _pop_n_elems(num_arg, inter_sp);
@@ -18476,7 +18587,7 @@
     function_name = simul_efunp[code].name;
 
     /* First, try calling the function in the given object */
-    if (!int_apply(function_name, ob, num_arg, MY_FALSE, MY_FALSE))
+    if (!int_apply(function_name, ob, num_arg, MY_FALSE, MY_FALSE, MY_FALSE)) // BAT
     {
         /* Function not found: try the alternative sefun objects */
         if (simul_efun_vector)
@@ -18494,7 +18605,7 @@
                 }
                 if ( !(ob = get_object(v->u.str)) )
                     continue;
-                if (int_apply(function_name, ob, num_arg, MY_FALSE, MY_FALSE))
+                if (int_apply(function_name, ob, num_arg, MY_FALSE, MY_FALSE, MY_FALSE)) // BAT
                     return;
             }
             return;
@@ -20725,7 +20836,7 @@
      */
     if (ob == master_ob)
         b_use_default = MY_FALSE;
-    rc = int_apply(arg[2].u.str, ob, num_arg-3, MY_FALSE, b_use_default);
+    rc = int_apply(arg[2].u.str, ob, num_arg-3, MY_FALSE, b_use_default, MY_FALSE); // BAT
     if (rc == APPLY_NOT_FOUND)
     {
         /* Function not found */
diff -ruN /home/favorit/ldmud-3.3.719/src/interpret.h ./interpret.h
--- /home/favorit/ldmud-3.3.719/src/interpret.h	2009-06-15 01:18:52.000000000 +0300
+++ ./interpret.h	2009-09-03 12:30:44.000000000 +0300
@@ -15,6 +15,8 @@
 #include "bytecode.h"
 #include "svalue.h"
 
+#include "bat.h"
+
 /* --- Types --- */
 
 /* --- struct control_stack: one control stack element
@@ -79,27 +81,48 @@
        * TODO:: to avoid accesses to wrong functions/variables.
        */
 #ifdef EVAL_COST_TRACE
-    int32 eval_cost;
-      /* The eval cost at that moment. */
+	int32 eval_cost;
+	/* The eval cost at that moment. */
 #endif
-};
-
-/* a general error handler structure. head is assigned as payload to an 
- * T_LVALUE svalue of type T_ERROR_HANDLER and pushed onto the value stack.
- * If the stack is unrolled during runtime errors the error_handler function
- * is called and frees buff. */
-typedef struct errorhandler_s {
-  svalue_t head;        /* The T_ERROR_HANDLER structure */
-  char     * buff;      /* The allocated buffer to free. */
-} errorhandler_t;
 
+//#ifdef BAT
+	unsigned long long call_time;
+	p_uint profil_line_offset;
+//#endif
 
-/* --- Constants --- */
-
+};
+/* a general error handler structure. head is assigned as payload to an                                                                                             
+ * T_LVALUE svalue of type T_ERROR_HANDLER and pushed onto the value stack.                                                                                         
+ * If the stack is unrolled during runtime errors the error_handler function                                                                                        
+ * is called and frees buff. */                                                                                                                                     
+typedef struct errorhandler_s {                                                                                                                                     
+  svalue_t head;        /* The T_ERROR_HANDLER structure */                                                                                                         
+  char     * buff;      /* The allocated buffer to free. */                                                                                                         
+} errorhandler_t;                                                                                                                                                   
+                                                                                                                                                                    
+                                                                                                                                                                    
+/* --- Constants --- */                                                                                                                                             
+                                                                                                                                                                      
 static const short MAX_SHIFT = (sizeof(p_int) << 3) - 1;
   /* The maximally useful shift (left or right) of a number in LPC.
    */
 
+/* Reset the evaluation cost/time counter.
+ */
+
+//#ifdef BAT
+#define CLEAR_EVAL_COST ((assigned_eval_cost = eval_cost = 0),(eval_cost_warning_start_ticks= eval_cost_warning_total_ticks= get_time ()))
+extern unsigned long long eval_cost_warning_start_ticks;
+extern unsigned long long eval_cost_warning_total_ticks;
+//#endif
+// #define CLEAR_EVAL_COST (assigned_eval_cost = eval_cost = 0)
+
+
+/* Check if the current evaluation took too long
+ */
+#define EVALUATION_TOO_LONG() \
+    (max_eval_cost && (eval_cost >= max_eval_cost || eval_cost < 0))
+
 /* --- Variables --- */
 
 extern program_t *current_prog;
diff -ruN /home/favorit/ldmud-3.3.719/src/lex.c ./lex.c
--- /home/favorit/ldmud-3.3.719/src/lex.c	2009-06-15 01:18:52.000000000 +0300
+++ ./lex.c	2009-09-03 12:30:44.000000000 +0300
@@ -475,6 +475,7 @@
  = { { "break",          L_BREAK         }
    , { "case",           L_CASE          }
    , { "catch",          L_CATCH         }
+   , { "catch_write",    L_CATCH_WRITE   } // BAT
    , { "closure",        L_CLOSURE_DECL  }
    , { "continue",       L_CONTINUE      }
    , { "default",        L_DEFAULT       }
@@ -1429,11 +1430,17 @@
         code = F_PARSE_COMMAND;
         break;
 #endif
+//#ifdef BAT
+    case L_CATCH_WRITE:
+		code = F_CATCH_WRITE + CLOSURE_EFUN_OFFS;
+		break;
+//#endif
+
     case L_CATCH:
         code = F_CATCH;
         break;
     }
-
+	
     return code;
 } /* symbol_resword() */
 
@@ -5780,7 +5787,7 @@
     pragma_no_inherit = MY_FALSE;
     pragma_no_shadow = MY_FALSE;
     pragma_pedantic = MY_FALSE;
-    pragma_warn_missing_return = MY_TRUE;
+    pragma_warn_missing_return = MY_FALSE; // BAT MY_TRUE;
     pragma_warn_deprecated = MY_FALSE;
     pragma_range_check = MY_FALSE;
     pragma_warn_empty_casts = MY_TRUE;
diff -ruN /home/favorit/ldmud-3.3.719/src/main.c ./main.c
--- /home/favorit/ldmud-3.3.719/src/main.c	2009-06-15 01:18:52.000000000 +0300
+++ ./main.c	2009-09-03 12:30:45.000000000 +0300
@@ -77,6 +77,13 @@
 #include "pkg-mysql.h"
 #endif
 
+//#ifdef BAT
+#include <locale.h>
+#include "bat.h"
+#include "actions.h" /* Blitzer */
+#include "sockets.h"
+//#endif
+
 #ifdef USE_XML
 #    if defined(HAS_XML2) && defined(HAS_IKSEMEL)
 #        error Both, libxml2 and iksemel enabled.
@@ -95,6 +102,8 @@
 
 #include "i-eval_cost.h"
 
+#include "i-eval_cost.h"
+
 #include "../mudlib/sys/regexp.h"
 
 /*-------------------------------------------------------------------------*/
@@ -269,6 +278,12 @@
     sigset_t set;
     volatile int rc;
 
+//#ifdef BAT
+    unsetenv("LANG");
+    setlocale(LC_ALL, "");
+    socket_init();
+//#endif
+
     rc = 0;
 
     /* On some systems, SIGALRM is sometimes blocked.
@@ -766,6 +781,20 @@
             set_cloexec_flag(fileno(fp));
     }
     (void)vfprintf(fp, fmt, va);
+//#ifdef BAT /* Blitzer */
+    if (command_giver != NULL)
+      {
+        (void) fprintf (fp, "Command giver: '%s'\n",
+                        get_txt (command_giver->name));
+
+        if (command_giver->super != NULL)
+          (void) fprintf (fp, "Environment: '%s'\n",
+                          get_txt (command_giver->super->name));
+
+        if (last_command != NULL)
+          (void) fprintf (fp, "Last command: '%s'\n", last_command);
+      }
+//#endif
     (void)fflush(fp);
 } /* vdebug_message() */
 
@@ -1774,6 +1803,10 @@
         "\nRelease:  " PROJ_VERSION
        , stdout);
 
+//#ifdef BAT
+  fputs(" (BatMud edition)", stdout);
+//#endif
+
   fputs(drivertag(), stdout);
 
   fputs("; " RELEASE_DATE
diff -ruN /home/favorit/ldmud-3.3.719/src/Makefile.in ./Makefile.in
--- /home/favorit/ldmud-3.3.719/src/Makefile.in	2009-06-15 01:18:52.000000000 +0300
+++ ./Makefile.in	2009-10-02 23:45:53.000000000 +0300
@@ -100,13 +100,14 @@
       interpret.c \
       lex.c main.c mapping.c md5.c mempools.c mregex.c mstrings.c object.c \
       otable.c\
-      parser.c parse.c pkg-alists.c pkg-iksemel.c pkg-xml2.c pkg-idna.c \
+      parser.c parse.c pkg-alists.c pgk-iksemel.c pkg-xml2.c pkg-idna.c \
       pkg-mccp.c pkg-mysql.c pkg-pcre.c pkg-gcrypt.c \
       pkg-pgsql.c pkg-sqlite.c pkg-tls.c pkg-openssl.c pkg-gnutls.c \
       ptmalloc.c port.c ptrtable.c \
       random.c regexp.c sha1.c simulate.c simul_efun.c stdstrings.c \
+      bat.c sockets.c \
       strfuns.c structs.c sprintf.c swap.c wiz_list.c xalloc.c 
-OBJ = access_check.o actions.o array.o arraylist.o backend.o bitstrings.o \
+OBJ = access_check.o actions.o array.o arraylist.o  backend.o bitstrings.o \
       call_out.o closure.o comm.o \
       dumpstat.o ed.o efuns.o files.o gcollect.o hash.o heartbeat.o \
       interpret.o \
@@ -117,6 +118,7 @@
       pkg-pgsql.o pkg-sqlite.o pkg-tls.o pkg-openssl.o pkg-gnutls.o \
       ptmalloc.o port.o ptrtable.o \
       random.o regexp.o sha1.o simulate.o simul_efun.o stdstrings.o \
+      bat.o sockets.o \
       strfuns.o structs.o sprintf.o swap.o wiz_list.o xalloc.o @ALLOCA@ 
 
 all: make-patchlevel ldmud@EXEEXT@
@@ -248,6 +250,15 @@
 #--------------------------------------------------------
 # Dependencies, manual and automatic.
 
+bat.o : svalue.h exec.h bat.h main.h object.h backend.h simulate.h \
+    interpret.h xalloc.h svalue.h array.h swap.h typedefs.h comm.h mstrings.h \
+    mapping.h stdstrings.h random.h dumpstat.h closure.h ptrtable.h structs.h
+
+#profil2.o : crc32.c profil2.h bat.h
+
+sockets.o : main.h svalue.h interpret.h simulate.h object.h mstrings.h comm.h \
+    actions.h gcollect.h
+
 # --- DO NOT MODIFY THIS LINE -- AUTO-DEPENDS FOLLOW ---
 access_check.o : xalloc.h filestat.h comm.h access_check.h driver.h \
     svalue.h strfuns.h pkg-tls.h simulate.h typedefs.h config.h port.h \
diff -ruN /home/favorit/ldmud-3.3.719/src/make_func.y ./make_func.y
--- /home/favorit/ldmud-3.3.719/src/make_func.y	2009-06-15 01:18:52.000000000 +0300
+++ ./make_func.y	2009-09-03 13:14:09.000000000 +0300
@@ -266,7 +266,7 @@
   /* Maximum number of functions and tokens we care to handle.
    */
 
-#define MAX_ARGTYPES  500
+#define MAX_ARGTYPES  600
   /* Size of the arg_types[] array.
    */
 
@@ -1653,7 +1653,11 @@
     if ( !strcmp(name, "REGEXP_PACKAGE") )
         return H_REGEXP_PACKAGE;
     if ( !strcmp(name, "MSG_DISCARDED") )
-        return H_MSG_DISCARDED;
+    	return H_MSG_DISCARDED;
+//#ifdef BAT
+    if ( !strcmp(name, "POST_COMMAND") )
+        return H_POST_COMMAND;
+//#endif
     return -1;
 }
 
diff -ruN /home/favorit/ldmud-3.3.719/src/object.c ./object.c
--- /home/favorit/ldmud-3.3.719/src/object.c	2009-06-15 01:18:52.000000000 +0300
+++ ./object.c	2009-09-03 13:15:51.000000000 +0300
@@ -200,6 +200,7 @@
 #include "svalue.h"
 #include "wiz_list.h"
 #include "xalloc.h"
+#include "bat.h"
 
 #include "../mudlib/sys/driver_hook.h"
 #include "../mudlib/sys/functionlist.h"
@@ -1279,7 +1280,7 @@
 
 /*-------------------------------------------------------------------------*/
 void
-tell_object (object_t *ob, string_t *str)
+tell_object (object_t *ob, string_t *str, int no_catch) // BAT
 
 /* Send message <str> to object <ob>. If <ob> is an interactive player,
  * it will go to his screen (unless a shadow catches it - see shadow_catch_
@@ -1298,7 +1299,15 @@
     {
         save_command_giver = command_giver;
         command_giver = ob;
-        add_message(FMT_STRING, str);
+//#ifdef BAT
+        save_catching_write = catching_write;
+        if (no_catch)
+          catching_write = 0;
+//#endif
+    	add_message(FMT_STRING, str);    
+//#ifdef BAT
+        catching_write = save_catching_write;
+//#endif
         command_giver = save_command_giver;
         return;
     }
@@ -3652,6 +3661,7 @@
 
 {
     object_t * ob = NULL;
+	int skip_catch_tell = (sp--)->u.number;
     svalue_t *arg = sp - 1;
 
     /* Get the arguments */
@@ -3666,7 +3676,7 @@
 
     if (arg[1].type == T_STRING)
     {
-        tell_object(ob, sp->u.str);
+        tell_object(ob, sp->u.str, skip_catch_tell); // BAT
         free_svalue(sp);
     }
     else
@@ -3678,7 +3688,7 @@
     sp--;
     free_svalue(sp);
     sp--;
-
+	
     return sp;
 } /* f_tell_object() */
 
@@ -4807,7 +4817,7 @@
         stmp.u.ob = ob;
         if (lookup_key(&stmp, avoid) >= 0)
             continue;
-        tell_object (ob, message);
+        tell_object (ob, message, 0);
     }
 
     pop_stack(); /* free avoid alist */
@@ -5030,7 +5040,7 @@
         if (ob->flags & O_DESTRUCTED) continue;
         stmp.u.ob = ob;
         if (lookup_key(&stmp, avoid) >= 0) continue;
-        tell_object(ob, message);
+        tell_object(ob, message, 0);
     }
 } /* e_tell_room() */
 
@@ -6739,9 +6749,9 @@
 
         /* Open the file */
 
-        /* Always write savefiles in 'binary mode'. (O_BINARY is 0 on all platforms
-         * except of Cygwin and therefore ignored. Cygwin may need it, if the
-         * volume with the mudlib is mounted in textmode. */
+	/* Always write savefiles in 'binary mode'. (O_BINARY is 0 on all platforms
+	 * except of Cygwin and therefore ignored. Cygwin may need it, if the
+	 * volume with the mudlib is mounted in textmode. */
         f = ixopen3(tmp_name, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0640);
 
         if (f < 0) {
diff -ruN /home/favorit/ldmud-3.3.719/src/object.h ./object.h
--- /home/favorit/ldmud-3.3.719/src/object.h	2009-06-15 01:18:52.000000000 +0300
+++ ./object.h	2009-09-03 12:20:25.000000000 +0300
@@ -264,7 +264,7 @@
 /* --- Prototypes --- */
 
 extern int32 renumber_programs(void);
-extern void tell_object(object_t *, string_t *);
+extern void tell_object(object_t *, string_t *, int); // BAT
 extern void tell_object_str(object_t *, const char *);
 extern void tell_npc(object_t *, string_t *);
 extern void tell_npc_str(object_t *, const char *);
diff -ruN /home/favorit/ldmud-3.3.719/src/patchlevel.h ./patchlevel.h
--- /home/favorit/ldmud-3.3.719/src/patchlevel.h	1970-01-01 02:00:00.000000000 +0200
+++ ./patchlevel.h	2009-09-03 12:59:14.000000000 +0300
@@ -0,0 +1,30 @@
+#ifndef PATCHLEVEL_H__
+#define PATCHLEVEL_H__ 1
+
+/*--------------------------------------------------------------------------
+ * Various version numbers and strings, collected here so that we have
+ * to modify only one file.
+ *--------------------------------------------------------------------------
+ */
+
+#define RELEASE_TYPE "stable"
+#define RELEASE_LONGTYPE "stable"
+
+#define RELEASE_DATE "2009-05-30"
+
+#define PROJ_VERSION "Build 2618"
+
+#define DRIVER_VERSION "3.3.719"
+
+#define VERSION_MAJOR "3"
+
+#define VERSION_MINOR "3"
+
+#define VERSION_MICRO "719"
+
+#define LOCAL_LEVEL ""
+
+/* TODO: Add something like the perl local patchlevel management. */
+/* TODO: Add LPC defines for the various version number parts */
+
+#endif /* PATCHLEVEL_H__ */
diff -ruN /home/favorit/ldmud-3.3.719/src/pkg-xml2.c ./pkg-xml2.c
--- /home/favorit/ldmud-3.3.719/src/pkg-xml2.c	2009-06-15 01:18:52.000000000 +0300
+++ ./pkg-xml2.c	2009-09-12 22:04:26.000000000 +0300
@@ -63,7 +63,7 @@
  * Realize malloc with the driver-internal xalloc rather than a direct malloc()
  */
 {
-    return xalloc(size);
+    return pxalloc(size);
 }
 
 static void
@@ -73,7 +73,7 @@
  * Realize free with the driver-internal xfree rather than a direct free()
  */
 {
-    xfree(ptr);
+    pfree(ptr);
 }
 
 static void *
@@ -84,7 +84,7 @@
  * and line rather the direct realloc()
  */
 {
-    return rexalloc(ptr, size);
+    return prexalloc(ptr, size);
 }
 
 static char *
@@ -95,7 +95,16 @@
  * strdup()
  */
 {
-    return string_copy(str);
+    char *p;
+    size_t len;
+
+    len = strlen(str)+1;
+    memsafe(p = pxalloc(len), len, "xml_pkg_strdup");
+    if (p)
+    {
+        memcpy(p, str, len);
+    }
+    return p;
 }
 
 static void
@@ -390,10 +399,12 @@
 void
 pkg_xml2_init ()
 {
-    /* Check for correct libxml version. */
+    // First override the default memory access functions
+    xmlMemSetup(xml_pkg_free, xml_pkg_malloc, xml_pkg_realloc, xml_pkg_strdup);
+
+    // Check for correct libxml version.
     LIBXML_TEST_VERSION
 
-    xmlMemSetup(xml_pkg_free, xml_pkg_malloc, xml_pkg_realloc, xml_pkg_strdup);
 }
 
 /*=========================================================================*/
diff -ruN /home/favorit/ldmud-3.3.719/src/profil2.c ./profil2.c
--- /home/favorit/ldmud-3.3.719/src/profil2.c	1970-01-01 02:00:00.000000000 +0200
+++ ./profil2.c	2009-09-03 11:49:53.000000000 +0300
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 2001 matti.suomalainen@iki.fi
+ * Free for non-commercial use.
+ *
+ * All rights reserved.
+ */
+
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "crc32.c"
+#include "profil2.h"
+#include "bat.h"
+
+
+/*
+ * pool alloc
+ */
+
+#define POOL_SIZE (1024*1024)
+
+struct pool
+{
+  struct pool *prev;
+
+  unsigned char pool [POOL_SIZE];
+  unsigned int space_left;
+};
+
+static struct pool *pool= NULL;
+
+static void
+_add_pool ()
+{
+  struct pool *p;
+
+  p= (struct pool*) calloc (1, sizeof (*p));
+  p->prev= pool;
+  pool= p;
+
+  p->space_left= POOL_SIZE;
+}
+
+static char*
+_pool_strdup (const char *txt)
+{
+#if 0
+  return txt;
+#else
+  unsigned int len= strlen (txt)+1;
+  char *p;
+
+  if (pool== NULL || pool->space_left <len)
+    _add_pool ();
+
+  p= &pool->pool [POOL_SIZE- pool->space_left];
+  pool->space_left -= len;
+  
+  strcpy (p, txt);
+  return p;
+#endif
+}
+
+static void
+_pool_free ()
+{
+  struct pool *p, *prev;
+
+  p= pool;
+
+  while (p)
+    {
+      prev= p->prev;
+      free (p);
+      p= prev;
+    }
+  pool= NULL;
+}
+  
+
+
+/*
+ * gathering data
+ */
+
+static unsigned int active_profiling;
+static profil_sample_t started;
+static profil_sample_t total_overhead;
+
+void
+profil_sample_add (profil_sample_t *to, const profil_sample_t *by)
+{
+  unsigned int idx;
+  
+  for (idx= 0; idx < PROFIL_MAX_COUNTER; idx++)
+    to->c [idx] += by->c [idx];
+}
+
+void
+profil_sample_sub (profil_sample_t *to, const profil_sample_t *by)
+{
+  unsigned int idx;
+
+  for (idx= 0; idx < PROFIL_MAX_COUNTER; idx++)
+    to->c [idx] -= by->c [idx];
+}
+
+void
+profil_sample_mul (profil_sample_t *to, int by)
+{
+  unsigned int idx;
+
+  for (idx= 0; idx < PROFIL_MAX_COUNTER; idx++)
+    to->c [idx] *= by;
+}
+
+void
+profil_sample_copy (profil_sample_t *to, profil_sample_t *from)
+{
+  memcpy (to, from, sizeof (profil_sample_t));
+}
+
+unsigned int
+profil_sample_gt (profil_sample_t *a, profil_sample_t *b)
+{
+  /* if any value in a is > b */
+  
+  unsigned int idx;
+
+  for (idx= 0; idx < PROFIL_MAX_COUNTER; idx++)
+    if (a->c [idx] > b->c [idx])
+      return 1;
+
+  return 0;
+}
+
+static unsigned long long
+_profil_timeval_to_longlong (struct timeval tv)
+{
+  return 
+    (unsigned long long) tv.tv_sec*1000+ 
+    (unsigned long long) tv.tv_usec/1000;
+}
+
+void
+_profil_get_sample (profil_sample_t *sample)
+{
+  memset (sample, 0, sizeof (profil_sample_t));
+
+  if (active_profiling & PROFILE_TIME)
+	  sample->c [PROFIL_SAMPLE_TIME] = get_time ();
+
+  if (active_profiling & PROFILE_RUSAGE)
+    {
+      struct rusage ru;
+
+      getrusage (RUSAGE_SELF, &ru);
+      
+      sample->c [PROFIL_SAMPLE_UTIME]=_profil_timeval_to_longlong(ru.ru_utime);
+      sample->c [PROFIL_SAMPLE_STIME]=_profil_timeval_to_longlong(ru.ru_stime);
+      sample->c [PROFIL_SAMPLE_RSS]= ru.ru_maxrss;
+      sample->c [PROFIL_SAMPLE_IXRSS]= ru.ru_ixrss;
+      sample->c [PROFIL_SAMPLE_IDRSS]= ru.ru_idrss;
+      sample->c [PROFIL_SAMPLE_ISRSS]= ru.ru_isrss;
+      sample->c [PROFIL_SAMPLE_MINFLT]= ru.ru_minflt;
+      sample->c [PROFIL_SAMPLE_MAJFLT]= ru.ru_majflt;
+      sample->c [PROFIL_SAMPLE_NSWAP]= ru.ru_nswap;
+      sample->c [PROFIL_SAMPLE_INBLOCK]= ru.ru_inblock;
+      sample->c [PROFIL_SAMPLE_OUBLOCK]= ru.ru_oublock;
+      sample->c [PROFIL_SAMPLE_MSGSND]= ru.ru_msgsnd;
+      sample->c [PROFIL_SAMPLE_MSGRCV]= ru.ru_msgrcv;
+      sample->c [PROFIL_SAMPLE_NSIGNALS]= ru.ru_nsignals;
+      sample->c [PROFIL_SAMPLE_NVCSW]= ru.ru_nvcsw;
+      sample->c [PROFIL_SAMPLE_NIVCSW]= ru.ru_nivcsw;
+    }
+}
+
+void
+profil_start_sample (profil_sample_t *sample)
+{
+  _profil_get_sample (sample);
+}
+
+void
+profil_end_sample (profil_sample_t *sample)
+{
+  if (active_profiling)
+    {
+      profil_sample_t tmp;
+      profil_sample_copy (&tmp, sample);
+      _profil_get_sample (sample);
+      profil_sample_sub (sample, &tmp);
+    }
+}
+
+/*
+ * updating and storing samples
+ */
+
+
+#define CACHE_SLOTS 1000000
+
+struct profil_entry
+{
+  profil_sample_t samples[2]; /* self, recur */
+
+  unsigned int owns_fun_name;
+  unsigned int fun_hash;
+  char *fun_name;
+
+  unsigned int owns_class_name;
+  unsigned int class_hash;
+  char *class_name;
+};
+
+struct hash
+{
+  struct profil_entry *data;
+  unsigned int len;
+  unsigned int len_in_use;
+};
+
+static struct hash sample_lookup;
+static struct hash class_lookup;
+static struct hash fun_lookup;
+
+
+void
+_hash_init (struct hash *h)
+{
+  memset (h, 0, sizeof (*h));
+}
+
+struct profil_entry*
+_hash_find (struct hash *h, const char *class_name, const char *fun_name)
+{
+  unsigned int class_hash= 
+    class_name? ssh_crc32 (class_name, strlen (class_name)): 0;
+
+  unsigned int fun_hash= 
+    fun_name? ssh_crc32 (fun_name, strlen (fun_name)): 0;
+
+  unsigned int idx;
+
+  if (h->len== 0 || h->len_in_use== 0)
+    return NULL;
+
+  idx= class_hash;
+
+  if (fun_name)
+    idx ^= fun_hash;
+
+  idx %= h->len;
+
+
+  while (h->data [idx].class_name != 0)
+    {
+      if (h->data [idx].class_hash== class_hash &&
+	  !strcmp (h->data [idx].class_name, class_name))
+	{
+	  if (!fun_name ||
+	      (h->data [idx].fun_hash== fun_hash &&
+	       !strcmp (h->data [idx].fun_name, fun_name)))
+	    {
+	      return &h->data [idx];
+	    }
+	}
+
+      idx++;
+      idx %= h->len;
+
+   }
+  
+  return NULL;
+}
+
+
+struct profil_entry*
+_hash_next (struct hash *h, struct profil_entry *from)
+{
+  if (h->len== 0)
+    return NULL;
+
+  if (from== NULL)
+    from= h->data;
+
+  from++;
+
+  if (from== &h->data [h->len])
+    return NULL;
+
+  if (from->class_name)
+    return from;
+
+  return _hash_next (h, from);
+}
+
+
+void
+_hash_insert (struct hash *h, struct profil_entry *entry)
+{
+  if (h->len_in_use >= h->len*2/3)
+    {
+      /* regrow */
+
+      struct hash h2;
+      struct profil_entry *iter;
+
+      //      fprintf (stderr, "REGROW %p, len=%d, use= %d\n",  h, h->len, h->len_in_use);
+
+      _hash_init (&h2);
+      h2.len= (h->len* 2)+1024;
+      h2.len_in_use= 0;
+      h2.data= (struct profil_entry*) calloc (1, 
+					      sizeof (struct profil_entry)*
+					      h2.len);
+
+      iter= NULL;
+
+      while ( (iter= _hash_next (h, iter)) )
+	_hash_insert (&h2, iter);
+      
+      free (h->data);
+      *h= h2;
+    }
+
+  {
+    unsigned int idx;
+
+    entry->class_hash= 
+      entry->class_name? ssh_crc32 (entry->class_name, strlen (entry->class_name)): 0;
+
+    entry->fun_hash= entry->fun_name? ssh_crc32 (entry->fun_name, strlen (entry->fun_name)): 0;
+
+
+    idx= entry->class_hash;
+    
+    if (entry->fun_name)
+      idx ^= entry->fun_hash;
+
+    idx %= h->len;
+
+    while (h->data [idx].class_name != 0)
+      {
+	if (h->data [idx].class_hash== entry->class_hash &&
+	    !strcmp (h->data [idx].class_name, entry->class_name))
+	  {
+	    if (!entry->fun_name ||
+	      (h->data [idx].fun_hash== entry->fun_hash &&
+	       !strcmp (h->data [idx].fun_name, entry->fun_name)))
+	      {
+		h->data [idx]= *entry;
+		return;
+	      }
+	  }
+      
+	idx++;
+	idx %= h->len;
+	
+      }
+
+    h->len_in_use++;
+    h->data [idx]= *entry;
+  }
+}  
+
+void		
+profil_enable (unsigned int how)
+{
+  active_profiling= how;
+  profil_reset ();
+}
+
+unsigned int	
+profil_is_enabled ()
+{
+  return active_profiling;
+}
+
+struct profil_entry*
+_profil_find_entry (const char *class_name, const char *fun_name)
+{
+  struct profil_entry *e;
+  struct profil_entry tmp;
+
+  if ( (e= _hash_find (&sample_lookup, class_name, fun_name)))
+    return e;
+
+  memset (&tmp, 0, sizeof (tmp));
+
+  if ( (e= _hash_find (&class_lookup, class_name, NULL)) )
+    {
+      tmp.class_name= e->class_name;
+      tmp.owns_class_name= 0;
+    }
+  else
+    {
+      tmp.class_name= _pool_strdup (class_name);
+      tmp.owns_class_name= 1;
+
+      _hash_insert (&class_lookup, &tmp);
+    }
+
+
+  if ( (e= _hash_find (&fun_lookup, fun_name, NULL)) )
+    {
+      tmp.fun_name= e->class_name;
+      tmp.owns_fun_name= 0;
+    }
+  else
+    {
+      tmp.class_name= _pool_strdup (fun_name);
+      _hash_insert (&fun_lookup, &tmp);
+
+      tmp.fun_name= tmp.class_name;
+      tmp.class_name= NULL;
+      tmp.owns_fun_name= 1;
+    }
+
+  _hash_insert (&sample_lookup, &tmp);
+
+  return _profil_find_entry (class_name, fun_name);
+}
+
+void
+profil_register_sample (const char *class_name, 
+			const char *fun_name,
+			profil_sample_t *self,
+			 profil_sample_t *recur,
+			int sample_mul)
+{
+  if (profil_is_enabled ())
+    {
+      profil_sample_t this_overhead;
+      struct profil_entry *e;
+
+      profil_start_sample (&this_overhead);
+
+      e= _profil_find_entry (class_name, fun_name);
+
+      if (self)
+	{
+	  self->c [PROFIL_SAMPLE_CALLS]++;
+  
+	  profil_sample_mul (self, sample_mul);
+	  profil_sample_add (&e->samples [PROFIL_SELF], self);
+	  
+	}
+
+      if (recur)
+	{
+	  recur->c [PROFIL_SAMPLE_CALLS]++;
+
+	  profil_sample_mul (recur, sample_mul);
+	  profil_sample_add (&e->samples [PROFIL_WCHILDREN], recur);
+	}
+
+
+
+
+      profil_end_sample (&this_overhead);
+      profil_sample_add (&total_overhead, &this_overhead);
+    }
+}
+
+/*
+ * output & reset
+ */
+
+static unsigned int profil_sorter_arg= 0;
+static unsigned int profil_sorter_self_time= 0;
+
+static int
+_profil_sorter (const void *_a, const void *_b)
+{
+  struct profil_entry *a= (struct profil_entry*) _a;
+  struct profil_entry *b= (struct profil_entry*) _b;
+
+  if (a->samples [profil_sorter_self_time].c [profil_sorter_arg]== 
+      b->samples [profil_sorter_self_time].c [profil_sorter_arg])
+    return 0;
+  
+  if (a->samples [profil_sorter_self_time].c [profil_sorter_arg] > 
+      b->samples [profil_sorter_self_time].c [profil_sorter_arg])
+    return -1;
+  else 
+    return 1;
+}
+
+static unsigned int
+_profil_output_entry (char *store_to, unsigned int max_len, 
+		      unsigned long long total_time,
+		      const char *class_name, const char *fun_name,
+		      profil_sample_t *sample)
+{
+  char buf [10*1024];
+
+  snprintf (buf, sizeof (buf), 
+	    "%s::%s %.1f %.2f %.2f %.2f %.2f "
+#if 1
+	    "%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f"
+#endif
+	    "\n", 
+	    class_name, fun_name,
+
+	   sample->c [PROFIL_SAMPLE_TIME]?
+	   (double)sample->c [PROFIL_SAMPLE_TIME]*100/total_time: 0,
+
+	   time_to_msec (sample->c [PROFIL_SAMPLE_TIME])/1000.0
+	    ,
+	   sample->c [PROFIL_SAMPLE_UTIME]/1000.0,
+	   sample->c [PROFIL_SAMPLE_STIME]/1000.0,
+	   sample->c [PROFIL_SAMPLE_RSS]/1000.0
+#if 1
+	    ,
+	   sample->c [PROFIL_SAMPLE_IXRSS]/1000.0,
+	   sample->c [PROFIL_SAMPLE_IDRSS]/1000.0,
+	   sample->c [PROFIL_SAMPLE_ISRSS]/1000.0,
+	   (double) sample->c [PROFIL_SAMPLE_MINFLT],
+	   (double) sample->c [PROFIL_SAMPLE_MAJFLT],
+	   (double) sample->c [PROFIL_SAMPLE_NSWAP],
+	   (double) sample->c [PROFIL_SAMPLE_INBLOCK],
+	   (double) sample->c [PROFIL_SAMPLE_OUBLOCK],
+	   (double) sample->c [PROFIL_SAMPLE_MSGSND],
+	   (double) sample->c [PROFIL_SAMPLE_MSGRCV],
+	   (double) sample->c [PROFIL_SAMPLE_NSIGNALS],
+	   (double) sample->c [PROFIL_SAMPLE_NVCSW],
+	   (double) sample->c [PROFIL_SAMPLE_NIVCSW],
+	    (double) sample->c [PROFIL_SAMPLE_ECOST],
+	   (double) sample->c [PROFIL_SAMPLE_CALLS]
+#endif
+	    );
+  
+
+
+  if (strlen (buf) < max_len)
+    strcpy (store_to, buf);
+  return strlen (buf);
+}
+
+
+unsigned int
+profil_query_results (char *store_to, unsigned int max_len,
+		      unsigned int self_time,
+		      unsigned int sort_order, 
+		      unsigned int max_entries)
+{
+  struct profil_entry *entries;
+  struct profil_entry *iter;
+  unsigned int idx;
+  unsigned long long low_water= 0;
+  unsigned int total_len= 0;
+  unsigned int len= 0;
+
+  profil_sample_t total_time;
+
+  if (self_time != PROFIL_SELF && self_time != PROFIL_WCHILDREN)
+    self_time= PROFIL_SELF;
+
+  entries= (struct profil_entry*) alloca (max_entries* 
+					  sizeof (struct profil_entry));
+  memset (entries, 0, max_entries* sizeof (struct profil_entry));
+
+  if (sort_order >= PROFIL_MAX_COUNTER)
+    return 0+1;
+
+  iter= NULL;
+
+  while ( (iter= _hash_next (&sample_lookup, iter)) )
+    if (iter->samples [self_time].c [sort_order] >= low_water)
+      {
+	entries [max_entries-1]= *iter;
+
+	profil_sorter_arg= sort_order;
+	profil_sorter_self_time= self_time;
+
+	qsort (entries, max_entries, sizeof (struct profil_entry), 
+	       _profil_sorter);
+	low_water= entries [max_entries-1].samples [self_time].c [sort_order];
+      }
+
+
+
+  profil_start_sample (&total_time);
+  profil_sample_sub (&total_time, &started);
+
+  len= _profil_output_entry (store_to, max_len, 
+			     total_time.c [PROFIL_SAMPLE_TIME],
+			     "total", "", &total_time);
+  
+    if (len <= max_len)
+      {
+	store_to+= len;
+	max_len -= len;
+      }
+    total_len += len;
+
+    len= _profil_output_entry (store_to, max_len,
+			     total_time.c [PROFIL_SAMPLE_TIME],
+
+			       "overhead", "", &total_overhead);
+    
+    if (len <= max_len)
+      {
+	store_to+= len;
+	max_len -= len;
+      }
+
+    total_len += len;
+
+
+    for (idx= 0; idx < max_entries; idx++)
+      if (entries [idx].class_name)
+	{
+	  len= _profil_output_entry (store_to, max_len,
+				     total_time.c [PROFIL_SAMPLE_TIME],
+				     entries [idx].class_name,
+				     entries [idx].fun_name,
+				     &entries [idx].samples [self_time]);
+	
+    if (len <= max_len)
+      {
+	store_to+= len;
+	max_len -= len;
+      }
+
+	total_len += len;
+      }
+
+  return total_len+1;
+}
+
+void
+profil_reset ()
+{
+
+#if 0
+  while ( (iter= _hash_next (&sample_lookup, iter)) )
+    {
+      if (iter->owns_fun_name)
+	free (iter->fun_name);
+
+      if (iter->owns_class_name)
+	free (iter->class_name);
+    }
+#endif
+
+  free (sample_lookup.data);
+  free (class_lookup.data);
+  free (fun_lookup.data);
+
+  memset (&sample_lookup, 0, sizeof (sample_lookup));
+  memset (&class_lookup, 0, sizeof (sample_lookup));
+  memset (&fun_lookup, 0, sizeof (sample_lookup));
+
+  _pool_free ();
+
+  profil_start_sample (&started);
+  memset (&total_overhead, 0, sizeof (total_overhead));
+}
+
+static profil_sample_t _sample;
+
+void profil_start()
+{
+	if(profil_is_enabled())
+	{
+		memset (&_sample, 0, sizeof (_sample));
+		
+		profil_start_sample(&_sample);
+	}
+}
+
+void profil_stop(const char* class_name, const char* func_name)
+{
+	if(profil_is_enabled() && _sample.c [1])
+	{
+		profil_end_sample (&_sample);
+		
+		profil_register_sample ( class_name,
+								 func_name,
+								 &_sample, NULL, 1);
+		
+	}
+}
+
diff -ruN /home/favorit/ldmud-3.3.719/src/profil2.h ./profil2.h
--- /home/favorit/ldmud-3.3.719/src/profil2.h	1970-01-01 02:00:00.000000000 +0200
+++ ./profil2.h	2009-09-03 11:49:53.000000000 +0300
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2001 matti.suomalainen@iki.fi
+ * Free for non-commercial use.
+ *
+ * All rights reserved.
+ */
+
+#ifndef _PROFIL_H_
+#define _PROFIL_H_
+
+#ifdef __cplusplus
+extern"C" {
+#endif
+
+
+#define PROFIL_MAX_COUNTER 31
+
+#define PROFIL_WCHILDREN		0
+#define PROFIL_SELF			1
+
+#define PROFIL_SAMPLE_ALL 		((unsigned) -1)
+#define PROFIL_SAMPLE_TIME		 1
+#define PROFIL_SAMPLE_UTIME		 2
+#define PROFIL_SAMPLE_STIME		 3
+#define PROFIL_SAMPLE_RSS		 4
+#define PROFIL_SAMPLE_IXRSS		 5
+#define PROFIL_SAMPLE_IDRSS		 6
+#define PROFIL_SAMPLE_ISRSS		 7
+#define PROFIL_SAMPLE_MINFLT		 8
+#define PROFIL_SAMPLE_MAJFLT		 9
+#define PROFIL_SAMPLE_NSWAP		10
+#define PROFIL_SAMPLE_INBLOCK		11
+#define PROFIL_SAMPLE_OUBLOCK		12
+#define PROFIL_SAMPLE_MSGSND		13
+#define PROFIL_SAMPLE_MSGRCV		14
+#define PROFIL_SAMPLE_NSIGNALS		15
+#define PROFIL_SAMPLE_NVCSW		16
+#define PROFIL_SAMPLE_NIVCSW		17
+#define PROFIL_SAMPLE_ECOST		18
+#define PROFIL_SAMPLE_CALLS		19
+
+#define PROFILE_TIME            (1 << 0)
+#define PROFILE_RUSAGE          (1 << 1)
+#define PROFILE_FUNCALLS        (1 << 2)
+#define PROFILE_BACKEND         (1 << 3)
+
+struct profil_sample
+{
+  unsigned long long c[PROFIL_MAX_COUNTER+1];
+};
+
+typedef struct profil_sample profil_sample_t;
+
+void	profil_sample_copy (profil_sample_t *to, profil_sample_t *from);
+void	profil_sample_add (profil_sample_t *to, const profil_sample_t *by);
+void	profil_sample_sub (profil_sample_t *to, const profil_sample_t *by);
+void	profil_sample_mul (profil_sample_t *to, int by);
+
+unsigned int	profil_sample_gt (profil_sample_t *a, profil_sample_t *b);
+
+
+void		profil_start_sample (profil_sample_t *sample);
+void		profil_end_sample (profil_sample_t *sample);
+
+void		profil_enable (unsigned int mask);
+unsigned int	profil_is_enabled ();
+
+void		profil_register_sample ( const char *class_name, 
+					 const char *func_name,
+					profil_sample_t *self,
+					profil_sample_t *recur,
+					int sample_mul);
+
+unsigned int	profil_query_results (char *store_to, unsigned int max_len,
+				      unsigned int self_time,
+				      unsigned int sort_order, 
+				      unsigned int max_entries);
+void		profil_reset ();
+
+void        profil_start();
+void        profil_stop(const char* class_name, const char* func_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff -ruN /home/favorit/ldmud-3.3.719/src/prolang.y ./prolang.y
--- /home/favorit/ldmud-3.3.719/src/prolang.y	2009-06-15 01:18:52.000000000 +0300
+++ ./prolang.y	2009-09-03 12:50:05.000000000 +0300
@@ -208,6 +208,7 @@
     H_PRINT_PROMPT:   SH(T_CLOSURE) SH(T_STRING), \
     H_REGEXP_PACKAGE: SH(T_NUMBER), \
     H_MSG_DISCARDED:  SH(T_CLOSURE) SH(T_STRING), \
+    H_POST_COMMAND:   SH(T_CLOSURE) SH(T_STRING), \ /* BAT */
 
 #undef SH
 
@@ -3907,6 +3908,11 @@
     if (returntype.typeflags & TYPE_MOD_MASK)
     {
         exact_types = returntype;
+		
+//#ifdef BAT
+		if (pragma_strict_types == PRAGMA_WEAK_TYPES)
+			exact_types.typeflags = 0;
+//#endif
     }
     else
     {
@@ -5641,6 +5647,7 @@
 %token L_BREAK
 %token L_CASE
 %token L_CATCH
+%token L_CATCH_WRITE /* BAT */
 %token L_CLOSURE
 %token L_CLOSURE_DECL
 %token L_COLON_COLON
@@ -5931,6 +5938,7 @@
 %type <lrvalue>      inline_fun
 %endif /* USE_NEW_INLINES */
 %type <lrvalue>      catch sscanf
+%type <lrvalue>      catch_write /* BAT */
 %type <lrvalue>      for_init_expr for_expr
 %type <lrvalue>      comma_expr_decl expr_decl
 %ifdef USE_PARSE_COMMAND
@@ -9782,11 +9790,18 @@
       {
           $$ = $2;
           $$.type = $1;
+
+//#ifdef BAT
+          if ($2.type.typeflags != TYPE_UNKNOWN
+           && $1.typeflags != TYPE_VOID)
+//#else
+/*		  
           if ($2.type.typeflags != TYPE_ANY
            && $2.type.typeflags != TYPE_UNKNOWN
            && $1.typeflags != TYPE_VOID
            && !equal_types($1, $2.type)
              )
+*/
           {
               switch($1.typeflags)
               {
@@ -9795,6 +9810,13 @@
                   if (IS_TYPE_STRUCT($1))
                       break; /* Do nothing, just adapt the type information */
 #endif /* USE_STRUCTS */
+//#ifdef BAT
+                if($1.typeflags & TYPE_MOD_POINTER)
+                  {
+                    ins_f_code(F_TO_ARRAY);
+                    break;
+                  }
+//#endif
                   type_error("Illegal cast", $1);
                   break;
               case TYPE_ANY:
@@ -9815,6 +9837,11 @@
               case TYPE_NUMBER|TYPE_MOD_POINTER:
                   ins_f_code(F_TO_ARRAY);
                   break;
+//#ifdef BAT
+              case TYPE_MAPPING:
+				  /* fall through. hope it works */
+				  break;
+//#endif
               }
           }
           else if (pragma_warn_empty_casts)
@@ -10253,6 +10280,7 @@
 %ifdef USE_PARSE_COMMAND
     | parse_command  %prec '~'
 %endif /* USE_PARSE_COMMAND */
+    | catch_write    %prec '~' /* BAT */
 
     /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
     | L_STRING
@@ -11875,7 +11903,7 @@
 
           /* Return the data */
 
-          $$.inst  = F_RANGE;
+          $$.inst  = F_AA_RANGE; // BAT F_RANGE;
           $$.start = $3.start;
           $$.end   = $3.end;
           $$.type1 = Type_Number;
@@ -11977,7 +12005,7 @@
               $4.end++;
           }
 
-          $$.inst  = F_RANGE;
+          $$.inst  = F_AA_RANGE; // BAT F_RANGE;
           $$.start = $2.start;
           $$.end   = $4.end;
           $$.type1 = $2.type;
@@ -12121,7 +12149,7 @@
               $2.end++;
           }
 
-          $$.inst  = F_NX_RANGE;
+          $$.inst  = F_AX_RANGE; // BAT F_NX_RANGE;
           $$.start = $2.start;
           $$.end   = $2.end;
           $$.type1 = $2.type;
@@ -13750,6 +13778,30 @@
       }
 ; /* opt_catch_modifier */
 
+//%ifdef BAT
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+/* catch_write()
+ *
+ */
+catch_write:
+      L_CATCH_WRITE
+{
+  $<number>$ = CURRENT_PROGRAM_SIZE;
+  ins_byte(F_CATCH_WRITE);
+  /*  ins_byte(0); */
+}
+'(' comma_expr ')'
+{
+%line
+   ins_f_code(F_END_CATCH_WRITE);
+
+ $$.start = $<number>2;
+ $$.type = Type_String;
+ /*.typeflags  = TYPE_STRING;*/
+ $$.code = -1;
+};
+//%endif
+
 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
 /* sscanf() and parse_command()
  *
diff -ruN /home/favorit/ldmud-3.3.719/src/random.c ./random.c
--- /home/favorit/ldmud-3.3.719/src/random.c	2009-06-15 01:18:52.000000000 +0300
+++ ./random.c	2009-09-03 16:32:48.000000000 +0300
@@ -107,3 +107,56 @@
 } /* seed_random() */
 
 /***************************************************************************/
+
+//#ifdef BAT
+/*-------------------------------------------------------------------------*/
+uint32
+random_integer (void)
+
+     /* Return a random integer (uint32). */
+
+{
+    return gen_rand64();
+}
+
+/*-------------------------------------------------------------------------*/
+double
+random_double (void)
+
+     /* Return a random double (32 bits of randomness). */
+
+{
+  return ((double)random_integer() / (double)0xFFFFFFFF);
+}
+
+/*-------------------------------------------------------------------------*/
+double
+random_gaussian (void)
+
+     /* Return a Gaussian random number. */
+
+{
+  double x1, x2, w;
+  static double y2;
+  static int use_last = 0;
+
+  if (use_last)                   /* use value from previous call */
+    {
+      use_last = 0;
+      return y2;
+    }
+  else
+    {
+      do {
+	x1 = 2.0 * random_double() - 1.0;
+	x2 = 2.0 * random_double() - 1.0;
+	w = x1 * x1 + x2 * x2;
+      } while ( w >= 1.0 );
+
+      w = sqrt( (-2.0 * log( w ) ) / w );
+      y2 = x2 * w;
+      use_last = 1;
+      return x1 * w;
+    }
+}
+//#endif
diff -ruN /home/favorit/ldmud-3.3.719/src/random.h ./random.h
--- /home/favorit/ldmud-3.3.719/src/random.h	2009-06-15 01:18:52.000000000 +0300
+++ ./random.h	2009-09-03 12:20:25.000000000 +0300
@@ -26,4 +26,10 @@
   svalue number type.
 #endif
 
+//#ifdef BAT
+extern uint32 random_integer(void);
+extern double random_double(void);
+extern double random_gaussian(void);
+//#endif
+
 #endif  /* RANDOM_H__ */
diff -ruN /home/favorit/ldmud-3.3.719/src/settings/bat ./settings/bat
--- /home/favorit/ldmud-3.3.719/src/settings/bat	2009-06-15 01:18:24.000000000 +0300
+++ ./settings/bat	2009-10-02 23:53:59.000000000 +0300
@@ -4,10 +4,17 @@
 #
 # configure will strip this part from the script.
 
-exec ./configure --prefix=/bat --libdir=/bat/mudlib --with-setting=bat $*
+exec ./configure --prefix=/bat --with-setting=bat $*
 
 exit 1
 
+# Debugging enabled
+enable_malloc_check=yes
+enable_malloc_trace=yes
+enable_malloc_lpc_trace=yes
+enable_trace_code=yes
+enable_debug=yes
+
 # --- The actual settings ---
 
 #
@@ -24,31 +31,42 @@
 enable_use_mysql=1
 enable_malloc_sbrk=no
 
-enable_use_parse_command=0
+enable_use_parse_command=yes
 enable_compat_mode=no
 enable_strict_euids=no
 enable_use_deprecated=yes
 enable_use_alists=yes
-enable_use_pcre=no
+enable_use_pcre=yes
+enable_use_xml=xml2
 with_time_to_reset=3600
 with_time_to_clean_up=7200
 
+enable_use_structs=yes
+enable_use_tls=no
 
+with_alarm_time=1
+with_heart_beat_interval=3
 
 #
 # debug
 #
-enable_malloc_trace=1
-enable_malloc_lpc_trace=1
+enable_malloc_trace=0
+enable_malloc_lpc_trace=0
+enable_trace_code=no
+enable_debug=no
+enable_debug_telnet=no
 # /debug
 
-with_malloc=sysmalloc
-
+enable_malloc_check=no
+with_malloc=smalloc
 
 #
 # performance options
 #
 
+with_optimize=no
+enable_use_pthreads=yes
+
 #no swapping
 with_time_to_swap=0
 with_time_to_swap_variables=0
@@ -67,10 +85,15 @@
 with_max_trace=125
 with_hard_malloc_limit=0
 
-with_max_cost=20000000
+with_max_cost=25000000
 with_catch_reserved_cost=150000
 
 with_max_array_size=50000
-with_max_mapping_keys=50000
+with_max_mapping_keys=100000
 with_max_mapping_size=150000
 with_max_bits=20000
+with_read_file_max_size=100000
+
+# preallocation
+with_min_malloced=671088640
+with_min_small_malloced=536870912
diff -ruN /home/favorit/ldmud-3.3.719/src/settings/bat.orig ./settings/bat.orig
--- /home/favorit/ldmud-3.3.719/src/settings/bat.orig	1970-01-01 02:00:00.000000000 +0200
+++ ./settings/bat.orig	2009-09-03 11:49:53.000000000 +0300
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Settings for the BatMUD
+#
+# configure will strip this part from the script.
+
+exec ./configure --prefix=/bat --with-setting=bat $*
+
+exit 1
+
+# --- The actual settings ---
+
+#
+# mudlib compatibility etc options
+#
+
+with_portno=1234
+with_max_players=1000
+
+with_master_name=secure/master
+with_input_escape="@"
+
+enable_erq=erq
+enable_use_mysql=1
+enable_malloc_sbrk=no
+
+enable_use_parse_command=yes
+enable_compat_mode=no
+enable_strict_euids=no
+enable_use_deprecated=yes
+enable_use_alists=yes
+enable_use_pcre=yes
+with_time_to_reset=3600
+with_time_to_clean_up=7200
+
+enable_use_structs=yes
+enable_use_tls=no
+
+with_alarm_time=1
+with_heart_beat_interval=3
+
+#
+# debug
+#
+enable_malloc_trace=0
+enable_malloc_lpc_trace=0
+enable_trace_code=no
+enable_debug=no
+enable_debug_telnet=no
+# /debug
+
+enable_malloc_check=no
+with_malloc=smalloc
+
+#
+# performance options
+#
+
+with_optimize=med
+enable_use_pthreads=yes
+
+#no swapping
+with_time_to_swap=0
+with_time_to_swap_variables=0
+
+# 1/5 of distinct strings
+with_htable_size=274566
+
+with_itable_size=16384
+
+# 1/4 of objects
+with_otable_size=32768
+
+with_evaluator_stack_size=3000
+with_compiler_stack_size=600
+with_max_user_trace=120
+with_max_trace=125
+with_max_malloced=0
+
+with_max_cost=25000000
+with_catch_reserved_cost=150000
+
+with_max_array_size=50000
+with_max_mapping_keys=100000
+with_max_mapping_size=150000
+with_max_bits=20000
+with_read_file_max_size=100000
+
+# preallocation
+with_min_malloced=671088640
+with_min_small_malloced=536870912
diff -ruN /home/favorit/ldmud-3.3.719/src/simulate.c ./simulate.c
--- /home/favorit/ldmud-3.3.719/src/simulate.c	2009-06-15 01:18:52.000000000 +0300
+++ ./simulate.c	2009-10-02 23:51:50.000000000 +0300
@@ -71,6 +71,10 @@
 #include "wiz_list.h"
 #include "xalloc.h"
 
+//#ifdef BAT
+#include "sockets.h"
+//#endif
+
 #include "i-eval_cost.h"
 
 #include "../mudlib/sys/debug_info.h"
@@ -1689,6 +1693,20 @@
     strbuf_free(&sbuf);
 
     fprintf(stderr, "%s %s", time_stamp(), buf);
+//#ifdef BAT /* Blitzer */
+    if (command_giver != NULL)
+      {
+        fprintf (stderr, "Command giver: '%s'\n",
+                 get_txt (command_giver->name));
+
+        if (command_giver->super != NULL)
+          fprintf (stderr, "Environment: '%s'\n",
+                   get_txt (command_giver->super->name));
+
+        if (last_command != NULL)
+          fprintf (stderr, "Last command: '%s'\n", last_command);
+      }
+//#endif
     errorf("%.*s", MIN(ERROR_BUF_LEN - 200, (int)strlen(buf)), buf);
 } /* load_object_error() */
 
@@ -2553,6 +2571,8 @@
     if (ob->flags & O_DESTRUCTED)
         return;
 
+	socket_object_destructed(ob); // BAT
+
 #ifdef CHECK_OBJECT_REF
     xallocate(shadow, sizeof(*shadow), "destructed object shadow");
 #endif /* CHECK_OBJECT_REF */
diff -ruN /home/favorit/ldmud-3.3.719/src/slaballoc.c ./slaballoc.c
--- /home/favorit/ldmud-3.3.719/src/slaballoc.c	2009-06-15 01:18:52.000000000 +0300
+++ ./slaballoc.c	2009-09-03 12:30:45.000000000 +0300
@@ -1056,7 +1056,11 @@
              * as mem_alloc() does it.
              */
                 unsigned long numObjects = getNumBlocks(i);
-                unsigned long avgNumObjects = extstats[i].cur_alloc
+                unsigned long avgNumObjects;
+				if(slabtable[i].numSlabs - slabtable[i].numFreeSlabs == 0)
+					avgNumObjects = 0;
+				else
+					avgNumObjects = extstats[i].cur_alloc
                                               / (slabtable[i].numSlabs - slabtable[i].numFreeSlabs);
 
                 strbuf_addf(sbuf, "            "
diff -ruN /home/favorit/ldmud-3.3.719/src/sockets.c ./sockets.c
--- /home/favorit/ldmud-3.3.719/src/sockets.c	1970-01-01 02:00:00.000000000 +0200
+++ ./sockets.c	2009-09-03 11:49:53.000000000 +0300
@@ -0,0 +1,1146 @@
+/*
+ * Sockets for ldmud 3.3
+ *
+ * 5-July-2002 - Tomba @ Batmud (tomba@bat.org)
+ *   * First version
+ *
+ * 20-July-2002 - Tomba @ Batmud (tomba@bat.org)
+ *   * Added ECONNRESET
+ *   * Fixed socket_address to return empty string on error
+ *   * sock->send_buffer was not always cleared when freed
+ *   * removed a few debug printfs
+ *
+ * 26-September-2002 - Tomba @ Batmud (tomba@bat.org)
+ *   * Sockets now send all the data in their buffer before actually
+ *     closing
+ *   * Some minor fixes
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+
+#include "main.h"
+#include "svalue.h"
+#include "interpret.h"
+#include "simulate.h"
+#include "object.h"
+#include "mstrings.h"
+#include "comm.h"
+#include "actions.h"
+#include "gcollect.h"
+
+#define SE_SUCCESS 0
+#define SE_UNKNOWN -1
+#define SE_CONNREFUSED -2
+#define SE_HOSTDOWN -3
+#define SE_HOSTUNREACH -4
+#define SE_NOMORESOCKETS -5
+#define SE_CREATESOCKET -6
+#define SE_SETNONBLOCKING -7
+#define SE_SETSOCKOPT -8
+#define SE_BADFD -9
+#define SE_ILLEGALADDR -10
+#define SE_INVALIDPORT -11
+#define SE_PIPE -12
+#define SE_ADDRINUSE -13
+#define SE_INVAL -14
+#define SE_CONNRESET -15
+#define SE_ILLEGAL_SOCKET_STATE -16
+#define SE_ALREADYBOUND -17
+
+static char *socket_errors[] = {
+	"Success",
+	"Unknown error",
+	"Connection refused",
+	"Host is down",
+	"No route to host",
+	"No more mud sockets available",
+	"Failed to create socket",
+	"Failed to set socket to non-blocking mode",
+	"Failed to set socket options",
+	"Bad socket descriptor",
+	"Illegal address",
+	"Invalid port",
+	"Broken pipe",
+	"Address already in use",
+	"Invalid argument",
+	"Connection reset by peer",
+	"Illegal socket state",
+	"Socket is already bound"
+};
+
+enum socket_state
+{
+	STATE_UNUSED = 0,
+	STATE_ALLOCATED,
+	STATE_READY,
+	STATE_LISTENING,
+	STATE_CONNECTING,
+	STATE_CONNECTED,
+	STATE_CLOSING
+};
+
+static char* socket_state_strs[] = { "unused", "allocated", "ready",
+									 "listening", "connecting", "connected",
+									 "closing" };
+
+struct socket_struct
+{
+	int fd;
+	int lpc_fd;				   /* "fd" shown to lpc */
+	enum socket_state state;
+	object_t* object;
+	int bound;
+	struct sockaddr_in local_addr;
+	struct sockaddr_in remote_addr;
+
+	svalue_t read_callback;
+	svalue_t close_callback;
+	svalue_t connect_callback;
+	svalue_t listen_callback;
+
+	char* send_buffer;
+	int send_buffer_size;
+	int send_buffer_used;
+} typedef socket_t;
+
+
+#define MAX_LPC_FD 100
+
+static socket_t g_socket_table[MAX_LPC_FD];
+static struct pollfd g_poll_table[MAX_LPC_FD];
+
+#define READ_BUFFER_SIZE 10240
+static char g_read_buffer[READ_BUFFER_SIZE];
+
+void socket_process_events(socket_t* sock, short events);
+
+
+
+void socket_init()
+{
+	int i;
+	for(i = 0; i < MAX_LPC_FD; i++)
+	{
+		struct socket_struct* sock = &g_socket_table[i];
+	
+		sock->state = STATE_UNUSED;
+		sock->lpc_fd = i;
+
+		g_poll_table[i].fd = -1;
+		g_poll_table[i].events = POLLIN | POLLOUT;
+		g_poll_table[i].revents = 0;
+
+	}
+}
+
+#if defined(GC_SUPPORT)
+
+void clear_socket_refs(void)
+{
+	int i;
+	for(i = 0; i < MAX_LPC_FD; i++)
+	{
+		socket_t* sock = &g_socket_table[i];
+		
+		if(sock->state != STATE_UNUSED)
+		{
+			clear_ref_in_vector(&sock->read_callback, 1);
+			clear_ref_in_vector(&sock->close_callback, 1);
+			clear_ref_in_vector(&sock->connect_callback, 1);
+			clear_ref_in_vector(&sock->listen_callback, 1);
+		}
+	}
+}
+
+void count_socket_refs(void)
+{
+	int i;
+	for(i = 0; i < MAX_LPC_FD; i++)
+	{
+		socket_t* sock = &g_socket_table[i];
+		
+		if(sock->state != STATE_UNUSED)
+		{
+			count_ref_in_vector(&sock->read_callback, 1);
+			count_ref_in_vector(&sock->close_callback, 1);
+			count_ref_in_vector(&sock->connect_callback, 1);
+			count_ref_in_vector(&sock->listen_callback, 1);
+		}
+	}
+}
+
+#endif
+
+int socket_conv_errno(int err)
+{
+	switch(err)
+	{
+		case ECONNREFUSED:
+			return SE_CONNREFUSED;
+
+		case EHOSTDOWN:
+			return SE_HOSTDOWN;
+
+		case EHOSTUNREACH:
+			return SE_HOSTUNREACH;
+
+		case EPIPE:
+			return SE_PIPE;
+
+		case EADDRINUSE:
+			return SE_ADDRINUSE;
+
+		case EINVAL:
+			return SE_INVAL;
+
+		case ECONNRESET:
+			return SE_CONNRESET;
+
+		default:
+			fprintf(stderr, "socket_conv_errno: unknown system error %d, %s\n", err, strerror(err));
+			return SE_UNKNOWN;
+	}
+}
+
+struct socket_struct* new_socket_entry()
+{
+	int i;
+	
+	for (i = 0; i < MAX_LPC_FD; i++)
+	{
+		struct socket_struct* sock = &g_socket_table[i];
+		
+		if (sock->state == STATE_UNUSED)
+		{
+			sock->fd = -1;
+			sock->state = STATE_ALLOCATED;
+			sock->object = 0;
+			sock->bound = 0;
+			memset(&sock->local_addr, 0, sizeof(sock->local_addr));
+			memset(&sock->remote_addr, 0, sizeof(sock->remote_addr));
+			
+			put_number(&sock->read_callback, 0);
+			put_number(&sock->close_callback, 0);
+			put_number(&sock->connect_callback, 0);
+			put_number(&sock->listen_callback, 0);
+			
+			sock->send_buffer = 0;
+			sock->send_buffer_size = 0;
+			sock->send_buffer_used = 0;
+			
+			return sock;
+		}
+	}
+	
+	return 0;
+}
+
+void free_socket_entry(socket_t* sock)
+{
+	if(sock != 0)
+	{
+//		printf("Freeing fd %d, lpcfd %d\n", sock->fd, sock->lpc_fd);
+		if(sock->fd != -1)
+		{
+			if(close(sock->fd) == -1)
+			{
+				perror("free_socket_entry: close");
+			}
+
+			sock->fd = -1;
+		}
+	
+		sock->state = STATE_UNUSED;
+		sock->object = 0;
+		
+		free_svalue(&sock->read_callback);
+		free_svalue(&sock->close_callback);
+		free_svalue(&sock->connect_callback);
+		free_svalue(&sock->listen_callback);
+		put_number(&sock->read_callback, 0);
+		put_number(&sock->close_callback, 0);
+		put_number(&sock->connect_callback, 0);
+		put_number(&sock->listen_callback, 0);
+		
+		if(sock->send_buffer)
+		{
+			free(sock->send_buffer);
+			sock->send_buffer = 0;
+			sock->send_buffer_size = 0;
+			sock->send_buffer_used = 0;
+		}
+
+		g_poll_table[sock->lpc_fd].fd = -1;
+	}
+}
+
+socket_t* get_socket_entry(int lpc_fd)
+{
+	if(lpc_fd < 0 || lpc_fd >= MAX_LPC_FD ||
+	   g_socket_table[lpc_fd].state == STATE_UNUSED)
+	{
+		return 0;
+	}
+
+	return &g_socket_table[lpc_fd];
+}
+
+void socket_object_destructed(object_t* ob)
+{
+	int i;
+	
+	for(i = 0; i < MAX_LPC_FD; i++)
+	{
+		struct socket_struct* sock = &g_socket_table[i];
+
+		if(sock->state != STATE_UNUSED)
+		{
+			if(sock->object == ob)
+			{
+				free_socket_entry(sock);
+			}
+		}
+	}
+}
+
+
+
+
+
+
+svalue_t* f_socket_strerror(svalue_t* sp)
+{
+	int err = 0 - sp->u.number;
+	sp--;
+
+	if(err < 0 || err > sizeof(socket_errors))
+	{
+		push_string(sp, new_mstring("Unspecified error"));
+	}
+	else
+	{	
+		push_string(sp, new_mstring(socket_errors[err]));
+	}
+	
+	return sp;
+}
+
+svalue_t* f_socket_print_stats(svalue_t* sp)
+{
+	int i;
+	
+	add_message("-- Socket stats --\n");
+		
+	for(i = 0; i < MAX_LPC_FD; i++)
+	{
+		socket_t* sock = &g_socket_table[i];
+	
+		if(sock->state != STATE_UNUSED)
+		{
+			add_message("lpc_fd %d, fd %d, state %s, buf %d, ob %s\n",
+						i, sock->fd,
+						socket_state_strs[sock->state],
+						sock->send_buffer ? sock->send_buffer_used : 0,
+						get_txt(sock->object->name));
+		}
+	}
+
+	return sp;
+}
+
+int socket_init_socket(socket_t* sock, int fd, svalue_t* close_callback,
+					   object_t* object)
+{
+	int tmp;
+	
+	if(fcntl(fd, F_SETFL, O_NDELAY) == -1)
+	{
+		perror("socket_init_socket: fcntl O_NDELAY");
+
+		return SE_SETNONBLOCKING;
+	}
+
+	tmp = 1;
+	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+				  (char *)&tmp, sizeof(tmp)) == -1)
+	{
+		perror("socket_init_socket: setsockopt SO_REUSEADDR");
+
+		return SE_SETSOCKOPT;
+	}
+
+	sock->fd = fd;
+	sock->state = STATE_READY;
+	sock->object = object;
+
+	assign_svalue(&sock->close_callback, close_callback);
+	
+	g_poll_table[sock->lpc_fd].fd = sock->fd;
+
+	return 0;
+}
+
+int socket_string_to_sockaddr(char* name, struct sockaddr_in* saddr)
+{
+#define ADDR_BUF_SIZE 512
+	int port;
+	char *cp, addr[ADDR_BUF_SIZE];
+
+	strncpy(addr, name, ADDR_BUF_SIZE);
+
+	cp = strchr(addr, ':');
+	if (cp == NULL)
+	{
+		return 0;
+	}
+	
+	*cp = '\0';
+	port = atoi(cp + 1);
+
+	saddr->sin_family = AF_INET;
+	saddr->sin_port = htons((unsigned short)port);
+	if(inet_aton(addr, &saddr->sin_addr) == 0)
+	{
+		return 0;
+	}
+
+	return 1;
+#undef ADDR_BUF_SIZE
+}
+
+
+
+svalue_t* f_socket_create(svalue_t* sp)
+{
+	int err, fd;
+	socket_t* sock;
+	
+	if(current_object->flags & O_DESTRUCTED)
+	{
+		errorf("socket_create: this_object has been destructed");
+	}
+
+	sock = new_socket_entry();
+	
+	if(sock == 0)
+	{
+		free_svalue(sp--);
+		push_number(sp, SE_NOMORESOCKETS);
+		return sp;
+	}
+
+	fd = socket(AF_INET, SOCK_STREAM, 0);
+
+	if(fd == -1)
+	{
+		perror("socket_create: socket");
+	
+		free_socket_entry(sock);
+	
+		free_svalue(sp--);
+		push_number(sp, SE_CREATESOCKET);
+		return sp;
+	}
+	
+	err = socket_init_socket(sock, fd, &sp[0], current_object);
+	
+	free_svalue(sp--);
+	
+	if(err != 0)
+	{
+		close(fd);
+	
+		free_socket_entry(sock);
+
+		push_number(sp, err);
+
+		return sp;
+	}
+	
+	push_number(sp, sock->lpc_fd);
+
+	return sp;
+}
+
+svalue_t* f_socket_close(svalue_t* sp)
+{
+	int lpc_fd = (sp--)->u.number;
+	
+	socket_t* sock = get_socket_entry(lpc_fd);
+
+	if(sock == 0)
+	{
+		push_number(sp, SE_BADFD);
+		return sp;
+	}
+
+	if(shutdown(sock->fd, 0) == -1)
+	{
+		fprintf(stderr, "socket_close: shutdown failed: %s\n", strerror(errno));
+	}
+	
+	sock->state = STATE_CLOSING;
+
+	push_number(sp, SE_SUCCESS);
+		
+	return sp;
+}
+
+svalue_t* f_socket_connect(svalue_t* sp)
+{
+	int lpc_fd, myerrno;
+	int ret;
+	socket_t* sock;
+
+	lpc_fd = sp[-3].u.number;
+
+	sock = get_socket_entry(lpc_fd);
+	if(sock == 0)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+	
+		push_number(sp, SE_BADFD);
+		return sp;
+	}
+
+	if(sock->state != STATE_READY)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+	
+		push_number(sp, SE_ILLEGAL_SOCKET_STATE);
+		return sp;
+	}
+
+	if(socket_string_to_sockaddr(get_txt(sp[-2].u.str), &sock->remote_addr) == 0)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+	
+		push_number(sp, SE_ILLEGALADDR);
+		return sp;
+	}
+	
+	ret = connect(sock->fd, (struct sockaddr *)&sock->remote_addr,
+				  sizeof(sock->remote_addr));
+	myerrno = errno;
+	
+	if(ret == -1 && myerrno != EINPROGRESS)
+	{
+		fprintf(stderr, "socket_connect: connect failed: %s\n", strerror(myerrno));
+
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+		
+		push_number(sp, socket_conv_errno(myerrno));
+	}
+	else
+	{
+		sock->state = STATE_CONNECTING;
+
+		transfer_svalue(&sock->connect_callback, &sp[-1]);
+		transfer_svalue(&sock->read_callback, &sp[0]);
+
+		sp--;
+		sp--;
+		free_svalue(sp--);
+		free_svalue(sp--);
+
+		push_number(sp, SE_SUCCESS);
+	}
+	
+	return sp;
+}
+
+svalue_t* f_socket_bind(svalue_t* sp)
+{
+	socket_t* sock;
+	int myerrno;
+   
+	int port = (sp--)->u.number;
+	int lpc_fd = (sp--)->u.number;
+
+	sock = get_socket_entry(lpc_fd);
+	if(sock == 0)
+	{
+		push_number(sp, SE_BADFD);
+		return sp;
+	}
+
+	if(sock->state != STATE_READY)
+	{
+		push_number(sp, SE_ILLEGAL_SOCKET_STATE);
+		return sp;
+	}
+
+	if(sock->bound)
+	{
+		push_number(sp, SE_ALREADYBOUND);
+		return sp;
+	}
+
+	if(port < 0)
+	{
+		push_number(sp, SE_INVALIDPORT);
+		return sp;
+	}
+
+	sock->local_addr.sin_port = htons((unsigned short)port);
+	sock->local_addr.sin_addr.s_addr = INADDR_ANY;
+	sock->local_addr.sin_family = AF_INET;
+
+	if(bind(sock->fd, (struct sockaddr*)&sock->local_addr,
+			sizeof(sock->local_addr)) == -1)
+	{
+		myerrno = errno;
+	
+		perror("socket_bind: bind");
+		push_number(sp, socket_conv_errno(myerrno));
+	}
+	else
+	{
+		sock->bound = 1;
+		push_number(sp, SE_SUCCESS);
+	}
+	 
+	return sp;
+}
+
+svalue_t* f_socket_listen(svalue_t* sp)
+{
+	socket_t* sock;
+	int myerrno;
+   
+	int lpc_fd = sp[-1].u.number;
+
+	sock = get_socket_entry(lpc_fd);
+	if(sock == 0)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		push_number(sp, SE_BADFD);
+		return sp;
+	}
+
+	if(sock->state != STATE_READY || !sock->bound)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		push_number(sp, SE_ILLEGAL_SOCKET_STATE);
+		return sp;
+	}
+	
+	if(listen(sock->fd, 10) == -1)
+	{
+		myerrno = errno;
+		perror("socket_listen: listen");
+		
+		free_svalue(sp--);
+		free_svalue(sp--);
+		push_number(sp, socket_conv_errno(myerrno));
+		return sp;
+	}
+	
+	assign_svalue(&sock->listen_callback, &sp[0]);
+	
+	free_svalue(sp--);
+	free_svalue(sp--);
+
+	sock->state = STATE_LISTENING;
+	push_number(sp, SE_SUCCESS);
+	
+	return sp;
+}
+
+svalue_t* f_socket_accept(svalue_t* sp)
+{
+	socket_t* sock;
+	socket_t* new_sock;
+	int new_fd;
+	struct sockaddr_in addr;
+	socklen_t addr_size = sizeof(addr);
+	int err;
+	int myerrno;
+   
+	int lpc_fd = sp[-2].u.number;
+	
+	sock = get_socket_entry(lpc_fd);
+	if(sock == 0)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+	
+		push_number(sp, SE_BADFD);
+		return sp;
+	}
+
+	if(sock->state != STATE_LISTENING)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+		push_number(sp, SE_ILLEGAL_SOCKET_STATE);
+		return sp;
+	}
+	
+	new_fd = accept(sock->fd, (struct sockaddr*)&addr, &addr_size);
+	myerrno = errno;
+	if(new_fd < 0)
+	{
+		perror("socket_accept: accept");
+	
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+
+		push_number(sp, socket_conv_errno(myerrno));
+		return sp;
+	}
+
+	new_sock = new_socket_entry();
+	
+	if(new_sock == 0)
+	{
+		close(new_fd);
+	
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+	
+		push_number(sp, SE_NOMORESOCKETS);
+		return sp;
+	}
+
+	err = socket_init_socket(new_sock, new_fd, &sp[0], current_object);
+
+	if(err != 0)
+	{
+		close(new_fd);
+
+		free_socket_entry(new_sock);
+	
+		free_svalue(sp--);
+		free_svalue(sp--);
+		free_svalue(sp--);
+
+		push_number(sp, err);
+	}
+
+	assign_svalue(&new_sock->read_callback, &sp[-1]);
+
+	memcpy(&new_sock->remote_addr, &addr, sizeof(addr));
+	
+	free_svalue(sp--);
+	free_svalue(sp--);
+	free_svalue(sp--);
+
+	new_sock->state = STATE_CONNECTED;
+	
+	push_number(sp, new_sock->lpc_fd);
+
+	return sp;
+}
+
+svalue_t* f_socket_address(svalue_t* sp)
+{
+#define ADDR_BUF_SIZE 512
+	char addrstr[ADDR_BUF_SIZE];
+	socket_t* sock;
+	
+	int lpc_fd = sp[0].u.number;
+	
+	sock = get_socket_entry(lpc_fd);
+	
+	free_svalue(sp--);
+	
+	if(sock == 0)
+	{
+		push_string(sp, new_mstring(""));
+		return sp;
+	}
+
+	if(sock->state != STATE_CONNECTED)
+	{
+		push_string(sp, new_mstring(""));
+		return sp;
+	}
+	
+	snprintf(addrstr, 511, "%s:%d",
+			 inet_ntoa(sock->remote_addr.sin_addr),
+			 ntohs(sock->remote_addr.sin_port));
+	addrstr[ADDR_BUF_SIZE-1] = 0;
+
+	push_string(sp, new_mstring(addrstr));
+
+	return sp;
+#undef ADDR_BUF_SIZE
+}
+
+void socket_flush_send_buffer(socket_t* sock)
+{
+	if( (sock->state == STATE_CONNECTED || sock->state == STATE_CLOSING) &&
+		sock->send_buffer_used > 0)
+	{
+		int len, myerrno;
+
+		len = send(sock->fd, sock->send_buffer, sock->send_buffer_used, 0);
+		myerrno = errno;
+
+		if(len == -1)
+		{
+			fprintf(stderr, "socket_flush_send_buffer: send failed %s\n", strerror(myerrno));
+
+			/* error -> clear buffer */
+			free(sock->send_buffer);
+			sock->send_buffer = 0;
+			sock->send_buffer_size = 0;
+			sock->send_buffer_used = 0;
+		}
+		else if(len < sock->send_buffer_used)
+		{
+			memmove(sock->send_buffer, sock->send_buffer + len,
+					sock->send_buffer_used - len);
+		
+			sock->send_buffer_used = sock->send_buffer_used - len;
+/*
+			printf("socket: sent %d of %d. %d in buffer\n", len, sock->send_buffer_used+len,
+				   sock->send_buffer_used);
+*/		
+		}
+		else
+		{
+/*
+			printf("socket: sent %d of %d. %d in buffer\n", len, sock->send_buffer_used,
+				   0);
+*/		
+			free(sock->send_buffer);
+			sock->send_buffer = 0;
+			sock->send_buffer_size = 0;
+			sock->send_buffer_used = 0;
+		}
+	}
+}
+
+svalue_t* f_socket_send(svalue_t* sp)
+{
+	int lpc_fd, len, myerrno;
+	socket_t* sock;
+	char* buf;
+	int buflen;
+	
+	lpc_fd = sp[-1].u.number;
+	
+	sock = get_socket_entry(lpc_fd);
+
+	if(sock == 0)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		push_number(sp, SE_BADFD);
+		return sp;
+	}
+
+	if(sock->state != STATE_CONNECTED)
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+		push_number(sp, SE_ILLEGAL_SOCKET_STATE);
+		return sp;
+	}
+	
+	buf =  get_txt(sp[0].u.str);
+	buflen = mstrsize(sp[0].u.str);
+
+	socket_flush_send_buffer(sock);
+	
+	if(sock->send_buffer_used == 0)
+	{
+		len = send(sock->fd, buf, buflen, 0);
+		myerrno = errno;
+	}
+	else
+	{
+		myerrno = 0;
+		len = 0;
+	}
+
+	if(len == -1 && (myerrno == EAGAIN || myerrno == EWOULDBLOCK))
+	{
+		len = 0;
+	}
+	
+	if(len == -1)
+	{
+		fprintf(stderr, "socket_send: send failed %s\n", strerror(myerrno));
+
+		free_svalue(sp--);
+		free_svalue(sp--);
+	
+		push_number(sp, socket_conv_errno(myerrno));
+	}
+	else if(len < buflen)
+	{
+		if(buflen - len > sock->send_buffer_size - sock->send_buffer_used)
+		{
+			sock->send_buffer = realloc(sock->send_buffer,
+										sock->send_buffer_used + buflen - len);
+
+			if(!sock->send_buffer)
+			{
+				errorf("out of memory");
+			}
+			else
+			{
+				sock->send_buffer_size = sock->send_buffer_used + buflen - len;
+			}
+		}
+
+		memcpy(sock->send_buffer + sock->send_buffer_used,
+			   buf + len,
+			   buflen - len);
+
+		sock->send_buffer_used += buflen - len;
+
+//		printf("socket_send(): sent %d of %d. %d in buffer\n", len, buflen, sock->send_buffer_used);
+
+		free_svalue(sp--);
+		free_svalue(sp--);
+	
+		push_number(sp, len);
+	}
+	else
+	{
+		free_svalue(sp--);
+		free_svalue(sp--);
+	
+		push_number(sp, SE_SUCCESS);
+	}
+	
+	return sp;
+}
+
+void socket_poll()
+{
+	int ret, i, myerrno;
+
+	object_t* hide_current_object = current_object;
+	program_t* hide_current_prog = current_prog;
+	object_t* hide_interactive = current_interactive;
+	object_t* hide_command_giver = command_giver;
+
+	current_interactive = 0;
+	command_giver = 0;
+
+	for(i = 0; i < MAX_LPC_FD; i++)
+	{
+		socket_t* sock = &g_socket_table[i];
+
+		/* if socket is closing and we have sent all the stuff out,
+		   free it */
+		if(sock->state == STATE_CLOSING && sock->send_buffer == 0)
+		{
+			free_socket_entry(sock);
+		}
+	}
+
+	ret = poll(g_poll_table, MAX_LPC_FD, 0);
+	myerrno = errno;
+	
+	if(ret == -1)
+	{
+		perror("socket_poll: poll");
+	}
+	else
+	{
+		for(i = 0; i < MAX_LPC_FD; i++)
+		{
+			if(g_poll_table[i].revents)
+			{
+				socket_t* sock = get_socket_entry(i);
+		
+				if(sock == 0)
+				{
+					errorf("Internal error in socket_poll()");
+				}
+		
+				socket_process_events(sock, g_poll_table[i].revents);
+			}
+		}
+	}
+	
+	current_interactive = hide_interactive;
+	command_giver = hide_command_giver;
+	current_object = hide_current_object;
+	current_prog = hide_current_prog;
+}
+
+
+void socket_process_events(socket_t* sock, short events)
+{
+	int err;
+	socklen_t errlen;
+	int myerrno;
+
+	if(events & POLLNVAL)
+	{
+//		printf("NVAL %d\n", sock->lpc_fd);
+		
+		push_number(inter_sp, sock->lpc_fd);
+		push_number(inter_sp, SE_UNKNOWN);
+		secure_callback_lambda(&sock->close_callback, 2);
+		free_socket_entry(sock);
+		return;
+	}
+	
+	if(events & POLLERR)
+	{
+//		printf("ERR %d\n", sock->lpc_fd);
+	
+		errlen = sizeof(err);
+		if(getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1)
+		{
+			perror("socket_process_events: getsockopt");
+		}
+	
+		if(sock->state == STATE_CONNECTING)
+		{
+			push_number(inter_sp, sock->lpc_fd);
+			push_number(inter_sp, socket_conv_errno(err));
+			secure_callback_lambda(&sock->connect_callback, 2);
+		}
+		else
+		{
+			push_number(inter_sp, sock->lpc_fd);
+			push_number(inter_sp, socket_conv_errno(err));
+			secure_callback_lambda(&sock->close_callback, 2);
+			free_socket_entry(sock);
+		}
+		
+		return;
+	}
+		
+	if(events & POLLHUP)
+	{
+//		printf("HUP %d\n", sock->lpc_fd);
+	
+		errlen = sizeof(err);
+		if(getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1)
+		{
+			perror("socket_process_events: getsockopt");
+		}
+
+		if(sock->state == STATE_CONNECTING)
+		{
+			push_number(inter_sp, sock->lpc_fd);
+			push_number(inter_sp, err);
+			secure_callback_lambda(&sock->connect_callback, 2);
+		}
+		else
+		{
+			push_number(inter_sp, sock->lpc_fd);
+			push_number(inter_sp, err);
+			secure_callback_lambda(&sock->close_callback, 2);
+			free_socket_entry(sock);
+		}
+				
+		return;
+	}
+
+	if(events & POLLOUT)
+	{
+//		printf("OUT %d\n", sock->lpc_fd);
+
+		if(sock->state == STATE_CONNECTING)
+		{
+			push_number(inter_sp, sock->lpc_fd);
+			push_number(inter_sp, 0);
+			secure_callback_lambda(&sock->connect_callback, 2);
+
+			sock->state = STATE_CONNECTED;
+		}
+		else if(sock->state == STATE_CONNECTED ||
+				sock->state == STATE_CLOSING)
+		{
+			socket_flush_send_buffer(sock);
+		}
+		else
+		{
+			fprintf(stderr, "Illegal socket state in POLLOUT. fd: %d, state %d\n", sock->fd, sock->state);
+		}
+	}
+	
+	if(events & POLLIN)
+	{
+//		printf("IN %d\n", sock->lpc_fd);
+		
+		if(sock->state == STATE_CONNECTED)
+		{
+			int len;
+			
+			len = recv(sock->fd, &g_read_buffer, READ_BUFFER_SIZE, 0);
+			myerrno = errno;
+
+			if(len == -1)
+			{
+				perror("socket_process_events: recv");
+		
+				push_number(inter_sp, sock->lpc_fd);
+				push_number(inter_sp, 0);
+				push_number(inter_sp, socket_conv_errno(myerrno));
+		
+				secure_callback_lambda(&sock->read_callback, 3);
+			}
+			else if(len == 0)
+			{
+				push_number(inter_sp, sock->lpc_fd);
+				push_number(inter_sp, 0);
+		
+				secure_callback_lambda(&sock->close_callback, 2);
+		
+				free_socket_entry(sock);
+			}
+			else
+			{
+				push_number(inter_sp, sock->lpc_fd);
+				push_string(inter_sp, mstring_new_n_string(g_read_buffer, len));
+				push_number(inter_sp, len);
+		
+				secure_callback_lambda(&sock->read_callback, 3);
+			}
+		}
+		else if(sock->state == STATE_LISTENING)
+		{
+			push_number(inter_sp, sock->lpc_fd);
+		
+			secure_callback_lambda(&sock->listen_callback, 1);
+		}
+		else if(sock->state == STATE_CLOSING)
+		{
+			
+		}
+		else
+		{
+			fprintf(stderr, "socket_poll: read event in unknown state. fd: %d, state %d\n", sock->fd, sock->state);
+		}
+	}
+}
diff -ruN /home/favorit/ldmud-3.3.719/src/sockets.h ./sockets.h
--- /home/favorit/ldmud-3.3.719/src/sockets.h	1970-01-01 02:00:00.000000000 +0200
+++ ./sockets.h	2009-09-03 11:49:53.000000000 +0300
@@ -0,0 +1,9 @@
+
+extern void socket_init();
+
+extern void socket_object_destructed(object_t* ob);
+
+extern void socket_poll();
+
+extern void clear_socket_refs(void);
+extern void count_socket_refs(void);
diff -ruN /home/favorit/ldmud-3.3.719/src/sprintf.c ./sprintf.c
--- /home/favorit/ldmud-3.3.719/src/sprintf.c	2009-06-15 01:18:52.000000000 +0300
+++ ./sprintf.c	2009-09-03 12:30:45.000000000 +0300
@@ -2443,7 +2443,7 @@
     str = string_print_formatted(get_txt((sp-num_arg+1)->u.str)
                                 , num_arg-1, sp-num_arg+2);
     if (command_giver)
-        tell_object(command_giver, str);
+        tell_object(command_giver, str, 0); // BAT
     else
         add_message(FMT_STRING, str);
     free_mstring(str);
diff -ruN /home/favorit/ldmud-3.3.719/src/string_spec ./string_spec
--- /home/favorit/ldmud-3.3.719/src/string_spec	2009-06-15 01:18:52.000000000 +0300
+++ ./string_spec	2009-09-03 12:30:45.000000000 +0300
@@ -116,6 +116,7 @@
 TAIL               "tail"
 WRITE_BYTES        "write_bytes"
 WRITE_FILE         "write_file"
+COUNT_LINES        "count_lines" /* BAT */
 
     /* Editor strings */
 
diff -ruN /home/favorit/ldmud-3.3.719/src/version.sh ./version.sh
--- /home/favorit/ldmud-3.3.719/src/version.sh	2009-06-15 01:18:52.000000000 +0300
+++ ./version.sh	2009-10-03 00:05:30.000000000 +0300
@@ -17,7 +17,7 @@
 # A timestamp, to be used by bumpversion and other scripts.
 # It can be used, for example, to 'touch' this file on every build, thus
 # forcing revision control systems to add it on every checkin automatically.
-version_stamp="2009-05-30 12:00:00"
+version_stamp="Sat Oct  3 00:05:30 EEST 2009"
 
 # The version number information
 version_micro=719
bat.custom.diff (144,337 bytes)   
bug683.diff (6,958 bytes)   
Index: trunk/test/t-0000683.c
===================================================================
--- trunk/test/t-0000683.c	(Revision 0)
+++ trunk/test/t-0000683.c	(Revision 0)
@@ -0,0 +1,67 @@
+#include "/inc/base.inc"
+
+#include "/sys/configuration.h"
+
+/* These functions are for the clone (the player object). */
+int sleeping;
+
+void start_client()
+{
+    net_connect("127.0.0.1", query_mud_port());
+    sleeping = 1;
+}
+
+int logon(int flag)
+{
+    set_prompt("");
+    if(!sleeping)
+    {
+        object me;
+
+        configure_interactive(this_object(), IC_MAX_WRITE_BUFFER_SIZE, 0);
+        set_buffer_size(0);
+
+        /* 100 MB should be enough. */
+        foreach(int i: 10240)
+            write("*"*10240+"\n");
+
+        me = clone_object(this_object());
+        exec(me, this_object());
+        write("Done.\n");
+    }
+
+    return 1;
+}
+
+/* Here comes the master. */
+
+object connect()
+{
+    set_prompt("");
+    return clone_object(this_object());
+}
+
+void run_test()
+{
+    msg("\nRunning test for #0000683:\n"
+          "--------------------------\n");
+
+    /* Waiting until LDMud is ready for users. */
+    call_out("run_test2", 0);
+}
+
+void run_test2()
+{
+    object dummy;
+
+    call_out(#'shutdown, __ALARM_TIME__ * 2, 0);
+
+    dummy = clone_object(this_object());
+    dummy->start_client();
+}
+
+string *epilog(int eflag)
+{
+    run_test();
+    return 0;
+}
Index: trunk/test/inc/base.inc
===================================================================
--- trunk/test/inc/base.inc	(Revision 2764)
+++ trunk/test/inc/base.inc	(Arbeitskopie)
@@ -38,6 +38,13 @@
 }
 #endif
 
+#ifndef OWN_VALID_EXEC
+mixed valid_exec(string prog, object ob, object obfrom)
+{
+    return 1;
+}
+#endif
+
 #ifndef OWN_PREPARE_DESTRUCT
 mixed prepare_destruct (object obj)
 {
Index: trunk/src/comm.c
===================================================================
--- trunk/src/comm.c	(Revision 2764)
+++ trunk/src/comm.c	(Arbeitskopie)
@@ -428,7 +428,7 @@
   /* The backend::current_time when urgent_data was set last.
    */
 
-static object_t *first_player_for_flush = NULL;
+static interactive_t *first_player_for_flush = NULL;
   /* First interactive user object to flush. Marks the head
    * of the list formed by interactive.{next,previous}_player_for_flush
    */
@@ -786,10 +786,10 @@
       putc('\n', stderr);
     fprintf(stderr, "  .message_length:    %d (%p)\n", ip->message_length, ip->message_buf+ip->message_length);
     fprintf(stderr, "  .next_for_flush:    %p", ip->next_player_for_flush);
-      if (ip->next_player_for_flush) fprintf(stderr, " (%s)", get_txt(ip->next_player_for_flush->name));
+      if (ip->next_player_for_flush) fprintf(stderr, " (%s)", get_txt(ip->next_player_for_flush->ob->name));
       putc('\n', stderr);
     fprintf(stderr, "  .prev_for_flush:    %p", ip->previous_player_for_flush);
-      if (ip->previous_player_for_flush) fprintf(stderr, " (%s)", get_txt(ip->previous_player_for_flush->name));
+      if (ip->previous_player_for_flush) fprintf(stderr, " (%s)", get_txt(ip->previous_player_for_flush->ob->name));
       putc('\n', stderr);
     fprintf(stderr, "  .access_class:      %ld\n", ip->access_class);
     fprintf(stderr, "  .charset:          ");
@@ -2075,16 +2075,15 @@
  */
 
 {
-    if ( ip->previous_player_for_flush || first_player_for_flush == ip->ob)
+    if ( ip->previous_player_for_flush || first_player_for_flush == ip)
         return;
 
     if ( NULL != (ip->next_player_for_flush = first_player_for_flush) )
     {
-        O_GET_INTERACTIVE(first_player_for_flush)->
-          previous_player_for_flush = ip->ob;
+        first_player_for_flush->previous_player_for_flush = ip;
     }
     ip->previous_player_for_flush = NULL;
-    first_player_for_flush = ip->ob;
+    first_player_for_flush = ip;
 } /* add_flush_entry() */
 
 /*-------------------------------------------------------------------------*/
@@ -2106,17 +2105,17 @@
 
     if ( ip->previous_player_for_flush )
     {
-        O_GET_INTERACTIVE(ip->previous_player_for_flush)->next_player_for_flush
+        ip->previous_player_for_flush->next_player_for_flush
           = ip->next_player_for_flush;
     }
-    else if (first_player_for_flush == ip->ob)
+    else if (first_player_for_flush == ip)
     {
         first_player_for_flush = ip->next_player_for_flush;
     }
 
     if ( ip->next_player_for_flush )
     {
-        O_GET_INTERACTIVE(ip->next_player_for_flush)->previous_player_for_flush
+        ip->next_player_for_flush->previous_player_for_flush
           = ip->previous_player_for_flush;
     }
 
@@ -2134,16 +2133,14 @@
  */
 
 {
-    object_t *p, *np;
-    interactive_t *ip;
+    interactive_t *ip, *nip;
     object_t *save = command_giver;
 
-    for ( p = first_player_for_flush; p != NULL; p = np)
+    for ( ip = first_player_for_flush; ip != NULL; ip = nip)
     {
-        ip = O_GET_INTERACTIVE(p);
-        np = ip->next_player_for_flush;
-          /* add_message() will clobber (p)->next_player_for_flush! */
-        command_giver = p;
+        nip = ip->next_player_for_flush;
+          /* add_message() will clobber (ip)->next_player_for_flush! */
+        command_giver = ip->ob;
         add_message(message_flush);
 
         if(ip->msg_discarded == DM_SEND_INFO)
@@ -3348,6 +3345,8 @@
         static unsigned char erq_welcome[] = { IAC, TELOPT_BINARY };
 
         add_message(message_flush);
+        remove_flush_entry(interactive); /* To be sure */
+
         erq_demon = interactive->socket;
         erq_proto_demon = -1;
         socket_write(erq_demon, erq_welcome, sizeof erq_welcome);
Index: trunk/src/comm.h
===================================================================
--- trunk/src/comm.h	(Revision 2764)
+++ trunk/src/comm.h	(Arbeitskopie)
@@ -236,8 +236,8 @@
                                    as name prefix. NULL traces everything. */
     int message_length;         /* Current length of message in message_buf[] */
 
-    object_t *next_player_for_flush;
-    object_t *previous_player_for_flush;
+    interactive_t *next_player_for_flush;
+    interactive_t *previous_player_for_flush;
       /* Double linked list of all active user objects with data pending
        * in message_buf[].
        */
Index: trunk/CHANGELOG
===================================================================
--- trunk/CHANGELOG	(Revision 2770)
+++ trunk/CHANGELOG	(Arbeitskopie)
@@ -1,6 +1,12 @@
 This file lists all changes made to the game driver in all glory detail.
 See the file HISTORY for a user-oriented summary of all the changes.
 
+21-Oct-2009 (Gnomi)
+  - Let the player_for_flush list point to interactive_t* directly.
+    This avoids inconsistencies between objects and their interactive
+    structure caused when exec()ing. (Bug #683)
+    (comm.c)
+
 18-Oct-2009 (Gnomi)
   - Free the xml buffer after xml reader and writer, because they might
     use it when cleaning up. (maybe #683)
bug683.diff (6,958 bytes)   

Activities

zesstra

2009-09-27 16:36

administrator   ~0001318

Just some first comments:
- output on stdout/stderr and maybe the debug.log might help as well.
- additional information about your platform and environment and configuration might help.
- the two backtraces are very different. But it may well be that there is a common memory corruption as root cause. For example, the address give to print_svalue() in the second crash (0xb7) nearly certainly wrong and maybe already the one given to f_write() - but where did it come from? But I guess, we won't find it by stacktraces alone, so...
- please consider disabling the optimization for some time, because it severely limits debugging and think about adding -ggdb3 to your compiler options if you use gcc/gdb.
- If not already active, please enable the following configuration options in your driver and recompile: --enable-malloc-check, --enable-malloc-trace, --enable-malloc-lpc-trace, --enable-trace-code, --enable-debug.
- please enable core files on your system and archive the _exact_ executable that produced the core along with it.
- if the crashes occur regularly: do you recognize any common thing prior to the crashes? Reproducing the issue would of course be best. ;-)

favoretti

2009-09-29 12:48

reporter   ~0001347

I have core + executable for you, for the rest I'll need to recompile the driver and then wait for it to crash again (which can take a week or so).

Cores are about 2G, where do you want them upped?

favoretti

2009-09-29 13:48

reporter   ~0001348

Attached stderr, stdout logs.

Platform: debian lenny 64-bit, 6G RAM.
Configuration is the same as you've seen in that XML bug I've reported earlier. But just in case, in your trunk under settings/bat.

Pasteing here as well:

#!/bin/sh
#
# Settings for the BatMUD
#
# configure will strip this part from the script.

exec ./configure --prefix=/bat --with-setting=bat $*

exit 1

# --- The actual settings ---

#
# mudlib compatibility etc options
#

with_portno=1234
with_max_players=1000

with_master_name=secure/master
with_input_escape="@"

enable_erq=erq
enable_use_mysql=1
enable_malloc_sbrk=no

enable_use_parse_command=yes
enable_compat_mode=no
enable_strict_euids=no
enable_use_deprecated=yes
enable_use_alists=yes
enable_use_pcre=yes
enable_use_xml=xml2
with_time_to_reset=3600
with_time_to_clean_up=7200

enable_use_structs=yes
enable_use_tls=no

with_alarm_time=1
with_heart_beat_interval=3

#
# debug
#
enable_malloc_trace=0
enable_malloc_lpc_trace=0
enable_trace_code=no
enable_debug=no
enable_debug_telnet=no
# /debug

enable_malloc_check=no
with_malloc=smalloc

#
# performance options
#

with_optimize=med
enable_use_pthreads=yes

#no swapping
with_time_to_swap=0
with_time_to_swap_variables=0

# 1/5 of distinct strings
with_htable_size=274566

with_itable_size=16384
# 1/4 of objects
with_otable_size=32768

with_evaluator_stack_size=3000
with_compiler_stack_size=600
with_max_user_trace=120
with_max_trace=125
with_hard_malloc_limit=0

with_max_cost=25000000
with_catch_reserved_cost=150000

with_max_array_size=50000
with_max_mapping_keys=100000
with_max_mapping_size=150000
with_max_bits=20000
with_read_file_max_size=100000

# preallocation
with_min_malloced=671088640
with_min_small_malloced=536870912

favoretti

2009-09-29 13:49

reporter   ~0001349

So far, in both cases executables were linked against:
batmud64:/bat/mudlib/crash-20090923-231937# ldd ldmud
        linux-vdso.so.1 => (0x00007fff3dbff000)
        libnsl.so.1 => /lib/libnsl.so.1 (0x00007f1735710000)
        libm.so.6 => /lib/libm.so.6 (0x00007f173548d000)
        libcrypt.so.1 => /lib/libcrypt.so.1 (0x00007f1735255000)
        libgcrypt.so.11 => /usr/lib/libgcrypt.so.11 (0x00007f1734fee000)
        libmysqlclient.so.15 => /usr/lib/libmysqlclient.so.15 (0x00007f1734be3000)
        libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00007f1734887000)
        libc.so.6 => /lib/libc.so.6 (0x00007f1734534000)
        libgpg-error.so.0 => /usr/lib/libgpg-error.so.0 (0x00007f1735a36000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x00007f1734318000)
        libz.so.1 => /usr/lib/libz.so.1 (0x00007f1734101000)
        libdl.so.2 => /lib/libdl.so.2 (0x00007f1733efd000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f1735928000)

zesstra

2009-09-30 13:52

administrator   ~0001356

2 GB? Mhmm, I'm afraid, I don't know where to upload that.
With enabled optimization I am also sceptical, that it will be worth the effort to upload such big cores as long as there are other possibilities.
Is all that memory actually in use? Until now all muds I know with such a memory consumption had a memory leak somewhere. ;-)

Did that crashes started after switching to the x86_64 architecture or after switching to 3.3.719? Or both?

BTW: a non-related comment to your configuration: a string hash table size >65535 is not different than 65535, it just consumes more memory. This has been changed until now only in 3.5.x. Maybe that work will someday be backported, but for now it still has to tested somewhat more.

favoretti

2009-09-30 15:06

reporter   ~0001369

Well, I suppose I could arrange you an account directly on the box to dig in it with GDB if you want.

Yes, that memory is in use, we are quite some HUGE(tm) MUD out there.
Crashes tarted occuring after both switching to 64 bit and to 3.3.719, it was all in one go, since previous attempts to switch to earlier driver versions were pathetic crash festshows :) This one seems to be stable, apart from these intermittent crashes.

Withregards to leaking memory, I'm not sure we do, it stays stable around 2.2g for most of the day after all objects have been loaded and most players have visited the game.

Thanks for the info about the hashtable size param, will adjust.
What I will do tomorrow is recompile the driver with full debugging options and will run on that hoping that it will crash again. Until that I can provide you access to those cores right on the machine where it runs.

favoretti

2009-10-02 08:10

reporter   ~0001423

Crashed again this morning, unfortunately I haven't recompiled driver with all the debugging yet. But this one is similar to one of the pervious crashes, might give you an idea on where to look.

(gdb) bt
#0 fatal (fmt=0x4cf720 "(free_svalue) Illegal svalue %p type %d\n") at simulate.c:593
0000001 0x000000000043d564 in int_free_svalue (v=0x7f0e6d9dbb30) at interpret.c:1136
0000002 0x000000000044a504 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:8865
0000003 0x00000000004579e7 in apply_low (fun=0x7f0e6d9dbb50, ob=0x7f0e3648b9f0, num_arg=0, b_ign_prot=false, allowRefs=false,
    b_ign_shadows=false) at interpret.c:17442
0000004 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
0000005 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:16645
0000006 0x00000000004579e7 in apply_low (fun=0x7f0e6db09ba8, ob=0x7f0e3648b9f0, num_arg=0, b_ign_prot=false, allowRefs=false,
    b_ign_shadows=false) at interpret.c:17442
0000007 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
0000008 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:16645
0000009 0x0000000000458a0b in int_call_lambda (lsvp=<value optimized out>, num_arg=1, allowRefs=false, external=true) at interpret.c:18447
0000010 0x0000000000458bc3 in v_funcall (sp=0x713100, num_arg=2) at interpret.c:21003
0000011 0x000000000044b621 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:8475
0000012 0x000000000045871c in int_call_lambda (lsvp=0x7e40e0, num_arg=<value optimized out>, allowRefs=false, external=true) at interpret.c:18589
0000013 0x000000000047aa12 in reset_object (ob=0x7f0e3648b9f0, arg=96) at object.c:875
#14 0x00000000004ae1d5 in clone_object (str1=<value optimized out>) at simulate.c:2356
#15 0x00000000004ae421 in f_clone_object (sp=0x7130c0) at simulate.c:4376
#16 0x000000000044ee88 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:8276
#17 0x00000000004ad373 in catch_instruction (flags=0, offset=<value optimized out>, i_sp=0x7e3d50,
    i_pc=0x7f0e6dfa9373 "e\b\002\037\001??g?4", i_fp=<value optimized out>, reserve_cost=150000, i_context=0x0) at simulate.c:455
#18 0x000000000044a9a2 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:9730
#19 0x00000000004579e7 in apply_low (fun=0x7f0e6db11110, ob=0x1c860, num_arg=1, b_ign_prot=false, allowRefs=false, b_ign_shadows=false)
    at interpret.c:17442#20 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
#21 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:16645
#22 0x00000000004579e7 in apply_low (fun=0x7f0e6db09ba8, ob=0x9660, num_arg=0, b_ign_prot=false, allowRefs=false, b_ign_shadows=false)
    at interpret.c:17442
#23 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
#24 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:16645
#25 0x0000000000458a0b in int_call_lambda (lsvp=<value optimized out>, num_arg=1, allowRefs=false, external=true) at interpret.c:18447
#26 0x0000000000458bc3 in v_funcall (sp=0x712fb0, num_arg=2) at interpret.c:21003
#27 0x000000000044b621 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:8475
#28 0x000000000045871c in int_call_lambda (lsvp=0x7e40d0, num_arg=<value optimized out>, allowRefs=false, external=true) at interpret.c:18589
#29 0x000000000047aa12 in reset_object (ob=0x7f0e27dcdc90, arg=80) at object.c:875
#30 0x00000000004aa17d in load_object (lname=<value optimized out>, create_super=false, depth=0, isMasterObj=false, chain=0x0)
    at simulate.c:2144
#31 0x00000000004aa454 in lookfor_object (str=<value optimized out>, bLoad=true) at simulate.c:2412
#32 0x0000000000452cb0 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:16618
#33 0x00000000004ad373 in catch_instruction (flags=0, offset=<value optimized out>, i_sp=0x7e3d50,
    i_pc=0x7f0e6dfb3bec "e\037\006\n\"?f\003t\200\002*?f)\002\037\002n\020\n#\b\025\037\001??,\n$,\003~\020\030\037\006\003u\200\006+e?\n-eg?5", i_fp=<value optimized out>, reserve_cost=150000, i_context=0x0) at simulate.c:455
#34 0x000000000044a9a2 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:9730
#35 0x00000000004579e7 in apply_low (fun=0x7f0e6d9fd3b8, ob=0x1b960, num_arg=1, b_ign_prot=false, allowRefs=false, b_ign_shadows=false)
    at interpret.c:17442
0000036 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
0000037 0x000000000044d623 in eval_instruction (first_instruction=<value optimized out>, initial_sp=<value optimized out>) at interpret.c:16645
0000038 0x00000000004579e7 in apply_low (fun=0x7f0e6da62098, ob=0x7f0e4714efe8, num_arg=0, b_ign_prot=false, allowRefs=false,
    b_ign_shadows=false) at interpret.c:17442
0000039 0x0000000000447584 in int_apply (fun=0x8, ob=0x0, num_arg=1, b_ign_prot=<value optimized out>, b_use_default=true,
    b_ign_shadows=<value optimized out>) at interpret.c:17546
0000040 0x00000000004480cb in sapply_int (fun=0x7f0e6da62098, ob=0x7f0e4714efe8, num_arg=0, b_find_static=72, b_use_default=true)
    at interpret.c:17707
0000041 0x000000000040718c in parse_command (buff=0x7fff9fa06b91 "west", from_efun=false) at actions.c:1162
0000042 0x0000000000408711 in execute_command (str=0x7fff9fa06b91 "west", ob=0x7f0e4714efe8) at actions.c:1333
0000043 0x000000000040f339 in backend () at backend.c:690
0000044 0x0000000000464f36 in main (argc=<value optimized out>, argv=<value optimized out>) at main.c:688


Recompiling driver with debugs now...

favoretti

2009-10-02 08:18

reporter   ~0001424

Last edited: 2009-10-02 08:19

Starting from boot tomorrow will run debugging driver.
When it crashes will let you know :)

In the meantime, if you have even a slightest idea why it crashes based on the traces we have so far (or if you want core access, let me know) - please let me know. We have 500+ players suffering daily :)

zesstra

2009-10-02 09:34

administrator   ~0001425

Phew, the stacks are basically identical. Thats good for a start.
Debugging a memory corruption issue is very hard, therefore we will need access to the cores, yes. (BTW: That should Gnomi as well as me, he has more experience in this and might be needed.) The problem is (as you see), that certain values have been optimized away from the compiler and that makes it harder to analyze the core. Additionally, the debug options from the driver itself which annotate memory blocks with their origin makes life usually again easier. Therefore I am the opinion that you will probably have to suffer at least one more of these crashes.
It would help to know, if this only occurs with 3.3.719 (then it has to be one of the changes there) or if it does only occur on x86_64 architectures. But I see, that this information is not available right now, so there remains only the cores. ;-)
The first and the second backtrace both involve someone going west in some object. During that at least one new object is loaded (probably a room) and some other object is cloned. The crash occurs during the initialization of this clone (create() or whatever you call it). It would help to find out, if that objects were the same in both cases (that means, the same room, where somebody went west). Then we could look at the code and check for unusual thing or try to create a test case.
I don't know if it promises any success, but maybe some of your players remembers going west directly before the crash?

favoretti

2009-10-02 09:38

reporter   ~0001426

Yeah, went in that way of thinking myself already, will try to dig out if someone remembers that.

Withregards to the cores access, let me quickly clear that out with the rest of game administration and I'll get back to you with login details. Would help if you could tell me how to reach you aside of bugtracker, not feeling very happy about leaving login details here :)

favoretti

2009-10-02 10:18

reporter   ~0001427

Aha.

We dug in a bit more. On the LPC side, it seems twice the same stuff triggered the crash:

Last crash:

2009.09.27 15:31:14 Command giver: 'players/malar/areas/outpost/mons/guard_capt#169325'
Last command: 'west'
(free_svalue) Illegal svalue 0xffffffff type 1189027840
Command giver: 'players/malar/areas/outpost/mons/guard_capt#169325'
Last command: 'west'
2009.09.27 15:31:14 Current object was players/malar/areas/outpost/mons/guard_capt#169325
Command giver: 'players/malar/areas/outpost/mons/guard_capt#169325'
Last command: 'west'
2009.09.27 15:31:14 Dump of the call chain:
Command giver: 'players/malar/areas/outpost/mons/guard_capt#169325'
Last command: 'west'
' command_hook' in ' obj/player.c' ('obj/race/p/ent-azgaroth') line 1952
' parser' in ' nroom/room.c' ('players/malar/areas/outpost/1floor/kentry') line 346
' CATCH' in ('players/malar/areas/outpost/1floor/kentry')
<lambda 0x7f961d036729> in 'players/malar/areas/outpost/1floor/kentry.c' ('players/malar/areas/outpost/1floor/kentry') offset 7
'_create_object_hook' in ' secure/master.c' (' secure/master') line 2319
' create' in ' nroom/room.c' ('players/malar/areas/outpost/1floor/kwest') line 85
' reset' in ' nroom/room.c' ('players/malar/areas/outpost/1floor/kwest') line 920
' reset_items' in ' nroom/items.c' ('players/malar/areas/outpost/1floor/kwest') line 462
' CATCH' in ('players/malar/areas/outpost/1floor/kwest')
<lambda 0x7f961d036699> in 'players/malar/areas/outpost/1floor/kwest.c' ('players/malar/areas/outpost/1floor/kwest') offset 7
'_create_object_hook' in ' secure/master.c' (' secure/master') line 2319
' create' in ' obj/monster.c' ('players/malar/areas/outpost/mons/guard_capt#169325') line 240
' extra_create' in 'players/malar/areas/outpost/mons/guard_capt.c' ('players/malar/areas/outpost/mons/guard_capt#169325') line 38
Command giver: 'players/malar/areas/outpost/mons/guard_capt#169325'
Last command: 'west'


Previous crash:
2009.10.02 04:00:27 Command giver: 'players/malar/areas/outpost/mons/guard_capt#527582'
Last command: 'west'
(free_svalue) Illegal svalue 0xffffffff type -1752428544
Command giver: 'players/malar/areas/outpost/mons/guard_capt#527582'
Last command: 'west'
2009.10.02 04:00:27 Current object was players/malar/areas/outpost/mons/guard_capt#527582
Command giver: 'players/malar/areas/outpost/mons/guard_capt#527582'
Last command: 'west'
2009.10.02 04:00:27 Dump of the call chain:
Command giver: 'players/malar/areas/outpost/mons/guard_capt#527582'
Last command: 'west'
' command_hook' in ' obj/player.c' ('obj/race/p/barsoomian-kheldor') line 1952
' parser' in ' nroom/room.c' ('players/malar/areas/outpost/1floor/kentry') line 346
' CATCH' in ('players/malar/areas/outpost/1floor/kentry')
<lambda 0x7f0e6db06729> in 'players/malar/areas/outpost/1floor/kentry.c' ('players/malar/areas/outpost/1floor/kentry') offset 7
'_create_object_hook' in ' secure/master.c' (' secure/master') line 2319
' create' in ' nroom/room.c' ('players/malar/areas/outpost/1floor/kwest') line 85
' reset' in ' nroom/room.c' ('players/malar/areas/outpost/1floor/kwest') line 920
' reset_items' in ' nroom/items.c' ('players/malar/areas/outpost/1floor/kwest') line 462
' CATCH' in ('players/malar/areas/outpost/1floor/kwest')
<lambda 0x7f0e6db06699> in 'players/malar/areas/outpost/1floor/kwest.c' ('players/malar/areas/outpost/1floor/kwest') offset 7
'_create_object_hook' in ' secure/master.c' (' secure/master') line 2319
' create' in ' obj/monster.c' ('players/malar/areas/outpost/mons/guard_capt#527582') line 240
' extra_create' in 'players/malar/areas/outpost/mons/guard_capt.c' ('players/malar/areas/outpost/mons/guard_capt#527582') line 38
Command giver: 'players/malar/areas/outpost/mons/guard_capt#527582'
Last command: 'west'

favoretti

2009-10-02 10:40

reporter   ~0001428

In both cases it crashes in eval_instruction in interpret.c in the piece where it processes default return:

CASE(F_RETURN):

...

        /* The caller might have a yet-unterminated SAVE_ARG_FRAME in
         * effect (this can happen in lambda closures, when the subclosure
         * to compute an efun argument executes a #'return) - undo them.
         */

        while (ap && ap > efp)
        {
            while (sp > ap)
                free_svalue(--sp); <-- this is where we crash
            sp = ap-1;
            ap = sp->u.lvalue;
        }

Crash occurs on attempting to load that guard_capt.c evaluating extra_create() as can be seen from the ldmud.log call trace and on processing the closing bracer, a.k.a. return statement.

fufu

2009-10-02 12:33

manager   ~0001429

The line numbers look odd to me for 3.3.719 - do you have any custom patches in your driver?

favoretti

2009-10-02 13:51

reporter   ~0001432

Yeah, there is a small number, but nothing that would actually break this. If there will be suspicion that it's one of our patches I can disable some of those. Can't disable all of the, however, since it will break the game heavily :)

favoretti

2009-10-02 13:54

reporter   ~0001433

On a sidenote, tried to load that room this boot again.. No crash, so it could be that it's just a coincidence that it crashed twice there, although in a world with 16000+ inside rooms and hardly countable outside rooms it sounds a bit odd.

zesstra

2009-10-03 03:14

administrator   ~0001434

Ok... But even if you think, your patches are completely unrelated, please put them in an archive and upload them here, so we speak about the same source and the same line numbers.
Concerning login details: yes, I can understand that. ;-) You can reach us at our our developer address ldmud-dev@UNItopia.rus.Uni-Stuttgart.DE or contact me at zesstra@zesstra.de.
The place where it crashes (that free_svalue()) is not the place of the root cause. Somehow a malformed svalue was pushed onto the stack (or corrupted while already being there is even more probable). Finding that is the challenge here. A test case whould ease that enormously. Knowing the involved code could help us finding one.
It may be a coincidence, but maybe there is something in there (e.g. a rarely used feature/efun) which can cause problems when certain circumstances are met. On the other hand, your second crash was somewhere else. But it looks also like a corrupted svalue on the stack. Here, the debugging feature you enabled now are useful, because then we get the last 4096 instructions before the crash (well, if that is a call to fatal() at least).

favoretti

2009-10-03 04:27

reporter   ~0001435

Last edited: 2009-10-03 04:38

Yesterday just to be sure, except adding debug to the driver I hacked the patching out of interpret.c and simulate.c.

We used to have our own code profiler built into the driver. However, it wasn't enabled during crashes, so from what I could see, the code was never evaluated. But just to be sure I disabled it.

Now there's not that much hacking left :) Attached.

EDIT: The line numbers after this patch won't match the crashes 1:1, cuz I hacked profiler code out for the debugging version. So in case it crashes again any time soon, we'll have the perfect match between trace and line numbers.

favoretti

2009-10-03 11:20

reporter   ~0001439

Ok, I think I figured a relation to crashes.

Every time we crashed someone fiddled with XML functions (I had xml2 package compiled it). Now I fiddled with those myself and we crashed almost immediately with the following stack trace:

(gdb) bt
#0 0x00000000004ee262 in UNLINK_SMALL_FREE (block=0x7f26f221e158) at smalloc.c:1137
0000001 0x00000000004ee77a in mem_alloc (size=32) at smalloc.c:1545
0000002 0x00000000004f17f1 in xalloc_traced (size=24) at xalloc.c:565
0000003 0x000000000049672a in f_restore_object (sp=0x745bf0) at object.c:8977
0000004 0x0000000000451b3a in eval_instruction (
    first_instruction=0x7f271e875b2b "\037\002\003V\200\003*?f)\003\037\003>n\001\031\b", initial_sp=0x745bd0)
    at interpret.c:8219
0000005 0x00000000004cdae4 in catch_instruction (flags=0, offset=8, i_sp=0x816360,
    i_pc=0x7f271e875b2b "\037\002\003V\200\003*?f)\003\037\003>n\001\031\b", i_fp=0x745b90, reserve_cost=150000, i_context=0x0)
    at simulate.c:453
0000006 0x00000000004541c6 in eval_instruction (first_instruction=0x7f271e875aba "d\001\003?>)\026e?g?", initial_sp=0x745bc0)
    at interpret.c:9652
0000007 0x0000000000467455 in apply_low (fun=0x7f27051119b0, ob=0x7f26d58bf4a0, num_arg=1, b_ign_prot=false, allowRefs=false,
    b_ign_shadows=false) at interpret.c:17207
0000008 0x00000000004675ca in int_apply (fun=0x7f27051119b0, ob=0x7f26d58bf4a0, num_arg=1, b_ign_prot=false, b_use_default=true,
    b_ign_shadows=false) at interpret.c:17285
0000009 0x00000000004634f4 in eval_instruction (first_instruction=0x7f271e8792fa "d\001\002e\037", initial_sp=0x745af0)
    at interpret.c:16528
0000010 0x0000000000467455 in apply_low (fun=0x7f27051173d8, ob=0x7f271e5a3300, num_arg=1, b_ign_prot=false, allowRefs=false,
    b_ign_shadows=false) at interpret.c:17207
0000011 0x00000000004675ca in int_apply (fun=0x7f27051173d8, ob=0x7f271e5a3300, num_arg=1, b_ign_prot=false, b_use_default=true,
    b_ign_shadows=false) at interpret.c:17285
0000012 0x00000000004634f4 in eval_instruction (first_instruction=0x7f271f26aa2a "d\002\n\037", initial_sp=0x745a90)
    at interpret.c:16528
0000013 0x0000000000467455 in apply_low (fun=0x7f270c2cd200, ob=0x7f271f2628e8, num_arg=2, b_ign_prot=false, allowRefs=false,
    b_ign_shadows=false) at interpret.c:17207
#14 0x00000000004675ca in int_apply (fun=0x7f270c2cd200, ob=0x7f271f2628e8, num_arg=2, b_ign_prot=false, b_use_default=true,
    b_ign_shadows=false) at interpret.c:17285
#15 0x00000000004634f4 in eval_instruction (first_instruction=0x7f271c4ac199 "e\n\026\037", initial_sp=0x7459a0)
    at interpret.c:16528
#16 0x00000000004cdae4 in catch_instruction (flags=0, offset=21, i_sp=0x816360, i_pc=0x7f271c4ac199 "e\n\026\037",
    i_fp=0x745970, reserve_cost=150000, i_context=0x0) at simulate.c:453
#17 0x00000000004541c6 in eval_instruction (first_instruction=0x7f271c4abf8a "e\037", initial_sp=0x745810) at interpret.c:9652
#18 0x0000000000468c69 in int_call_lambda (lsvp=0x7cd840, num_arg=3, allowRefs=false, external=true) at interpret.c:18186
#19 0x00000000004696dc in secure_call_lambda (closure=0x7cd840, num_arg=3, external=true) at interpret.c:18560
#20 0x00000000004dd558 in socket_process_events (sock=0x7cd800, events=5) at sockets.c:1128
#21 0x00000000004dce88 in socket_poll () at sockets.c:986
#22 0x000000000040f695 in backend () at backend.c:755
#23 0x00000000004775f6 in main (argc=8, argv=0x7fff4b6dc048) at main.c:688


I recompiled the driver using libiksemel for now... Still running debugging mode in case it's unrelated.

favoretti

2009-10-03 11:38

reporter   ~0001440

Last call on mudside was:
2009.10.03 19:03:32 Caught error: Bad arg 1 to xml_generate(): tag is not an array with 3 elements.
Command giver: 'obj/race/p/coder-favorit'
Environment: 'city/main/boardroom'
Last command: 'call ./guilds_server_xml update_guild_object find_object("nguild/d/mage")'
' command_hook' in ' obj/player.c' ('obj/race/p/coder-favorit') line 1904
' do_command' in ' cmds/cmd_inherit.c' (' cmds/wiz/call') line 37
' CATCH' in (' cmds/wiz/call')
' command' in ' cmds/wiz/call.c' (' cmds/wiz/call') line 99
' CATCH' in (' cmds/wiz/call')
' call_lpc' in 'players/favorit/TMP.c' (' players/favorit/TMP') line 1
'update_guild_object' in 'lib/gameinfo/guilds_server_xml.c' ('lib/gameinfo/guilds_server_xml') line 61
'update_guild_info' in 'lib/gameinfo/guilds_server_xml.c' ('lib/gameinfo/guilds_server_xml') line 116
Command giver: 'obj/race/p/coder-favorit'
Environment: 'city/main/boardroom'
Last command: 'call ./guilds_server_xml update_guild_object find_object("nguild/d/mage")'
2009.10.03 19:03:32 ... execution continues.
Command giver: 'obj/race/p/coder-favorit'
Environment: 'city/main/boardroom'
Last command: 'call ./guilds_server_xml update_guild_object find_object("nguild/d/mage")'

What I did, I fed a 3-d array to xml_generate, which wasn't formed correctly.

zesstra

2009-10-05 08:14

administrator   ~0001458

Mhmm, I had a look at your diff. I wouldn't call 144k "not much", even if you deactivated some of it. That differs substantially from the source we have.

May I ask, what exactly you fed into xml_generate() and what the others did?

BTW: I noticed two things you should check in your diff, while having a short glance (although I may have overlooked something, I had only a few minutes to spare):
- the efun random_integer() won't return [INT_MIN .. INT_MAX] on x86_64 platforms, because your random_integer() returns uint32, but an LPC int has 64 bits on x86_64.
- f_recompile_object(). You seem to replace the blueprint of the object, while keeping the variable block. If the new program differs in variables or sequence of variables, this will cause problems, especially if the new program uses more variables, because it will read garbage beyond the variable block or overwrite stuff there.

favoretti

2009-10-05 09:38

reporter   ~0001462

Basically I fed a huge mapping containing multi-dimensional arrays, while being curious what kinda output it will produce.

What others did - was pretty much twiddling around, generating XML about guild information, etc. I could probably paste some code here, but I don't think it will be largely relevant. There was nothing out of ordinary which, for instance, some code typo won't produce.

([ /* 0000001 */
  0: ({ /* 0000002, size: 1 */
   ({ /* 0000003, size: 2 */
     "background",
     ({ /* 0000004, size: 1 */
       "magical"
     })
   })
 }),
  2: ({ /* 0000005, size: 7 */
   ({ /* 0000006, size: 3 */
     "skill_min",
     "cast_generic",
     6
   }),
   ({ /* 0000007, size: 3 */
     "skill_min",
     "cast_protection",
     12
   }),
   ({ /* 0000008, size: 3 */
     "skill_min",
     "consider",
     15
   }),

...

This will go on for another page or five :)

Thanks for the notices on the diff, I'll have a look. That diff has also been a long-term hacking effort of a gazillion of people so maybe now would be the time to look at it closely again.

favoretti

2009-10-05 09:39

reporter   ~0001463

Oh yeah, and as to 144k, if you notice, there's mostly efuns that are added.
The only hacking into the driver self would be adding ability to bypass shadows, which is rather straightforward and otherwise harmless.

The biggest part of the diff is sockets implementation.

zesstra

2009-10-05 10:25

administrator   ~0001465

It doesn't really matter, if it is only efuns or not. Any efun may corrupt memory (.e.g. your mentioned xml_generate()), they are not separated from the rest of the driver. I wrote some efuns which wrote somewhere they shouldn't the first time I tested them... ;-) But sometimes a memory corruption in an efun takes a long time to be noticed.
Any piece of code changing data may be the root cause of an avalanche of small or big changes leading in the end to a crash.

Concerning your last crash: if you believe, that it is caused by some call to xml_generate(), please try to reproduce it. If that does work, create a minimal test case (just enough code to trigger the problem). Otherwise, we will never make progress finding the real problem. I can't just try random arguments to xml_generate() until I find some problem (especially, because the crash by be platform/architecture-specific). ;-)

favoretti

2009-10-12 05:40

reporter   ~0001518

Just crashed again:

#0 0x0000000000422bf0 in add_flush_entry (ip=0x7fbc722ad0a8) at comm.c:2118
0000001 0x0000000000422b74 in add_message (fmt=0x421e27 "UH\211?H\201??\r") at comm.c:2060
0000002 0x00000000004d48b7 in print_svalue (arg=0x7453d0) at simulate.c:4322
0000003 0x00000000004d5913 in f_write (sp=0x7453d0) at simulate.c:4955
0000004 0x00000000004517ea in eval_instruction (
    first_instruction=0x7fbcb4181aba "d\001\004\n\017\003T_e\n\017\022\005\022<.\a\b\017f_\037", initial_sp=0x7453c0)
    at interpret.c:8219
0000005 0x0000000000466d3e in apply_low (fun=0x7fbc9e3bd0e8, ob=0x7fbc7226db08, num_arg=0, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17093
0000006 0x000000000046727a in int_apply (fun=0x7fbc9e3bd0e8, ob=0x7fbc7226db08, num_arg=0, b_ign_prot=false,
    b_use_default=true, b_ign_shadows=false) at interpret.c:17285
0000007 0x00000000004631a4 in eval_instruction (first_instruction=0x7fbcb4a7bb92 "\037\001{$+\037\001o\016eeg?",
    initial_sp=0x7452e0) at interpret.c:16528
0000008 0x0000000000466d3e in apply_low (fun=0x7fbc9a559590, ob=0x7fbc72c27210, num_arg=2, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17093
0000009 0x000000000046727a in int_apply (fun=0x7fbc9a559590, ob=0x7fbc72c27210, num_arg=2, b_ign_prot=false,
    b_use_default=true, b_ign_shadows=false) at interpret.c:17285
0000010 0x00000000004631a4 in eval_instruction (first_instruction=0x7fbcb4181e12 "d\001\005\037", initial_sp=0x745220)
    at interpret.c:16528
0000011 0x0000000000466d3e in apply_low (fun=0x7fbc9a5564f8, ob=0x7fbc72c26b60, num_arg=1, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17093
0000012 0x000000000046727a in int_apply (fun=0x7fbc9a5564f8, ob=0x7fbc72c26b60, num_arg=1, b_ign_prot=false,
    b_use_default=true, b_ign_shadows=false) at interpret.c:17285
0000013 0x0000000000467673 in sapply_int (fun=0x7fbc9a5564f8, ob=0x7fbc72c26b60, num_arg=1, b_find_static=false,
    b_use_default=true) at interpret.c:17446
#14 0x00000000004d43cf in execute_callback (cb=0x73d948, nargs=1, keep=false, toplevel=true) at simulate.c:4075
#15 0x000000000042638f in call_input_to (i=0x7fbc722ad0a8, str=0x7fffe1266540 "use ref at mahogany in dw",
    it=0x7fbcc2db05a8) at comm.c:4127
#16 0x000000000042652d in call_function_interactive (i=0x7fbc722ad0a8, str=0x7fffe1266540 "use ref at mahogany in dw")
    at comm.c:4212
#17 0x000000000040f1fe in backend () at backend.c:679
#18 0x00000000004772a6 in main (argc=8, argv=0x7fffe1267f58) at main.c:688

zesstra

2009-10-16 02:35

administrator   ~0001524

The stack traces alone give not enough information, so we need access to core + binary + source. If you still consider giving us access to your machine for that purpose, the easiest would be to give you Gnomis and my public ssh key which you temporarily add to your authorized_keys.
Otherwise, you need to pack the stuff and put it somewhere for download. Although then we might have problems having the right gdb for your platform+architecture.

zesstra

2009-10-16 03:20

administrator   ~0001525

BTW: You might want to have a look at your garbage collector. You added some socket stuff. During the early phase of the GC run you call clear_socket_refs() to clear the refcounters, but later you don't call count_socket_refs(), but clear_socket_refs() again. That will lead to uncounted references and later to memory that might be used twice (because the GC will mark memory as free which is not referenced). So, if you ever have a GC run while having socket callbacks, your system will be unstable.

Gnomi

2009-10-18 07:52

manager   ~0001528

We found a possible cause in the xml2 functions. I committed a fix in 3.3/trunk as r2770. Maybe you could try it out?

favoretti

2009-10-18 08:42

reporter   ~0001529

Will do. Although last 2 crashes occured with libiksemel, since I was trying to exclude the possible reason of crashing.

favoretti

2009-10-18 08:49

reporter   ~0001530

@Zesstra: that's a brilliant find! Thanks. That sounds like something I overlooked/mistyped while manually transferring some failed hunks...

Could might as well be responsible for those crashes of late..

We're gonna try to run test version of mud under valgrind.. Maybe that will give us some pointers.

Withregards to access to cores and source... There's some commotion about accessing internal net here, Would it be enough if I give you some VM off-site where no mudlib will be present, but just those cores, source and logs?

zesstra

2009-10-18 08:55

administrator   ~0001531

Yes, that will be sufficient. But don't forget the correct binary as well and gdb. ;-)

favoretti

2009-10-18 10:03

reporter   ~0001532

BTW, something unrelated, but maybe nice to fix just in case.
While waiting on startup of ldmud under valgrind, this popped out:
==19441== Conditional jump or move depends on uninitialised value(s)
==19441== at 0x43BA54: e_sscanf (efuns.c:4134)
==19441== by 0x4539E1: eval_instruction (interpret.c:9516)
==19441== by 0x467104: apply_low (interpret.c:17207)
==19441== by 0x467279: int_apply (interpret.c:17285)
==19441== by 0x4631A3: eval_instruction (interpret.c:16528)
==19441== by 0x467104: apply_low (interpret.c:17207)
==19441== by 0x467279: int_apply (interpret.c:17285)
==19441== by 0x4631A3: eval_instruction (interpret.c:16528)
==19441== by 0x468918: int_call_lambda (interpret.c:18186)
==19441== by 0x46B9ED: v_funcall (interpret.c:20742)
==19441== by 0x451F74: eval_instruction (interpret.c:8418)
==19441== by 0x468CEF: int_call_lambda (interpret.c:18328)

He's generally right, checking for value initialization is a Good Thing(tm) ;-)

I'll set up the VM for you in a few hours and will place those crashcollections there. Our crashchecker autocopies the current binary next to core, so you'll have precise binaries with each crashdump.

favoretti

2009-10-18 15:59

reporter   ~0001535

Ok, if you could send me your ssh keys, I'll add them to the machine with all you need to have for analyzing crashdumps.

Details of the machine: 89.105.199.206, user bat.
You'll have full sudo rights, that VM is totally yours, feel free to install any tools you might find missing.

It's identical to the machine we're using s/w-wise.

Source is in /home/bat/ldmud-src.

Thanks for looking into it!

Gnomi

2009-10-20 04:07

manager   ~0001537

The last two crashes (in add_flush_entry) are unrelated to the previous ones and result from inconsistencies in the list of dirty players.

favoretti

2009-10-21 00:53

reporter   ~0001538

Yeah, that I got as well, question is, how come it got dirty...

Gnomi

2009-10-21 01:05

manager   ~0001539

Players get dirty all the time. And normally when add_message(message_flush); is called they get clean again. And exec() is counting on that. But this time too many messages were sent to a particular player (a login object), so that its socket buffer got full and messages had to be discarded. This interactive object won't get clean until the master object was notified. And that was something exec() didn't expect and so it left some non-interactive object (the former interactive object) in the list of dirty players which lead to the crash.

It can be fixed quite easily and I'll have a fix for that shortly.

Gnomi

2009-10-21 03:33

manager   ~0001540

I attached a patch for 3.3 and committed a patch for 3.5 as r2771.

favoretti

2009-10-21 06:45

reporter   ~0001541

Thanks! Will patch it up right away.
Any thoughts on the previous crashes though?

Gnomi

2009-10-21 08:03

manager   ~0001542

Last edited: 2009-10-21 08:03

The first one (20090927-153151) has the same cause as the last two ones (exec() on dirty players).

The next two crashes (20090927-153151 and 20091002-040121) I don't know. They're hard to debug (optimized and no TRACE_CODE). They both occurred when 'players/malar/areas/outpost/mons/guard_capt' was loaded and the driver was returning from the function extra_create and cleaned up the stack. So somebody put an illegal value on the stack but who or how I cannot see in the core.

The fourth one (20091003-190417) had probably something to do with libxml2 processing, which we already fixed. It shows memory corruption and has the following call trace:
    lib/finger/finger_ob#266918->load_character
    lib/finger/server->query_web_player_data
    lib/webgw/modules/xmlfinger->request
So this one is fixed.

favoretti

2009-10-21 09:00

reporter   ~0001543

Thanks! I'd say we mark this as resolved, and wait if something else occures. If so, we keep running in debug mode, so that we can produce usable cores and I'll just log a new one.

Guys, I can't express my thanks for bearing with me and helping us solving it. You made a ton of players very happy :)

favoretti

2009-10-29 14:53

reporter   ~0001572

Aaaand, we have a winner:

Core was generated by `/bat/bin/ldmud -m /bat/mudlib --pidfile /bat/mudlib/ldmud.pid --debug-file /bat'.
Program terminated with signal 8, Arithmetic exception.
[New process 23645]
#0 0x00000000004cdd27 in dump_core () at simulate.c:591
591 *((char*)0) = 0/a;
(gdb) bt
#0 0x00000000004cdd27 in dump_core () at simulate.c:591
0000001 0x00000000004cdc5e in fatal (fmt=0x4fe1e8 "'illegal' instruction encountered.\n") at simulate.c:653
0000002 0x00000000004513e0 in eval_instruction (first_instruction=0x7f828ab2fef2 "", initial_sp=0x745a40)
    at interpret.c:8103
0000003 0x00000000004670d1 in apply_low (fun=0x7f82d7763338, ob=0x7f828ab3fa70, num_arg=0, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17207
0000004 0x0000000000467246 in int_apply (fun=0x7f82d7763338, ob=0x7f828ab3fa70, num_arg=0, b_ign_prot=false,
    b_use_default=true, b_ign_shadows=false) at interpret.c:17285
0000005 0x0000000000463170 in eval_instruction (
    first_instruction=0x7f82d7bef608 "e?\n??fn\006\020\200\032*m\004\017\200\032*?f>\200\033+\037\032(\002\037\033n\a\017{i+\037", initial_sp=0x745880) at interpret.c:16528
0000006 0x00000000004cd6c8 in catch_instruction (flags=0, offset=19, i_sp=0x815d60,
    i_pc=0x7f82d7bef608 "e?\n??fn\006\020\200\032*m\004\017\200\032*?f>\200\033+\037\032(\002\037\033n\a\017{i+\037",
    i_fp=0x745680, reserve_cost=150000, i_context=0x0) at simulate.c:453
0000007 0x0000000000453e42 in eval_instruction (first_instruction=0x7f82d7c58a12 "d\004\002e\037", initial_sp=0x745660)
    at interpret.c:9652
0000008 0x0000000000466d0a in apply_low (fun=0x7f82d7787898, ob=0x7f828ab3fa70, num_arg=2, b_ign_prot=false,
    allowRefs=false, b_ign_shadows=false) at interpret.c:17093
0000009 0x0000000000467246 in int_apply (fun=0x7f82d7787898, ob=0x7f828ab3fa70, num_arg=2, b_ign_prot=false,
    b_use_default=true, b_ign_shadows=false) at interpret.c:17285
0000010 0x0000000000463170 in eval_instruction (first_instruction=0x7f82df49a7f2 "d", initial_sp=0x745200)
    at interpret.c:16528
0000011 0x0000000000469562 in call_function (progp=0x7f82df49a310, fx=18) at interpret.c:18650
0000012 0x000000000044a660 in call_heart_beat () at heartbeat.c:289
0000013 0x000000000040f2f5 in backend () at backend.c:732
#14 0x0000000000477272 in main (argc=8, argv=0x7fff09864498) at main.c:688


Uploading cores to the same vm again.

This time it's again that Malar's area. All it does it puts some objects into arrays and then removes arrays with

array -= ({ 0 });

Seems it somehow screws something up. If need be I'll upload LPC code of the relevant pieces.

Appreciate a lot if you could have a glance.

Thanks!!

favoretti

2009-11-03 16:44

reporter   ~0001594

Guys, any luck? Did you have a chance to have a look yet? Thanks!

favoretti

2009-11-03 16:51

reporter   ~0001595

Actually to elaborate a bit more on what the LPC code does.
That array contains object references. On certain occasions LPC code fires
array -= ({ 0 }) to remove object references to objects that have been destructed.

It also happens in second_life() of monsters, so somewhere just before object gets destructed.

We tried to reproduce it under valgrind, but couldn't find anything even remotely useful. But we thought that maybe explaining this to you will light the bulb somewhere :)

Gnomi

2009-11-04 03:51

manager   ~0001598

I had a first look and didn't found anything that could be the cause. But I hadn't enough time to look deeper so far. In the next few days I shall have enough time to do that.

Gnomi

2009-11-05 03:31

manager   ~0001605

I had another look yesterday. I'm I right that second_life() in /players/malar/areas/outpost/mons/veteran_guard1.c just contains a return; (or nothing at all)?

It seems to me that 2 bytes of the function were overwritten or wrongly written (the first four bytes consist of 00 19 00 80, but should have been 00 00 19 <whatever>). But at this time I have no idea how this could have happened.

Which LPC code did you refer to in comment 0000683:0001595 (about removing destructed objects from an array)? Could we have a look at the LPC code from Malar's area?

favoretti

2009-11-05 03:37

reporter   ~0001606

Second life from the mob:
second_life()
{
        MSERV->remove(this_object());
}

MSERV:

FILE /players/malar/areas/outpost/monster_server.c 548 bytes, 42 lines.
---------------------------------------------------------------------------
// Monster server for outpost
// Malar

object *guards = ({ });

add(object mons)
{
  guards+= ({ mons });
}

get()
{
  update();
  if (sizeof(guards))
    {
      object mons = guards[0];
      guards= guards[1..];
      mons->set_aggressive(1);
      return mons;
    }
  else return 0;
}

remove(object ob)
{
  if(member_array(ob, guards) != -1) guards -= ({ ob });
  update();
  return 1;
}

query_mons()
{
  update();
  write(guards);
  return guards;
}

update()
{
  guards-=({ 0 });
  return 1;
}
EOF

Gnomi

2009-11-05 05:01

manager   ~0001607

Then there's something badly wrong with the program structure. As I read the bytecode, second_life() is the last function in veteran_guard1.c. And there are only 8 bytes left in the bytecode block (afterwards comes the function lookup table, which seems fine), such an call_other needs 12 bytes.

favoretti

2009-11-05 05:24

reporter   ~0001608

Right... Any ideas on the cause?

Gnomi

2009-11-06 08:06

manager   ~0001614

None whatsoever.

The whole program structure is consistent (malloc size, program size, pointers to the several tables of the program) in this regard, that there are only 8 bytes left for this particular function. So if there really should have been a call_other, then either the LPC compiler aborted the compilation at that function (and returned the compiled program nevertheless) or removed some bytes from the program at the end of the compilation. The first possibility I cannot image. And for the second one: The compiler does remove some opcodes during the compilation, but I went over these places and haven't found anything wrong with them. And it should be fairly reproducible. Did you try to call second_life on it again?

Could I have a look on veteran_guard1.c?

favoretti

2009-11-07 09:11

reporter   ~0001615

FILE /players/malar/areas/outpost/mons/veteran_guard1.c 1246 bytes, 37 lines.
---------------------------------------------------------------------------
#define MSERV "/players/malar/areas/outpost/monster_server"
inherit "obj/monster";
inherit "/players/shardik/monster/renaming_limbs";
extra_create ()
{
  set_name ("veteran guard");
  set_short ("an old veteran guard");
  set_long (line_wrap("This old elf is a typical elven soldier. Proud of"+
                      " his art and sure in his swordsmanship he stands tall"+
                      " and secure. You are certain this man knows how to"+
                      " fight. At least he looks like he knows. He has a fist"+
                      " insignia on his breastplate."));
  set_id (({"elf","guard","elite guard","guard with insignia",
                "elven guard","an elven guard",
                "guard with a flaming fist insignia"}));
  set_hunt_range(5);
  set_al(500);
  set_defender(1);
  set_level(21);
  set_wimpy(0);
  set_aggressive(0);
  set_gender(1);
  set_race("elf");
  set_skill("cleave",80,"attacker","while_fighting");

  set_skill_chance("dodge",70);
  set_skill_chance("negate offhand penalty",80);
  rename_limbs( ({"right hand", 1, "fist", "left hand", 1, "fist",
                      "right foot", 0, "kick"}) );
  add_random_weapon("random_katana");
  if(file_name() != base_name()) MSERV->add(this_object());
}

second_life()
{
        MSERV->remove(this_object());
}

Gnomi

2009-11-10 14:37

manager   ~0001618

The patch against crashes because of dirty users was committed to 3.3 as r2806.

favoretti

2009-11-10 14:39

reporter   ~0001619

Cool. Any ideas on the last crash, though? :-P Sorry if I become annoying :)

Gnomi

2009-11-11 04:20

manager   ~0001621

I can confirm that the compiler did compile the second_life() function (because there is a string for "remove" in the program's string table). I have searched for the missing bytecodes and found veteran_guard3.c seems to be identical to veteran_guard1.c. Can you confirm that both files are the same?

favoretti

2009-11-11 04:32

reporter   ~0001622

Pretty much, yeah.

favorit@batmud64:/bat/mudlib/players/malar/areas/outpost/mons$ cat veteran_guard3.c
#define MSERV "/players/malar/areas/outpost/monster_server"
inherit "obj/monster";
inherit "/players/shardik/monster/renaming_limbs";
extra_create ()
{
  set_name ("veteran guard");
  set_short ("a tall elven guard with a fist insignia");
  set_long (line_wrap("This tall guard has seen many battles. He is one of"+
              " the veteran guards in the outpost."+
              " He is vigilant and alert. He wears a thin armour of"+
              " elven chain and a green tunica on top of that with"+
              " a fist insignia."));
  set_id (({"elf","guard","elite guard","guard with insignia",
        "elven guard","an elven guard","guard with a flaming fist"+
        " insignia"}));
  set_hunt_range(5);
    set_al(500);
    set_defender(1);
    set_level(21);
    set_wimpy(0);
    set_aggressive(0);
    set_gender(1);
    set_race("elf");
  set_skill("cleave",80,"attacker","while_fighting");
  
  set_skill_chance("dodge",70);
  set_skill_chance("negate offhand penalty",80);
    rename_limbs( ({"right hand", 1, "fist", "left hand", 1, "fist", "right foot", 0, "kick"}) );
    add_random_weapon("random_katana");
    if(file_name() != base_name()) MSERV->add(this_object());
}

second_life()
{
    MSERV->remove(this_object());
}

Gnomi

2009-11-11 06:46

manager   ~0001623

I'm still out of ideas. We have two nearly identical files which give identical programs with identical bytecodes (except the string content which doesn't matter because it's stored in the global string table, not in the program). But one of them is missing 10 bytes before the final and automatic 'return' bytecode in second_life(), while the other is fine.

favoretti

2009-11-11 06:54

reporter   ~0001624

Heh, indeed. Why that happens is beyond my comprehension. So far it's been 10 days stable, if it will ever crash again I hope a new crash dump will give more ideas.

Gnomi

2009-12-22 09:22

manager   ~0001666

And we've found the cause for the last one. Wildcat could reproduce this bug in 0000709, so we finally tracked that one down. The bugfix was committed as r2809. So I'm now closing this bug.

Issue History

Date Modified Username Field Change
2009-09-27 15:16 favoretti New Issue
2009-09-27 16:36 zesstra Note Added: 0001318
2009-09-27 16:36 zesstra Status new => acknowledged
2009-09-29 12:48 favoretti Note Added: 0001347
2009-09-29 13:45 favoretti File Added: ldmud-crash-20090923.tar.gz
2009-09-29 13:46 favoretti File Added: ldmud-crash-20090927.tar.gz
2009-09-29 13:48 favoretti Note Added: 0001348
2009-09-29 13:49 favoretti Note Added: 0001349
2009-09-30 13:52 zesstra Note Added: 0001356
2009-09-30 15:06 favoretti Note Added: 0001369
2009-10-02 08:10 favoretti Note Added: 0001423
2009-10-02 08:18 favoretti Note Added: 0001424
2009-10-02 08:19 favoretti Note Edited: 0001424
2009-10-02 09:34 zesstra Note Added: 0001425
2009-10-02 09:38 favoretti Note Added: 0001426
2009-10-02 10:18 favoretti Note Added: 0001427
2009-10-02 10:40 favoretti Note Added: 0001428
2009-10-02 12:33 fufu Note Added: 0001429
2009-10-02 13:51 favoretti Note Added: 0001432
2009-10-02 13:54 favoretti Note Added: 0001433
2009-10-03 03:14 zesstra Note Added: 0001434
2009-10-03 04:27 favoretti Note Added: 0001435
2009-10-03 04:28 favoretti File Added: bat.custom.diff
2009-10-03 04:38 favoretti Note Edited: 0001435
2009-10-03 11:20 favoretti Note Added: 0001439
2009-10-03 11:38 favoretti Note Added: 0001440
2009-10-05 08:14 zesstra Note Added: 0001458
2009-10-05 09:38 favoretti Note Added: 0001462
2009-10-05 09:39 favoretti Note Added: 0001463
2009-10-05 10:25 zesstra Note Added: 0001465
2009-10-12 05:40 favoretti Note Added: 0001518
2009-10-16 02:35 zesstra Note Added: 0001524
2009-10-16 03:20 zesstra Note Added: 0001525
2009-10-18 07:52 Gnomi Note Added: 0001528
2009-10-18 08:42 favoretti Note Added: 0001529
2009-10-18 08:49 favoretti Note Added: 0001530
2009-10-18 08:55 zesstra Note Added: 0001531
2009-10-18 10:03 favoretti Note Added: 0001532
2009-10-18 15:59 favoretti Note Added: 0001535
2009-10-20 04:07 Gnomi Note Added: 0001537
2009-10-20 04:07 Gnomi Assigned To => Gnomi
2009-10-20 04:07 Gnomi Status acknowledged => confirmed
2009-10-21 00:53 favoretti Note Added: 0001538
2009-10-21 01:05 Gnomi Note Added: 0001539
2009-10-21 03:32 Gnomi File Added: bug683.diff
2009-10-21 03:33 Gnomi Note Added: 0001540
2009-10-21 06:45 favoretti Note Added: 0001541
2009-10-21 08:03 Gnomi Note Added: 0001542
2009-10-21 08:03 Gnomi Note Edited: 0001542
2009-10-21 09:00 favoretti Note Added: 0001543
2009-10-29 14:53 favoretti Note Added: 0001572
2009-11-03 16:44 favoretti Note Added: 0001594
2009-11-03 16:51 favoretti Note Added: 0001595
2009-11-04 03:51 Gnomi Note Added: 0001598
2009-11-05 03:31 Gnomi Note Added: 0001605
2009-11-05 03:37 favoretti Note Added: 0001606
2009-11-05 05:01 Gnomi Note Added: 0001607
2009-11-05 05:24 favoretti Note Added: 0001608
2009-11-06 08:06 Gnomi Note Added: 0001614
2009-11-07 09:11 favoretti Note Added: 0001615
2009-11-10 14:37 Gnomi Note Added: 0001618
2009-11-10 14:39 favoretti Note Added: 0001619
2009-11-11 04:20 Gnomi Note Added: 0001621
2009-11-11 04:32 favoretti Note Added: 0001622
2009-11-11 06:46 Gnomi Note Added: 0001623
2009-11-11 06:54 favoretti Note Added: 0001624
2009-12-22 09:18 Gnomi Project LDMud => LDMud 3.3
2009-12-22 09:22 Gnomi Note Added: 0001666
2009-12-22 09:22 Gnomi Status confirmed => resolved
2009-12-22 09:22 Gnomi Fixed in Version => 3.3.720
2009-12-22 09:22 Gnomi Resolution open => fixed
2010-11-16 09:42 Gnomi Source_changeset_attached => ldmud.git master 0d53a0bf
2010-11-16 09:42 Gnomi Source_changeset_attached => ldmud.git master-3.3 9572f9cd
2011-02-23 22:22 zesstra Target Version => 3.3.720
2018-01-29 18:59 Gnomi Source_changeset_attached => ldmud.git master 0d53a0bf
2018-01-29 18:59 Gnomi Source_changeset_attached => ldmud.git master-3.3 9572f9cd
2018-01-29 21:57 Gnomi Source_changeset_attached => ldmud.git master 0d53a0bf
2018-01-29 21:57 Gnomi Source_changeset_attached => ldmud.git master-3.3 9572f9cd