Support clients using CR+NUL to signal EOL
[mudpy.git] / mudpy / misc.py
index 3cd8e64..4e43353 100644 (file)
@@ -64,8 +64,9 @@ class Element:
 
     def reload(self):
         """Create a new element and replace this one."""
-        Element(self.key, self.universe, self.origin)
-        del(self)
+        args = (self.key, self.universe, self.origin)
+        self.destroy()
+        Element(*args)
 
     def destroy(self):
         """Remove an element from the universe and destroy it."""
@@ -363,23 +364,9 @@ class Universe:
         # it's possible for this to enter before logging configuration is read
         pending_loglines = []
 
-        # the files dict must exist and filename needs to be read-only
-        if not hasattr(
-           self, "files"
-           ) or not (
-            self.filename in self.files and self.files[
-                self.filename
-            ].is_writeable()
-        ):
-
-            # clear out all read-only files
-            if hasattr(self, "files"):
-                for data_filename in list(self.files.keys()):
-                    if not self.files[data_filename].is_writeable():
-                        del self.files[data_filename]
-
-            # start loading from the initial file
-            mudpy.data.Data(self.filename, self)
+        # start populating the (re)files dict from the base config
+        self.files = {}
+        mudpy.data.Data(self.filename, self)
 
         # load default storage locations for groups
         if hasattr(self, "contents") and "mudpy.filing" in self.contents:
@@ -404,17 +391,6 @@ class Universe:
             if user.avatar in inactive_avatars:
                 inactive_avatars.remove(user.avatar)
 
-        # go through all elements to clear out inactive avatar locations
-        for element in self.contents.values():
-            area = element.get("location")
-            if element in inactive_avatars and area:
-                if area in self.contents and element.key in self.contents[
-                   area
-                   ].contents:
-                    del self.contents[area].contents[element.key]
-                element.set("default_location", area)
-                element.remove_facet("location")
-
         # another pass to straighten out all the element contents
         for element in self.contents.values():
             element.update_location()
@@ -584,26 +560,30 @@ class User:
     def reload(self):
         """Save, load a new user and relocate the connection."""
 
+        # copy old attributes
+        attributes = self.__dict__
+
         # get out of the list
         self.remove()
 
+        # get rid of the old user object
+        del(self)
+
         # create a new user object
         new_user = User()
 
         # set everything equivalent
-        for attribute in vars(self).keys():
-            exec("new_user." + attribute + " = self." + attribute)
+        new_user.__dict__ = attributes
 
         # the avatar needs a new owner
         if new_user.avatar:
+            new_user.account = universe.contents[new_user.account.key]
+            new_user.avatar = universe.contents[new_user.avatar.key]
             new_user.avatar.owner = new_user
 
         # add it to the list
         universe.userlist.append(new_user)
 
-        # get rid of the old user object
-        del(self)
-
     def replace_old_connections(self):
         """Disconnect active users with the same name."""
 
@@ -863,11 +843,15 @@ class User:
             mudpy.telnet.negotiate_telnet_options(self)
 
             # separate multiple input lines
-            new_input_lines = self.partial_input.split(b"\n")
+            new_input_lines = self.partial_input.split(b"\r\0")
+            if len(new_input_lines) == 1:
+                new_input_lines = new_input_lines[0].split(b"\r\n")
 
             # if input doesn't end in a newline, replace the
             # held partial input with the last line of it
-            if not self.partial_input.endswith(b"\n"):
+            if not (
+                    self.partial_input.endswith(b"\r\0") or
+                    self.partial_input.endswith(b"\r\n")):
                 self.partial_input = new_input_lines.pop()
 
             # otherwise, chop off the extra null input and reset
@@ -1414,12 +1398,13 @@ def on_pulse():
 
 def reload_data():
     """Reload all relevant objects."""
-    for user in universe.userlist[:]:
-        user.reload()
-    for element in universe.contents.values():
-        if element.origin.is_writeable():
-            element.reload()
+    universe.save()
+    old_userlist = universe.userlist[:]
+    for element in list(universe.contents.values()):
+        element.destroy()
     universe.load()
+    for user in old_userlist:
+        user.reload()
 
 
 def check_for_connection(listening_socket):