def update_defaults(self): """ We make sure to store the most important object defaults here, so we can catch if they change and update them on-objects automatically. This allows for changing default cmdset locations and default typeclasses in the settings file and have them auto-update all already existing objects. """ # setting names settings_names = ( "CMDSET_CHARACTER", "CMDSET_PLAYER", "BASE_PLAYER_TYPECLASS", "BASE_OBJECT_TYPECLASS", "BASE_CHARACTER_TYPECLASS", "BASE_ROOM_TYPECLASS", "BASE_EXIT_TYPECLASS", "BASE_SCRIPT_TYPECLASS", "BASE_CHANNEL_TYPECLASS", ) # get previous and current settings so they can be compared settings_compare = zip( [ServerConfig.objects.conf(name) for name in settings_names], [settings.__getattr__(name) for name in settings_names], ) mismatches = [i for i, tup in enumerate(settings_compare) if tup[0] and tup[1] and tup[0] != tup[1]] if len(mismatches): # can't use any() since mismatches may be [0] which reads as False for any() # we have a changed default. Import relevant objects and # run the update from evennia.objects.models import ObjectDB from evennia.comms.models import ChannelDB # from evennia.players.models import PlayerDB for i, prev, curr in ((i, tup[0], tup[1]) for i, tup in enumerate(settings_compare) if i in mismatches): # update the database print( " %s:\n '%s' changed to '%s'. Updating unchanged entries in database ..." % (settings_names[i], prev, curr) ) if i == 0: ObjectDB.objects.filter(db_cmdset_storage__exact=prev).update(db_cmdset_storage=curr) if i == 1: PlayerDB.objects.filter(db_cmdset_storage__exact=prev).update(db_cmdset_storage=curr) if i == 2: PlayerDB.objects.filter(db_typeclass_path__exact=prev).update(db_typeclass_path=curr) if i in (3, 4, 5, 6): ObjectDB.objects.filter(db_typeclass_path__exact=prev).update(db_typeclass_path=curr) if i == 7: ScriptDB.objects.filter(db_typeclass_path__exact=prev).update(db_typeclass_path=curr) if i == 8: ChannelDB.objects.filter(db_typeclass_path__exact=prev).update(db_typeclass_path=curr) # store the new default and clean caches ServerConfig.objects.conf(settings_names[i], curr) ObjectDB.flush_instance_cache() PlayerDB.flush_instance_cache() ScriptDB.flush_instance_cache() ChannelDB.flush_instance_cache() # if this is the first start we might not have a "previous" # setup saved. Store it now. [ServerConfig.objects.conf(settings_names[i], tup[1]) for i, tup in enumerate(settings_compare) if not tup[0]]
def update_defaults(self): """ We make sure to store the most important object defaults here, so we can catch if they change and update them on-objects automatically. This allows for changing default cmdset locations and default typeclasses in the settings file and have them auto-update all already existing objects. """ # setting names settings_names = ("CMDSET_CHARACTER", "CMDSET_PLAYER", "BASE_PLAYER_TYPECLASS", "BASE_OBJECT_TYPECLASS", "BASE_CHARACTER_TYPECLASS", "BASE_ROOM_TYPECLASS", "BASE_EXIT_TYPECLASS", "BASE_SCRIPT_TYPECLASS", "BASE_CHANNEL_TYPECLASS") # get previous and current settings so they can be compared settings_compare = zip([ServerConfig.objects.conf(name) for name in settings_names], [settings.__getattr__(name) for name in settings_names]) mismatches = [i for i, tup in enumerate(settings_compare) if tup[0] and tup[1] and tup[0] != tup[1]] if len(mismatches): # can't use any() since mismatches may be [0] which reads as False for any() # we have a changed default. Import relevant objects and # run the update from evennia.objects.models import ObjectDB from evennia.comms.models import ChannelDB #from evennia.players.models import PlayerDB for i, prev, curr in ((i, tup[0], tup[1]) for i, tup in enumerate(settings_compare) if i in mismatches): # update the database print(" %s:\n '%s' changed to '%s'. Updating unchanged entries in database ..." % (settings_names[i], prev, curr)) if i == 0: ObjectDB.objects.filter(db_cmdset_storage__exact=prev).update(db_cmdset_storage=curr) if i == 1: PlayerDB.objects.filter(db_cmdset_storage__exact=prev).update(db_cmdset_storage=curr) if i == 2: PlayerDB.objects.filter(db_typeclass_path__exact=prev).update(db_typeclass_path=curr) if i in (3, 4, 5, 6): ObjectDB.objects.filter(db_typeclass_path__exact=prev).update(db_typeclass_path=curr) if i == 7: ScriptDB.objects.filter(db_typeclass_path__exact=prev).update(db_typeclass_path=curr) if i == 8: ChannelDB.objects.filter(db_typeclass_path__exact=prev).update(db_typeclass_path=curr) # store the new default and clean caches ServerConfig.objects.conf(settings_names[i], curr) ObjectDB.flush_instance_cache() PlayerDB.flush_instance_cache() ScriptDB.flush_instance_cache() ChannelDB.flush_instance_cache() # if this is the first start we might not have a "previous" # setup saved. Store it now. [ServerConfig.objects.conf(settings_names[i], tup[1]) for i, tup in enumerate(settings_compare) if not tup[0]]
def run_init_hooks(self): """ Called every server start """ from evennia.objects.models import ObjectDB #from evennia.players.models import PlayerDB #update eventual changed defaults self.update_defaults() [o.at_init() for o in ObjectDB.get_all_cached_instances()] [p.at_init() for p in PlayerDB.get_all_cached_instances()] with open(SERVER_RESTART, 'r') as f: mode = f.read() if mode in ('True', 'reload'): from evennia.scripts.monitorhandler import MONITOR_HANDLER MONITOR_HANDLER.restore() from evennia.scripts.tickerhandler import TICKER_HANDLER TICKER_HANDLER.restore(mode in ('True', 'reload')) # call correct server hook based on start file value if mode in ('True', 'reload'): # True was the old reload flag, kept for compatibilty self.at_server_reload_start() elif mode == 'reset': # only run hook, don't purge sessions self.at_server_cold_start() elif mode in ('reset', 'shutdown'): self.at_server_cold_start() # clear eventual lingering session storages ObjectDB.objects.clear_all_sessids() # always call this regardless of start type self.at_server_start()
def run_init_hooks(self, mode): """ Called by the amp client once receiving sync back from Portal Args: mode (str): One of shutdown, reload or reset """ from evennia.objects.models import ObjectDB # update eventual changed defaults self.update_defaults() [o.at_init() for o in ObjectDB.get_all_cached_instances()] [p.at_init() for p in AccountDB.get_all_cached_instances()] # call correct server hook based on start file value if mode == 'reload': logger.log_msg("Server successfully reloaded.") self.at_server_reload_start() elif mode == 'reset': # only run hook, don't purge sessions self.at_server_cold_start() logger.log_msg( "Evennia Server successfully restarted in 'reset' mode.") elif mode == 'shutdown': self.at_server_cold_start() # clear eventual lingering session storages ObjectDB.objects.clear_all_sessids() logger.log_msg("Evennia Server successfully started.") # always call this regardless of start type self.at_server_start()
def run_init_hooks(self): """ Called every server start """ from evennia.objects.models import ObjectDB #from evennia.players.models import PlayerDB #update eventual changed defaults self.update_defaults() [o.at_init() for o in ObjectDB.get_all_cached_instances()] [p.at_init() for p in PlayerDB.get_all_cached_instances()] mode = self.getset_restart_mode() # call correct server hook based on start file value if mode == 'reload': # True was the old reload flag, kept for compatibilty self.at_server_reload_start() elif mode == 'reset': # only run hook, don't purge sessions self.at_server_cold_start() elif mode in ('reset', 'shutdown'): self.at_server_cold_start() # clear eventual lingering session storages ObjectDB.objects.clear_all_sessids() # always call this regardless of start type self.at_server_start()
def _batch_create_object(*objparams): """ This is a cut-down version of the create_object() function, optimized for speed. It does NOT check and convert various input so make sure the spawned Typeclass works before using this! Args: objsparams (any): Aach argument should be a tuple of arguments for the respective creation/add handlers in the following order: (create, permissions, locks, aliases, nattributes, attributes) Returns: objects (list): A list of created objects """ # bulk create all objects in one go # unfortunately this doesn't work since bulk_create doesn't creates pks; # the result are double objects at the next stage #dbobjs = _ObjectDB.objects.bulk_create(dbobjs) dbobjs = [ObjectDB(**objparam[0]) for objparam in objparams] objs = [] for iobj, obj in enumerate(dbobjs): # call all setup hooks on each object objparam = objparams[iobj] # setup obj._createdict = { "permissions": objparam[1], "locks": objparam[2], "aliases": objparam[3], "nattributes": objparam[4], "attributes": objparam[5], "tags": objparam[6] } # this triggers all hooks obj.save() # run eventual extra code for code in objparam[7]: if code: exec(code, {}, {"evennia": evennia, "obj": obj}) objs.append(obj) return objs
def create_object( typeclass=None, key=None, location=None, home=None, permissions=None, locks=None, aliases=None, tags=None, destination=None, report_to=None, nohome=False, attributes=None, nattributes=None, ): """ Create a new in-game object. Kwargs: typeclass (class or str): Class or python path to a typeclass. key (str): Name of the new object. If not set, a name of #dbref will be set. home (Object or str): Obj or #dbref to use as the object's home location. permissions (list): A list of permission strings or tuples (permstring, category). locks (str): one or more lockstrings, separated by semicolons. aliases (list): A list of alternative keys or tuples (aliasstring, category). tags (list): List of tag keys or tuples (tagkey, category) or (tagkey, category, data). destination (Object or str): Obj or #dbref to use as an Exit's target. report_to (Object): The object to return error messages to. nohome (bool): This allows the creation of objects without a default home location; only used when creating the default location itself or during unittests. attributes (list): Tuples on the form (key, value) or (key, value, category), (key, value, lockstring) or (key, value, lockstring, default_access). to set as Attributes on the new object. nattributes (list): Non-persistent tuples on the form (key, value). Note that adding this rarely makes sense since this data will not survive a reload. Returns: object (Object): A newly created object of the given typeclass. Raises: ObjectDB.DoesNotExist: If trying to create an Object with `location` or `home` that can't be found. """ global _ObjectDB if not _ObjectDB: from evennia.objects.models import ObjectDB as _ObjectDB typeclass = typeclass if typeclass else settings.BASE_OBJECT_TYPECLASS # convenience converters to avoid common usage mistake permissions = make_iter(permissions) if permissions is not None else None locks = make_iter(locks) if locks is not None else None aliases = make_iter(aliases) if aliases is not None else None tags = make_iter(tags) if tags is not None else None attributes = make_iter(attributes) if attributes is not None else None if isinstance(typeclass, str): # a path is given. Load the actual typeclass typeclass = class_from_module(typeclass, settings.TYPECLASS_PATHS) # Setup input for the create command. We use ObjectDB as baseclass here # to give us maximum freedom (the typeclasses will load # correctly when each object is recovered). location = dbid_to_obj(location, _ObjectDB) destination = dbid_to_obj(destination, _ObjectDB) home = dbid_to_obj(home, _ObjectDB) if not home: try: home = dbid_to_obj(settings.DEFAULT_HOME, _ObjectDB) if not nohome else None except _ObjectDB.DoesNotExist: raise _ObjectDB.DoesNotExist( "settings.DEFAULT_HOME (= '%s') does not exist, or the setting is malformed." % settings.DEFAULT_HOME) # create new instance new_object = typeclass( db_key=key, db_location=location, db_destination=destination, db_home=home, db_typeclass_path=typeclass.path, ) # store the call signature for the signal new_object._createdict = dict( key=key, location=location, destination=destination, home=home, typeclass=typeclass.path, permissions=permissions, locks=locks, aliases=aliases, tags=tags, report_to=report_to, nohome=nohome, attributes=attributes, nattributes=nattributes, ) # this will trigger the save signal which in turn calls the # at_first_save hook on the typeclass, where the _createdict can be # used. new_object.save() signals.SIGNAL_OBJECT_POST_CREATE.send(sender=new_object) return new_object
def shutdown(self, mode=None, _reactor_stopping=False): """ Shuts down the server from inside it. mode - sets the server restart mode. 'reload' - server restarts, no "persistent" scripts are stopped, at_reload hooks called. 'reset' - server restarts, non-persistent scripts stopped, at_shutdown hooks called but sessions will not be disconnected. 'shutdown' - like reset, but server will not auto-restart. None - keep currently set flag from flag file. _reactor_stopping - this is set if server is stopped by a kill command OR this method was already called once - in both cases the reactor is dead/stopping already. """ if _reactor_stopping and hasattr(self, "shutdown_complete"): # this means we have already passed through this method # once; we don't need to run the shutdown procedure again. defer.returnValue(None) mode = self.set_restart_mode(mode) from evennia.objects.models import ObjectDB #from evennia.players.models import PlayerDB from evennia.server.models import ServerConfig if mode == 'reload': # call restart hooks ServerConfig.objects.conf("server_restart_mode", "reload") yield [o.at_server_reload() for o in ObjectDB.get_all_cached_instances()] yield [p.at_server_reload() for p in PlayerDB.get_all_cached_instances()] yield [(s.pause(manual_pause=False), s.at_server_reload()) for s in ScriptDB.get_all_cached_instances() if s.is_active] yield self.sessions.all_sessions_portal_sync() self.at_server_reload_stop() # only save monitor state on reload, not on shutdown/reset from evennia.scripts.monitorhandler import MONITOR_HANDLER MONITOR_HANDLER.save() else: if mode == 'reset': # like shutdown but don't unset the is_connected flag and don't disconnect sessions yield [o.at_server_shutdown() for o in ObjectDB.get_all_cached_instances()] yield [p.at_server_shutdown() for p in PlayerDB.get_all_cached_instances()] if self.amp_protocol: yield self.sessions.all_sessions_portal_sync() else: # shutdown yield [_SA(p, "is_connected", False) for p in PlayerDB.get_all_cached_instances()] yield [o.at_server_shutdown() for o in ObjectDB.get_all_cached_instances()] yield [(p.unpuppet_all(), p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()] yield ObjectDB.objects.clear_all_sessids() yield [(s.pause(manual_pause=False), s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()] ServerConfig.objects.conf("server_restart_mode", "reset") self.at_server_cold_stop() # tickerhandler state should always be saved. from evennia.scripts.tickerhandler import TICKER_HANDLER TICKER_HANDLER.save() # always called, also for a reload self.at_server_stop() # if _reactor_stopping is true, reactor does not need to # be stopped again. if os.name == 'nt' and os.path.exists(SERVER_PIDFILE): # for Windows we need to remove pid files manually os.remove(SERVER_PIDFILE) if not _reactor_stopping: # this will also send a reactor.stop signal, so we set a # flag to avoid loops. self.shutdown_complete = True # kill the server reactor.callLater(0, reactor.stop)
def batch_create_object(*objparams): """ This is a cut-down version of the create_object() function, optimized for speed. It does NOT check and convert various input so make sure the spawned Typeclass works before using this! Args: objsparams (tuple): Each paremter tuple will create one object instance using the parameters within. The parameters should be given in the following order: - `create_kwargs` (dict): For use as new_obj = `ObjectDB(**create_kwargs)`. - `permissions` (str): Permission string used with `new_obj.batch_add(permission)`. - `lockstring` (str): Lockstring used with `new_obj.locks.add(lockstring)`. - `aliases` (list): A list of alias strings for adding with `new_object.aliases.batch_add(*aliases)`. - `nattributes` (list): list of tuples `(key, value)` to be loop-added to add with `new_obj.nattributes.add(*tuple)`. - `attributes` (list): list of tuples `(key, value[,category[,lockstring]])` for adding with `new_obj.attributes.batch_add(*attributes)`. - `tags` (list): list of tuples `(key, category)` for adding with `new_obj.tags.batch_add(*tags)`. - `execs` (list): Code strings to execute together with the creation of each object. They will be executed with `evennia` and `obj` (the newly created object) available in the namespace. Execution will happend after all other properties have been assigned and is intended for calling custom handlers etc. Returns: objects (list): A list of created objects Notes: The `exec` list will execute arbitrary python code so don't allow this to be available to unprivileged users! """ # bulk create all objects in one go # unfortunately this doesn't work since bulk_create doesn't creates pks; # the result would be duplicate objects at the next stage, so we comment # it out for now: # dbobjs = _ObjectDB.objects.bulk_create(dbobjs) dbobjs = [ObjectDB(**objparam[0]) for objparam in objparams] objs = [] for iobj, obj in enumerate(dbobjs): # call all setup hooks on each object objparam = objparams[iobj] # setup obj._createdict = { "permissions": make_iter(objparam[1]), "locks": objparam[2], "aliases": make_iter(objparam[3]), "nattributes": objparam[4], "attributes": objparam[5], "tags": make_iter(objparam[6]) } # this triggers all hooks obj.save() # run eventual extra code for code in objparam[7]: if code: exec(code, {}, {"evennia": evennia, "obj": obj}) objs.append(obj) return objs
def shutdown(self, mode="reload", _reactor_stopping=False): """ Shuts down the server from inside it. mode - sets the server restart mode. 'reload' - server restarts, no "persistent" scripts are stopped, at_reload hooks called. 'reset' - server restarts, non-persistent scripts stopped, at_shutdown hooks called but sessions will not be disconnected. 'shutdown' - like reset, but server will not auto-restart. _reactor_stopping - this is set if server is stopped by a kill command OR this method was already called once - in both cases the reactor is dead/stopping already. """ if _reactor_stopping and hasattr(self, "shutdown_complete"): # this means we have already passed through this method # once; we don't need to run the shutdown procedure again. defer.returnValue(None) from evennia.objects.models import ObjectDB from evennia.server.models import ServerConfig from evennia.utils import gametime as _GAMETIME_MODULE if mode == "reload": # call restart hooks ServerConfig.objects.conf("server_restart_mode", "reload") yield [ o.at_server_reload() for o in ObjectDB.get_all_cached_instances() ] yield [ p.at_server_reload() for p in AccountDB.get_all_cached_instances() ] yield [ (s.pause(manual_pause=False), s.at_server_reload()) for s in ScriptDB.get_all_cached_instances() if s.id and (s.is_active or s.attributes.has("_manual_pause")) ] yield self.sessions.all_sessions_portal_sync() self.at_server_reload_stop() # only save monitor state on reload, not on shutdown/reset from evennia.scripts.monitorhandler import MONITOR_HANDLER MONITOR_HANDLER.save() else: if mode == "reset": # like shutdown but don't unset the is_connected flag and don't disconnect sessions yield [ o.at_server_shutdown() for o in ObjectDB.get_all_cached_instances() ] yield [ p.at_server_shutdown() for p in AccountDB.get_all_cached_instances() ] if self.amp_protocol: yield self.sessions.all_sessions_portal_sync() else: # shutdown yield [ _SA(p, "is_connected", False) for p in AccountDB.get_all_cached_instances() ] yield [ o.at_server_shutdown() for o in ObjectDB.get_all_cached_instances() ] yield [(p.unpuppet_all(), p.at_server_shutdown()) for p in AccountDB.get_all_cached_instances()] yield ObjectDB.objects.clear_all_sessids() yield [( s.pause(manual_pause=s.attributes.get("_manual_pause", False)), s.at_server_shutdown(), ) for s in ScriptDB.get_all_cached_instances()] ServerConfig.objects.conf("server_restart_mode", "reset") self.at_server_cold_stop() # tickerhandler state should always be saved. from evennia.scripts.tickerhandler import TICKER_HANDLER TICKER_HANDLER.save() # always called, also for a reload self.at_server_stop() if hasattr(self, "web_root"): # not set very first start yield self.web_root.empty_threadpool() if not _reactor_stopping: # kill the server self.shutdown_complete = True reactor.callLater(1, reactor.stop) # we make sure the proper gametime is saved as late as possible ServerConfig.objects.conf("runtime", _GAMETIME_MODULE.runtime())
def create_object(typeclass=None, key=None, location=None, home=None, permissions=None, locks=None, aliases=None, tags=None, destination=None, report_to=None, nohome=False): """ Create a new in-game object. Kwargs: typeclass (class or str): Class or python path to a typeclass. key (str): Name of the new object. If not set, a name of #dbref will be set. home (Object or str): Obj or #dbref to use as the object's home location. permissions (str): A comma-separated string of permissions. locks (str): one or more lockstrings, separated by semicolons. aliases (list): A list of alternative keys. tags (list): List of tag keys (using no category). destination (Object or str): Obj or #dbref to use as an Exit's target. report_to (Object): The object to return error messages to. nohome (bool): This allows the creation of objects without a default home location; only used when creating the default location itself or during unittests. Returns: object (Object): A newly created object of the given typeclass. Raises: ObjectDB.DoesNotExist: If trying to create an Object with `location` or `home` that can't be found. """ global _ObjectDB if not _ObjectDB: from evennia.objects.models import ObjectDB as _ObjectDB typeclass = typeclass if typeclass else settings.BASE_OBJECT_TYPECLASS if isinstance(typeclass, basestring): # a path is given. Load the actual typeclass typeclass = class_from_module(typeclass, settings.TYPECLASS_PATHS) # Setup input for the create command. We use ObjectDB as baseclass here # to give us maximum freedom (the typeclasses will load # correctly when each object is recovered). location = dbid_to_obj(location, _ObjectDB) destination = dbid_to_obj(destination, _ObjectDB) home = dbid_to_obj(home, _ObjectDB) if not home: try: home = dbid_to_obj(settings.DEFAULT_HOME, _ObjectDB) if not nohome else None except _ObjectDB.DoesNotExist: raise _ObjectDB.DoesNotExist( "settings.DEFAULT_HOME (= '%s') does not exist, or the setting is malformed." % settings.DEFAULT_HOME) # create new instance new_object = typeclass(db_key=key, db_location=location, db_destination=destination, db_home=home, db_typeclass_path=typeclass.path) # store the call signature for the signal new_object._createdict = dict(key=key, location=location, destination=destination, home=home, typeclass=typeclass.path, permissions=permissions, locks=locks, aliases=aliases, tags=tags, report_to=report_to, nohome=nohome) # this will trigger the save signal which in turn calls the # at_first_save hook on the typeclass, where the _createdict can be # used. new_object.save() return new_object
def create_object(typeclass=None, key=None, location=None, home=None, permissions=None, locks=None, aliases=None, destination=None, report_to=None, nohome=False): """ Create a new in-game object. keywords: typeclass - class or python path to a typeclass key - name of the new object. If not set, a name of #dbref will be set. home - obj or #dbref to use as the object's home location permissions - a comma-separated string of permissions locks - one or more lockstrings, separated by semicolons aliases - a list of alternative keys destination - obj or #dbref to use as an Exit's target nohome - this allows the creation of objects without a default home location; only used when creating the default location itself or during unittests """ global _ObjectDB if not _ObjectDB: from evennia.objects.models import ObjectDB as _ObjectDB typeclass = typeclass if typeclass else settings.BASE_OBJECT_TYPECLASS if isinstance(typeclass, basestring): # a path is given. Load the actual typeclass typeclass = class_from_module(typeclass, settings.TYPECLASS_PATHS) # Setup input for the create command. We use ObjectDB as baseclass here # to give us maximum freedom (the typeclasses will load # correctly when each object is recovered). location = dbid_to_obj(location, _ObjectDB) destination = dbid_to_obj(destination, _ObjectDB) home = dbid_to_obj(home, _ObjectDB) if not home: try: home = dbid_to_obj(settings.DEFAULT_HOME, _ObjectDB) if not nohome else None except _ObjectDB.DoesNotExist: raise _ObjectDB.DoesNotExist( "settings.DEFAULT_HOME (= '%s') does not exist, or the setting is malformed." % settings.DEFAULT_HOME) # create new instance new_object = typeclass(db_key=key, db_location=location, db_destination=destination, db_home=home, db_typeclass_path=typeclass.path) # store the call signature for the signal new_object._createdict = { "key": key, "location": location, "destination": destination, "home": home, "typeclass": typeclass.path, "permissions": permissions, "locks": locks, "aliases": aliases, "destination": destination, "report_to": report_to, "nohome": nohome } # this will trigger the save signal which in turn calls the # at_first_save hook on the typeclass, where the _createdict can be # used. new_object.save() return new_object
class PlayerChars(models.Model): char_name = ObjectDB.CharField(max_length=80, verbose_name='Character Name') background = ObjectDB.TextField(verbose_name='Background') player_id = ObjectDB.IntegerField(default=1, verbose_name='Player ID')