From 00584365ae4b1addf41a8aa35948006c074d2622 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 2 Feb 2013 22:41:56 +0100 Subject: [PATCH] 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. --- src/commands/cmdhandler.py | 4 +- src/objects/models.py | 28 ++++---- src/players/models.py | 125 +++++++++++++++++++++++------------ src/server/serversession.py | 39 +++-------- src/server/sessionhandler.py | 13 ++-- 5 files changed, 113 insertions(+), 96 deletions(-) diff --git a/src/commands/cmdhandler.py b/src/commands/cmdhandler.py index f182f5e5ad..05fcf2d7a0 100644 --- a/src/commands/cmdhandler.py +++ b/src/commands/cmdhandler.py @@ -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'): diff --git a/src/objects/models.py b/src/objects/models.py index 97b0eb74e6..2b48734b61 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -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" diff --git a/src/players/models.py b/src/players/models.py index 66d03e3334..a59850da42 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -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): """ diff --git a/src/server/serversession.py b/src/server/serversession.py index f1303e8d8f..46afb68188 100644 --- a/src/server/serversession.py +++ b/src/server/serversession.py @@ -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) diff --git a/src/server/sessionhandler.py b/src/server/sessionhandler.py index dcd1d6c49d..7b469895c3 100644 --- a/src/server/sessionhandler.py +++ b/src/server/sessionhandler.py @@ -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):