Updated more of server/ to google docstrings as per #709.

This commit is contained in:
Griatch 2015-06-23 10:42:51 +02:00
parent 57b2396af7
commit 00b5309295
3 changed files with 251 additions and 64 deletions

View file

@ -60,12 +60,13 @@ class ServerSession(Session):
def at_sync(self):
"""
This is called whenever a session has been resynced with the portal.
At this point all relevant attributes have already been set and
self.player been assigned (if applicable).
This is called whenever a session has been resynced with the
portal. At this point all relevant attributes have already
been set and self.player been assigned (if applicable).
Since this is often called after a server restart we need to
set up the session as it was.
Since this is often called after a server restart we need to set up
the session as it was.
"""
global _ObjectDB
if not _ObjectDB:
@ -94,7 +95,9 @@ class ServerSession(Session):
"""
Hook called by sessionhandler when the session becomes authenticated.
player - the player associated with the session
Args:
player (Player): The player associated with the session.
"""
self.player = player
self.uid = self.player.id
@ -115,6 +118,7 @@ class ServerSession(Session):
def at_disconnect(self):
"""
Hook called by sessionhandler when disconnecting this session.
"""
if self.logged_in:
sessid = self.sessid
@ -136,21 +140,32 @@ class ServerSession(Session):
def get_player(self):
"""
Get the player associated with this session
Returns:
player (Player): The associated Player.
"""
return self.logged_in and self.player
def get_puppet(self):
"""
Returns the in-game character associated with this session.
This returns the typeclass of the object.
Get the in-game character associated with this session.
Returns:
puppet (Object): The puppeted object, if any.
"""
return self.logged_in and self.puppet
get_character = get_puppet
def get_puppet_or_player(self):
"""
Returns session if not logged in; puppet if one exists,
otherwise return the player.
Get puppet or player.
Returns:
controller (Object or Player): The puppet if one exists,
otherwise return the player.
"""
if self.logged_in:
return self.puppet if self.puppet else self.player
@ -159,6 +174,12 @@ class ServerSession(Session):
def log(self, message, channel=True):
"""
Emits session info to the appropriate outputs and info channels.
Args:
message (str): The message to log.
channel (bool, optional): Log to the CHANNEL_CONNECTINFO channel
in addition to the server log.
"""
if channel:
try:
@ -173,7 +194,8 @@ class ServerSession(Session):
"""
Return eventual eventual width and height reported by the
client. Note that this currently only deals with a single
client window (windowID==0) as in traditional telnet session
client window (windowID==0) as in a traditional telnet session.
"""
flags = self.protocol_flags
width = flags.get('SCREENWIDTH', {}).get(0, settings.CLIENT_DEFAULT_WIDTH)
@ -182,8 +204,9 @@ class ServerSession(Session):
def update_session_counters(self, idle=False):
"""
Hit this when the user enters a command in order to update idle timers
and command counters.
Hit this when the user enters a command in order to update
idle timers and command counters.
"""
# Store the timestamp of the user's last command.
if not idle:
@ -194,12 +217,16 @@ class ServerSession(Session):
def data_in(self, text=None, **kwargs):
"""
Send User->Evennia. This will in effect
execute a command string on the server.
Send data User->Evennia. This will in effect execute a command
string on the server.
Note that oob data is already sent to the
Note that oob data is already sent separately to the
oobhandler at this point.
Kwargs:
text (str): A text to relay
kwargs (any): Other parameters from the protocol.
"""
#explicitly check for None since text can be an empty string, which is
#also valid
@ -227,6 +254,11 @@ class ServerSession(Session):
def data_out(self, text=None, **kwargs):
"""
Send Evennia -> User
Kwargs:
text (str): A text to relay
kwargs (any): Other parameters to the protocol.
"""
text = text if text else ""
if _INLINEFUNC_ENABLED and not "raw" in kwargs:
@ -244,12 +276,14 @@ class ServerSession(Session):
msg = data_out
def __eq__(self, other):
"Handle session comparisons"
return self.address == other.address
def __str__(self):
"""
String representation of the user session class. We use
this a lot in the server logs.
"""
symbol = ""
if self.logged_in and hasattr(self, "player") and self.player:
@ -264,15 +298,16 @@ class ServerSession(Session):
return "%s%s@%s" % (self.uname, symbol, address)
def __unicode__(self):
"""
Unicode representation
"""
"Unicode representation"
return u"%s" % str(self)
# Dummy API hooks for use during non-loggedin operation
def at_cmdset_get(self, **kwargs):
"dummy hook all objects with cmdsets need to have"
"""
A dummy hook all objects with cmdsets need to have
"""
pass
# Mock db/ndb properties for allowing easy storage on the session
@ -286,6 +321,7 @@ class ServerSession(Session):
to this is guaranteed to be cleared when a server is shutdown.
Syntax is same as for the _get_db_holder() method and
property, e.g. obj.ndb.attr = value etc.
"""
try:
return self._ndb_holder
@ -307,7 +343,13 @@ class ServerSession(Session):
#@ndb.setter
def ndb_set(self, value):
"Stop accidentally replacing the db object"
"""
Stop accidentally replacing the db object
Args:
value (any): A value to store in the ndb.
"""
string = "Cannot assign directly to ndb object! "
string = "Use ndb.attr=value instead."
raise Exception(string)
@ -322,5 +364,5 @@ class ServerSession(Session):
# Mock access method for the session (there is no lock info
# at this stage, so we just present a uniform API)
def access(self, *args, **kwargs):
"Dummy method."
"Dummy method to mimic the logged-in API."
return True

View file

@ -1,6 +1,6 @@
"""
This defines a generic session class. All connection instances (both
on Portal and Server side) should inherit from this class.
This module defines a generic session class. All connection instances
(both on Portal and Server side) should inherit from this class.
"""
@ -18,13 +18,13 @@ class Session(object):
Each connection will see two session instances created:
1) A Portal session. This is customized for the respective connection
1. A Portal session. This is customized for the respective connection
protocols that Evennia supports, like Telnet, SSH etc. The Portal
session must call init_session() as part of its initialization. The
respective hook methods should be connected to the methods unique
for the respective protocol so that there is a unified interface
to Evennia.
2) A Server session. This is the same for all connected players,
2. A Server session. This is the same for all connected players,
regardless of how they connect.
The Portal and Server have their own respective sessionhandlers. These
@ -44,9 +44,14 @@ class Session(object):
"""
Initialize the Session. This should be called by the protocol when
a new session is established.
protocol_key - telnet, ssh, ssl or web
address - client address
sessionhandler - reference to the sessionhandler instance
Args:
protocol_key (str): By default, one of 'telnet', 'ssh',
'ssl' or 'web'.
address (str): Client address.
sessionhandler (SessionHandler): Reference to the
main sessionhandler instance.
"""
# This is currently 'telnet', 'ssh', 'ssl' or 'web'
self.protocol_key = protocol_key
@ -85,15 +90,24 @@ class Session(object):
def get_sync_data(self):
"""
Return all data relevant to sync the session
Get all data relevant to sync the session.
Args:
syncdata (dict): All syncdata values, based on
the keys given by self._attrs_to_sync.
"""
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 properties of the session.
Takes a session dictionary, as created by get_sync_data, and
loads it into the correct properties of the session.
Args:
sessdata (dict): Session data dictionary.
"""
for propname, value in sessdata.items():
setattr(self, propname, value)
@ -103,6 +117,7 @@ class Session(object):
Called after a session has been fully synced (including
secondary operations such as setting self.player based
on uid etc).
"""
pass
@ -112,19 +127,33 @@ class Session(object):
"""
generic hook called from the outside to disconnect this session
should be connected to the protocols actual disconnect mechanism.
Args:
reason (str): Eventual text motivating the disconnect.
"""
pass
def data_out(self, text=None, **kwargs):
"""
generic hook for sending data out through the protocol. Server
Generic hook for sending data out through the protocol. Server
protocols can use this right away. Portal sessions
should overload this to format/handle the outgoing data as needed.
Kwargs:
text (str): Text data
kwargs (any): Other data to the protocol.
"""
pass
def data_in(self, text=None, **kwargs):
"""
hook for protocols to send incoming data to the engine.
Hook for protocols to send incoming data to the engine.
Kwargs:
text (str): Text data
kwargs (any): Other data from the protocol.
"""
pass

View file

@ -3,10 +3,11 @@ This module defines handlers for storing sessions when handles
sessions of users connecting to the server.
There are two similar but separate stores of sessions:
ServerSessionHandler - this stores generic game sessions
- ServerSessionHandler - this stores generic game sessions
for the game. These sessions has no knowledge about
how they are connected to the world.
PortalSessionHandler - this stores sessions created by
- PortalSessionHandler - this stores sessions created by
twisted protocols. These are dumb connectors that
handle network communication but holds no game info.
@ -53,7 +54,10 @@ _MAX_SERVER_COMMANDS_PER_SECOND = 100.0
_MAX_SESSION_COMMANDS_PER_SECOND = 5.0
def delayed_import():
"Helper method for delayed import of all needed entities"
"""
Helper method for delayed import of all needed entities.
"""
global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
if not _ServerSession:
# we allow optional arbitrary serversession class for overloading
@ -76,16 +80,26 @@ def delayed_import():
class SessionHandler(object):
"""
This handler holds a stack of sessions.
"""
def __init__(self):
"""
Init the handler.
"""
self.sessions = {}
def get_sessions(self, include_unloggedin=False):
"""
Returns the connected session objects.
Args:
include_unloggedin (bool, optional): Also list Sessions
that have not yet authenticated.
Returns:
sessions (list): A list of `Session` objects.
"""
if include_unloggedin:
return self.sessions.values()
@ -94,7 +108,14 @@ class SessionHandler(object):
def get_session(self, sessid):
"""
Get session by sessid
Get session by sessid.
Args:
sessid (int): Session id.
Returns:
session (Session): A `Session` object, if found.
"""
return self.sessions.get(sessid, None)
@ -102,6 +123,10 @@ class SessionHandler(object):
"""
Create a dictionary of sessdata dicts representing all
sessions in store.
Returns:
syncdata (dict): A dict of sync data.
"""
return dict((sessid, sess.get_sync_data()) for sessid, sess in self.sessions.items())
@ -128,26 +153,29 @@ class ServerSessionHandler(SessionHandler):
def __init__(self):
"""
Init the handler.
"""
self.sessions = {}
self.server = None
self.server_data = {"servername": _SERVERNAME}
def portal_connect(self, portalsession):
def portal_connect(self, portalsessiondata):
"""
Called by Portal when a new session has connected.
Creates a new, unlogged-in game session.
portalsession is a dictionary of all property:value keys
defining the session and which is marked to
be synced.
Args:
portalsessiondata (dict): 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)
sess.load_sync_data(portalsessiondata)
if sess.logged_in and sess.uid:
# this can happen in the case of auto-authenticating
# protocols like SSH
@ -162,6 +190,12 @@ class ServerSessionHandler(SessionHandler):
"""
Called by Portal when it wants to update a single session (e.g.
because of all negotiation protocols have finally replied)
Args:
portalsessiondata (dict): a dictionary of all property:value
keys defining the session and which is marked to be
synced.
"""
sessid = portalsessiondata.get("sessid")
session = self.sessions.get(sessid)
@ -177,20 +211,26 @@ class ServerSessionHandler(SessionHandler):
"""
Called by Portal when portal reports a closing of a session
from the portal side.
Args:
sessid (int): Session id.
"""
session = self.sessions.get(sessid, None)
if not session:
return
self.disconnect(session)
def portal_sessions_sync(self, portalsessions):
def portal_sessions_sync(self, portalsessionsdata):
"""
Syncing all session ids of the portal with the ones of the
server. This is instantiated by the portal when reconnecting.
portalsessions is a dictionary {sessid: {property:value},...} defining
each session and the properties in it which should
be synced.
Args:
portalsessionsdata (dict): A dictionary
`{sessid: {property:value},...}` defining each session and
the properties in it which should be synced.
"""
delayed_import()
global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
@ -200,7 +240,7 @@ class ServerSessionHandler(SessionHandler):
# lingering references.
del sess
for sessid, sessdict in portalsessions.items():
for sessid, sessdict in portalsessionsdata.items():
sess = _ServerSession()
sess.sessionhandler = self
sess.load_sync_data(sessdict)
@ -221,14 +261,26 @@ class ServerSessionHandler(SessionHandler):
def start_bot_session(self, protocol_path, configdict):
"""
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 "evennia.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.
This method allows the server-side to force the Portal to
create a new bot session.
Args:
protocol_path (str): The full python path to the bot's
class.
configdict (dict): This dict will be used to configure
the bot (this depends on the bot protocol).
Examples:
start_bot_session("evennia.server.portal.irc.IRCClient",
{"uid":1, "botname":"evbot", "channel":"#evennia",
"network:"irc.freenode.net", "port": 6667})
Notes:
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,
"config":configdict}
@ -239,6 +291,7 @@ class ServerSessionHandler(SessionHandler):
def portal_shutdown(self):
"""
Called by server when shutting down the portal.
"""
self.server.amp_protocol.call_remote_PortalAdmin(0,
operation=SSHUTD,
@ -247,11 +300,15 @@ class ServerSessionHandler(SessionHandler):
def login(self, session, player, testmode=False):
"""
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.
now should know is connected to it. After this point we assume
the session to be logged in one way or another.
Args:
session (Session): The Session to authenticate.
player (Player): The Player identified as associated with this Session.
testmode (bool, optional): This is used by unittesting for
faking login without any AMP being actually active.
testmode - this is used by unittesting for faking login without
any AMP being actually active
"""
# we have to check this first before uid has been assigned
@ -295,6 +352,11 @@ class ServerSessionHandler(SessionHandler):
"""
Called from server side to remove session and inform portal
of this fact.
Args:
session (Session): The Session to disconnect.
reason (str, optional): A motivation for the disconnect.
"""
session = self.sessions.get(session.sessid)
if not session:
@ -319,6 +381,7 @@ class ServerSessionHandler(SessionHandler):
"""
This is called by the server when it reboots. It syncs all session data
to the portal. Returns a deferred!
"""
sessdata = self.get_all_sync_data()
return self.server.amp_protocol.call_remote_PortalAdmin(0,
@ -328,6 +391,10 @@ class ServerSessionHandler(SessionHandler):
def disconnect_all_sessions(self, reason="You have been disconnected."):
"""
Cleanly disconnect all of the connected sessions.
Args:
reason (str, optional): The reason for the disconnection.
"""
for session in self.sessions:
@ -341,6 +408,11 @@ class ServerSessionHandler(SessionHandler):
reason=_("Logged in from elsewhere. Disconnecting.")):
"""
Disconnects any existing sessions with the same user.
args:
curr_session (Session): Disconnect all Sessions matching this one.
reason (str, optional): A motivation for disconnecting.
"""
uid = curr_session.uid
doublet_sessions = [sess for sess in self.sessions.values()
@ -352,8 +424,9 @@ class ServerSessionHandler(SessionHandler):
def validate_sessions(self):
"""
Check all currently connected sessions (logged in and not)
and see if any are dead or idle
Check all currently connected sessions (logged in and not) and
see if any are dead or idle.
"""
tcurr = time()
reason = _("Idle timeout exceeded, disconnecting.")
@ -387,7 +460,14 @@ class ServerSessionHandler(SessionHandler):
def session_from_sessid(self, sessid):
"""
Return session based on sessid, or None if not found
Get session based on sessid, or None if not found
Args:
sessid (int or list): Session id(s)
Return:
sessions (Session or list): Session(s) found.
"""
if is_iter(sessid):
return [self.sessions.get(sid) for sid in sessid if sid in self.sessions]
@ -395,7 +475,16 @@ class ServerSessionHandler(SessionHandler):
def session_from_player(self, player, sessid):
"""
Given a player and a session id, return the actual session object
Given a player and a session id, return the actual session
object.
Args:
player (Player): The Player to get the Session from.
sessid (int or list): Session id(s).
Returns:
sessions (Session or list): Session(s) found.
"""
if is_iter(sessid):
sessions = [self.sessions.get(sid) for sid in sessid]
@ -407,6 +496,13 @@ class ServerSessionHandler(SessionHandler):
def sessions_from_player(self, player):
"""
Given a player, return all matching sessions.
Args:
player (Player): Player to get sessions from.
Returns:
sessions (list): All Sessions associated with this player.
"""
uid = player.uid
return [session for session in self.sessions.values() if session.logged_in and session.uid == uid]
@ -414,6 +510,14 @@ class ServerSessionHandler(SessionHandler):
def sessions_from_puppet(self, puppet):
"""
Given a puppeted object, return all controlling sessions.
Args:
puppet (Object): Object puppeted
Returns.
sessions (Session or list): Can be more than one of Object is controlled by
more than one Session (MULTISESSION_MODE > 1).
"""
sessid = puppet.sessid.get()
if is_iter(sessid):
@ -424,6 +528,10 @@ class ServerSessionHandler(SessionHandler):
def announce_all(self, message):
"""
Send message to all connected sessions
Args:
message (str): Message to send.
"""
for sess in self.sessions.values():
self.data_out(sess, message)
@ -482,6 +590,14 @@ class ServerSessionHandler(SessionHandler):
"""
Data Portal -> Server.
We also intercept OOB communication here.
Args:
sessid (int): Session id.
Kwargs:
text (str): Text from protocol.
kwargs (any): Other data from protocol.
"""
session = self.sessions.get(sessid, None)
if session: