Added sessids to command objects and changed how the "login"-hooks are called. Those will probably have to be changed to better names, at least for characters.

This commit is contained in:
Griatch 2013-02-02 22:41:56 +01:00
parent 231af4a351
commit 00584365ae
5 changed files with 113 additions and 96 deletions

View file

@ -41,7 +41,6 @@ from twisted.internet.defer import inlineCallbacks, returnValue
from django.conf import settings
from src.comms.channelhandler import CHANNELHANDLER
from src.utils import logger, utils
from src.commands.cmdset import CmdSet
from src.commands.cmdparser import at_multimatch_cmd
from src.utils.utils import string_suggestions
@ -165,7 +164,7 @@ def get_and_merge_cmdsets(caller):
# Main command-handler function
@inlineCallbacks
def cmdhandler(caller, raw_string, testing=False):
def cmdhandler(caller, raw_string, testing=False, sessid=None):
"""
This is the main function to handle any string sent to the engine.
@ -251,6 +250,7 @@ def cmdhandler(caller, raw_string, testing=False):
cmd.cmdstring = cmdname
cmd.args = args
cmd.cmdset = cmdset
cmd.sessid = sessid
cmd.raw_string = unformatted_raw_string
if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):

View file

@ -21,7 +21,7 @@ from django.conf import settings
from src.utils.idmapper.models import SharedMemoryModel
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache, hashid
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
from src.typeclasses.typeclass import TypeClass
from src.players.models import PlayerNick
from src.objects.manager import ObjectManager
@ -178,7 +178,7 @@ class ObjectDB(TypedObject):
help_text='a Player connected to this object, if any.')
# the session id associated with this player, if any
db_sessid = models.IntegerField(null=True, verbose_name="session id",
help_text="unique session id of connected Player, if any."
help_text="unique session id of connected Player, if any.")
# The location in the game world. Since this one is likely
# to change often, we set this with the 'location' property
# to transparently handle Typeclassing.
@ -271,20 +271,18 @@ class ObjectDB(TypedObject):
a sessid without a player being connected (but the
opposite could be true).
"""
if not get_field_cache(self, "player"):
if not get_field_cache(self, "sessid"):
del_field_cache(self, "sessid")
return get_field_cache(self, "sessid")
#@sessid.setter
def __sessid_set(self, player):
def __sessid_set(self, sessid):
"Setter. Allows for self.player = value"
if inherits_from(player, TypeClass):
player = player.dbobj
set_field_cache(self, "player", player)
set_field_cache(self, "sessid", sessid)
#@sessid.deleter
def __player_del(self):
def __sessid_del(self):
"Deleter. Allows for del self.player"
del_field_cache(self, "player")
player = property(__player_get, __player_set, __player_del)
del_field_cache(self, "sessid")
player = property(__sessid_get, __sessid_set, __sessid_del)
# location property (wraps db_location)
#@property
def __location_get(self):
@ -637,7 +635,7 @@ class ObjectDB(TypedObject):
# Execution/action methods
#
def execute_cmd(self, raw_string):
def execute_cmd(self, raw_string, sessid=None):
"""
Do something as this object. This command transparently
lets its typeclass execute the command. Evennia also calls
@ -669,11 +667,11 @@ class ObjectDB(TypedObject):
if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break
return cmdhandler.cmdhandler(_GA(self, "typeclass"), raw_string)
return cmdhandler.cmdhandler(_GA(self, "typeclass"), raw_string, sessid=sessid)
def msg(self, message, from_obj=None, data=None):
"""
Emits something to any sessions attached to the object.
Emits something to a session attached to the object.
message (str): The message to send
from_obj (obj): object that is sending.
@ -681,8 +679,8 @@ class ObjectDB(TypedObject):
be used by the protocol.
"""
if _GA(self, 'player'):
# note that we check the typeclass' msg, otherwise one couldn't overload it.
_GA(_GA(self, 'player'), "typeclass").msg(message, from_obj=from_obj, data=data)
# note that we must call the player *typeclass'* msg(), otherwise one couldn't overload it.
_GA(_GA(self, 'player'), "typeclass").msg(message, from_obj=from_obj, data=data, sessid=_GA(self, "sessid"))
def emit_to(self, message, from_obj=None, data=None):
"Deprecated. Alias for msg"

View file

@ -387,7 +387,7 @@ class PlayerDB(TypedObject):
for sess in _GA(self, 'get_sessions'):
sess.msg(outgoing_string, data)
def inmsg(self, ingoing_string, data, sessid):
def inmsg(self, ingoing_string, sessid):
"""
This is the reverse of msg - used by sessions to relay
messages/data back into the game. It is normally not called
@ -400,58 +400,101 @@ class PlayerDB(TypedObject):
character = _GA(self, "get_character")(sessid)
if character:
# execute command on character
_GA(character, "execute_cmd")(ingoing_string)
_GA(character, "execute_cmd")(ingoing_string, sessid=sessid)
else:
# a non-character session; this goes to player directly
_GA(self, "execute_cmd")(ingoing_string)
_GA(self, "execute_cmd")(ingoing_string, sessid=sessid)
def connect_session(self, sessid):
"""
Connect session to this player to a session through
its session id.
def connect_session_to_character(self, sessid, character, force=False):
"""
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().
def get_session(self, sessid=None):
force - drop existing connection to other character
"""
Return session with given sessid connected to this player. If sessid is
not given, return all connected sessions.
Note that this method will always return a list, even if it only has one
(or zero) element(s).
# first check if we already have a character tied to this session
char = _GA(self, "get_character")(sessid=sessid)
if char:
if force and char != character:
_GA(self, "disconnect_session_from_character")(sessid)
else:
return
# do the connection
character.sessid = sessid
# update cache
cache = get_prop_cache(self, "_characters") or {}
cache[sessid] = character
set_prop_cache(self, "_characters", cache)
# call hooks
character.at_init()
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
character.at_pre_login()
character.at_post_login()
def disconnect_session_from_character(self, sessid):
"""
Disconnect a session from the characterm (still keeping the
connection to the Player)
"""
char = _GA(self, "get_character")(sessid=sessid)
if char:
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)
def get_session(self, sessid):
"""
Return session with given sessid connected to this player.
"""
return SESSIONS.get_session_from_player(self, sessid=sessid)
def get_character(self, sessid=None, key=None):
def get_all_sessions(self):
"Return all sessions connected to this player"
return SESSIONS.get_session_from_player(self)
def get_character(self, sessid=None, character=None):
"""
Get the character connected through this sessid, if any. May also
try to return a character based on key. If neither sessid nor key
is given, return all characters connected to this player
Get the character connected to this player
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.
"""
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 = self.db_objs.filter(player=self, sessid=sessid) or None
char = _GA(self, "db_objs").filter(player=self, sessid=sessid) or None
if char:
cache[sessid] = char[0]
set_prop_cache(self, "_characters", cache)
if key:
return char.key.lower() == key.lower() and char or None
if character:
return char == character.dbobj or None
return char
elif key:
char = self.db_objs.filter(player=self, db_key__iexact=key)
elif character:
char = _GA(self, "db_objs").filter(character)
return char and char[0] or None
else:
# no sessid given - return all available characters
return list(self.db_objs.filter(player=self, sessid=sessid))
return list(self.db_objs.all())
def get_all_characters(self):
"""
Readability-wrapper for getting all characters
"""
return self.get_character(sessid=None)
return _GA(self, "get_character")(sessid=None, character=None)
def connect_character(self, character, sessid):
def connect_character(self, char):
"""
Use the Player to connect a Character to a session. Note that
we don't do any access checks at this point. Note that if the
@ -459,38 +502,34 @@ class PlayerDB(TypedObject):
used, since sessids will have changed as players reconnect.
"""
# first disconnect any other character from this session
self.disconnect_character(sessid=sessid)
character = character.dbobj
character.player = self
character.sessid = sessid
self.db_objs.add(character)
self.save()
# update cache
cache = get_prop_cache(self, "_characters") or {}
cache[sessid] = character
set_prop_cache(self, "_characters", cache)
char = char.dbobj
_GA(self, "disconnect_character")(char)
char = char.dbobj
char.player = self
_GA(self, "db_objs").add(char)
_GA(self, "save")()
def disconnect_character(self, sessid=None, char=None):
def disconnect_character(self, char):
"""
Disconnect a character from this player, either based
on sessid or by giving the character object directly
"""
char = char.dbobj
key = char and char.key or None
char = self.get_character(sessid=sessid, key=key)
char = _GA(self, "get_character")(key=key)
if char:
self.db_objs.remove(char)
_GA(self, "db_objs").remove(char)
del char.player
del char.sessid
self.save()
# clear cache
cache = get_prop_cache(self, "_characters") or {}
if cache and sessid in cache:
del cache[sessid]
set_prop_cache(self, "_characters", cache)
[cache.pop(sessid) for sessid,stored_char in cache.items() if stored_char==char]
set_prop_cache(self, "_characters", cache)
def disconnect_all_characters(self):
for char in self.db_objs.all():
self.disconnect_character(char)
_GA(self, "disconnect_character")(char)
def swap_character(self, new_character, delete_old_character=False):
"""
@ -514,7 +553,7 @@ class PlayerDB(TypedObject):
# Execution/action methods
#
def execute_cmd(self, raw_string):
def execute_cmd(self, raw_string, sessid=None):
"""
Do something as this player. This command transparently
lets its typeclass execute the command.
@ -530,7 +569,7 @@ class PlayerDB(TypedObject):
if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break
return cmdhandler.cmdhandler(self.typeclass, raw_string)
return cmdhandler.cmdhandler(self.typeclass, raw_string, sessid=sessid)
def search(self, ostring, return_character=False):
"""

View file

@ -91,25 +91,14 @@ class ServerSession(Session):
self.user.save()
# player init
#print "at_init() - player"
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()
character = player.character
if character:
# this player has a character. Check if it's the
# first time *this character* logs in
character.at_init()
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# run character login hook
character.at_pre_login()
player.at_pre_login()
self.log(_('Logged in: %(self)s') % {'self': self})
@ -119,10 +108,7 @@ class ServerSession(Session):
#add session to connected list
self.sessionhandler.login(self)
# post-login hooks
player.at_post_login()
if character:
character.at_post_login()
def session_disconnect(self):
"""
@ -193,20 +179,13 @@ class ServerSession(Session):
if str(command_string).strip() == IDLE_COMMAND:
self.update_session_counters(idle=True)
return
# all other inputs, including empty inputs
character = self.get_character()
if character:
character.execute_cmd(command_string)
if self.logged_in:
# the inmsg handler will relay to the right place
self.player.inmsg(command_string, self.sessid)
else:
if self.logged_in:
# there is no character, but we are logged in. Use player instead.
self.get_player().execute_cmd(command_string)
else:
# we are not logged in. Use the session directly
# (it uses the settings.UNLOGGEDIN cmdset)
cmdhandler.cmdhandler(self, command_string)
self.update_session_counters()
# we are not logged in. Use the session directly
# (it uses the settings.UNLOGGEDIN cmdset)
cmdhandler.cmdhandler(self, command_string)
def data_out(self, msg, data=None):
"""
@ -243,11 +222,11 @@ class ServerSession(Session):
func = OOB_FUNC_MODULE.__dict__.get(functuple[0])
if func:
try:
outdata[funcname] = func(oobkey, self, *functuple[1], **functuple[2])
outdata[functuple[0]] = func(oobkey, self, *functuple[1], **functuple[2])
except Exception:
logger.log_trace()
else:
logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname)
logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % functuple[0])
if outdata:
# we have a direct result - send it back right away
self.oob_data_out(outdata)

View file

@ -223,8 +223,8 @@ class ServerSessionHandler(SessionHandler):
def player_count(self):
"""
Get the number of connected players (not sessions since a player
may have more than one session connected if ALLOW_MULTISESSION is True)
Get the number of connected players (not sessions since a
player may have more than one session depending on settings).
Only logged-in players are counted here.
"""
return len(set(session.uid for session in self.sessions.values() if session.logged_in))
@ -235,7 +235,8 @@ class ServerSessionHandler(SessionHandler):
"""
uid = player.uid
if sessid:
return [session for session in self.sessions.values() if session.logged_in and session.sessid == sessid and session.uid == uid]
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]
@ -243,9 +244,9 @@ class ServerSessionHandler(SessionHandler):
"""
Given a game character, return any matching sessions.
"""
player = character.player
if player:
return self.sessions_from_player(player)
sessid = character.sessid
if sessid:
return self.sessions.get(sessid)
return None
def announce_all(self, message):