Imported from archive.
authorJeremy Stanley <fungi@yuggoth.org>
Thu, 24 Nov 2005 00:06:25 +0000 (00:06 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Thu, 24 Nov 2005 00:06:25 +0000 (00:06 +0000)
* mudpy.py (Element.can_run, User.can_run): Moved this method from
the User class to the Element class, allowing for future
flexibility.
(Element.new_event, Universe.get_time): Began adding framework for
event elements and queues.
(command_create, command_delete, command_destroy, command_error)
(command_halt, command_help, command_look, command_move)
(command_quit, command_reload, command_say, command_set)
(command_show): Adjusted commands and messaging to be relative to an
actor element instead of a user object.

command
example/second_square/prop
mudpy.py

diff --git a/command b/command
index a789137..907335a 100644 (file)
--- a/command
+++ b/command
@@ -2,69 +2,69 @@
 read_only = yes
 
 [command:create]
 read_only = yes
 
 [command:create]
-action = command_create(user, parameters)
+action = command_create(actor, parameters)
 administrative = yes
 description = Create a new element in the universe.
 help = Ways to create an element:$(eol)$(eol)   create actor:fred$(eol)   create other:garply foo/bar/baz
 
 [command:delete]
 administrative = yes
 description = Create a new element in the universe.
 help = Ways to create an element:$(eol)$(eol)   create actor:fred$(eol)   create other:garply foo/bar/baz
 
 [command:delete]
-action = command_delete(user, parameters)
+action = command_delete(actor, parameters)
 administrative = yes
 description = Delete an existing facet from an element.
 help = You can delete any facet of an element as follows:$(eol)$(eol)   delete location:boardroom terrain
 
 [command:destroy]
 administrative = yes
 description = Delete an existing facet from an element.
 help = You can delete any facet of an element as follows:$(eol)$(eol)   delete location:boardroom terrain
 
 [command:destroy]
-action = command_destroy(user, parameters)
+action = command_destroy(actor, parameters)
 administrative = yes
 description = Destroy an existing element in the universe.
 help = You can destroy any element in the universe as follows:$(eol)$(eol)   destroy prop:dagger
 
 [command:halt]
 administrative = yes
 description = Destroy an existing element in the universe.
 help = You can destroy any element in the universe as follows:$(eol)$(eol)   destroy prop:dagger
 
 [command:halt]
-action = command_halt(user, parameters)
+action = command_halt(actor, parameters)
 administrative = yes
 description = Shut down the world.
 help = This will save all active accounts, disconnect all clients and stop the entire program.
 
 [command:help]
 administrative = yes
 description = Shut down the world.
 help = This will save all active accounts, disconnect all clients and stop the entire program.
 
 [command:help]
-action = command_help(user, parameters)
+action = command_help(actor, parameters)
 description = List commands or get help on one.
 help = This will list all comand words available to you along with a brief description or, alternatively, give you detailed information on one command.
 
 [command:look]
 description = List commands or get help on one.
 help = This will list all comand words available to you along with a brief description or, alternatively, give you detailed information on one command.
 
 [command:look]
-action = command_look(user, parameters)
+action = command_look(actor, parameters)
 description = Look around.
 help = With the look command, you can see where you are.
 
 [command:move]
 description = Look around.
 help = With the look command, you can see where you are.
 
 [command:move]
-action = command_move(user, parameters)
+action = command_move(actor, parameters)
 description = Move in a specific direction.
 help = You move in a direction by entering:$(eol)   move north
 
 [command:quit]
 description = Move in a specific direction.
 help = You move in a direction by entering:$(eol)   move north
 
 [command:quit]
-action = command_quit(user)
+action = command_quit(actor)
 description = Leave Example.
 help = This will save your account and disconnect your client connection.
 
 [command:reload]
 description = Leave Example.
 help = This will save your account and disconnect your client connection.
 
 [command:reload]
-action = command_reload(user)
+action = command_reload(actor)
 administrative = yes
 description = Reload code modules and data.
 help = This will reload all python code modules, reload configuration files and re-read data files.
 
 [command:say]
 administrative = yes
 description = Reload code modules and data.
 help = This will reload all python code modules, reload configuration files and re-read data files.
 
 [command:say]
-action = command_say(user, parameters)
+action = command_say(actor, parameters)
 description = State something out loud.
 help = This allows you to speak to other characters within the same room. If you end your sentence with specific punctuation, the aparent speech action (ask, exclaim, et cetera) will be adapted accordingly. It will also add punctuation and capitalize your message where needed.
 
 [command:set]
 description = State something out loud.
 help = This allows you to speak to other characters within the same room. If you end your sentence with specific punctuation, the aparent speech action (ask, exclaim, et cetera) will be adapted accordingly. It will also add punctuation and capitalize your message where needed.
 
 [command:set]
-action = command_set(user, parameters)
+action = command_set(actor, parameters)
 administrative = yes
 description = Set a facet of an element.
 help = Invoke it like this:$(eol)$(eol)   set actor:dominique description You see nothing special.
 
 [command:show]
 administrative = yes
 description = Set a facet of an element.
 help = Invoke it like this:$(eol)$(eol)   set actor:dominique description You see nothing special.
 
 [command:show]
-action = command_show(user, parameters)
+action = command_show(actor, parameters)
 administrative = yes
 description = Show element data.
 administrative = yes
 description = Show element data.
-help = Here are the possible incantations:$(eol)$(eol)   show categories$(eol)   show category actor$(eol)   show element location:1:2:3:4$(eol)   show files$(eol)   show log 20$(eol)   show result user.avatar.get("name")$(eol)   show time
+help = Here are the possible incantations:$(eol)$(eol)   show categories$(eol)   show category actor$(eol)   show element location:1:2:3:4$(eol)   show files$(eol)   show log 20$(eol)   show result avatar.get("name")$(eol)   show time
 
 
index 64d1fd8..798b9d8 100644 (file)
@@ -1,5 +1,4 @@
 [prop:fountain]
 impression = An inviting public fountain bubbles here, tempting you with thirst.
 keywords = fountain water
 [prop:fountain]
 impression = An inviting public fountain bubbles here, tempting you with thirst.
 keywords = fountain water
-location = location:0,0,0
 
 
index 4155214..d1c434f 100644 (file)
--- a/mudpy.py
+++ b/mudpy.py
@@ -38,7 +38,7 @@ sys.excepthook = excepthook
 
 class Element:
        """An element of the universe."""
 
 class Element:
        """An element of the universe."""
-       def __init__(self, key, universe, filename=""):
+       def __init__(self, key, universe, filename=None):
                """Set up a new element."""
 
                # not owned by a user by default (used for avatars)
                """Set up a new element."""
 
                # not owned by a user by default (used for avatars)
@@ -47,6 +47,9 @@ class Element:
                # no contents in here by default
                self.contents = {}
 
                # no contents in here by default
                self.contents = {}
 
+               # an event queue for the element
+               self.events = {}
+
                # keep track of our key name
                self.key = key
 
                # keep track of our key name
                self.key = key
 
@@ -78,7 +81,6 @@ class Element:
 
        def destroy(self):
                """Remove an element from the universe and destroy it."""
 
        def destroy(self):
                """Remove an element from the universe and destroy it."""
-               log("Destroying: " + self.key + ".", 2)
                self.origin.data.remove_section(self.key)
                del universe.categories[self.category][self.subkey]
                del universe.contents[self.key]
                self.origin.data.remove_section(self.key)
                del universe.categories[self.category][self.subkey]
                del universe.contents[self.key]
@@ -172,9 +174,38 @@ class Element:
                newlist = self.getlist(facet)
                newlist.append(value)
                self.set(facet, newlist)
                newlist = self.getlist(facet)
                newlist.append(value)
                self.set(facet, newlist)
+
+       def new_event(self, action, when=None):
+               """Create, attach and enqueue an event element."""
+
+               # if when isn't specified, that means now
+               if not when: when = universe.get_time()
+
+               # events are elements themselves
+               event = Element("event:" + self.key + ":" + counter)
+
        def send(self, message, eol="$(eol)"):
                """Convenience method to pass messages to an owner."""
                if self.owner: self.owner.send(message, eol)
        def send(self, message, eol="$(eol)"):
                """Convenience method to pass messages to an owner."""
                if self.owner: self.owner.send(message, eol)
+
+       def can_run(self, command):
+               """Check if the user can run this command object."""
+
+               # has to be in the commands category
+               if command not in universe.categories["command"].values(): result = False
+
+               # avatars of administrators can run any command
+               elif self.owner and self.owner.account.getboolean("administrator"): result = True
+
+               # everyone can run non-administrative commands
+               elif not command.getboolean("administrative"): result = True
+
+               # otherwise the command cannot be run by this actor
+               else: result = False
+
+               # pass back the result
+               return result
+
        def go_to(self, location):
                """Relocate the element to a specific location."""
                current = self.get("location")
        def go_to(self, location):
                """Relocate the element to a specific location."""
                current = self.get("location")
@@ -304,6 +335,9 @@ class DataFile:
                        file_descriptor.flush()
                        file_descriptor.close()
 
                        file_descriptor.flush()
                        file_descriptor.close()
 
+                       # unset the modified flag
+                       self.modified = False
+
 class Universe:
        """The universe."""
        def __init__(self, filename=""):
 class Universe:
        """The universe."""
        def __init__(self, filename=""):
@@ -313,7 +347,9 @@ class Universe:
                self.default_origins = {}
                self.files = {}
                self.private_files = []
                self.default_origins = {}
                self.files = {}
                self.private_files = []
-               self.loglist = []
+               self.loglines = {}
+               self.pending_events_long = {}
+               self.pending_events_short = {}
                self.userlist = []
                self.terminate_world = False
                self.reload_modules = False
                self.userlist = []
                self.terminate_world = False
                self.reload_modules = False
@@ -362,6 +398,10 @@ class Universe:
                # note that we're now ready for user connections
                log("Waiting for connection(s)...")
 
                # note that we're now ready for user connections
                log("Waiting for connection(s)...")
 
+       def get_time(self):
+               """Convenience method to get the elapsed time counter."""
+               return self.categories["internal"]["counters"].getint("elapsed")
+
 class User:
        """This is a connected user."""
 
 class User:
        """This is a connected user."""
 
@@ -702,24 +742,6 @@ class User:
                # replace the input with our cleaned-up text
                self.partial_input = text
 
                # replace the input with our cleaned-up text
                self.partial_input = text
 
-       def can_run(self, command):
-               """Check if the user can run this command object."""
-
-               # has to be in the commands category
-               if command not in universe.categories["command"].values(): result = False
-
-               # administrators can run any command
-               elif self.account.getboolean("administrator"): result = True
-
-               # everyone can run non-administrative commands
-               elif not command.getboolean("administrative"): result = True
-
-               # otherwise the command cannot be run by this user
-               else: result = False
-
-               # pass back the result
-               return result
-
        def new_avatar(self):
                """Instantiate a new, unconfigured avatar for this user."""
                counter = 0
        def new_avatar(self):
                """Instantiate a new, unconfigured avatar for this user."""
                counter = 0
@@ -818,8 +840,44 @@ def log(message, level=0):
 
        # add to the recent log list
        for line in lines:
 
        # add to the recent log list
        for line in lines:
-               while 0 < len(universe.loglist) >= max_log_lines: del universe.loglist[0]
-               universe.loglist.append(timestamp + " " + line)
+               while 0 < len(universe.loglines) >= max_log_lines: del universe.loglines[0]
+               universe.loglines.append((level, timestamp + " " + line))
+
+def get_loglines(level, start, stop=0):
+       """Return a specific range of loglines filtered by level."""
+
+       # begin with a blank message
+       message = ""
+
+       # filter the log lines
+       loglines = filter(lambda x,y: x>=level, universe.loglines)
+
+       # we need this in several places
+       count = len(loglines)
+
+       # don't proceed if there are no lines
+       if count:
+
+               # can't start before the begining or at the end
+               if start > count: start = count
+               if start < 1: start = 1
+
+               # can't stop before we start
+               if stop >= start: stop = start - 1
+
+               # some preamble
+               message += "There are " + str(len(universe.loglist))
+               message += " log lines in memory and " + str(count)
+               message += " at or above level " + str(level) + "."
+               message += " The lines from " + str(stop)
+               message += " to " + str(start) + " are:$(eol)$(eol)"
+
+               # add the text from the selected lines
+               for line in loglines[-start:-stop]:
+                       message += "   " + line[1] + "$(eol)"
+
+       # pass it back
+       return message
 
 def wrap_ansi_text(text, width):
        """Wrap text with arbitrary width while ignoring ANSI colors."""
 
 def wrap_ansi_text(text, width):
        """Wrap text with arbitrary width while ignoring ANSI colors."""
@@ -1387,48 +1445,51 @@ def handler_active(user):
                else: command = None
 
                # if it's allowed, do it
                else: command = None
 
                # if it's allowed, do it
-               if user.can_run(command): exec(command.get("action"))
+               if user.avatar.can_run(command): exec(command.get("action"))
 
                # otherwise, give an error
 
                # otherwise, give an error
-               elif command_name: command_error(user, input_data)
+               elif command_name: command_error(user.avatar, input_data)
 
        # if no input, just idle back with a prompt
        else: user.send("", just_prompt=True)
        
 
        # if no input, just idle back with a prompt
        else: user.send("", just_prompt=True)
        
-def command_halt(user, parameters):
+def command_halt(actor, parameters):
        """Halt the world."""
        """Halt the world."""
+       if actor.owner:
 
 
-       # see if there's a message or use a generic one
-       if parameters: message = "Halting: " + parameters
-       else: message = "User " + user.account.get("name") + " halted the world."
+               # see if there's a message or use a generic one
+               if parameters: message = "Halting: " + parameters
+               else: message = "User " + actor.owner.account.get("name") + " halted the world."
 
 
-       # let everyone know
-       broadcast(message, add_prompt=False)
-       log(message, 8)
+               # let everyone know
+               broadcast(message, add_prompt=False)
+               log(message, 8)
 
 
-       # set a flag to terminate the world
-       universe.terminate_world = True
+               # set a flag to terminate the world
+               universe.terminate_world = True
 
 
-def command_reload(user):
+def command_reload(actor):
        """Reload all code modules, configs and data."""
        """Reload all code modules, configs and data."""
+       if actor.owner:
 
 
-       # let the user know and log
-       user.send("Reloading all code modules, configs and data.")
-       log("User " + user.account.get("name") + " reloaded the world.", 8)
+               # let the user know and log
+               actor.send("Reloading all code modules, configs and data.")
+               log("User " + actor.owner.account.get("name") + " reloaded the world.", 8)
 
 
-       # set a flag to reload
-       universe.reload_modules = True
+               # set a flag to reload
+               universe.reload_modules = True
 
 
-def command_quit(user):
+def command_quit(actor):
        """Leave the world and go back to the main menu."""
        """Leave the world and go back to the main menu."""
-       user.deactivate_avatar()
-       user.state = "main_utility"
+       if actor.owner:
+               actor.owner.deactivate_avatar()
+               actor.owner.state = "main_utility"
 
 
-def command_help(user, parameters):
+def command_help(actor, parameters):
        """List available commands and provide help for commands."""
 
        # did the user ask for help on a specific command word?
        """List available commands and provide help for commands."""
 
        # did the user ask for help on a specific command word?
-       if parameters:
+       if parameters and actor.owner:
 
                # is the command word one for which we have data?
                if parameters in universe.categories["command"]:
 
                # is the command word one for which we have data?
                if parameters in universe.categories["command"]:
@@ -1436,7 +1497,7 @@ def command_help(user, parameters):
                else: command = None
 
                # only for allowed commands
                else: command = None
 
                # only for allowed commands
-               if user.can_run(command):
+               if actor.can_run(command):
 
                        # add a description if provided
                        description = command.get("description")
 
                        # add a description if provided
                        description = command.get("description")
@@ -1465,7 +1526,7 @@ def command_help(user, parameters):
                sorted_commands.sort()
                for item in sorted_commands:
                        command = universe.categories["command"][item]
                sorted_commands.sort()
                for item in sorted_commands:
                        command = universe.categories["command"][item]
-                       if user.can_run(command):
+                       if actor.can_run(command):
                                description = command.get("description")
                                if not description:
                                        description = "(no short description provided)"
                                description = command.get("description")
                                if not description:
                                        description = "(no short description provided)"
@@ -1475,25 +1536,25 @@ def command_help(user, parameters):
                output += "$(eol)Enter \"help COMMAND\" for help on a command named \"COMMAND\"."
 
        # send the accumulated output to the user
                output += "$(eol)Enter \"help COMMAND\" for help on a command named \"COMMAND\"."
 
        # send the accumulated output to the user
-       user.send(output)
+       actor.send(output)
 
 
-def command_move(user, parameters):
+def command_move(actor, parameters):
        """Move the avatar in a given direction."""
        """Move the avatar in a given direction."""
-       if parameters in universe.contents[user.avatar.get("location")].portals():
-               user.avatar.move_direction(parameters)
-       else: user.send("You cannot go that way.")
+       if parameters in universe.contents[actor.get("location")].portals():
+               actor.move_direction(parameters)
+       else: actor.send("You cannot go that way.")
 
 
-def command_look(user, parameters):
+def command_look(actor, parameters):
        """Look around."""
        """Look around."""
-       if parameters: user.send("You can't look at or in anything yet.")
-       else: user.avatar.look_at(user.avatar.get("location"))
+       if parameters: actor.send("You can't look at or in anything yet.")
+       else: actor.look_at(actor.get("location"))
 
 
-def command_say(user, parameters):
+def command_say(actor, parameters):
        """Speak to others in the same room."""
 
        # check for replacement macros
        """Speak to others in the same room."""
 
        # check for replacement macros
-       if replace_macros(user, parameters, True) != parameters:
-               user.send("You cannot speak $_(replacement macros).")
+       if replace_macros(actor.owner, parameters, True) != parameters:
+               actor.send("You cannot speak $_(replacement macros).")
 
        # the user entered a message
        elif parameters:
 
        # the user entered a message
        elif parameters:
@@ -1528,14 +1589,14 @@ def command_say(user, parameters):
                        message = message.replace(" " + word + " ", " " + word.capitalize() + " ")
 
                # tell the room
                        message = message.replace(" " + word + " ", " " + word.capitalize() + " ")
 
                # tell the room
-               user.avatar.echo_to_location(user.avatar.get("name") + " " + action + "s, \"" + message + "\"")
-               user.send("You " + action + ", \"" + message + "\"")
+               actor.echo_to_location(actor.get("name") + " " + action + "s, \"" + message + "\"")
+               actor.send("You " + action + ", \"" + message + "\"")
 
        # there was no message
        else:
 
        # there was no message
        else:
-               user.send("What do you want to say?")
+               actor.send("What do you want to say?")
 
 
-def command_show(user, parameters):
+def command_show(actor, parameters):
        """Show program data."""
        message = ""
        if parameters.find(" ") < 1:
        """Show program data."""
        message = ""
        if parameters.find(" ") < 1:
@@ -1578,7 +1639,7 @@ def command_show(user, parameters):
                elif arguments[0] == "log":
                        if match("^\d+$", arguments[1]) and int(arguments[1]) > 0:
                                linecount = int(arguments[1])
                elif arguments[0] == "log":
                        if match("^\d+$", arguments[1]) and int(arguments[1]) > 0:
                                linecount = int(arguments[1])
-                               if linecount > len(universe.loglist): linecount = len(universe.loglist)
+                               if linecount > len(universe.loglines): linecount = len(universe.loglist)
                                message = "There are " + str(len(universe.loglist)) + " log lines in memory."
                                message += " The most recent " + str(linecount) + " lines are:$(eol)$(eol)"
                                for line in universe.loglist[-linecount:]:
                                message = "There are " + str(len(universe.loglist)) + " log lines in memory."
                                message += " The most recent " + str(linecount) + " lines are:$(eol)$(eol)"
                                for line in universe.loglist[-linecount:]:
@@ -1587,11 +1648,12 @@ def command_show(user, parameters):
        if not message:
                if parameters: message = "I don't know what \"" + parameters + "\" is."
                else: message = "What do you want to show?"
        if not message:
                if parameters: message = "I don't know what \"" + parameters + "\" is."
                else: message = "What do you want to show?"
-       user.send(message)
+       actor.send(message)
 
 
-def command_create(user, parameters):
+def command_create(actor, parameters):
        """Create an element if it does not exist."""
        if not parameters: message = "You must at least specify an element to create."
        """Create an element if it does not exist."""
        if not parameters: message = "You must at least specify an element to create."
+       elif not actor.owner: message = ""
        else:
                arguments = parameters.split()
                if len(arguments) == 1: arguments.append("")
        else:
                arguments = parameters.split()
                if len(arguments) == 1: arguments.append("")
@@ -1600,7 +1662,7 @@ def command_create(user, parameters):
                        if element in universe.contents: message = "The \"" + element + "\" element already exists."
                        else:
                                message = "You create \"" + element + "\" within the universe."
                        if element in universe.contents: message = "The \"" + element + "\" element already exists."
                        else:
                                message = "You create \"" + element + "\" within the universe."
-                               logline = user.account.get("name") + " created an element: " + element
+                               logline = actor.owner.account.get("name") + " created an element: " + element
                                if filename:
                                        logline += " in file " + filename
                                        if filename not in universe.files:
                                if filename:
                                        logline += " in file " + filename
                                        if filename not in universe.files:
@@ -1608,20 +1670,21 @@ def command_create(user, parameters):
                                Element(element, universe, filename)
                                log(logline, 6)
                elif len(arguments) > 2: message = "You can only specify an element and a filename."
                                Element(element, universe, filename)
                                log(logline, 6)
                elif len(arguments) > 2: message = "You can only specify an element and a filename."
-       user.send(message)
+       actor.send(message)
 
 
-def command_destroy(user, parameters):
+def command_destroy(actor, parameters):
        """Destroy an element if it exists."""
        """Destroy an element if it exists."""
-       if not parameters: message = "You must specify an element to destroy."
-       else:
-               if parameters not in universe.contents: message = "The \"" + parameters + "\" element does not exist."
+       if actor.owner:
+               if not parameters: message = "You must specify an element to destroy."
                else:
                else:
-                       universe.contents[parameters].destroy()
-                       message = "You destroy \"" + parameters + "\" within the universe."
-                       log(user.account.get("name") + " destroyed an element: " + parameters, 6)
-       user.send(message)
+                       if parameters not in universe.contents: message = "The \"" + parameters + "\" element does not exist."
+                       else:
+                               universe.contents[parameters].destroy()
+                               message = "You destroy \"" + parameters + "\" within the universe."
+                               log(actor.owner.account.get("name") + " destroyed an element: " + parameters, 6)
+               actor.send(message)
 
 
-def command_set(user, parameters):
+def command_set(actor, parameters):
        """Set a facet of an element."""
        if not parameters: message = "You must specify an element, a facet and a value."
        else:
        """Set a facet of an element."""
        if not parameters: message = "You must specify an element, a facet and a value."
        else:
@@ -1634,9 +1697,9 @@ def command_set(user, parameters):
                        else:
                                universe.contents[element].set(facet, value)
                                message = "You have successfully (re)set the \"" + facet + "\" facet of element \"" + element + "\". Try \"show element " + element + "\" for verification."
                        else:
                                universe.contents[element].set(facet, value)
                                message = "You have successfully (re)set the \"" + facet + "\" facet of element \"" + element + "\". Try \"show element " + element + "\" for verification."
-       user.send(message)
+       actor.send(message)
 
 
-def command_delete(user, parameters):
+def command_delete(actor, parameters):
        """Delete a facet from an element."""
        if not parameters: message = "You must specify an element and a facet."
        else:
        """Delete a facet from an element."""
        if not parameters: message = "You must specify an element and a facet."
        else:
@@ -1650,9 +1713,9 @@ def command_delete(user, parameters):
                        else:
                                universe.contents[element].remove_facet(facet)
                                message = "You have successfully deleted the \"" + facet + "\" facet of element \"" + element + "\". Try \"show element " + element + "\" for verification."
                        else:
                                universe.contents[element].remove_facet(facet)
                                message = "You have successfully deleted the \"" + facet + "\" facet of element \"" + element + "\". Try \"show element " + element + "\" for verification."
-       user.send(message)
+       actor.send(message)
 
 
-def command_error(user, input_data):
+def command_error(actor, input_data):
        """Generic error for an unrecognized command word."""
 
        # 90% of the time use a generic error
        """Generic error for an unrecognized command word."""
 
        # 90% of the time use a generic error
@@ -1664,7 +1727,7 @@ def command_error(user, input_data):
                message = "Arglebargle, glop-glyf!?!"
 
        # send the error message
                message = "Arglebargle, glop-glyf!?!"
 
        # send the error message
-       user.send(message)
+       actor.send(message)
 
 # if there is no universe, create an empty one
 if not "universe" in locals(): universe = Universe()
 
 # if there is no universe, create an empty one
 if not "universe" in locals(): universe = Universe()