Repository: evennia/evennia

Issue:
Wilderness.py item manifest breaks when objects are deleted
#### Brief summary of issue / Description of requested feature:
When an object is placed into a WildernessRoom and deleted, it breaks the script's item manifest (WildernessScript.itemcoordinates).

This breaks a few things, including allowing the player to enter some locked WildernessExits.

I'll submit a PR then link to this issue.

#### Steps to reproduce the issue / Reasons for adding feature:

1.  Follow the example in contrib.wilderness to set up the pyramid example in a sample game
2.  When in the pyramid, `@create/drop` an object
3.  `@del` the object
4. Walk out of the room and then re-enter it

Further steps:
5. `@reload` the server and watch the log

#### Error output / Expected result of feature
Traceback occurs after step 4
Similar traceback in the log after step 5

These errors interrupt important functionality

#### Extra information, such as Evennia revision/repo/branch, operating system and ideas for how to solve / implement:

Server reload error:
[::] Traceback (most recent call last):
[::]   File "e:\muddev\evennia\evennia\scripts\scripts.py", line 325, in start
[::]     self.at_start()
[::]   File "e:\muddev\evennia\evennia\contrib\wilderness.py", line 256, in at_start
[::]     item.ndb.wilderness = self
[::] AttributeError: 'NoneType' object has no attribute 'ndb'

Entering coordinates with a previously deleted object:
[::] Traceback (most recent call last):
[::]   File "e:\muddev\evennia\evennia\commands\cmdhandler.py", line 509, in _run_command
[::]     ret = yield cmd.func()
[::]   File "e:\muddev\evennia\evennia\objects\objects.py", line 1683, in func
[::]     self.obj.at_traverse(self.caller, self.obj.destination)
[::]   File "e:\muddev\evennia\evennia\contrib\wilderness.py", line 696, in at_traverse
[::]     self.location.wilderness.move_obj(traversing_object, new_coordinates)
[::]   File "e:\muddev\evennia\evennia\contrib\wilderness.py", line 360, in move_obj
[::]     room.set_active_coordinates(new_coordinates, obj)
[::]   File "e:\muddev\evennia\evennia\contrib\wilderness.py", line 567, in set_active_coordinates
[::]     item.location = self
[::] AttributeError: 'NoneType' object has no attribute 'location'



Discussion:

Resolving patch:
diff --git a/evennia/contrib/wilderness.py b/evennia/contrib/wilderness.py
--- a/evennia/contrib/wilderness.py
+++ b/evennia/contrib/wilderness.py
@@ -88,7 +88,7 @@ def get_location_name(self, coordinates):
         def at_prepare_room(self, coordinates, caller, room):
             "Any other changes done to the room before showing it"
             x, y = coordinates
-            desc = "This is a room in the pyramid.
+            desc = "This is a room in the pyramid."
             if y == 3 :
                 desc = "You can see far and wide from the top of the pyramid."
             room.db.desc = desc
@@ -157,7 +157,7 @@ def enter_wilderness(obj, coordinates=(0, 0), name="default"):
             default one
 
     Returns:
-        bool: True if obj succesfully moved into the wilderness.
+        bool: True if obj successfully moved into the wilderness.
     """
     if not WildernessScript.objects.filter(db_key=name).exists():
         return False
@@ -253,6 +253,11 @@ def at_start(self):
             room.ndb.wildernessscript = self
             room.ndb.active_coordinates = coordinates
         for item in self.db.itemcoordinates.keys():
+            # Items deleted from the wilderness leave None type 'ghosts'
+            # that must be cleaned out
+            if item is None:
+                del self.db.itemcoordinates[item]
+                continue
             item.ndb.wilderness = self
 
     def is_valid_coordinates(self, coordinates):
@@ -298,6 +303,11 @@ def get_objs_at_coordinates(self, coordinates):
         """
         result = []
         for item, item_coordinates in self.itemcoordinates.items():
+            # Items deleted from the wilderness leave None type 'ghosts'
+            # that must be cleaned out
+            if item is None:
+                del self.db.itemcoordinates[item]
+                continue
             if coordinates == item_coordinates:
                 result.append(item)
         return result


Test patch: