2006-12-22 01:40:40 +00:00
|
|
|
"""
|
2012-03-30 23:57:04 +02:00
|
|
|
This module defines handlers for storing sessions when handles
|
|
|
|
|
sessions of users connecting to the server.
|
2010-12-07 02:34:59 +00:00
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
There are two similar but separate stores of sessions:
|
2012-03-30 23:57:04 +02:00
|
|
|
ServerSessionHandler - this stores generic game sessions
|
2011-09-03 10:22:19 +00:00
|
|
|
for the game. These sessions has no knowledge about
|
2012-03-30 23:57:04 +02:00
|
|
|
how they are connected to the world.
|
2011-09-03 10:22:19 +00:00
|
|
|
PortalSessionHandler - this stores sessions created by
|
|
|
|
|
twisted protocols. These are dumb connectors that
|
|
|
|
|
handle network communication but holds no game info.
|
2012-03-30 23:57:04 +02:00
|
|
|
|
2006-12-22 01:40:40 +00:00
|
|
|
"""
|
2011-09-03 10:22:19 +00:00
|
|
|
|
|
|
|
|
import time
|
2010-12-07 02:34:59 +00:00
|
|
|
from django.conf import settings
|
2011-11-06 18:53:10 +01:00
|
|
|
from src.commands.cmdhandler import CMD_LOGINSTART
|
2014-09-10 09:33:44 +02:00
|
|
|
from src.utils.utils import variable_from_module, is_iter, \
|
|
|
|
|
to_str, to_unicode, strip_control_sequences
|
2013-09-14 09:10:31 +02:00
|
|
|
try:
|
|
|
|
|
import cPickle as pickle
|
|
|
|
|
except ImportError:
|
|
|
|
|
import pickle
|
|
|
|
|
|
2013-02-17 12:31:58 +01:00
|
|
|
# delayed imports
|
|
|
|
|
_PlayerDB = None
|
|
|
|
|
_ServerSession = None
|
|
|
|
|
_ServerConfig = None
|
|
|
|
|
_ScriptDB = None
|
2012-04-29 21:17:14 +02:00
|
|
|
|
2013-10-07 19:57:01 +02:00
|
|
|
|
2012-03-30 23:57:04 +02:00
|
|
|
# AMP signals
|
2013-11-14 19:31:17 +01:00
|
|
|
PCONN = chr(1) # portal session connect
|
|
|
|
|
PDISCONN = chr(2) # portal session disconnect
|
|
|
|
|
PSYNC = chr(3) # portal session sync
|
|
|
|
|
SLOGIN = chr(4) # server session login
|
|
|
|
|
SDISCONN = chr(5) # server session disconnect
|
|
|
|
|
SDISCONNALL = chr(6) # server session disconnect all
|
|
|
|
|
SSHUTD = chr(7) # server shutdown
|
|
|
|
|
SSYNC = chr(8) # server session sync
|
2014-02-19 00:13:05 +01:00
|
|
|
SCONN = chr(9) # server portal connection (for bots)
|
2014-08-14 10:28:50 +02:00
|
|
|
PCONNSYNC = chr(10) # portal post-syncing session
|
2012-02-25 19:26:54 +01:00
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
# i18n
|
|
|
|
|
from django.utils.translation import ugettext as _
|
2008-06-15 17:21:02 +00:00
|
|
|
|
2012-04-29 21:17:14 +02:00
|
|
|
SERVERNAME = settings.SERVERNAME
|
2013-02-02 15:55:42 +01:00
|
|
|
MULTISESSION_MODE = settings.MULTISESSION_MODE
|
2011-09-03 10:22:19 +00:00
|
|
|
IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
|
|
|
|
|
2013-11-14 19:31:17 +01:00
|
|
|
|
2013-02-17 12:31:58 +01:00
|
|
|
def delayed_import():
|
|
|
|
|
"Helper method for delayed import of all needed entities"
|
|
|
|
|
global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
|
|
|
|
|
if not _ServerSession:
|
2013-09-08 00:14:06 +02:00
|
|
|
# we allow optional arbitrary serversession class for overloading
|
|
|
|
|
modulename, classname = settings.SERVER_SESSION_CLASS.rsplit(".", 1)
|
|
|
|
|
_ServerSession = variable_from_module(modulename, classname)
|
2013-02-17 12:31:58 +01:00
|
|
|
if not _PlayerDB:
|
|
|
|
|
from src.players.models import PlayerDB as _PlayerDB
|
|
|
|
|
if not _ServerConfig:
|
|
|
|
|
from src.server.models import ServerConfig as _ServerConfig
|
|
|
|
|
if not _ScriptDB:
|
|
|
|
|
from src.scripts.models import ScriptDB as _ScriptDB
|
|
|
|
|
# including once to avoid warnings in Python syntax checkers
|
|
|
|
|
_ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
|
|
|
|
|
|
2013-11-14 19:31:17 +01:00
|
|
|
|
2012-04-29 21:17:14 +02:00
|
|
|
#-----------------------------------------------------------
|
|
|
|
|
# SessionHandler base class
|
|
|
|
|
#------------------------------------------------------------
|
2011-09-03 10:22:19 +00:00
|
|
|
|
|
|
|
|
class SessionHandler(object):
|
|
|
|
|
"""
|
|
|
|
|
This handler holds a stack of sessions.
|
|
|
|
|
"""
|
|
|
|
|
def __init__(self):
|
|
|
|
|
"""
|
2012-03-30 23:57:04 +02:00
|
|
|
Init the handler.
|
2011-09-03 10:22:19 +00:00
|
|
|
"""
|
|
|
|
|
self.sessions = {}
|
|
|
|
|
|
|
|
|
|
def get_sessions(self, include_unloggedin=False):
|
|
|
|
|
"""
|
|
|
|
|
Returns the connected session objects.
|
|
|
|
|
"""
|
|
|
|
|
if include_unloggedin:
|
|
|
|
|
return self.sessions.values()
|
|
|
|
|
else:
|
|
|
|
|
return [session for session in self.sessions.values() if session.logged_in]
|
|
|
|
|
|
|
|
|
|
def get_session(self, sessid):
|
|
|
|
|
"""
|
|
|
|
|
Get session by sessid
|
|
|
|
|
"""
|
|
|
|
|
return self.sessions.get(sessid, None)
|
|
|
|
|
|
|
|
|
|
def get_all_sync_data(self):
|
|
|
|
|
"""
|
2012-03-30 23:57:04 +02:00
|
|
|
Create a dictionary of sessdata dicts representing all
|
|
|
|
|
sessions in store.
|
2011-09-03 10:22:19 +00:00
|
|
|
"""
|
2012-05-01 14:19:54 +02:00
|
|
|
return dict((sessid, sess.get_sync_data()) for sessid, sess in self.sessions.items())
|
2009-04-20 22:34:16 +00:00
|
|
|
|
2013-10-15 20:00:18 +02:00
|
|
|
def oobstruct_parser(self, oobstruct):
|
|
|
|
|
"""
|
|
|
|
|
Helper method for each session to use to parse oob structures
|
2014-06-27 01:13:48 +02:00
|
|
|
(The 'oob' kwarg of the msg() method).
|
2014-06-25 23:35:21 +02:00
|
|
|
|
2014-06-27 01:13:48 +02:00
|
|
|
Allowed input oob structures are:
|
2013-10-15 20:00:18 +02:00
|
|
|
cmdname
|
|
|
|
|
((cmdname,), (cmdname,))
|
|
|
|
|
(cmdname,(arg, ))
|
|
|
|
|
(cmdname,(arg1,arg2))
|
|
|
|
|
(cmdname,{key:val,key2:val2})
|
|
|
|
|
(cmdname, (args,), {kwargs})
|
|
|
|
|
((cmdname, (arg1,arg2)), cmdname, (cmdname, (arg1,)))
|
|
|
|
|
outputs an ordered structure on the form
|
2013-11-14 19:31:17 +01:00
|
|
|
((cmdname, (args,), {kwargs}), ...), where the two last
|
|
|
|
|
parts of each tuple may be empty
|
2013-10-15 20:00:18 +02:00
|
|
|
"""
|
|
|
|
|
def _parse(oobstruct):
|
|
|
|
|
slen = len(oobstruct)
|
|
|
|
|
if not oobstruct:
|
|
|
|
|
return tuple(None, (), {})
|
|
|
|
|
elif not hasattr(oobstruct, "__iter__"):
|
|
|
|
|
# a singular command name, without arguments or kwargs
|
|
|
|
|
return (oobstruct.lower(), (), {})
|
2013-11-14 19:31:17 +01:00
|
|
|
# regardless of number of args/kwargs, the first element must be
|
|
|
|
|
# the function name. We will not catch this error if not, but
|
|
|
|
|
# allow it to propagate.
|
2013-10-15 20:00:18 +02:00
|
|
|
if slen == 1:
|
|
|
|
|
return (oobstruct[0].lower(), (), {})
|
|
|
|
|
elif slen == 2:
|
|
|
|
|
if isinstance(oobstruct[1], dict):
|
|
|
|
|
# cmdname, {kwargs}
|
|
|
|
|
return (oobstruct[0].lower(), (), dict(oobstruct[1]))
|
|
|
|
|
elif isinstance(oobstruct[1], (tuple, list)):
|
|
|
|
|
# cmdname, (args,)
|
2014-06-25 23:35:21 +02:00
|
|
|
return (oobstruct[0].lower(), list(oobstruct[1]), {})
|
2014-06-28 17:38:21 +02:00
|
|
|
else:
|
|
|
|
|
# cmdname, cmdname
|
|
|
|
|
return ((oobstruct[0].lower(), (), {}), (oobstruct[1].lower(), (), {}))
|
2013-10-15 20:00:18 +02:00
|
|
|
else:
|
|
|
|
|
# cmdname, (args,), {kwargs}
|
2014-06-25 23:35:21 +02:00
|
|
|
return (oobstruct[0].lower(), list(oobstruct[1]), dict(oobstruct[2]))
|
2013-10-15 20:00:18 +02:00
|
|
|
|
|
|
|
|
if hasattr(oobstruct, "__iter__"):
|
2013-11-14 19:31:17 +01:00
|
|
|
# differentiate between (cmdname, cmdname),
|
2014-06-28 17:38:21 +02:00
|
|
|
# (cmdname, (args), {kwargs}) and ((cmdname,(args),{kwargs}),
|
|
|
|
|
# (cmdname,(args),{kwargs}), ...)
|
2013-10-15 20:00:18 +02:00
|
|
|
|
|
|
|
|
if oobstruct and isinstance(oobstruct[0], basestring):
|
2014-06-25 23:35:21 +02:00
|
|
|
return (list(_parse(oobstruct)),)
|
2013-10-15 20:00:18 +02:00
|
|
|
else:
|
|
|
|
|
out = []
|
|
|
|
|
for oobpart in oobstruct:
|
|
|
|
|
out.append(_parse(oobpart))
|
2014-06-25 23:35:21 +02:00
|
|
|
return (list(out),)
|
2013-10-15 20:00:18 +02:00
|
|
|
return (_parse(oobstruct),)
|
|
|
|
|
|
2013-11-14 19:31:17 +01:00
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
#------------------------------------------------------------
|
2011-09-03 10:22:19 +00:00
|
|
|
# Server-SessionHandler class
|
2010-12-07 02:34:59 +00:00
|
|
|
#------------------------------------------------------------
|
2007-04-25 20:11:29 +00:00
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
class ServerSessionHandler(SessionHandler):
|
2008-06-13 19:52:29 +00:00
|
|
|
"""
|
2010-12-07 02:34:59 +00:00
|
|
|
This object holds the stack of sessions active in the game at
|
2012-03-30 23:57:04 +02:00
|
|
|
any time.
|
2010-12-07 02:34:59 +00:00
|
|
|
|
|
|
|
|
A session register with the handler in two steps, first by
|
|
|
|
|
registering itself with the connect() method. This indicates an
|
|
|
|
|
non-authenticated session. Whenever the session is authenticated
|
|
|
|
|
the session together with the related player is sent to the login()
|
2012-03-30 23:57:04 +02:00
|
|
|
method.
|
2007-05-21 20:52:05 +00:00
|
|
|
|
2008-06-13 19:52:29 +00:00
|
|
|
"""
|
2007-05-09 15:28:12 +00:00
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
# AMP communication methods
|
|
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
def __init__(self):
|
|
|
|
|
"""
|
2012-03-30 23:57:04 +02:00
|
|
|
Init the handler.
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2011-09-03 10:22:19 +00:00
|
|
|
self.sessions = {}
|
2012-03-30 23:57:04 +02:00
|
|
|
self.server = None
|
2013-11-14 19:31:17 +01:00
|
|
|
self.server_data = {"servername": SERVERNAME}
|
2011-02-21 12:56:44 +00:00
|
|
|
|
2013-02-17 12:31:58 +01:00
|
|
|
def portal_connect(self, portalsession):
|
2011-09-03 10:22:19 +00:00
|
|
|
"""
|
2012-03-30 23:57:04 +02:00
|
|
|
Called by Portal when a new session has connected.
|
2011-09-03 10:22:19 +00:00
|
|
|
Creates a new, unlogged-in game session.
|
2013-02-17 12:31:58 +01:00
|
|
|
|
|
|
|
|
portalsession is a dictionary of all property:value keys
|
|
|
|
|
defining the session and which is marked to
|
|
|
|
|
be synced.
|
|
|
|
|
"""
|
|
|
|
|
delayed_import()
|
|
|
|
|
global _ServerSession, _PlayerDB, _ScriptDB
|
|
|
|
|
|
|
|
|
|
sess = _ServerSession()
|
|
|
|
|
sess.sessionhandler = self
|
|
|
|
|
sess.load_sync_data(portalsession)
|
|
|
|
|
if sess.logged_in and sess.uid:
|
2013-11-14 19:31:17 +01:00
|
|
|
# this can happen in the case of auto-authenticating
|
|
|
|
|
# protocols like SSH
|
2013-02-17 12:31:58 +01:00
|
|
|
sess.player = _PlayerDB.objects.get_player_from_uid(sess.uid)
|
|
|
|
|
sess.at_sync()
|
2014-02-26 22:25:05 +01:00
|
|
|
# validate all scripts
|
2013-02-17 12:31:58 +01:00
|
|
|
_ScriptDB.objects.validate()
|
|
|
|
|
self.sessions[sess.sessid] = sess
|
2013-04-09 15:59:21 +02:00
|
|
|
sess.data_in(CMD_LOGINSTART)
|
2011-09-03 10:22:19 +00:00
|
|
|
|
2014-08-14 10:28:50 +02:00
|
|
|
def portal_session_sync(self, portalsessiondata):
|
|
|
|
|
"""
|
|
|
|
|
Called by Portal when it wants to update a single session (e.g.
|
|
|
|
|
because of all negotiation protocols have finally replied)
|
|
|
|
|
"""
|
|
|
|
|
sessid = portalsessiondata.get("sessid")
|
|
|
|
|
session = self.sessions.get(sessid)
|
|
|
|
|
if session:
|
2014-10-19 19:11:56 +02:00
|
|
|
# since some of the session properties may have had
|
|
|
|
|
# a chance to change already before the portal gets here
|
|
|
|
|
# the portal doesn't send all sessiondata here, but only
|
|
|
|
|
# ones which should only be changed from portal (like
|
|
|
|
|
# protocol_flags etc)
|
2014-08-14 10:28:50 +02:00
|
|
|
session.load_sync_data(portalsessiondata)
|
|
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
def portal_disconnect(self, sessid):
|
|
|
|
|
"""
|
|
|
|
|
Called by Portal when portal reports a closing of a session
|
|
|
|
|
from the portal side.
|
|
|
|
|
"""
|
|
|
|
|
session = self.sessions.get(sessid, None)
|
2013-05-18 18:18:32 -05:00
|
|
|
if not session:
|
|
|
|
|
return
|
|
|
|
|
player = session.player
|
|
|
|
|
if player:
|
|
|
|
|
nsess = len(self.sessions_from_player(player))
|
|
|
|
|
remaintext = nsess and "%i session%s remaining" % (nsess, nsess > 1 and "s" or "") or "no more sessions"
|
|
|
|
|
session.log(_('Connection dropped: %s %s (%s)' % (session.player, session.address, remaintext)))
|
|
|
|
|
session.at_disconnect()
|
|
|
|
|
session.disconnect()
|
|
|
|
|
del self.sessions[session.sessid]
|
2011-09-03 10:22:19 +00:00
|
|
|
|
2014-08-14 10:28:50 +02:00
|
|
|
def portal_sessions_sync(self, portalsessions):
|
2011-09-03 10:22:19 +00:00
|
|
|
"""
|
2013-11-14 19:31:17 +01:00
|
|
|
Syncing all session ids of the portal with the ones of the
|
|
|
|
|
server. This is instantiated by the portal when reconnecting.
|
2012-03-30 23:57:04 +02:00
|
|
|
|
2013-02-17 12:31:58 +01:00
|
|
|
portalsessions is a dictionary {sessid: {property:value},...} defining
|
2013-11-14 19:31:17 +01:00
|
|
|
each session and the properties in it which should
|
|
|
|
|
be synced.
|
2012-03-30 23:57:04 +02:00
|
|
|
"""
|
2013-02-17 12:31:58 +01:00
|
|
|
delayed_import()
|
|
|
|
|
global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
|
|
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
for sess in self.sessions.values():
|
2013-11-14 19:31:17 +01:00
|
|
|
# we delete the old session to make sure to catch eventual
|
|
|
|
|
# lingering references.
|
2011-09-03 10:22:19 +00:00
|
|
|
del sess
|
2013-02-17 12:31:58 +01:00
|
|
|
|
|
|
|
|
for sessid, sessdict in portalsessions.items():
|
|
|
|
|
sess = _ServerSession()
|
|
|
|
|
sess.sessionhandler = self
|
|
|
|
|
sess.load_sync_data(sessdict)
|
|
|
|
|
if sess.uid:
|
|
|
|
|
sess.player = _PlayerDB.objects.get_player_from_uid(sess.uid)
|
|
|
|
|
self.sessions[sessid] = sess
|
2011-09-03 10:22:19 +00:00
|
|
|
sess.at_sync()
|
|
|
|
|
|
2013-11-14 19:31:17 +01:00
|
|
|
# after sync is complete we force-validate all scripts
|
|
|
|
|
# (this also starts them)
|
2013-02-17 12:31:58 +01:00
|
|
|
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)
|
|
|
|
|
# announce the reconnection
|
2013-02-17 13:59:36 +01:00
|
|
|
self.announce_all(_(" ... Server restarted."))
|
2013-02-17 12:31:58 +01:00
|
|
|
|
2014-02-19 00:13:05 +01:00
|
|
|
# server-side access methods
|
|
|
|
|
|
2014-02-24 00:37:32 +01:00
|
|
|
def start_bot_session(self, protocol_path, configdict):
|
2014-02-19 00:13:05 +01:00
|
|
|
"""
|
|
|
|
|
This method allows the server-side to force the Portal to create
|
|
|
|
|
a new bot session using the protocol specified by protocol_path,
|
|
|
|
|
which should be the full python path to the class, including the
|
|
|
|
|
class name, like "src.server.portal.irc.IRCClient".
|
|
|
|
|
The new session will use the supplied player-bot uid to
|
|
|
|
|
initiate an already logged-in connection. The Portal will
|
|
|
|
|
treat this as a normal connection and henceforth so will the
|
|
|
|
|
Server.
|
|
|
|
|
"""
|
|
|
|
|
data = {"protocol_path":protocol_path,
|
2014-02-22 09:56:21 +01:00
|
|
|
"config":configdict}
|
2014-02-19 00:13:05 +01:00
|
|
|
self.server.amp_protocol.call_remote_PortalAdmin(0,
|
|
|
|
|
operation=SCONN,
|
|
|
|
|
data=data)
|
|
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
def portal_shutdown(self):
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2011-09-03 10:22:19 +00:00
|
|
|
Called by server when shutting down the portal.
|
2012-03-30 23:57:04 +02:00
|
|
|
"""
|
2011-09-03 10:22:19 +00:00
|
|
|
self.server.amp_protocol.call_remote_PortalAdmin(0,
|
2012-02-25 19:26:54 +01:00
|
|
|
operation=SSHUTD,
|
2012-03-30 23:57:04 +02:00
|
|
|
data="")
|
2011-09-03 10:22:19 +00:00
|
|
|
|
2013-09-22 16:29:02 +02:00
|
|
|
def login(self, session, player, testmode=False):
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
|
|
|
|
Log in the previously unloggedin session and the player we by
|
|
|
|
|
now should know is connected to it. After this point we
|
|
|
|
|
assume the session to be logged in one way or another.
|
2013-09-22 16:29:02 +02:00
|
|
|
|
|
|
|
|
testmode - this is used by unittesting for faking login without
|
|
|
|
|
any AMP being actually active
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2012-03-30 23:57:04 +02:00
|
|
|
|
2013-02-17 18:48:48 +01:00
|
|
|
# we have to check this first before uid has been assigned
|
|
|
|
|
# this session.
|
|
|
|
|
|
|
|
|
|
if not self.sessions_from_player(player):
|
|
|
|
|
player.is_connected = True
|
|
|
|
|
|
|
|
|
|
# sets up and assigns all properties on the session
|
|
|
|
|
session.at_login(player)
|
|
|
|
|
|
|
|
|
|
# player init
|
|
|
|
|
player.at_init()
|
|
|
|
|
|
|
|
|
|
# Check if this is the first time the *player* logs in
|
|
|
|
|
if player.db.FIRST_LOGIN:
|
|
|
|
|
player.at_first_login()
|
|
|
|
|
del player.db.FIRST_LOGIN
|
|
|
|
|
|
|
|
|
|
player.at_pre_login()
|
|
|
|
|
|
2013-02-02 15:55:42 +01:00
|
|
|
if MULTISESSION_MODE == 0:
|
|
|
|
|
# disconnect all previous sessions.
|
2010-12-07 02:34:59 +00:00
|
|
|
self.disconnect_duplicate_sessions(session)
|
2013-02-17 18:48:48 +01:00
|
|
|
|
2013-04-09 17:51:30 +02:00
|
|
|
nsess = len(self.sessions_from_player(player))
|
|
|
|
|
totalstring = "%i session%s total" % (nsess, nsess > 1 and "s" or "")
|
|
|
|
|
session.log(_('Logged in: %s %s (%s)' % (player, session.address, totalstring)))
|
|
|
|
|
|
2012-03-30 23:57:04 +02:00
|
|
|
session.logged_in = True
|
2013-02-17 18:48:48 +01:00
|
|
|
# sync the portal to the session
|
2011-09-03 10:22:19 +00:00
|
|
|
sessdata = session.get_sync_data()
|
2013-09-22 16:29:02 +02:00
|
|
|
if not testmode:
|
|
|
|
|
self.server.amp_protocol.call_remote_PortalAdmin(session.sessid,
|
2012-02-25 19:26:54 +01:00
|
|
|
operation=SLOGIN,
|
2011-09-03 10:22:19 +00:00
|
|
|
data=sessdata)
|
2013-04-18 09:13:31 +02:00
|
|
|
player.at_post_login(sessid=session.sessid)
|
2012-03-30 23:57:04 +02:00
|
|
|
|
2013-04-09 17:51:30 +02:00
|
|
|
def disconnect(self, session, reason=""):
|
|
|
|
|
"""
|
|
|
|
|
Called from server side to remove session and inform portal
|
|
|
|
|
of this fact.
|
|
|
|
|
"""
|
|
|
|
|
session = self.sessions.get(session.sessid)
|
|
|
|
|
if not session:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if hasattr(session, "player") and session.player:
|
|
|
|
|
# only log accounts logging off
|
|
|
|
|
nsess = len(self.sessions_from_player(session.player)) - 1
|
|
|
|
|
remaintext = nsess and "%i session%s remaining" % (nsess, nsess > 1 and "s" or "") or "no more sessions"
|
|
|
|
|
session.log(_('Logged out: %s %s (%s)' % (session.player, session.address, remaintext)))
|
|
|
|
|
|
|
|
|
|
session.at_disconnect()
|
|
|
|
|
sessid = session.sessid
|
|
|
|
|
del self.sessions[sessid]
|
|
|
|
|
# inform portal that session should be closed.
|
|
|
|
|
self.server.amp_protocol.call_remote_PortalAdmin(sessid,
|
|
|
|
|
operation=SDISCONN,
|
|
|
|
|
data=reason)
|
|
|
|
|
|
2012-05-01 14:19:54 +02:00
|
|
|
def all_sessions_portal_sync(self):
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2011-09-03 10:22:19 +00:00
|
|
|
This is called by the server when it reboots. It syncs all session data
|
2012-05-01 14:19:54 +02:00
|
|
|
to the portal. Returns a deferred!
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2011-09-03 10:22:19 +00:00
|
|
|
sessdata = self.get_all_sync_data()
|
2012-05-01 14:19:54 +02:00
|
|
|
return self.server.amp_protocol.call_remote_PortalAdmin(0,
|
|
|
|
|
operation=SSYNC,
|
2011-09-03 10:22:19 +00:00
|
|
|
data=sessdata)
|
2010-08-29 18:46:58 +00:00
|
|
|
|
2012-02-25 19:26:54 +01:00
|
|
|
def disconnect_all_sessions(self, reason=_("You have been disconnected.")):
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
|
|
|
|
Cleanly disconnect all of the connected sessions.
|
|
|
|
|
"""
|
2012-03-30 23:57:04 +02:00
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
for session in self.sessions:
|
|
|
|
|
del session
|
|
|
|
|
# tell portal to disconnect all sessions
|
|
|
|
|
self.server.amp_protocol.call_remote_PortalAdmin(0,
|
2012-02-25 19:26:54 +01:00
|
|
|
operation=SDISCONNALL,
|
2011-09-03 10:22:19 +00:00
|
|
|
data=reason)
|
2010-12-07 02:34:59 +00:00
|
|
|
|
2013-11-14 19:31:17 +01:00
|
|
|
def disconnect_duplicate_sessions(self, curr_session,
|
|
|
|
|
reason=_("Logged in from elsewhere. Disconnecting.")):
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2013-04-03 18:31:53 +02:00
|
|
|
Disconnects any existing sessions with the same user.
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2013-04-03 18:31:53 +02:00
|
|
|
uid = curr_session.uid
|
2012-09-17 19:19:20 +02:00
|
|
|
doublet_sessions = [sess for sess in self.sessions.values()
|
2012-03-30 23:57:04 +02:00
|
|
|
if sess.logged_in
|
2013-04-03 18:31:53 +02:00
|
|
|
and sess.uid == uid
|
2011-09-03 10:22:19 +00:00
|
|
|
and sess != curr_session]
|
2012-04-15 19:05:50 +02:00
|
|
|
for session in doublet_sessions:
|
2012-03-30 23:57:04 +02:00
|
|
|
self.disconnect(session, reason)
|
2010-12-07 02:34:59 +00:00
|
|
|
|
|
|
|
|
def validate_sessions(self):
|
|
|
|
|
"""
|
2012-03-30 23:57:04 +02:00
|
|
|
Check all currently connected sessions (logged in and not)
|
2010-12-07 02:34:59 +00:00
|
|
|
and see if any are dead.
|
|
|
|
|
"""
|
2011-09-03 10:22:19 +00:00
|
|
|
tcurr = time.time()
|
2013-11-14 19:31:17 +01:00
|
|
|
reason = _("Idle timeout exceeded, disconnecting.")
|
2012-03-30 23:57:04 +02:00
|
|
|
for session in (session for session in self.sessions.values()
|
|
|
|
|
if session.logged_in and IDLE_TIMEOUT > 0
|
2012-02-25 19:26:54 +01:00
|
|
|
and (tcurr - session.cmd_last) > IDLE_TIMEOUT):
|
|
|
|
|
self.disconnect(session, reason=reason)
|
2010-12-07 02:34:59 +00:00
|
|
|
|
2010-12-11 13:37:26 +00:00
|
|
|
def player_count(self):
|
|
|
|
|
"""
|
2013-02-02 22:41:56 +01:00
|
|
|
Get the number of connected players (not sessions since a
|
|
|
|
|
player may have more than one session depending on settings).
|
2010-12-11 13:37:26 +00:00
|
|
|
Only logged-in players are counted here.
|
|
|
|
|
"""
|
2011-09-03 10:22:19 +00:00
|
|
|
return len(set(session.uid for session in self.sessions.values() if session.logged_in))
|
2012-03-30 23:57:04 +02:00
|
|
|
|
2013-09-14 23:18:36 +02:00
|
|
|
def session_from_sessid(self, sessid):
|
|
|
|
|
"""
|
|
|
|
|
Return session based on sessid, or None if not found
|
|
|
|
|
"""
|
2014-08-04 15:32:48 +02:00
|
|
|
if is_iter(sessid):
|
|
|
|
|
return [self.sessions.get(sid) for sid in sessid if sid in self.sessions]
|
2013-09-14 23:18:36 +02:00
|
|
|
return self.sessions.get(sessid)
|
|
|
|
|
|
2013-04-09 15:59:21 +02:00
|
|
|
def session_from_player(self, player, sessid):
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2013-04-09 15:59:21 +02:00
|
|
|
Given a player and a session id, return the actual session object
|
|
|
|
|
"""
|
2014-08-04 15:32:48 +02:00
|
|
|
if is_iter(sessid):
|
|
|
|
|
sessions = [self.sessions.get(sid) for sid in sessid]
|
|
|
|
|
s = [sess for sess in sessions if sess and sess.logged_in and player.uid == sess.uid]
|
|
|
|
|
return s
|
2013-04-09 15:59:21 +02:00
|
|
|
session = self.sessions.get(sessid)
|
|
|
|
|
return session and session.logged_in and player.uid == session.uid and session or None
|
|
|
|
|
|
|
|
|
|
def sessions_from_player(self, player):
|
|
|
|
|
"""
|
|
|
|
|
Given a player, return all matching sessions.
|
2010-12-07 02:34:59 +00:00
|
|
|
"""
|
2012-04-26 17:47:25 +02:00
|
|
|
uid = player.uid
|
2013-04-09 15:59:21 +02:00
|
|
|
return [session for session in self.sessions.values() if session.logged_in and session.uid == uid]
|
2010-12-07 02:34:59 +00:00
|
|
|
|
|
|
|
|
def sessions_from_character(self, character):
|
|
|
|
|
"""
|
|
|
|
|
Given a game character, return any matching sessions.
|
|
|
|
|
"""
|
2014-08-05 09:16:00 +02:00
|
|
|
sessid = character.sessid.get()
|
|
|
|
|
if is_iter(sessid):
|
|
|
|
|
return [self.sessions.get(sess) for sess in sessid if sessid in self.sessions]
|
|
|
|
|
return self.sessions.get(sessid)
|
2010-12-07 02:34:59 +00:00
|
|
|
|
2011-09-03 10:22:19 +00:00
|
|
|
def announce_all(self, message):
|
|
|
|
|
"""
|
|
|
|
|
Send message to all connected sessions
|
|
|
|
|
"""
|
|
|
|
|
for sess in self.sessions.values():
|
|
|
|
|
self.data_out(sess, message)
|
|
|
|
|
|
2013-09-14 09:10:31 +02:00
|
|
|
def data_out(self, session, text="", **kwargs):
|
2011-09-03 10:22:19 +00:00
|
|
|
"""
|
|
|
|
|
Sending data Server -> Portal
|
|
|
|
|
"""
|
2014-09-10 09:33:44 +02:00
|
|
|
text = text and to_str(to_unicode(text), encoding=session.encoding)
|
2011-09-03 10:22:19 +00:00
|
|
|
self.server.amp_protocol.call_remote_MsgServer2Portal(sessid=session.sessid,
|
2013-09-14 09:10:31 +02:00
|
|
|
msg=text,
|
|
|
|
|
data=kwargs)
|
2013-11-14 19:31:17 +01:00
|
|
|
|
2013-09-21 17:33:48 +02:00
|
|
|
def data_in(self, sessid, text="", **kwargs):
|
2011-09-03 10:22:19 +00:00
|
|
|
"""
|
|
|
|
|
Data Portal -> Server
|
2012-03-30 23:57:04 +02:00
|
|
|
"""
|
|
|
|
|
session = self.sessions.get(sessid, None)
|
|
|
|
|
if session:
|
2014-09-10 09:33:44 +02:00
|
|
|
text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding)
|
2013-09-21 17:33:48 +02:00
|
|
|
session.data_in(text=text, **kwargs)
|
2011-09-03 10:22:19 +00:00
|
|
|
|
|
|
|
|
SESSIONS = ServerSessionHandler()
|