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_DEFAULT", "CMDSET_OOC", "BASE_PLAYER_TYPECLASS", "BASE_OBJECT_TYPECLASS", "BASE_CHARACTER_TYPECLASS", "BASE_ROOM_TYPECLASS", "BASE_EXIT_TYPECLASS", "BASE_SCRIPT_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 src.objects.models import ObjectDB #from src.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 " one or more default cmdset/typeclass settings changed. Updating defaults stored in database ..." if i == 0: [obj.__setattr__("cmdset_storage", curr) for obj in ObjectDB.objects.filter(db_cmdset_storage__exact=prev)] if i == 1: [ply.__setattr__("cmdset_storage", curr) for ply in PlayerDB.objects.filter(db_cmdset_storage__exact=prev)] if i == 2: [ply.__setattr__("typeclass_path", curr) for ply in PlayerDB.objects.filter(db_typeclass_path__exact=prev)] if i in (3,4,5,6): [obj.__setattr__("typeclass_path",curr) for obj in ObjectDB.objects.filter(db_typeclass_path__exact=prev)] if i == 7: [scr.__setattr__("typeclass_path", curr) for scr in ScriptDB.objects.filter(db_typeclass_path__exact=prev)] # 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() # 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 make_exit(item): """ We'll do special stuff with these later. """ aliases = item["name"].split(';') name = aliases[0] aliases = aliases[1:] thing = ObjectDB(name=name) thing.aliases.add(aliases) thing.save()
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 src.objects.models import ObjectDB from src.comms.models import ChannelDB #from src.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: [obj.__setattr__("cmdset_storage", curr) for obj in ObjectDB.objects.filter(db_cmdset_storage__exact=prev)] if i == 1: [ply.__setattr__("cmdset_storage", curr) for ply in PlayerDB.objects.filter(db_cmdset_storage__exact=prev)] if i == 2: [ply.__setattr__("typeclass_path", curr) for ply in PlayerDB.objects.filter(db_typeclass_path__exact=prev)] if i in (3, 4, 5, 6): [obj.__setattr__("typeclass_path", curr) for obj in ObjectDB.objects.filter(db_typeclass_path__exact=prev)] if i == 7: [scr.__setattr__("typeclass_path", curr) for scr in ScriptDB.objects.filter(db_typeclass_path__exact=prev)] if i == 8: [scr.__setattr__("typeclass_path", curr) for scr in ChannelDB.objects.filter(db_typeclass_path__exact=prev)] # 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 src.objects.models import ObjectDB #from src.players.models import PlayerDB #update eventual changed defaults self.update_defaults() #print "run_init_hooks:", ObjectDB.get_all_cached_instances() [(o.typeclass, o.at_init()) for o in ObjectDB.get_all_cached_instances()] [(p.typeclass, p.at_init()) for p in PlayerDB.get_all_cached_instances()] if SERVER_STARTSTOP_MODULE: # call correct server hook based on start file value with open(SERVER_RESTART, 'r') as f: mode = f.read() if mode in ('True', 'reload'): # True was the old reload flag, kept for compatibilty SERVER_STARTSTOP_MODULE.at_server_reload_start() elif mode in ('reset', 'shutdown'): SERVER_STARTSTOP_MODULE.at_server_cold_start() # always call this regardless of start type SERVER_STARTSTOP_MODULE.at_server_start()
def run_init_hooks(self): """ Called every server start """ from src.objects.models import ObjectDB #from src.players.models import PlayerDB #update eventual changed defaults self.update_defaults() #print "run_init_hooks:", ObjectDB.get_all_cached_instances() [(o.typeclass, o.at_init()) for o in ObjectDB.get_all_cached_instances()] [(p.typeclass, 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 src.server.oobhandler import OOB_HANDLER OOB_HANDLER.restore() from src.scripts.tickerhandler import TICKER_HANDLER TICKER_HANDLER.restore() if SERVER_STARTSTOP_MODULE: # call correct server hook based on start file value if mode in ('True', 'reload'): # True was the old reload flag, kept for compatibilty SERVER_STARTSTOP_MODULE.at_server_reload_start() elif mode in ('reset', 'shutdown'): SERVER_STARTSTOP_MODULE.at_server_cold_start() # clear eventual lingering session storages ObjectDB.objects.clear_all_sessids() # always call this regardless of start type SERVER_STARTSTOP_MODULE.at_server_start()
def run_init_hooks(self): """ Called every server start """ from src.objects.models import ObjectDB #from src.players.models import PlayerDB #update eventual changed defaults self.update_defaults() #print "run_init_hooks:", ObjectDB.get_all_cached_instances() [(o.typeclass, o.at_init()) for o in ObjectDB.get_all_cached_instances()] [(p.typeclass, 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 src.server.oobhandler import OOB_HANDLER OOB_HANDLER.restore() from src.scripts.tickerhandler import TICKER_HANDLER TICKER_HANDLER.restore() # 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 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 reset_loop(): "Reload and restart all entities that can be reloaded." # run the reset loop on all objects cemit_info(" Resetting all cached database entities ...") t1 = time.time() [h.locks.reset() for h in HelpEntry.objects.all()] [m.locks.reset() for m in Msg.objects.all()] [c.locks.reset() for c in Channel.objects.all()] [s.locks.reset() for s in ScriptDB.objects.all()] [(o.typeclass(o), o.cmdset.reset(), o.locks.reset(), o.at_cache()) for o in ObjectDB.get_all_cached_instances()] [(p.typeclass(p), p.cmdset.reset(), p.locks.reset()) for p in PlayerDB.get_all_cached_instances()] t2 = time.time() cemit_info(" ... Reset finished in %g seconds." % (t2-t1))
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! Input: objsparams - each argument should be a tuple of arguments for the respective creation/add handlers in the following order: (create, permissions, locks, aliases, nattributes, attributes) Returns: A list of created objects """ # bulk create all objects in one go dbobjs = [ObjectDB(**objparam[0]) for objparam in objparams] # unfortunately this doesn't work since bulk_create don't creates pks; # the result are double objects at the next stage #dbobjs = _ObjectDB.objects.bulk_create(dbobjs) objs = [] for iobj, dbobj in enumerate(dbobjs): # call all setup hooks on each object objparam = objparams[iobj] obj = dbobj.typeclass # this saves dbobj if not done already obj.basetype_setup() obj.at_object_creation() if objparam[1]: # permissions obj.permissions.add(objparam[1]) if objparam[2]: # locks obj.locks.add(objparam[2]) if objparam[3]: # aliases obj.aliases.add(objparam[3]) if objparam[4]: # nattributes for key, value in objparam[4].items(): obj.nattributes.add(key, value) if objparam[5]: # attributes keys, values = objparam[5].keys(), objparam[5].values() obj.attributes.batch_add(keys, values) obj.basetype_posthook_setup() objs.append(obj) return objs
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. Any game object is a combination of a database object that stores data persistently to the database, and a typeclass, which on-the-fly 'decorates' the database object into whataver different type of object it is supposed to be in the game. See src.objects.managers for methods to manipulate existing objects in the database. src.objects.objects holds the base typeclasses and src.objects.models hold the database model. report_to is an optional object for reporting errors to in string form. If report_to is not set, errors will be raised as en Exception containing the error message. If set, this method will return None upon errors. nohome - this allows the creation of objects without a default home location; this only used when creating the default location itself or during unittests """ global _Object, _ObjectDB if not _Object: from src.objects.objects import Object as _Object if not _ObjectDB: from src.objects.models import ObjectDB as _ObjectDB # input validation if not typeclass: typeclass = settings.BASE_OBJECT_TYPECLASS elif isinstance(typeclass, _ObjectDB): # this is already an objectdb instance, extract its typeclass typeclass = typeclass.typeclass.path elif isinstance(typeclass, _Object) or utils.inherits_from( typeclass, _Object): # this is already an object typeclass, extract its path typeclass = typeclass.path typeclass = utils.to_unicode(typeclass) # Setup input for the create command location = handle_dbref(location, _ObjectDB) destination = handle_dbref(destination, _ObjectDB) home = handle_dbref(home, _ObjectDB) if not home: try: home = handle_dbref(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 database object all in one go new_db_object = _ObjectDB(db_key=key, db_location=location, db_destination=destination, db_home=home, db_typeclass_path=typeclass) if not key: # the object should always have a key, so if not set we give a default new_db_object.key = "#%i" % new_db_object.dbid # this will either load the typeclass or the default one (will also save object) new_object = new_db_object.typeclass if not _GA(new_object, "is_typeclass")(typeclass, exact=True): # this will fail if we gave a typeclass as input and it still # gave us a default try: SharedMemoryModel.delete(new_db_object) except AssertionError: # this happens if object was never created pass if report_to: report_to = handle_dbref(report_to, _ObjectDB) _GA(report_to, "msg")("Error creating %s (%s).\n%s" % (new_db_object.key, typeclass, _GA(new_db_object, "typeclass_last_errmsg"))) return None else: raise Exception(_GA(new_db_object, "typeclass_last_errmsg")) # from now on we can use the typeclass object # as if it was the database object. # call the hook methods. This is where all at_creation # customization happens as the typeclass stores custom # things on its database object. # note - this may override input keys, locations etc! new_object.basetype_setup() # setup the basics of Exits, Characters etc. new_object.at_object_creation() # we want the input to override that set in the hooks, so # we re-apply those if needed if new_object.key != key: new_object.key = key if new_object.location != location: new_object.location = location if new_object.home != home: new_object.home = home if new_object.destination != destination: new_object.destination = destination # custom-given perms/locks do overwrite hooks if permissions: new_object.permissions.add(permissions) if locks: new_object.locks.add(locks) if aliases: new_object.aliases.add(aliases) # trigger relevant move_to hooks in order to display messages. if location: new_object.at_object_receive(new_object, None) new_object.at_after_move(new_object) # post-hook setup (mainly used by Exits) new_object.basetype_posthook_setup() 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. '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) # call shutdown hooks on all cached objects from src.objects.models import ObjectDB #from src.players.models import PlayerDB from src.server.models import ServerConfig if mode == 'reload': # call restart hooks yield [(o.typeclass, o.at_server_reload()) for o in ObjectDB.get_all_cached_instances()] yield [(p.typeclass, p.at_server_reload()) for p in PlayerDB.get_all_cached_instances()] yield [(s.typeclass, s.pause(), s.at_server_reload()) for s in ScriptDB.get_all_cached_instances()] yield self.sessions.all_sessions_portal_sync() ServerConfig.objects.conf("server_restart_mode", "reload") if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.at_server_reload_stop() else: if mode == 'reset': # don't call disconnect hooks on reset yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()] else: # shutdown yield [_SA(p, "is_connected", False) for p in PlayerDB.get_all_cached_instances()] yield [(o.typeclass, o.at_disconnect(), o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()] yield [(p.typeclass, p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()] yield [(s.typeclass, s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()] ServerConfig.objects.conf("server_restart_mode", "reset") if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.at_server_cold_stop() if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.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 reactor.callLater(0, reactor.stop)
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. '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) # call shutdown hooks on all cached objects from src.objects.models import ObjectDB #from src.players.models import PlayerDB from src.server.models import ServerConfig if mode == 'reload': # call restart hooks yield [(o.typeclass, o.at_server_reload()) for o in ObjectDB.get_all_cached_instances()] yield [(p.typeclass, p.at_server_reload()) for p in PlayerDB.get_all_cached_instances()] yield [(s.typeclass, s.pause(), s.at_server_reload()) for s in ScriptDB.get_all_cached_instances()] yield self.sessions.all_sessions_portal_sync() ServerConfig.objects.conf("server_restart_mode", "reload") from src.server.oobhandler import OOB_HANDLER OOB_HANDLER.save() from src.scripts.tickerhandler import TICKER_HANDLER TICKER_HANDLER.save() if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.at_server_reload_stop() else: if mode == 'reset': # don't unset the is_connected flag on reset, otherwise # same as shutdown yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()] yield [(p.typeclass, p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()] else: # shutdown yield [_SA(p, "is_connected", False) for p in PlayerDB.get_all_cached_instances()] yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()] yield [(p.typeclass, p.unpuppet_all(), p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()] yield [(s.typeclass, s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()] yield ObjectDB.objects.clear_all_sessids() ServerConfig.objects.conf("server_restart_mode", "reset") if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.at_server_cold_stop() # stopping time from src.utils import gametime gametime.save() if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.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 reactor.callLater(0, reactor.stop)
def dummy(item): """This function exists for types we don't want to do anything with yet.""" thing = ObjectDB(name=item["name"]) thing.save()
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. Any game object is a combination of a database object that stores data persistently to the database, and a typeclass, which on-the-fly 'decorates' the database object into whataver different type of object it is supposed to be in the game. See src.objects.managers for methods to manipulate existing objects in the database. src.objects.objects holds the base typeclasses and src.objects.models hold the database model. report_to is an optional object for reporting errors to in string form. If report_to is not set, errors will be raised as en Exception containing the error message. If set, this method will return None upon errors. nohome - this allows the creation of objects without a default home location; this only used when creating default location itself or during unittests """ global _Object, _ObjectDB if not _Object: from src.objects.objects import Object as _Object if not _ObjectDB: from src.objects.models import ObjectDB as _ObjectDB # input validation if not typeclass: typeclass = settings.BASE_OBJECT_TYPECLASS elif isinstance(typeclass, _ObjectDB): # this is already an objectdb instance, extract its typeclass typeclass = typeclass.typeclass.path elif isinstance(typeclass, _Object) or utils.inherits_from( typeclass, _Object): # this is already an object typeclass, extract its path typeclass = typeclass.path # handle eventual #dbref input location = handle_dbref(location, _ObjectDB) home = handle_dbref(home, _ObjectDB) destination = handle_dbref(destination, _ObjectDB) report_to = handle_dbref(report_to, _ObjectDB) # create new database object new_db_object = _ObjectDB() # assign the typeclass typeclass = utils.to_unicode(typeclass) new_db_object.typeclass_path = typeclass # the name/key is often set later in the typeclass. This # is set here as a failsafe. if key: new_db_object.key = key else: new_db_object.key = "#%i" % new_db_object.dbid # this will either load the typeclass or the default one new_object = new_db_object.typeclass if not _GA(new_object, "is_typeclass")(typeclass, exact=True): # this will fail if we gave a typeclass as input and it still # gave us a default SharedMemoryModel.delete(new_db_object) if report_to: _GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_object.key, typeclass, _GA(new_db_object, "typeclass_last_errmsg"))) return None else: raise Exception(_GA(new_db_object, "typeclass_last_errmsg")) # from now on we can use the typeclass object # as if it was the database object. new_object.destination = destination # call the hook method. This is where all at_creation # customization happens as the typeclass stores custom # things on its database object. new_object.basetype_setup() # setup the basics of Exits, Characters etc. new_object.at_object_creation() # custom-given perms/locks overwrite hooks if permissions: new_object.permissions.add(permissions) if locks: new_object.locks.add(locks) if aliases: new_object.aliases.add(aliases) if home: new_object.home = home else: # we shouldn't need to handle dbref here (home handler should fix it), but some have # reported issues here (issue 446). try: new_object.home = handle_dbref(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) # perform a move_to in order to display eventual messages. if location: new_object.move_to(location, quiet=True) else: # rooms would have location=None. new_object.location = None # post-hook setup (mainly used by Exits) new_object.basetype_posthook_setup() new_object.save() return new_object