Heavily reworked the many-char system, cleaner and more consistent by not having any persistent links on the Object side once a player has unconnected.

This commit is contained in:
Griatch 2013-04-09 15:59:21 +02:00
parent 5100a0561f
commit 26ced2cb90
8 changed files with 321 additions and 251 deletions

View file

@ -403,6 +403,7 @@ class CmdQuit(MuxCommand):
def func(self):
"hook function"
# always operate on the player
if hasattr(self.caller, "player"):
player = self.caller.player
else:
@ -772,7 +773,7 @@ class CmdColorTest(MuxCommand):
#------------------------------------------------------------
# OOC commands
# Player commands
#
# Note that in commands inheriting from MuxCommandOOC,
# self.caller is always the Player object, not the Character.
@ -803,15 +804,14 @@ class CmdOOCLook(MuxCommandOOC, CmdLook):
def look_target(self):
"Hook method for when an argument is given."
# caller is assumed to be a player object here.
caller = self.caller
player = self.caller
key = self.args.lower()
chars = dict((utils.to_str(char.key.lower()), char) for char in caller.db._playable_characters)
chars = dict((utils.to_str(char.key.lower()), char) for char in player.db._playable_characters)
looktarget = chars.get(key)
if looktarget:
caller.msg(looktarget.return_appearance(caller))
self.msg(looktarget.return_appearance(caller))
else:
caller.msg("No such character.")
self.msg("No such character.")
return
def no_look_target(self):
@ -887,7 +887,7 @@ class CmdCharCreate(MuxCommandOOC):
"create the new character"
player = self.caller
if not self.args:
player.msg("Usage: @charcreate <charname> [= description]")
self.msg("Usage: @charcreate <charname> [= description]")
return
key = self.lhs
desc = self.rhs
@ -941,44 +941,43 @@ class CmdIC(MuxCommandOOC):
"""
Simple puppet method
"""
caller = self.caller
player = self.caller
sessid = self.sessid
old_character = self.character
new_character = None
if not self.args:
new_character = caller.db._last_puppet
new_character = player.db._last_puppet
if not new_character:
self.msg("Usage: @ic <character>")
return
if not new_character:
# search for a matching character
new_character = search.objects(self.args, caller)
new_character = search.objects(self.args, player)
if new_character:
new_character = new_character[0]
else:
self.msg("That is not a valid character choice.")
return
# permission checks
if caller.get_character(sessid=sessid, character=new_character):
if player.get_puppet(sessid) == new_character:
self.msg("{RYou already act as {c%s{n." % new_character.name)
return
if new_character.player:
if new_character.sessid == sessid:
self.msg("{RYou already act as {c%s{n." % new_character.name)
return
elif new_character.sessid and new_character.player == caller:
# may not puppet an already puppeted character
if new_character.sessid and new_character.player == player:
self.msg("{RYou already act as {c%s{n in another session." % new_character.name)
return
elif not caller.get_character(character=new_character):
elif new_character.player != player and new_character.player.is_connected:
self.msg("{c%s{r is already acted by another player.{n" % new_character.name)
return
if not new_character.access(caller, "puppet"):
if not new_character.access(player, "puppet"):
# main acccess check
self.msg("{rYou may not become %s.{n" % new_character.name)
return
if caller.connect_character(new_character, sessid=sessid):
if player.puppet_object(sessid, new_character):
self.msg("\n{gYou become {c%s{n.\n" % new_character.name)
caller.db._last_puppet = old_character
player.db._last_puppet = old_character
if not new_character.location:
# this might be due to being hidden away at logout; check
loc = new_character.db.prelogout_location
@ -1012,15 +1011,16 @@ class CmdOOC(MuxCommandOOC):
def func(self):
"Implement function"
caller = self.caller
player = self.caller
sessid = self.sessid
old_char = caller.get_character(sessid=self.sessid)
old_char = player.get_puppet(sessid)
if not old_char:
string = "You are already OOC."
self.msg(string)
return
caller.db._last_puppet = old_char
player.db._last_puppet = old_char
# save location as if we were disconnecting from the game entirely.
if old_char.location:
old_char.location.msg_contents("%s has left the game." % old_char.key, exclude=[old_char])
@ -1028,7 +1028,9 @@ class CmdOOC(MuxCommandOOC):
old_char.location = None
# disconnect
err = caller.disconnect_character(self.character)
self.msg("\n{GYou go OOC.{n\n")
caller.execute_cmd("look")
if player.unpuppet_object(sessid):
self.msg("\n{GYou go OOC.{n\n")
player.execute_cmd("look")
else:
raise RuntimeError("Could not unpuppet!")

View file

@ -147,7 +147,8 @@ class ObjectDB(TypedObject):
ndb - non-persistent attribute storage
The ObjectDB adds the following properties:
player - optional connected player
player - optional connected player (always together with sessid)
sessid - optional connection session id (always together with player)
location - in-game location of object
home - safety location for object (handler)

View file

@ -288,24 +288,6 @@ class PlayerDB(TypedObject):
raise Exception("User id cannot be deleted!")
uid = property(uid_get, uid_set, uid_del)
# sessions property
#@property
def sessions_get(self):
"Getter. Retrieve sessions related to this player/user"
global _SESSIONS
if not _SESSIONS:
from src.server.sessionhandler import SESSIONS as _SESSIONS
return _SESSIONS.sessions_from_player(self)
#@sessions.setter
def sessions_set(self, value):
"Setter. Protects the sessions property from adding things"
raise Exception("Cannot set sessions manually!")
#@sessions.deleter
def sessions_del(self):
"Deleter. Protects the sessions property from deletion"
raise Exception("Cannot delete sessions manually!")
sessions = property(sessions_get, sessions_set, sessions_del)
#@property
def is_superuser_get(self):
"Superusers have all permissions."
@ -344,7 +326,7 @@ class PlayerDB(TypedObject):
session = _MULTISESSION_MODE == 2 and sessid and _GA(self, "get_session")(sessid) or None
if session:
char = _GA(self, "get_character")(sessid=sessid)
obj = session.puppet
if char and not char.at_msg_receive(outgoing_string, from_obj=from_obj, data=data):
# if hook returns false, cancel send
return
@ -354,24 +336,29 @@ class PlayerDB(TypedObject):
for sess in _GA(self, 'get_all_sessions')():
sess.msg(outgoing_string, data)
def inmsg(self, ingoing_string, sessid):
def inmsg(self, ingoing_string, session):
"""
User -> Evennia
This is the reverse of msg - used by sessions to relay
messages/data back into the game. It is normally not called
messages/data back into the game. It is not called
from inside game code but only by the serversessions directly.
ingoing_string - text string (i.e. command string)
data - dictionary of optional data
sessid - session sending this data
session - session sending this data (no need to look it up again)
"""
character = _GA(self, "get_character")(sessid=sessid)
if character:
# execute command on character
_GA(character, "execute_cmd")(ingoing_string, sessid=sessid)
puppet = session.puppet
if puppet:
# execute command on the puppeted object (this will include
# cmdsets both on object and on player)
_GA(puppet, "execute_cmd")(ingoing_string, sessid=session.sessid)
else:
# a non-character session; this goes to player directly
_GA(self, "execute_cmd")(ingoing_string, sessid=sessid)
# a non-character session; this executes on player directly
# (this will only include cmdsets on player)
_GA(self, "execute_cmd")(ingoing_string, sessid=session.sessid)
# session-related methods
def get_session(self, sessid):
"""
@ -380,7 +367,7 @@ class PlayerDB(TypedObject):
global _SESSIONS
if not _SESSIONS:
from src.server.sessionhandler import SESSIONS as _SESSIONS
return _SESSIONS.sessions_from_player(self, sessid=sessid)
return _SESSIONS.session_from_player(self, sessid)
def get_all_sessions(self):
"Return all sessions connected to this player"
@ -388,208 +375,272 @@ class PlayerDB(TypedObject):
if not _SESSIONS:
from src.server.sessionhandler import SESSIONS as _SESSIONS
return _SESSIONS.sessions_from_player(self)
sessions = property(get_all_sessions) # alias shortcut
def get_session_from_sessid(self, sessid):
"""
Get the session object from sessid. If session with sessid is not
connected to this player, return None.
"""
global _SESSIONS
if not _SESSIONS:
from src.server.sessionhandler import SESSIONS as _SESSIONS
return _SESSIONS.sessions_from_player(self, sessid=sessid)
def disconnect_session_from_player(self, sessid):
"""
Access method for disconnecting a given session from the player.
Access method for disconnecting a given session from the player
(connection happens automatically in the sessionhandler)
"""
sessions = self.get_session_from_sessid(sessid)
for session in make_iter(sessions):
# this will also trigger disconnection of character(s)
# this should only be one value, loop just to make sure to clean everything
sessions = (session for session in self.get_all_sessions(sessid) if session.sessid == sessid)
for session in sessions:
# this will also trigger unpuppeting
session.sessionhandler.disconnect(session)
def connect_session_to_character(self, sessid, character, force=False, call_hooks=True):
"""
Connect the given session to a character through this player.
Note that this assumes the character has previously been
linked to the player using self.connect_character().
# puppeting operations
force - drop existing connection to other character
call_hooks - call puppet/unpuppet hooks. This is not wanted e.g. if
server is reloading
Returns True if connection was successful, False otherwise
def puppet_object(self, sessid, obj, normal_mode=True):
"""
# first check if we already have a character tied to this session
char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
if char:
if force and char != character:
_GA(self, "disconnect_session_from_character")(sessid)
else:
return
# pre-puppet hook
if call_hooks:
# if e.g. server reloads we don't want to call any hooks anew
_GA(character.typeclass, "at_pre_puppet")(self.typeclass)
Use the given session to control (puppet) the given object (usually
a Character type). Note that we make no puppet checks here, that must
have been done before calling this method.
sessid - session id of session to connect
obj - the object to connect to
normal_mode - trigger hooks and extra checks - this is turned off when
the server reloads, to quickly re-connect puppets.
returns True if successful, False otherwise
"""
session = self.get_session(sessid)
if not session:
return False
if normal_mode and session.puppet:
# cleanly unpuppet eventual previous object puppeted by this session
self.unpuppet_object(self, sessid)
if obj.player and obj.player.is_connected and obj.player != self:
# we don't allow to puppet an object already controlled by an active
# player. To kick a player, call unpuppet_object on them explicitly.
return
# if we get to this point the character is ready to puppet or it was left
# with a lingering player/sessid reference from an unclean server kill or similar
if normal_mode:
_GA(obj.typeclass, "at_pre_puppet")(self.typeclass)
# do the connection
character.sessid = sessid
# update cache
cache = get_prop_cache(self, "_characters") or {}
cache[sessid] = character
set_prop_cache(self, "_characters", cache)
# start/validate (persistent) scripts on this object
ScriptDB.objects.validate(obj=character)
# post-puppet hook
if call_hooks:
_GA(character.typeclass, "at_post_puppet")()
obj.sessid = sessid
obj.player = self
session.puid = obj.id
session.puppet = obj
# validate/start persistent scripts on object
ScriptDB.objects.validate(obj=obj)
if normal_mode:
_GA(obj.typeclass, "at_post_puppet")()
return True
def disconnect_session_from_character(self, sessid):
def unpuppet_object(self, sessid):
"""
Disconnect a session from the characterm (still keeping the
connection to the Player)
returns the newly disconnected character, if it existed
"""
print "player disconnect_session_from_character", sessid
if not sessid:
return
char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
print char
if char:
# call hook before disconnecting
_GA(char.typeclass, "at_pre_unpuppet")()
del char.sessid
# update cache
cache = get_prop_cache(self, "_characters") or {}
if sessid in cache:
del cache[sessid]
set_prop_cache(self, "_characters", cache)
# call post-unpuppet hook
_GA(char.typeclass, "at_post_unpuppet")(self.typeclass)
print "... leaving player disconnect_session_from_character", sessid
return char
Disengage control over an object
def server_reconnect_session_to_character(self, sessid):
"""
Auto-re-connect a session to a character. This is called by the sessionhandler
during a server reload. It goes through the characters stored in this player's
db_objs many2many fields and checks if any of those has the given sessid
stored on themselves - if so they connect them. This should ONLY be called
automatically by sessionhandler after a reload - after a portal shutdown
the portal sessids will be out of sync with whatever is stored on character
objects which could lead to a session being linked to the wrong character.
"""
char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
if not char:
return
_GA(self, "connect_session_to_character")(sessid, char, force=True, call_hooks=False)
sessid - the session id to disengage
def get_character(self, sessid=None, character=None, return_dbobj=False):
returns True if successful
"""
Get the character connected to this player and sessid. This is the main
method for retrieving the character from the player's end.
session = self.get_session(sessid)
if not session:
return False
obj = hasattr(session, "puppet") and session.puppet or None
if not obj:
return False
# do the disconnect
_GA(obj.typeclass, "at_pre_unpuppet")()
del obj.dbobj.sessid
del obj.dbobj.player
session.puppet = None
session.puid = None
_GA(obj.typeclass, "at_post_unpuppet")(self)
return True
def unpuppet_all(self):
"""
Disconnect all puppets. This is called by server
before a reset/shutdown.
"""
for session in self.get_all_sessions():
self.unpuppet_object(session.sessid)
def get_puppet(self, sessid, return_dbobj=False):
"""
Get an object puppeted by this session through this player. This is the main
method for retrieving the puppeted object from the player's end.
sessid - return character connected to this sessid,
character - return character if connected to this player, else None.
Combining both keywords will check the entire connection - if the
given session is currently connected to the given char. If no
keywords are given, returns all connected characters as a list.
"""
cache = get_prop_cache(self, "_characters") or {}
if sessid:
# try to return a character with a given sessid
char = cache.get(sessid)
if not char:
char = _GA(self, "db_objs").filter(db_player=self, db_sessid=sessid) or None
if char:
char = char[0]
cache[sessid] = char
set_prop_cache(self, "_characters", cache)
if character:
return char and (char == character.dbobj and (return_dbobj and char or char.typeclass)) or None
return char and (return_dbobj and char or char.typeclass) or None
elif character:
char = _GA(self, "db_objs").filter(id=_GA(character.dbobj, "id"))
return char and (return_dbobj and char[0] or char[0].typeclass) or None
else:
# no sessid given - return all available characters
chars = list(return_dbobj and o or o.typeclass for o in self.db_objs.all())
return len(chars) == 1 and chars[0] or chars
session = self.get_session(sessid)
if not session:
return None
if return_dbobj:
return session.puppet
return session.puppet and session.puppet.typeclass or None
def get_all_characters(self):
def get_all_puppets(self, return_dbobj=False):
"""
Readability-wrapper for getting all characters
Get all currently puppeted objects as a list
"""
return _GA(self, "get_character")(sessid=None, character=None)
puppets = [session.puppet for session in self.get_all_sessions() if session.puppet]
if return_dbobj:
return puppets
return [puppet.typeclass for puppet in puppets]
def get_all_connected_characters(self):
def has_puppet(self, obj):
"""
Return all characters with an active session connected
to them through this player
Checks of this player currently puppets this object or not
"""
chars = make_iter(_GA(self, "get_character")(sessid=None, character=None))
sessids = [sess.sessid for sess in _GA(self, "get_all_sessions")()]
return [char for char in chars if char.sessid in sessids]
return obj in self.get_all_puppets()
def connect_character(self, character, sessid=None):
"""
Use the Player to connect a Character to the Player. Note that
we don't do any access checks at this point. If the
game was fully restarted (including the Portal), this must be
used, since sessids will have changed as players reconnect.
# def connect_session_to_character(self, sessid, character, force=False, call_hooks=True):
# """
# Connect the given session to a character through this player.
# Note that this assumes the character has previously been
# linked to the player using self.connect_character().
#
# force - drop existing connection to other character
# call_hooks - call puppet/unpuppet hooks. This is not wanted e.g. if
# server is reloading
#
# Returns True if connection was successful, False otherwise
# """
# # first check if we already have a character tied to this session
# char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
# if char:
# if force and char != character:
# _GA(self, "disconnect_session_from_character")(sessid)
# else:
# return
# # pre-puppet hook
# if call_hooks:
# # if e.g. server reloads we don't want to call any hooks anew
# _GA(character.typeclass, "at_pre_puppet")(self.typeclass)
# # do the connection
# character.sessid = sessid
# # update cache
# cache = get_prop_cache(self, "_characters") or {}
# cache[sessid] = character
# set_prop_cache(self, "_characters", cache)
# # start/validate (persistent) scripts on this object
# ScriptDB.objects.validate(obj=character)
# # post-puppet hook
# if call_hooks:
# _GA(character.typeclass, "at_post_puppet")()
# return True
#
# def disconnect_session_from_character(self, sessid):
# """
# Disconnect a session from the characterm (still keeping the
# connection to the Player)
# returns the newly disconnected character, if it existed
# """
# print "player disconnect_session_from_character", sessid
# if not sessid:
# return
# char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
# print char
# if char:
# # call hook before disconnecting
# _GA(char.typeclass, "at_pre_unpuppet")()
# del char.sessid
# # update cache
# cache = get_prop_cache(self, "_characters") or {}
# if sessid in cache:
# del cache[sessid]
# set_prop_cache(self, "_characters", cache)
# # call post-unpuppet hook
# _GA(char.typeclass, "at_post_unpuppet")(self.typeclass)
# print "... leaving player disconnect_session_from_character", sessid
# return char
#
# def server_reconnect_session_to_character(self, sessid):
# """
# Auto-re-connect a session to a character. This is called by the sessionhandler
# during a server reload. It goes through the characters stored in this player's
# db_objs many2many fields and checks if any of those has the given sessid
# stored on themselves - if so they connect them. This should ONLY be called
# automatically by sessionhandler after a reload - after a portal shutdown
# the portal sessids will be out of sync with whatever is stored on character
# objects which could lead to a session being linked to the wrong character.
# """
# char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
# if not char:
# return
# _GA(self, "connect_session_to_character")(sessid, char, force=True, call_hooks=False)
if sessid is given, also connect the sessid to the character directly.
"""
# first disconnect any other character from this session
char = character.dbobj
_GA(self, "disconnect_character")(char)
char.player = self
_GA(self, "db_objs").add(char)
_GA(self, "save")()
if sessid:
return _GA(self, "connect_session_to_character")(sessid=sessid, character=char)
return True
def disconnect_character(self, character):
"""
Disconnect a character from this player, either based
on sessid or by giving the character object directly
Returns newly disconnected character.
"""
if not character:
return
char = _GA(self, "get_character")(character=character, return_dbobj=True)
if char:
err = _GA(self, "disconnect_session_from_character")(char.sessid)
_GA(self, "db_objs").remove(char)
del char.player
self.save()
# clear cache
cache = get_prop_cache(self, "_characters") or {}
[cache.pop(sessid) for sessid,stored_char in cache.items() if stored_char==char]
set_prop_cache(self, "_characters", cache)
return char
# def get_all_characters(self):
# """
# Readability-wrapper for getting all characters
# """
# return _GA(self, "get_character")(sessid=None, character=None)
#
# def get_all_connected_characters(self):
# """
# Return all characters with an active session connected
# to them through this player
# """
# chars = make_iter(_GA(self, "get_character")(sessid=None, character=None))
# sessids = [sess.sessid for sess in _GA(self, "get_all_sessions")()]
# return [char for char in chars if char.sessid in sessids]
def disconnect_all_characters(self):
for char in self.db_objs.all():
_GA(self, "disconnect_character")(char)
# def connect_character(self, character, sessid=None):
# """
# Use the Player to connect a Character to the Player. Note that
# we don't do any access checks at this point. If the
# game was fully restarted (including the Portal), this must be
# used, since sessids will have changed as players reconnect.
#
# if sessid is given, also connect the sessid to the character directly.
# """
# # first disconnect any other character from this session
# char = character.dbobj
# _GA(self, "disconnect_character")(char)
# char.player = self
# _GA(self, "db_objs").add(char)
# _GA(self, "save")()
# if sessid:
# return _GA(self, "connect_session_to_character")(sessid=sessid, character=char)
# return True
#
# def disconnect_character(self, character):
# """
# Disconnect a character from this player, either based
# on sessid or by giving the character object directly
#
# Returns newly disconnected character.
# """
# if not character:
# return
# char = _GA(self, "get_character")(character=character, return_dbobj=True)
# if char:
# err = _GA(self, "disconnect_session_from_character")(char.sessid)
# _GA(self, "db_objs").remove(char)
# del char.player
# self.save()
# # clear cache
# cache = get_prop_cache(self, "_characters") or {}
# [cache.pop(sessid) for sessid,stored_char in cache.items() if stored_char==char]
# set_prop_cache(self, "_characters", cache)
# return char
#
# def disconnect_all_characters(self):
# for char in self.db_objs.all():
# _GA(self, "disconnect_character")(char)
def swap_character(self, old_character, new_character):
"""
Swaps character between sessions, if possible
"""
this_sessid = old_character.sessid
other_sessd = new_character.sessid
this_char = _GA(self, "disconnect_session_from_character")(this_sessid)
other_char = _GA(self, "disconnect_session_from_character")(other_sessid)
_GA(self, "connect_session_to_character")(this_sessid, other_char)
_GA(self, "connect_session_to_character")(other_sessid, this_char)
def delete(self, *args, **kwargs):
"Make sure to delete user also when deleting player - the two may never exist separately."
"""
Deletes the player permanently.
Makes sure to delete user also when deleting player - the two may never exist separately.
"""
for session in self.get_all_sessions():
# unpuppeting all objects and disconnecting the user, if any
# sessions remain (should usually be handled from the deleting command)
self.unpuppet_object(session.sessid)
session.sessionhandler.disconnect(session, reason=_("Player being deleted."))
try:
if _GA(self, "user"):
_GA(_GA(self, "user"), "delete")()

View file

@ -327,7 +327,7 @@ class Player(TypeClass):
print "player at_post_login", self
self._send_to_connect_channel("{G%s connected{n" % self.key)
if _MULTISESSION_MODE == 2 or not self.get_all_characters():
if _MULTISESSION_MODE == 2:
# Character.at_post_login also looks around. Only use
# this as a backup when logging in without a character
self.execute_cmd("look")

View file

@ -268,13 +268,13 @@ class Evennia(object):
else:
if mode == 'reset':
# don't call disconnect hooks on reset
# don't unset the is_connected flag on reset, otherwise same as shutdown
yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
else: # shutdown
yield [_SA(p, "is_connected", False) for p in PlayerDB.get_all_cached_instances()]
yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
yield [(p.typeclass, p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()]
yield [(p.typeclass, p.unpuppet_all(), 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()]
yield ObjectDB.objects.clear_all_sessids()
ServerConfig.objects.conf("server_restart_mode", "reset")

View file

@ -18,6 +18,7 @@ from src.server.session import Session
IDLE_COMMAND = settings.IDLE_COMMAND
_GA = object.__getattribute__
_ObjectDB = None
# load optional out-of-band function module
OOB_FUNC_MODULE = settings.OOB_FUNC_MODULE
@ -51,14 +52,19 @@ class ServerSession(Session):
Since this is often called after a server restart we need to set up
the session as it was.
"""
global _ObjectDB
if not _ObjectDB:
from src.objects.models import ObjectDB as _ObjectDB
if not self.logged_in:
# assign the unloggedin-command set.
self.cmdset = cmdsethandler.CmdSetHandler(self)
self.cmdset_storage = [settings.CMDSET_UNLOGGEDIN]
self.cmdset.update(init_mode=True)
return
else:
self.player.server_reconnect_session_to_character(self.sessid)
elif self.puid:
self.puppet = None
obj = _ObjectDB.objects.get(id=self.puid)
self.player.puppet_object(self.sessid, obj, normal_mode=False)
def at_login(self, player):
"""
@ -72,6 +78,8 @@ class ServerSession(Session):
self.uname = self.user.username
self.logged_in = True
self.conn_time = time.time()
self.puid = None
self.puppet = None
# Update account's last login time.
self.user.last_login = datetime.now()
@ -85,7 +93,7 @@ class ServerSession(Session):
sessid = self.sessid
player = self.player
print "session at_disconnect", self
_GA(player.dbobj, "disconnect_session_from_character")(sessid)
_GA(player.dbobj, "unpuppet_object")(sessid)
uaccount = _GA(player.dbobj, "user")
uaccount.last_login = datetime.now()
uaccount.save()
@ -102,12 +110,13 @@ class ServerSession(Session):
"""
return self.logged_in and self.player
def get_character(self):
def get_puppet(self):
"""
Returns the in-game character associated with this session.
This returns the typeclass of the object.
"""
return self.logged_in and self.player.get_character(self.sessid) or None
return self.logged_in and self.puppet
get_character = get_puppet
def log(self, message, channel=True):
"""
@ -135,9 +144,11 @@ class ServerSession(Session):
# Player-visible idle time, not used in idle timeout calcs.
self.cmd_last_visible = time.time()
def execute_cmd(self, command_string):
def data_in(self, command_string):
"""
Execute a command string on the server.
Send Player->Evennia. This will in effect
execute a command string on the server.
Eventual extra data moves through oob_data_in
"""
# handle the 'idle' command
if str(command_string).strip() == IDLE_COMMAND:
@ -145,11 +156,12 @@ class ServerSession(Session):
return
if self.logged_in:
# the inmsg handler will relay to the right place
self.player.inmsg(command_string, sessid=self.sessid)
self.player.inmsg(command_string, self)
else:
# we are not logged in. Use the session directly
# we are not logged in. Execute cmd with the the session directly
# (it uses the settings.UNLOGGEDIN cmdset)
cmdhandler.cmdhandler(self, command_string, sessid=self.sessid)
execute_cmd = data_in # alias
def data_out(self, msg, data=None):
"""
@ -157,6 +169,7 @@ class ServerSession(Session):
"""
self.sessionhandler.data_out(self, msg, data)
def oob_data_in(self, data):
"""
This receives out-of-band data from the Portal.
@ -249,7 +262,7 @@ class ServerSession(Session):
self.data_out(string, data=data)
# Dummy API hooks for use a non-loggedin operation
# Dummy API hooks for use during non-loggedin operation
def at_cmdset_get(self):
"dummy hook all objects with cmdsets need to have"

View file

@ -34,9 +34,9 @@ class Session(object):
# names of attributes that should be affected by syncing.
_attrs_to_sync = ('protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname',
'logged_in', 'cid', 'encoding',
'logged_in', 'puid', 'encoding',
'conn_time', 'cmd_last', 'cmd_last_visible', 'cmd_total',
'server_data')
'protocol_flags', 'server_data')
def init_session(self, protocol_key, address, sessionhandler):
"""
@ -63,15 +63,15 @@ class Session(object):
# if user has authenticated already or not
self.logged_in = False
# database id of character/object connected to this player session (if any)
self.cid = None
self.encoding = "utf-8"
# database id of puppeted object (if any)
self.puid = None
# session time statistics
self.conn_time = time.time()
self.cmd_last_visible = self.conn_time
self.cmd_last = self.conn_time
self.cmd_total = 0
self.encoding = "utf-8"
self.protocol_flags = {}
self.server_data = {}

View file

@ -138,7 +138,7 @@ class ServerSessionHandler(SessionHandler):
# validate all script
_ScriptDB.objects.validate()
self.sessions[sess.sessid] = sess
sess.execute_cmd(CMD_LOGINSTART)
sess.data_in(CMD_LOGINSTART)
def portal_disconnect(self, sessid):
"""
@ -196,7 +196,7 @@ class ServerSessionHandler(SessionHandler):
Called from server side to remove session and inform portal
of this fact.
"""
session = self.sessions.get(session.sessid, None)
session = self.sessions.get(session.sessid)
if session:
session.at_disconnect()
sessid = session.sessid
@ -305,16 +305,19 @@ class ServerSessionHandler(SessionHandler):
"""
return len(set(session.uid for session in self.sessions.values() if session.logged_in))
def sessions_from_player(self, player, sessid=None):
def session_from_player(self, player, sessid):
"""
Given a player, return any matching sessions.
Given a player and a session id, return the actual session object
"""
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.
"""
uid = player.uid
if sessid:
session = self.sessions.get(sessid)
return session and session.logged_in and session.uid == uid and session or None
else:
return [session for session in self.sessions.values() if session.logged_in and session.uid == uid]
return [session for session in self.sessions.values() if session.logged_in and session.uid == uid]
def sessions_from_character(self, character):
"""
@ -345,7 +348,7 @@ class ServerSessionHandler(SessionHandler):
"""
session = self.sessions.get(sessid, None)
if session:
session.execute_cmd(string)
session.data_in(string)
# ignore 'data' argument for now; this is otherwise the place
# to put custom effects on the server due to data input, e.g.