diff --git a/src/commands/default/general.py b/src/commands/default/general.py index cd4d7a2f55..79b8122d18 100644 --- a/src/commands/default/general.py +++ b/src/commands/default/general.py @@ -649,7 +649,12 @@ class CmdAccess(MuxCommand): string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms) caller.msg(string) + + + +#------------------------------------------------------------ # OOC commands +#------------------------------------------------------------ class CmdOOCLook(MuxCommandOOC, CmdLook): """ @@ -658,28 +663,67 @@ class CmdOOCLook(MuxCommandOOC, CmdLook): Usage: look - This is an OOC version of the look command. Since a - Player doesn't have an in-game existence, there is no - concept of location or "self". If we are controlling - a character, pass control over to normal look. - + Look in the ooc state. """ + #This is an OOC version of the look command. Since a + #Player doesn't have an in-game existence, there is no + #concept of location or "self". If we are controlling + #a character, pass control over to normal look. + key = "look" aliases = ["l", "ls"] locks = "cmd:all()" help_category = "General" + def look_target(self): + "Hook method for when an argument is given." + # caller is assumed to be a player object here. + caller = self.caller + looktarget = caller.get_character(key=self.args) + if looktarget: + caller.msg(looktarget.return_appearance()) + else: + caller.msg("No such character.") + return + + def no_look_target(self): + "Hook method for default look without a specified target" + # caller is always a player at this point. + player = self.caller + sessid = self.sessid + # get all our characters + characters = player.get_all_characters() # get all characters + string = "You are logged in as {g%s{n." % player.key + string += " Use {w@ic {n to enter the game." + string += "\n\nAvailable character%s:" % (characters.count() > 1 and "s" or "") + for char in characters: + csessid = char.sessid + if csessid: + # character is already puppeted + if csessid == sessid: + # this should not happen. + string += "\n - {r%s{n (sessid not properly cleared! Contact an admin!)" % char.key + elif player.get_session(csessid): + string += "\n - {G%s{n (played by you in another session)" + else: + string += "\n - {R%s{n (played by someone else)" % char.key + else: + # character is "free to puppet" + string += "\n - %s" % char.key + player.msg(string) + def func(self): "implement the ooc look command" - if not self.character: - string = "You are out-of-character (OOC). " - string += "Use {w@ic{n to get back to the game, {whelp{n for more info." - self.caller.msg(string) - else: - self.caller = self.character # we have to put this back for normal look to work. + if utils.inherits_from(self.caller, "src.objects.objects.Object"): + # An object of some type is calling. Use default look instead. super(CmdOOCLook, self).func() + elif self.args: + self.look_target() + else: + self.no_look_target() + class CmdIC(MuxCommandOOC): """ diff --git a/src/objects/models.py b/src/objects/models.py index f0dd76a16c..97b0eb74e6 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -274,14 +274,13 @@ class ObjectDB(TypedObject): if not get_field_cache(self, "player"): del_field_cache(self, "sessid") return get_field_cache(self, "sessid") - - #@player.setter + #@sessid.setter def __sessid_set(self, player): "Setter. Allows for self.player = value" if inherits_from(player, TypeClass): player = player.dbobj set_field_cache(self, "player", player) - #@player.deleter + #@sessid.deleter def __player_del(self): "Deleter. Allows for del self.player" del_field_cache(self, "player") diff --git a/src/players/models.py b/src/players/models.py index 59ac2c4aaf..66d03e3334 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -225,7 +225,7 @@ class PlayerDB(TypedObject): #@property def character_get(self): "Getter. Allows for value = self.character" - return get_field_cache(self, "obj) + return get_field_cache(self, "obj") #@character.setter def character_set(self, character): "Setter. Allows for self.character = value" @@ -378,7 +378,7 @@ class PlayerDB(TypedObject): session = _GA(self, "get_session")(sessid) if session: char = _GA(self, "get_character")(sessid) - if char and not char.at_msg_receive(outgoing_string, from_obj=from_obj, data=data)): + if char and not char.at_msg_receive(outgoing_string, from_obj=from_obj, data=data): # if hook returns false, cancel send return session.msg(outgoing_string, data) @@ -411,7 +411,7 @@ class PlayerDB(TypedObject): its session id. """ - def get_sessions(self, sessid=None): + def get_session(self, sessid=None): """ Return session with given sessid connected to this player. If sessid is not given, return all connected sessions. @@ -420,24 +420,36 @@ class PlayerDB(TypedObject): """ return SESSIONS.get_session_from_player(self, sessid=sessid) - def get_character(self, sessid): + def get_character(self, sessid=None, key=None): """ - Get the character connected through this sessid, if any + 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 """ - if not sessid: # sessid is always > 0 - return None - try: - char = get_prop_cache(self, "_characters").get(sessid) - except AttributeError: - set_prop_cache(self, "_characters", {}) - char = None - if not char: - char = self.db_objs.filter(player=self, sessid=sessid) - if char.count(): - chars_cache = get_prop_cache(self, "_characters") - chars_cache[sessid] = char[0] - set_prop_cache(self, "_characters") - return char + 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 + if char: + cache[sessid] = char[0] + set_prop_cache(self, "_characters", cache) + if key: + return char.key.lower() == key.lower() and char or None + return char + elif key: + char = self.db_objs.filter(player=self, db_key__iexact=key) + 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)) + + def get_all_characters(self): + """ + Readability-wrapper for getting all characters + """ + return self.get_character(sessid=None) def connect_character(self, character, sessid): """ @@ -446,25 +458,39 @@ class PlayerDB(TypedObject): game was fully restarted (including the Portal), this must be 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) - def disconnect_character(self, character): + def disconnect_character(self, sessid=None, char=None): """ - Disconnect a character from this player. + Disconnect a character from this player, either based + on sessid or by giving the character object directly """ - character = character.dbobj - if self.db_objs.filter(id=_GA(character,"id")): - self.db_objs.remove(character) - character.player = None - character.sessid = None + key = char and char.key or None + char = self.get_character(sessid=sessid, key=key) + if char: + 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) def disconnect_all_characters(self): for char in self.db_objs.all(): + self.disconnect_character(char) def swap_character(self, new_character, delete_old_character=False): """ diff --git a/src/server/sessionhandler.py b/src/server/sessionhandler.py index e171343095..dcd1d6c49d 100644 --- a/src/server/sessionhandler.py +++ b/src/server/sessionhandler.py @@ -32,7 +32,8 @@ SSYNC = chr(8) # server session sync from django.utils.translation import ugettext as _ SERVERNAME = settings.SERVERNAME -ALLOW_MULTISESSION = settings.ALLOW_MULTISESSION +#ALLOW_MULTISESSION = settings.ALLOW_MULTISESSION +MULTISESSION_MODE = settings.MULTISESSION_MODE IDLE_TIMEOUT = settings.IDLE_TIMEOUT #----------------------------------------------------------- @@ -163,8 +164,8 @@ class ServerSessionHandler(SessionHandler): """ # prep the session with player/user info - if not ALLOW_MULTISESSION: - # disconnect previous sessions. + if MULTISESSION_MODE == 0: + # disconnect all previous sessions. self.disconnect_duplicate_sessions(session) session.logged_in = True # sync the portal to this session @@ -235,7 +236,7 @@ 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] - else + else: return [session for session in self.sessions.values() if session.logged_in and session.uid == uid] def sessions_from_character(self, character): diff --git a/src/settings_default.py b/src/settings_default.py index abab559836..8dd69a004e 100644 --- a/src/settings_default.py +++ b/src/settings_default.py @@ -59,12 +59,12 @@ SSL_ENABLED = False SSL_PORTS = [4001] # Interface addresses to listen to. If 0.0.0.0, listen to all. SSL_INTERFACES = ['0.0.0.0'] -# If multisessions are allowed, a user can log into the game -# from several different computers/clients at the same time. -# All feedback from the game will be echoed to all sessions. -# If false, only one session is allowed, all other are logged off -# when a new connects. -ALLOW_MULTISESSION = False +# Multisession modes allow a player (=account) to connect to the game simultaneously +# with multiple clients in various ways according to the set mode: +# 0 - no multisession - when a new session is connected, the old one is disconnected +# 1 - multiple sessions, one player, one character, each session getting the same data +# 2 - multiple sessions, one player, each session controlling different characters +MULTISESSION_MODE = 0 # The path that contains this settings.py file (no trailing slash). BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Path to the src directory containing the bulk of the codebase's code.