mirror of
https://github.com/evennia/evennia.git
synced 2026-03-24 08:46:31 +01:00
Fix error in MonitorHandler recovering a saved Session across a reload. This probably affected the TickerHandler as well. Add a new hook to the server object that gets called once the portal has synced, and run the monitorhandler/tickerhandler restores there. Also some changes to the serialization of Sessions. Resolves #1164.
This commit is contained in:
parent
b46bc9b2aa
commit
052e1845a2
6 changed files with 70 additions and 34 deletions
|
|
@ -133,7 +133,7 @@ class ScriptDBManager(TypedObjectManager):
|
|||
return nr_deleted
|
||||
|
||||
def validate(self, scripts=None, obj=None, key=None, dbref=None,
|
||||
init_mode=False):
|
||||
init_mode=None):
|
||||
"""
|
||||
This will step through the script database and make sure
|
||||
all objects run scripts that are still valid in the context
|
||||
|
|
@ -153,7 +153,7 @@ class ScriptDBManager(TypedObjectManager):
|
|||
particular id.
|
||||
init_mode (str, optional): This is used during server
|
||||
upstart and can have three values:
|
||||
- `False` (no init mode). Called during run.
|
||||
- `None` (no init mode). Called during run.
|
||||
- `"reset"` - server reboot. Kill non-persistent scripts
|
||||
- `"reload"` - server reload. Keep non-persistent scripts.
|
||||
Returns:
|
||||
|
|
|
|||
|
|
@ -71,11 +71,16 @@ class MonitorHandler(object):
|
|||
restored_monitors = dbunserialize(restored_monitors)
|
||||
for (obj, fieldname, idstring, path, persistent, kwargs) in restored_monitors:
|
||||
try:
|
||||
if not persistent and not server_reload:
|
||||
if not server_reload and not persistent:
|
||||
# this monitor will not be restarted
|
||||
continue
|
||||
if "session" in kwargs and not kwargs["session"]:
|
||||
# the session was removed because it no longer
|
||||
# exists. Don't restart the monitor.
|
||||
continue
|
||||
modname, varname = path.rsplit(".", 1)
|
||||
callback = variable_from_module(modname, varname)
|
||||
|
||||
if obj and hasattr(obj, fieldname):
|
||||
self.monitors[obj][fieldname][idstring] = (callback, persistent, kwargs)
|
||||
except Exception:
|
||||
|
|
@ -116,6 +121,13 @@ class MonitorHandler(object):
|
|||
persistent (bool, optional): If False, the monitor will survive
|
||||
a server reload but not a cold restart. This is default.
|
||||
|
||||
Kwargs:
|
||||
session (Session): If this keyword is given, the monitorhandler will
|
||||
correctly analyze it and remove the monitor if after a reload/reboot
|
||||
the session is no longer valid.
|
||||
any (any): Any other kwargs are passed on to the callback. Remember that
|
||||
all kwargs must be possible to pickle!
|
||||
|
||||
"""
|
||||
if not fieldname.startswith("db_") or not hasattr(obj, fieldname):
|
||||
# an Attribute - we track its db_value field
|
||||
|
|
|
|||
|
|
@ -355,7 +355,10 @@ def _on_monitor_change(**kwargs):
|
|||
obj = kwargs["obj"]
|
||||
name = kwargs["name"]
|
||||
session = kwargs["session"]
|
||||
session.msg(monitor={"name": name, "value": _GA(obj, fieldname)})
|
||||
# the session may be None if the char quits and someone
|
||||
# else then edits the object
|
||||
if session:
|
||||
session.msg(monitor={"name": name, "value": _GA(obj, fieldname)})
|
||||
|
||||
|
||||
def monitor(session, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -278,17 +278,10 @@ class Evennia(object):
|
|||
[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'))
|
||||
mode = self.getset_restart_mode()
|
||||
|
||||
# call correct server hook based on start file value
|
||||
if mode in ('True', 'reload'):
|
||||
if mode == 'reload':
|
||||
# True was the old reload flag, kept for compatibilty
|
||||
self.at_server_reload_start()
|
||||
elif mode == 'reset':
|
||||
|
|
@ -301,15 +294,19 @@ class Evennia(object):
|
|||
# always call this regardless of start type
|
||||
self.at_server_start()
|
||||
|
||||
def set_restart_mode(self, mode=None):
|
||||
def getset_restart_mode(self, mode=None):
|
||||
"""
|
||||
This manages the flag file that tells the runner if the server is
|
||||
reloading, resetting or shutting down. Valid modes are
|
||||
'reload', 'reset', 'shutdown' and None.
|
||||
If mode is None, no change will be done to the flag file.
|
||||
reloading, resetting or shutting down.
|
||||
|
||||
Args:
|
||||
mode (string or None, optional): Valid values are
|
||||
'reload', 'reset', 'shutdown' and `None`. If mode is `None`,
|
||||
no change will be done to the flag file.
|
||||
Returns:
|
||||
mode (str): The currently active restart mode, either just
|
||||
set or previously set.
|
||||
|
||||
Either way, the active restart setting (Restart=True/False) is
|
||||
returned so the server knows which more it's in.
|
||||
"""
|
||||
if mode is None:
|
||||
with open(SERVER_RESTART, 'r') as f:
|
||||
|
|
@ -343,7 +340,7 @@ class Evennia(object):
|
|||
# once; we don't need to run the shutdown procedure again.
|
||||
defer.returnValue(None)
|
||||
|
||||
mode = self.set_restart_mode(mode)
|
||||
mode = self.getset_restart_mode(mode)
|
||||
|
||||
from evennia.objects.models import ObjectDB
|
||||
#from evennia.players.models import PlayerDB
|
||||
|
|
@ -423,6 +420,26 @@ class Evennia(object):
|
|||
if SERVER_STARTSTOP_MODULE:
|
||||
SERVER_STARTSTOP_MODULE.at_server_reload_start()
|
||||
|
||||
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)
|
||||
|
||||
# delete the temporary setting
|
||||
ServerConfig.objects.conf("server_restart_mode", delete=True)
|
||||
|
||||
def at_server_reload_stop(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -327,14 +327,12 @@ class ServerSessionHandler(SessionHandler):
|
|||
self[sessid] = sess
|
||||
sess.at_sync()
|
||||
|
||||
# after sync is complete we force-validate all scripts
|
||||
# (this also starts them)
|
||||
init_mode = _ServerConfig.objects.conf("server_restart_mode", default=None)
|
||||
_ScriptDB.objects.validate(init_mode=init_mode)
|
||||
_ServerConfig.objects.conf("server_restart_mode", delete=True)
|
||||
# tell the server hook we synced
|
||||
self.server.at_post_portal_sync()
|
||||
# announce the reconnection
|
||||
self.announce_all(_(" ... Server restarted."))
|
||||
|
||||
|
||||
def portal_disconnect(self, session):
|
||||
"""
|
||||
Called from Portal when Portal session closed from the portal
|
||||
|
|
|
|||
|
|
@ -375,19 +375,25 @@ def pack_session(item):
|
|||
can't be safely serialized).
|
||||
|
||||
Args:
|
||||
item (packed_session): The fact that item is a packed Session
|
||||
should be checked before this call.
|
||||
item (Session)): This item must have all properties of a session
|
||||
before entering this call.
|
||||
|
||||
Returns:
|
||||
unpacked (any): Either the original input or converts the
|
||||
internal store back to a Session. If the Session no longer
|
||||
exists, None is returned.
|
||||
packed (tuple or None): A session-packed tuple on the form
|
||||
`(__packed_session__, sessid, conn_time)`. If this sessid
|
||||
does not match a session in the Session handler, None is returned.
|
||||
|
||||
"""
|
||||
_init_globals()
|
||||
return item.conn_time and item.sessid and ('__packed_session__',
|
||||
_GA(item, "sessid"),
|
||||
_GA(item, "conn_time"))
|
||||
session = _SESSION_HANDLER.get(item.sessid)
|
||||
if session and session.conn_time == item.conn_time:
|
||||
# we require connection times to be identical for the Session
|
||||
# to be accepted as actually being a session (sessids gets
|
||||
# reused all the time).
|
||||
return item.conn_time and item.sessid and ('__packed_session__',
|
||||
_GA(item, "sessid"),
|
||||
_GA(item, "conn_time"))
|
||||
return None
|
||||
|
||||
def unpack_session(item):
|
||||
"""
|
||||
|
|
@ -454,7 +460,7 @@ def to_pickle(data):
|
|||
return item.__class__([process_item(val) for val in item])
|
||||
except (AttributeError, TypeError):
|
||||
return [process_item(val) for val in item]
|
||||
elif hasattr(item, "sessid") and hasattr(item, "conn_time") and item.sessid in _SESSION_HANDLER:
|
||||
elif hasattr(item, "sessid") and hasattr(item, "conn_time"):
|
||||
return pack_session(item)
|
||||
return pack_dbobj(item)
|
||||
return process_item(data)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue