Example #1
0
def monitor(session, *args, **kwargs):
    """
    Adds monitoring to a given property or Attribute.

    Kwargs:
      name (str): The name of the property or Attribute
        to report. No db_* prefix is needed. Only names
        in the _monitorable dict earlier in this module
        are accepted.
      stop (bool): Stop monitoring the above name.

    """
    from evennia.scripts.monitorhandler import MONITOR_HANDLER

    name = kwargs.get("name", None)
    if name and name in _monitorable and session.puppet:
        field_name = _monitorable[name]
        obj = session.puppet
        if kwargs.get("stop", False):
            MONITOR_HANDLER.remove(obj, field_name, idstring=session.sessid)
        else:
            # the handler will add fieldname and obj to the kwargs automatically
            MONITOR_HANDLER.add(
                obj,
                field_name,
                _on_monitor_change,
                idstring=session.sessid,
                persistent=False,
                name=name,
                session=session,
            )
Example #2
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()
Example #3
0
    def at_post_portal_sync(self, mode):
        """
        This is called just after the portal has finished syncing back data to the server
        after reconnecting.

        Args:
            mode (str): One of reload, reset or shutdown.

        """

        from evennia.scripts.monitorhandler import MONITOR_HANDLER
        MONITOR_HANDLER.restore(mode == 'reload')

        from evennia.scripts.tickerhandler import TICKER_HANDLER
        TICKER_HANDLER.restore(mode == 'reload')

        # after sync is complete we force-validate all scripts
        # (this also starts any that didn't yet start)
        ScriptDB.objects.validate(init_mode=mode)

        # start the task handler
        from evennia.scripts.taskhandler import TASK_HANDLER
        TASK_HANDLER.load()
        TASK_HANDLER.create_delays()

        # delete the temporary setting
        ServerConfig.objects.conf("server_restart_mode", delete=True)
Example #4
0
    def at_post_portal_sync(self):
        """
        This is called just after the portal has finished syncing back data to the server
        after reconnecting.
        """
        # one of reload, reset or shutdown
        mode = self.getset_restart_mode()

        from evennia.scripts.monitorhandler import MONITOR_HANDLER
        MONITOR_HANDLER.restore(mode == 'reload')

        from evennia.scripts.tickerhandler import TICKER_HANDLER
        TICKER_HANDLER.restore(mode == 'reload')

        # after sync is complete we force-validate all scripts
        # (this also starts any that didn't yet start)
        ScriptDB.objects.validate(init_mode=mode)

        # start the task handler
        from evennia.scripts.taskhandler import TASK_HANDLER
        TASK_HANDLER.load()
        TASK_HANDLER.create_delays()

        # delete the temporary setting
        ServerConfig.objects.conf("server_restart_mode", delete=True)
Example #5
0
def monitor(session, *args, **kwargs):
    """
    Adds monitoring to a given property or Attribute.

    Kwargs:
      name (str): The name of the property or Attribute
        to report. No db_* prefix is needed. Only names
        in the _monitorable dict earlier in this module
        are accepted.
      stop (bool): Stop monitoring the above name.

    """
    from evennia.scripts.monitorhandler import MONITOR_HANDLER
    name = kwargs.get("name", None)
    if name and name in _monitorable and session.puppet:
        field_name = _monitorable[name]
        obj = session.puppet
        if kwargs.get("stop", False):
            MONITOR_HANDLER.remove(obj, field_name, idstring=session.sessid)
        else:
            # the handler will add fieldname and obj to the kwargs automatically
            MONITOR_HANDLER.add(obj,
                                field_name,
                                _on_monitor_change,
                                idstring=session.sessid,
                                persistent=False,
                                name=name,
                                session=session)
Example #6
0
    def at_post_portal_sync(self, mode):
        """
        This is called just after the portal has finished syncing back data to the server
        after reconnecting.

        Args:
            mode (str): One of reload, reset or shutdown.

        """

        from evennia.scripts.monitorhandler import MONITOR_HANDLER

        MONITOR_HANDLER.restore(mode == "reload")

        from evennia.scripts.tickerhandler import TICKER_HANDLER

        TICKER_HANDLER.restore(mode == "reload")

        # after sync is complete we force-validate all scripts
        # (this also starts any that didn't yet start)
        ScriptDB.objects.validate(init_mode=mode)

        # start the task handler
        from evennia.scripts.taskhandler import TASK_HANDLER

        TASK_HANDLER.load()
        TASK_HANDLER.create_delays()

        # check so default channels exist
        from evennia.comms.models import ChannelDB
        from evennia.accounts.models import AccountDB
        from evennia.utils.create import create_channel

        god_account = AccountDB.objects.get(id=1)
        # mudinfo
        mudinfo_chan = settings.CHANNEL_MUDINFO
        if not mudinfo_chan:
            raise RuntimeError("settings.CHANNEL_MUDINFO must be defined.")
        if not ChannelDB.objects.filter(db_key=mudinfo_chan["key"]):
            channel = create_channel(**mudinfo_chan)
            channel.connect(god_account)
        # connectinfo
        connectinfo_chan = settings.CHANNEL_MUDINFO
        if connectinfo_chan:
            if not ChannelDB.objects.filter(db_key=mudinfo_chan["key"]):
                channel = create_channel(**connectinfo_chan)
        # default channels
        for chan_info in settings.DEFAULT_CHANNELS:
            if not ChannelDB.objects.filter(db_key=chan_info["key"]):
                channel = create_channel(**chan_info)
                channel.connect(god_account)

        # delete the temporary setting
        ServerConfig.objects.conf("server_restart_mode", delete=True)
Example #7
0
def webclient_options(session, *args, **kwargs):
    """
    Handles retrieving and changing of options related to the webclient.

    If kwargs is empty (or contains just a "cmdid"), the saved options will be
    sent back to the session.
    A monitor handler will be created to inform the client of any future options
    that changes.

    If kwargs is not empty, the key/values stored in there will be persisted
    to the account object.

    Keyword Args:
        <option name>: an option to save

    """
    account = session.account

    clientoptions = account.db._saved_webclient_options
    if not clientoptions:
        # No saved options for this account, copy and save the default.
        account.db._saved_webclient_options = settings.WEBCLIENT_OPTIONS.copy()
        # Get the _SaverDict created by the database.
        clientoptions = account.db._saved_webclient_options

    # The webclient adds a cmdid to every kwargs, but we don't need it.
    try:
        del kwargs["cmdid"]
    except KeyError:
        pass

    if not kwargs:
        # No kwargs: we are getting the stored options
        # Convert clientoptions to regular dict for sending.
        session.msg(webclient_options=dict(clientoptions))

        # Create a monitor. If a monitor already exists then it will replace
        # the previous one since it would use the same idstring
        from evennia.scripts.monitorhandler import MONITOR_HANDLER

        MONITOR_HANDLER.add(
            account,
            "_saved_webclient_options",
            _on_webclient_options_change,
            idstring=session.sessid,
            persistent=False,
            session=session,
        )
    else:
        # kwargs provided: persist them to the account object
        for key, value in kwargs.items():
            clientoptions[key] = value
Example #8
0
    def save(self, *args, **kwargs):
        """
        Central database save operation.

        Notes:
            Arguments as per Django documentation.
            Calls `self.at_<fieldname>_postsave(new)`
            (this is a wrapper set by oobhandler:
            self._oob_at_<fieldname>_postsave())

        """
        global _MONITOR_HANDLER
        if not _MONITOR_HANDLER:
            from evennia.scripts.monitorhandler import MONITOR_HANDLER as _MONITOR_HANDLER

        if _IS_SUBPROCESS:
            # we keep a store of objects modified in subprocesses so
            # we know to update their caches in the central process
            global PROC_MODIFIED_COUNT, PROC_MODIFIED_OBJS
            PROC_MODIFIED_COUNT += 1
            PROC_MODIFIED_OBJS[PROC_MODIFIED_COUNT] = self

        if _IS_MAIN_THREAD:
            # in main thread - normal operation
            super(SharedMemoryModel, self).save(*args, **kwargs)
        else:
            # in another thread; make sure to save in reactor thread
            def _save_callback(cls, *args, **kwargs):
                super(SharedMemoryModel, cls).save(*args, **kwargs)

            callFromThread(_save_callback, self, *args, **kwargs)

        # update field-update hooks and eventual OOB watchers
        new = False
        if "update_fields" in kwargs and kwargs["update_fields"]:
            # get field objects from their names
            update_fields = (self._meta.get_field(fieldname)
                             for fieldname in kwargs.get("update_fields"))
        else:
            # meta.fields are already field objects; get them all
            new = True
            update_fields = self._meta.fields
        for field in update_fields:
            fieldname = field.name
            # trigger eventual monitors
            _MONITOR_HANDLER.at_update(self, fieldname)
            # if a hook is defined it must be named exactly on this form
            hookname = "at_%s_postsave" % fieldname
            if hasattr(self, hookname) and callable(_GA(self, hookname)):
                _GA(self, hookname)(new)
Example #9
0
    def save(self, *args, **kwargs):
        """
        Central database save operation.

        Notes:
            Arguments as per Django documentation.
            Calls `self.at_<fieldname>_postsave(new)`
            (this is a wrapper set by oobhandler:
            self._oob_at_<fieldname>_postsave())

        """
        global _MONITOR_HANDLER
        if not _MONITOR_HANDLER:
            from evennia.scripts.monitorhandler import MONITOR_HANDLER as _MONITORHANDLER

        if _IS_SUBPROCESS:
            # we keep a store of objects modified in subprocesses so
            # we know to update their caches in the central process
            global PROC_MODIFIED_COUNT, PROC_MODIFIED_OBJS
            PROC_MODIFIED_COUNT += 1
            PROC_MODIFIED_OBJS[PROC_MODIFIED_COUNT] = self

        if _IS_MAIN_THREAD:
            # in main thread - normal operation
            super(SharedMemoryModel, self).save(*args, **kwargs)
        else:
            # in another thread; make sure to save in reactor thread
            def _save_callback(cls, *args, **kwargs):
                super(SharedMemoryModel, cls).save(*args, **kwargs)
            callFromThread(_save_callback, self, *args, **kwargs)

        # update field-update hooks and eventual OOB watchers
        new = False
        if "update_fields" in kwargs and kwargs["update_fields"]:
            # get field objects from their names
            update_fields = (self._meta.get_field(fieldname)
                             for fieldname in kwargs.get("update_fields"))
        else:
            # meta.fields are already field objects; get them all
            new =True
            update_fields = self._meta.fields
        for field in update_fields:
            fieldname = field.name
            # trigger eventual monitors
            _MONITORHANDLER.at_update(self, fieldname)
            # if a hook is defined it must be named exactly on this form
            hookname = "at_%s_postsave" % fieldname
            if hasattr(self, hookname) and callable(_GA(self, hookname)):
                _GA(self, hookname)(new)
Example #10
0
def msdp_list(session, *args, **kwargs):
    """
    MSDP LIST command

    """
    from evennia.scripts.monitorhandler import MONITOR_HANDLER
    args_lower = [arg.lower() for arg in args]
    if "commands" in args_lower:
        inputfuncs = [
            key[5:] if key.startswith("msdp_") else key
            for key in session.sessionhandler.get_inputfuncs().keys()
        ]
        session.msg(commands=(inputfuncs, {}))
    if "lists" in args_lower:
        session.msg(lists=([
            'commands', 'lists', 'configurable_variables',
            'reportable_variables', 'reported_variables', 'sendable_variables'
        ], {}))
    if "configurable_variables" in args_lower:
        session.msg(configurable_variables=(_CLIENT_OPTIONS, {}))
    if "reportable_variables" in args_lower:
        session.msg(reportable_variables=(_monitorable, {}))
    if "reported_variables" in args_lower:
        obj = session.puppet
        monitor_infos = MONITOR_HANDLER.all(obj=obj)
        fieldnames = [tup[1] for tup in monitor_infos]
        session.msg(reported_variables=(fieldnames, {}))
    if "sendable_variables" in args_lower:
        # no default sendable variables
        session.msg(sendable_variables=([], {}))
Example #11
0
def webclient_options(session, *args, **kwargs):
    """
    Handles retrieving and changing of options related to the webclient.

    If kwargs is empty (or contains just a "cmdid"), the saved options will be
    sent back to the session.
    A monitor handler will be created to inform the client of any future options
    that changes.

    If kwargs is not empty, the key/values stored in there will be persisted
    to the account object.

    Kwargs:
        <option name>: an option to save
    """
    account = session.account

    clientoptions = account.db._saved_webclient_options
    if not clientoptions:
        # No saved options for this account, copy and save the default.
        account.db._saved_webclient_options = settings.WEBCLIENT_OPTIONS.copy()
        # Get the _SaverDict created by the database.
        clientoptions = account.db._saved_webclient_options

    # The webclient adds a cmdid to every kwargs, but we don't need it.
    try:
        del kwargs["cmdid"]
    except KeyError:
        pass

    if not kwargs:
        # No kwargs: we are getting the stored options
        # Convert clientoptions to regular dict for sending.
        session.msg(webclient_options=dict(clientoptions))

        # Create a monitor. If a monitor already exists then it will replace
        # the previous one since it would use the same idstring
        from evennia.scripts.monitorhandler import MONITOR_HANDLER
        MONITOR_HANDLER.add(account, "_saved_webclient_options",
                            _on_webclient_options_change,
                            idstring=session.sessid, persistent=False,
                            session=session)
    else:
        # kwargs provided: persist them to the account object
        for key, value in kwargs.iteritems():
            clientoptions[key] = value
Example #12
0
def webclient_options(session, *args, **kwargs):
    """
    Handles retrieving and changing of options related to the webclient.

    If kwargs is empty (or contains just a "cmdid"), the saved options will be
    sent back to the session.
    A monitor handler will be created to inform the client of any future options
    that changes.

    If kwargs is not empty, the key/values stored in there will be persisted
    to the player object.

    Kwargs:
        <option name>: an option to save
    """
    player = session.player

    clientoptions = settings.WEBCLIENT_OPTIONS.copy()
    storedoptions = player.db._saved_webclient_options or {}
    clientoptions.update(storedoptions)

    # The webclient adds a cmdid to every kwargs, but we don't need it.
    try:
        del kwargs["cmdid"]
    except KeyError:
        pass

    if not kwargs:
        # No kwargs: we are getting the stored options
        session.msg(webclient_options=clientoptions)

        # Create a monitor. If a monitor already exists then it will replace
        # the previous one since it would use the same idstring
        from evennia.scripts.monitorhandler import MONITOR_HANDLER
        MONITOR_HANDLER.add(player,
                            "_saved_webclient_options",
                            _on_webclient_options_change,
                            idstring=session.sessid,
                            persistent=False,
                            session=session)
    else:
        # kwargs provided: persist them to the player object
        for key, value in kwargs.iteritems():
            clientoptions[key] = value

        player.db._saved_webclient_options = clientoptions
Example #13
0
def monitored(session, *args, **kwargs):
    """
    Report on what is being monitored

    """
    from evennia.scripts.monitorhandler import MONITOR_HANDLER
    obj = session.puppet
    monitors = MONITOR_HANDLER.all(obj=obj)
    session.msg(monitored=(monitors, {}))
Example #14
0
    def at_disconnect(self, reason=None):
        """
        Hook called by sessionhandler when disconnecting this session.

        """
        if self.logged_in:
            account = self.account
            if self.puppet:
                account.unpuppet_object(self)
            uaccount = account
            uaccount.last_login = timezone.now()
            uaccount.save()
            # calling account hook
            account.at_disconnect(reason)
            self.logged_in = False
            if not self.sessionhandler.sessions_from_account(account):
                # no more sessions connected to this account
                account.is_connected = False
            # this may be used to e.g. delete account after disconnection etc
            account.at_post_disconnect()
            # remove any webclient settings monitors associated with this
            # session
            MONITOR_HANDLER.remove(account, "_saved_webclient_options", self.sessid)
Example #15
0
    def at_disconnect(self):
        """
        Hook called by sessionhandler when disconnecting this session.

        """
        if self.logged_in:
            player = self.player
            if self.puppet:
                player.unpuppet_object(self)
            uaccount = player
            uaccount.last_login = timezone.now()
            uaccount.save()
            # calling player hook
            player.at_disconnect()
            self.logged_in = False
            if not self.sessionhandler.sessions_from_player(player):
                # no more sessions connected to this player
                player.is_connected = False
            # this may be used to e.g. delete player after disconnection etc
            player.at_post_disconnect()
            # remove any webclient settings monitors associated with this
            # session
            MONITOR_HANDLER.remove(player, "_saved_webclient_options",
                                   self.sessid)
Example #16
0
def monitor(session, *args, **kwargs):
    """
    Adds monitoring to a given property or Attribute.

    Keyword Args:
      name (str): The name of the property or Attribute
        to report. No db_* prefix is needed. Only names
        in the _monitorable dict earlier in this module
        are accepted.
      stop (bool): Stop monitoring the above name.
      outputfunc_name (str, optional): Change the name of
        the outputfunc name. This is used e.g. by MSDP which
        has its own specific output format.

    """
    from evennia.scripts.monitorhandler import MONITOR_HANDLER

    name = kwargs.get("name", None)
    outputfunc_name = kwargs("outputfunc_name", "monitor")
    if name and name in _monitorable and session.puppet:
        field_name = _monitorable[name]
        obj = session.puppet
        if kwargs.get("stop", False):
            MONITOR_HANDLER.remove(obj, field_name, idstring=session.sessid)
        else:
            # the handler will add fieldname and obj to the kwargs automatically
            MONITOR_HANDLER.add(
                obj,
                field_name,
                _on_monitor_change,
                idstring=session.sessid,
                persistent=False,
                name=name,
                session=session,
                outputfunc_name=outputfunc_name,
            )
Example #17
0
    def at_disconnect(self, reason=None):
        """
        Hook called by sessionhandler when disconnecting this session.

        """
        if self.logged_in:
            account = self.account
            if self.puppet:
                account.unpuppet_object(self)
            uaccount = account
            uaccount.last_login = timezone.now()
            uaccount.save()
            # calling account hook
            account.at_disconnect(reason)
            self.logged_in = False
            if not self.sessionhandler.sessions_from_account(account):
                # no more sessions connected to this account
                account.is_connected = False
            # this may be used to e.g. delete account after disconnection etc
            account.at_post_disconnect()
            # remove any webclient settings monitors associated with this
            # session
            MONITOR_HANDLER.remove(account, "_saved_webclient_options",
                                   self.sessid)
Example #18
0
    def at_disconnect(self):
        """
        Hook called by sessionhandler when disconnecting this session.

        """
        if self.logged_in:
            player = self.player
            if self.puppet:
                player.unpuppet_object(self)
            uaccount = player
            uaccount.last_login = timezone.now()
            uaccount.save()
            # calling player hook
            player.at_disconnect()
            self.logged_in = False
            if not self.sessionhandler.sessions_from_player(player):
                # no more sessions connected to this player
                player.is_connected = False
            # this may be used to e.g. delete player after disconnection etc
            player.at_post_disconnect()
            # remove any webclient settings monitors associated with this
            # session
            MONITOR_HANDLER.remove(player, "_saved_webclient_options",
                                   self.sessid)
Example #19
0
    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)
Example #20
0
    def save(self, *args, **kwargs):
        """
        Central database save operation.

        Notes:
            Arguments as per Django documentation.
            Calls `self.at_<fieldname>_postsave(new)`
            (this is a wrapper set by oobhandler:
            self._oob_at_<fieldname>_postsave())

        """
        global _MONITOR_HANDLER
        if not _MONITOR_HANDLER:
            from evennia.scripts.monitorhandler import MONITOR_HANDLER as _MONITOR_HANDLER

        if _IS_SUBPROCESS:
            # we keep a store of objects modified in subprocesses so
            # we know to update their caches in the central process
            global PROC_MODIFIED_COUNT, PROC_MODIFIED_OBJS
            PROC_MODIFIED_COUNT += 1
            PROC_MODIFIED_OBJS[PROC_MODIFIED_COUNT] = self

        if _IS_MAIN_THREAD:
            # in main thread - normal operation
            try:
                super().save(*args, **kwargs)
            except DatabaseError:
                # we handle the 'update_fields did not update any rows' error that
                # may happen due to timing issues with attributes
                ufields_removed = kwargs.pop('update_fields', None)
                if ufields_removed:
                    super().save(*args, **kwargs)
                else:
                    raise
        else:
            # in another thread; make sure to save in reactor thread
            def _save_callback(cls, *args, **kwargs):
                super().save(*args, **kwargs)

            callFromThread(_save_callback, self, *args, **kwargs)

        if not self.pk:
            # this can happen if some of the startup methods immediately
            # delete the object (an example are Scripts that start and die immediately)
            return

        # update field-update hooks and eventual OOB watchers
        new = False
        if "update_fields" in kwargs and kwargs["update_fields"]:
            # get field objects from their names
            update_fields = (self._meta.get_field(fieldname)
                             for fieldname in kwargs.get("update_fields"))
        else:
            # meta.fields are already field objects; get them all
            new = True
            update_fields = self._meta.fields
        for field in update_fields:
            fieldname = field.name
            # trigger eventual monitors
            _MONITOR_HANDLER.at_update(self, fieldname)
            # if a hook is defined it must be named exactly on this form
            hookname = "at_%s_postsave" % fieldname
            if hasattr(self, hookname) and callable(_GA(self, hookname)):
                _GA(self, hookname)(new)


#            # if a trackerhandler is set on this object, update it with the
#            # fieldname and the new value
#            fieldtracker = "_oob_at_%s_postsave" % fieldname
#            if hasattr(self, fieldtracker):
#                _GA(self, fieldtracker)(fieldname)
        pass
Example #21
0
    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())