Updated the reload and shutdown mecanism to avoid a loop when shutting down from inside the game. Made sure to have server sync correctly with portal at @reload (some session info were lost before). Some other cleanups.
This commit is contained in:
parent
e82515f8cb
commit
94477b8340
8 changed files with 171 additions and 118 deletions
|
|
@ -1,6 +1,7 @@
|
|||
"""
|
||||
Contains the protocols, commands, and client factory needed for the server
|
||||
to service the MUD portal proxy.
|
||||
Contains the protocols, commands, and client factory needed for the Server and Portal
|
||||
to communicate with each other, letting Portal work as a proxy. Both sides use this
|
||||
same protocol.
|
||||
|
||||
The separation works like this:
|
||||
|
||||
|
|
@ -13,24 +14,25 @@ Server - (AMP server) Handles all mud operations. The server holds its own list
|
|||
and when a session connects/disconnects
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
# imports needed on both server and portal side
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
from twisted.protocols import amp
|
||||
from twisted.internet import protocol
|
||||
from django.conf import settings
|
||||
from src.utils.utils import to_str
|
||||
|
||||
from src.server.models import ServerConfig
|
||||
from src.scripts.models import ScriptDB
|
||||
from src.players.models import PlayerDB
|
||||
from src.server.serversession import ServerSession
|
||||
|
||||
PORTAL_RESTART = os.path.join(settings.GAME_DIR, "portal.restart")
|
||||
SERVER_RESTART = os.path.join(settings.GAME_DIR, "server.restart")
|
||||
# these are only needed on the server side, so we delay loading of them
|
||||
# so as to not have to load them on the portal too. Note: It's doubtful
|
||||
# if this really matters, considering many of the
|
||||
# protocols require import of django components (at least settings).
|
||||
_ServerConfig = None
|
||||
_ScriptDB = None
|
||||
_PlayerDB = None
|
||||
_ServerSession = None
|
||||
_ = None #i18n hook
|
||||
|
||||
# communication bits
|
||||
|
||||
|
|
@ -43,10 +45,6 @@ SDISCONNALL = chr(6) # server session disconnect all
|
|||
SSHUTD = chr(7) # server shutdown
|
||||
SSYNC = chr(8) # server session sync
|
||||
|
||||
# i18n
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
def get_restart_mode(restart_file):
|
||||
"""
|
||||
Parse the server/portal restart status
|
||||
|
|
@ -87,7 +85,7 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
|
|||
"""
|
||||
# Initial reconnect delay in seconds.
|
||||
initialDelay = 1
|
||||
#factor = 1.5
|
||||
factor = 1.5
|
||||
maxDelay = 1
|
||||
|
||||
def __init__(self, portal):
|
||||
|
|
@ -115,14 +113,19 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
|
|||
"""
|
||||
Called when the AMP connection to the MUD server is lost.
|
||||
"""
|
||||
if not get_restart_mode(SERVER_RESTART):
|
||||
self.portal.sessions.announce_all(_(" Portal lost connection to Server."))
|
||||
if not hasattr(self, "server_restart_mode"):
|
||||
# Don't translate this; avoiding loading django on portal side.
|
||||
self.portal.sessions.announce_all(" Portal lost connection to Server.")
|
||||
protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
"""
|
||||
Called when an AMP connection attempt to the MUD server fails.
|
||||
"""
|
||||
if hasattr(self, "server_restart_mode"):
|
||||
self.maxDelay = 1
|
||||
else:
|
||||
self.maxDelay = 10
|
||||
self.portal.sessions.announce_all(" ...")
|
||||
protocol.ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
|
||||
|
||||
|
|
@ -214,27 +217,27 @@ class AMPProtocol(amp.AMP):
|
|||
def connectionMade(self):
|
||||
"""
|
||||
This is called when a connection is established
|
||||
between server and portal. It is called on both sides,
|
||||
between server and portal. AMP calls it on both sides,
|
||||
so we need to make sure to only trigger resync from the
|
||||
server side.
|
||||
portal side.
|
||||
"""
|
||||
if hasattr(self.factory, "portal"):
|
||||
# only the portal has the 'portal' property, so we know we are
|
||||
# on the portal side and can initialize the connection.
|
||||
sessdata = self.factory.portal.sessions.get_all_sync_data()
|
||||
#print sessdata
|
||||
self.call_remote_ServerAdmin(0,
|
||||
PSYNC,
|
||||
data=sessdata)
|
||||
if get_restart_mode(SERVER_RESTART):
|
||||
msg = _(" ... Server restarted.")
|
||||
self.factory.portal.sessions.announce_all(msg)
|
||||
self.factory.portal.sessions.at_server_connection()
|
||||
if hasattr(self.factory, "server_restart_mode"):
|
||||
del self.factory.server_restart_mode
|
||||
|
||||
# Error handling
|
||||
|
||||
def errback(self, e, info):
|
||||
"error handler, to avoid dropping connections on server tracebacks."
|
||||
e.trap(Exception)
|
||||
print _("AMP Error for %(info)s: %(e)s") % {'info': info, 'e': e.getErrorMessage()}
|
||||
print "AMP Error for %(info)s: %(e)s" % {'info': info, 'e': e.getErrorMessage()}
|
||||
|
||||
|
||||
# Message definition + helper methods to call/create each message type
|
||||
|
|
@ -255,7 +258,7 @@ class AMPProtocol(amp.AMP):
|
|||
Access method called by the Portal and executed on the Portal.
|
||||
"""
|
||||
#print "msg portal->server (portal side):", sessid, msg
|
||||
self.callRemote(MsgPortal2Server,
|
||||
return self.callRemote(MsgPortal2Server,
|
||||
sessid=sessid,
|
||||
msg=msg,
|
||||
data=dumps(data)).addErrback(self.errback, "MsgPortal2Server")
|
||||
|
|
@ -276,7 +279,7 @@ class AMPProtocol(amp.AMP):
|
|||
Access method called by the Server and executed on the Server.
|
||||
"""
|
||||
#print "msg server->portal (server side):", sessid, msg, data
|
||||
self.callRemote(MsgServer2Portal,
|
||||
return self.callRemote(MsgServer2Portal,
|
||||
sessid=sessid,
|
||||
msg=to_str(msg),
|
||||
data=dumps(data)).addErrback(self.errback, "OOBServer2Portal")
|
||||
|
|
@ -319,7 +322,7 @@ class AMPProtocol(amp.AMP):
|
|||
Access method called by the Server and executed on the Portal.
|
||||
"""
|
||||
#print "oob server->portal (server side):", sessid, data
|
||||
self.callRemote(OOBServer2Portal,
|
||||
return self.callRemote(OOBServer2Portal,
|
||||
sessid=sessid,
|
||||
data=dumps(data)).addErrback(self.errback, "OOBServer2Portal")
|
||||
|
||||
|
|
@ -335,14 +338,28 @@ class AMPProtocol(amp.AMP):
|
|||
|
||||
#print "serveradmin (server side):", sessid, operation, data
|
||||
|
||||
# late import of django-related stuff. This avoids having to
|
||||
# load these also for the portal side.
|
||||
global _ServerConfig, _ScriptDB, _PlayerDB, _ServerSession, _
|
||||
if not _ServerConfig:
|
||||
from src.server.models import ServerConfig as _ServerConfig
|
||||
if not _ScriptDB:
|
||||
from src.scripts.models import ScriptDB as _ScriptDB
|
||||
if not _PlayerDB:
|
||||
from src.players.models import PlayerDB as _PlayerDB
|
||||
if not _ServerSession:
|
||||
from src.server.serversession import ServerSession as _ServerSession
|
||||
if not _:
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
if operation == PCONN: #portal_session_connect
|
||||
# create a new session and sync it
|
||||
sess = ServerSession()
|
||||
sess = _ServerSession()
|
||||
sess.sessionhandler = self.factory.server.sessions
|
||||
sess.load_sync_data(data)
|
||||
if sess.logged_in and sess.uid:
|
||||
# this can happen in the case of auto-authenticating protocols like SSH
|
||||
sess.player = PlayerDB.objects.get_player_from_uid(sess.uid)
|
||||
sess.player = _PlayerDB.objects.get_player_from_uid(sess.uid)
|
||||
sess.at_sync() # this runs initialization without acr
|
||||
|
||||
self.factory.server.sessions.portal_connect(sessid, sess)
|
||||
|
|
@ -358,21 +375,22 @@ class AMPProtocol(amp.AMP):
|
|||
sesslist = []
|
||||
server_sessionhandler = self.factory.server.sessions
|
||||
for sessid, sessdict in data.items():
|
||||
sess = ServerSession()
|
||||
sess = _ServerSession()
|
||||
sess.sessionhandler = server_sessionhandler
|
||||
sess.load_sync_data(sessdict)
|
||||
if sess.uid:
|
||||
sess.player = PlayerDB.objects.get_player_from_uid(sess.uid)
|
||||
sess.player = _PlayerDB.objects.get_player_from_uid(sess.uid)
|
||||
sesslist.append(sess)
|
||||
# replace sessions on server
|
||||
server_sessionhandler.portal_session_sync(sesslist)
|
||||
# after sync is complete we force-validate all scripts (this starts everything)
|
||||
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)
|
||||
|
||||
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)
|
||||
# let the server announce the reconnection
|
||||
server_sessionhandler.announce_all(_(" ... Server restarted."))
|
||||
else:
|
||||
raise Exception(_("operation %(op)s not recognized.") % {'op': operation})
|
||||
raise Exception("operation %(op)s not recognized." % {'op': operation})
|
||||
|
||||
return {}
|
||||
ServerAdmin.responder(amp_server_admin)
|
||||
|
|
@ -384,7 +402,7 @@ class AMPProtocol(amp.AMP):
|
|||
#print "serveradmin (portal side):", sessid, operation, data
|
||||
data = dumps(data)
|
||||
|
||||
self.callRemote(ServerAdmin,
|
||||
return self.callRemote(ServerAdmin,
|
||||
sessid=sessid,
|
||||
operation=operation,
|
||||
data=data).addErrback(self.errback, "ServerAdmin")
|
||||
|
|
@ -398,7 +416,7 @@ class AMPProtocol(amp.AMP):
|
|||
"""
|
||||
data = loads(data)
|
||||
|
||||
#print "portaladmin (portal side):", sessid, operation, data
|
||||
#print "portaladmin (portal side):", sessid, ord(operation), data
|
||||
if operation == SLOGIN: # 'server_session_login'
|
||||
# a session has authenticated; sync it.
|
||||
sess = self.factory.portal.sessions.get_session(sessid)
|
||||
|
|
@ -421,20 +439,19 @@ class AMPProtocol(amp.AMP):
|
|||
# it's about to shut down. We don't overwrite any sessions,
|
||||
# just update data on them and remove eventual ones that are
|
||||
# out of sync (shouldn't happen normally).
|
||||
|
||||
portal_sessionhandler = self.factory.portal.sessions.sessions
|
||||
|
||||
portal_sessionhandler = self.factory.portal.sessions
|
||||
to_save = [sessid for sessid in data if sessid in portal_sessionhandler.sessions]
|
||||
to_delete = [sessid for sessid in data if sessid not in to_save]
|
||||
|
||||
# save protocols
|
||||
for sessid in to_save:
|
||||
portal_sessionhandler.sessions[sessid].load_sync_data(data[sessid])
|
||||
# disconnect missing protocols
|
||||
for sessid in to_delete:
|
||||
portal_sessionhandler.server_disconnect(sessid)
|
||||
# save a flag in case connection is soon lost.
|
||||
self.factory.server_restart_mode = True
|
||||
else:
|
||||
raise Exception(_("operation %(op)s not recognized.") % {'op': operation})
|
||||
raise Exception("operation %(op)s not recognized." % {'op': operation})
|
||||
return {}
|
||||
PortalAdmin.responder(amp_portal_admin)
|
||||
|
||||
|
|
@ -442,10 +459,10 @@ class AMPProtocol(amp.AMP):
|
|||
"""
|
||||
Access method called by the server side.
|
||||
"""
|
||||
#print "portaladmin (server side):", sessid, operation, data
|
||||
#print "portaladmin (server side):", sessid, ord(operation), data
|
||||
data = dumps(data)
|
||||
|
||||
self.callRemote(PortalAdmin,
|
||||
return self.callRemote(PortalAdmin,
|
||||
sessid=sessid,
|
||||
operation=operation,
|
||||
data=data).addErrback(self.errback, "PortalAdmin")
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ class Portal(object):
|
|||
|
||||
# set a callback if the server is killed abruptly,
|
||||
# by Ctrl-C, reboot etc.
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _abrupt=True)
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _reactor_stopping=True)
|
||||
|
||||
self.game_running = False
|
||||
|
||||
|
|
@ -139,26 +139,33 @@ class Portal(object):
|
|||
f.write(str(mode))
|
||||
f.close()
|
||||
|
||||
def shutdown(self, restart=None, _abrupt=False):
|
||||
def shutdown(self, restart=None, _reactor_stopping=False):
|
||||
"""
|
||||
Shuts down the server from inside it.
|
||||
|
||||
restart - True/False sets the flags so the server will be
|
||||
restarted or not. If None, the current flag setting
|
||||
(set at initialization or previous runs) is used.
|
||||
_abrupt - this is set if server is stopped by a kill command,
|
||||
in which case the reactor is dead anyway.
|
||||
_reactor_stopping - this is set if server is already in the process of
|
||||
shutting down; in this case we don't need to stop it again.
|
||||
|
||||
Note that restarting (regardless of the setting) will not work
|
||||
if the Portal is currently running in daemon mode. In that
|
||||
case it always needs to be restarted manually.
|
||||
"""
|
||||
if _reactor_stopping and hasattr(self, "shutdown_complete"):
|
||||
# we get here due to us calling reactor.stop below. No need
|
||||
# to do the shutdown procedure again.
|
||||
return
|
||||
self.set_restart_mode(restart)
|
||||
if not _abrupt:
|
||||
reactor.callLater(0, reactor.stop)
|
||||
if os.name == 'nt' and os.path.exists(PORTAL_PIDFILE):
|
||||
# for Windows we need to remove pid files manually
|
||||
os.remove(PORTAL_PIDFILE)
|
||||
if not _reactor_stopping:
|
||||
# shutting down the reactor will trigger another signal. We set
|
||||
# a flag to avoid loops.
|
||||
self.shutdown_complete = True
|
||||
reactor.callLater(0, reactor.stop)
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ if os.name == 'nt':
|
|||
os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from twisted.application import internet, service
|
||||
from twisted.internet import protocol, reactor, defer
|
||||
from twisted.web import server, static
|
||||
from twisted.internet import reactor, defer
|
||||
import django
|
||||
from django.db import connection
|
||||
from django.conf import settings
|
||||
|
|
@ -106,7 +105,7 @@ class Evennia(object):
|
|||
|
||||
# set a callback if the server is killed abruptly,
|
||||
# by Ctrl-C, reboot etc.
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _abrupt=True)
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _reactor_stopping=True)
|
||||
|
||||
self.game_running = True
|
||||
|
||||
|
|
@ -133,6 +132,7 @@ class Evennia(object):
|
|||
"""
|
||||
This attempts to run the initial_setup script of the server.
|
||||
It returns if this is not the first time the server starts.
|
||||
Once finished the last_initial_setup_step is set to -1.
|
||||
"""
|
||||
last_initial_setup_step = ServerConfig.objects.conf('last_initial_setup_step')
|
||||
if not last_initial_setup_step:
|
||||
|
|
@ -184,10 +184,12 @@ class Evennia(object):
|
|||
returned so the server knows which more it's in.
|
||||
"""
|
||||
if mode == None:
|
||||
if os.path.exists(SERVER_RESTART) and 'True' == open(SERVER_RESTART, 'r').read():
|
||||
f = open(SERVER_RESTART, 'r')
|
||||
if os.path.exists(SERVER_RESTART) and 'True' == f.read():
|
||||
mode = 'reload'
|
||||
else:
|
||||
mode = 'shutdown'
|
||||
f.close()
|
||||
else:
|
||||
restart = mode in ('reload', 'reset')
|
||||
f = open(SERVER_RESTART, 'w')
|
||||
|
|
@ -195,7 +197,8 @@ class Evennia(object):
|
|||
f.close()
|
||||
return mode
|
||||
|
||||
def shutdown(self, mode=None, _abrupt=False):
|
||||
@defer.inlineCallbacks
|
||||
def shutdown(self, mode=None, _reactor_stopping=False):
|
||||
"""
|
||||
Shuts down the server from inside it.
|
||||
|
||||
|
|
@ -204,11 +207,15 @@ class Evennia(object):
|
|||
'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.
|
||||
_abrupt - this is set if server is stopped by a kill command,
|
||||
in which case the reactor is dead anyway.
|
||||
_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.
|
||||
"""
|
||||
mode = self.set_restart_mode(mode)
|
||||
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
|
||||
|
|
@ -217,31 +224,34 @@ class Evennia(object):
|
|||
|
||||
if mode == 'reload':
|
||||
# call restart hooks
|
||||
[(o.typeclass, o.at_server_reload()) for o in ObjectDB.get_all_cached_instances()]
|
||||
[(p.typeclass, p.at_server_reload()) for p in PlayerDB.get_all_cached_instances()]
|
||||
[(s.typeclass, s.pause(), s.at_server_reload()) for s in ScriptDB.get_all_cached_instances()]
|
||||
|
||||
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")
|
||||
|
||||
else:
|
||||
if mode == 'reset':
|
||||
# don't call disconnect hooks on reset
|
||||
[(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
yield self.all_sessions_portal_sync()
|
||||
else: # shutdown
|
||||
[(o.typeclass, o.at_disconnect(), o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
yield [(o.typeclass, o.at_disconnect(), o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
|
||||
[(p.typeclass, p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()]
|
||||
[(s.typeclass, s.at_server_shutdown()) for s in ScriptDB.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 not _abrupt:
|
||||
if SERVER_HOOK_MODULE:
|
||||
SERVER_HOOK_MODULE.at_server_stop()
|
||||
reactor.callLater(0, reactor.stop)
|
||||
if SERVER_HOOK_MODULE:
|
||||
SERVER_HOOK_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)
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ class Session(object):
|
|||
"""
|
||||
|
||||
# names of attributes that should be affected by syncing.
|
||||
_attrs_to_sync = ['protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname',
|
||||
_attrs_to_sync = ('protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname',
|
||||
'logged_in', 'cid', 'encoding',
|
||||
'conn_time', 'cmd_last', 'cmd_last_visible', 'cmd_total',
|
||||
'server_data']
|
||||
'server_data')
|
||||
|
||||
def init_session(self, protocol_key, address, sessionhandler):
|
||||
"""
|
||||
|
|
@ -84,18 +84,15 @@ class Session(object):
|
|||
"""
|
||||
Return all data relevant to sync the session
|
||||
"""
|
||||
sessdata = {}
|
||||
for attrname in self._attrs_to_sync:
|
||||
sessdata[attrname] = self.__dict__.get(attrname, None)
|
||||
return sessdata
|
||||
return dict((key, value) for key, value in self.__dict__.items() if key in self._attrs_to_sync)
|
||||
|
||||
def load_sync_data(self, sessdata):
|
||||
"""
|
||||
Takes a session dictionary, as created by get_sync_data,
|
||||
and loads it into the correct attributes of the session.
|
||||
and loads it into the correct properties of the session.
|
||||
"""
|
||||
for attrname, value in sessdata.items():
|
||||
self.__dict__[attrname] = value
|
||||
for propname, value in sessdata.items():
|
||||
self.__dict__[propname] = value
|
||||
|
||||
def at_sync(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -69,11 +69,7 @@ class SessionHandler(object):
|
|||
Create a dictionary of sessdata dicts representing all
|
||||
sessions in store.
|
||||
"""
|
||||
sessdict = {}
|
||||
for sess in self.sessions.values():
|
||||
# copy all relevant data from all sessions
|
||||
sessdict[sess.sessid] = sess.get_sync_data()
|
||||
return sessdict
|
||||
return dict((sessid, sess.get_sync_data()) for sessid, sess in self.sessions.items())
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Server-SessionHandler class
|
||||
|
|
@ -179,14 +175,14 @@ class ServerSessionHandler(SessionHandler):
|
|||
operation=SLOGIN,
|
||||
data=sessdata)
|
||||
|
||||
def session_sync(self):
|
||||
def all_sessions_portal_sync(self):
|
||||
"""
|
||||
This is called by the server when it reboots. It syncs all session data
|
||||
to the portal.
|
||||
to the portal. Returns a deferred!
|
||||
"""
|
||||
sessdata = self.get_all_sync_data()
|
||||
self.server.amp_protocol.call_remote_PortalAdmin(0,
|
||||
SSYNC,
|
||||
return self.server.amp_protocol.call_remote_PortalAdmin(0,
|
||||
operation=SSYNC,
|
||||
data=sessdata)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue