From d496606a3cbec65abc0b60d3d15f2bbe52c721e7 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 14 Nov 2015 20:32:58 +0100 Subject: [PATCH] Further development of the reworking of systems using Sessions rather than session id. --- evennia/commands/command.py | 38 ++----- evennia/commands/default/admin.py | 2 +- evennia/commands/default/building.py | 12 +-- evennia/commands/default/muxcommand.py | 2 +- evennia/commands/default/player.py | 36 +++---- evennia/commands/default/system.py | 4 +- evennia/commands/default/tests.py | 3 +- evennia/commands/default/unloggedin.py | 2 +- evennia/contrib/gendersub.py | 8 +- evennia/game_template/typeclasses/objects.py | 5 +- evennia/game_template/typeclasses/players.py | 4 +- evennia/players/bots.py | 20 ++-- evennia/players/players.py | 11 +- evennia/server/amp.py | 45 ++++---- evennia/server/oob_cmds.py | 14 +-- evennia/server/oobhandler.py | 101 ++++++++---------- evennia/server/portal/portalsessionhandler.py | 21 ++-- evennia/server/serversession.py | 9 +- evennia/server/sessionhandler.py | 38 ++++--- 19 files changed, 166 insertions(+), 209 deletions(-) diff --git a/evennia/commands/command.py b/evennia/commands/command.py index e3b5f4a818..5a2f47b819 100644 --- a/evennia/commands/command.py +++ b/evennia/commands/command.py @@ -148,7 +148,8 @@ class Command(with_metaclass(CommandMeta, object)): # auto-set (by Evennia on command instantiation) are: # obj - which object this command is defined on - # sessid - which session-id (if any) is responsible for triggering this command + # session - which session is responsible for triggering this command. Only set + # if triggered by a player. def __init__(self, **kwargs): """ @@ -297,22 +298,18 @@ class Command(with_metaclass(CommandMeta, object)): return self.lockhandler.check(srcobj, access_type, default=default) def msg(self, msg="", to_obj=None, from_obj=None, - sessid=None, all_sessions=False, **kwargs): + session=None, **kwargs): """ This is a shortcut instad of calling msg() directly on an object - it will detect if caller is an Object or a Player and - also appends self.sessid automatically. + also appends self.session automatically. Args: msg (str, optional): Text string of message to send. to_obj (Object, optional): Target object of message. Defaults to self.caller. from_obj (Object, optional): Source of message. Defaults to to_obj. - sessid (int, optional): Supply data only to a unique - session id (normally not used - this is only potentially - useful if to_obj is a Player object different from - self.caller or self.caller.player). - all_sessions (bool): Default is to send only to the session - connected to the target object + session (Session, optional): Supply data only to a unique + session. Kwargs: kwargs (any): These are all passed on to the message mechanism. Common @@ -321,26 +318,9 @@ class Command(with_metaclass(CommandMeta, object)): """ from_obj = from_obj or self.caller to_obj = to_obj or from_obj - if not sessid: - if hasattr(to_obj, "sessid"): - # this is the case when to_obj is e.g. a Character - toobj_sessions = to_obj.sessid.get() - - # If to_obj has more than one session MULTISESSION_MODE=3 - # we need to send to every session. - #(setting it to None, does it) - session_tosend = None - if len(toobj_sessions) == 1: - session_tosend=toobj_sessions[0] - sessid = all_sessions and None or session_tosend - elif to_obj == self.caller: - # this is the case if to_obj is the calling Player - sessid = all_sessions and None or self.sessid - else: - # if to_obj is a different Player, all their sessions - # will be notified unless sessid was given specifically - sessid = None - to_obj.msg(msg, from_obj=from_obj, sessid=sessid, **kwargs) + if not session or to_obj == self.caller: + session = to_obj.sessions.get() + to_obj.msg(msg, from_obj=from_obj, session=session, **kwargs) # Common Command hooks diff --git a/evennia/commands/default/admin.py b/evennia/commands/default/admin.py index 357df8ae64..c40fe48358 100644 --- a/evennia/commands/default/admin.py +++ b/evennia/commands/default/admin.py @@ -92,7 +92,7 @@ class CmdBoot(MuxCommand): for session in boot_list: session.msg(feedback) - pobj.disconnect_session_from_player(session.sessid) + pobj.disconnect_session_from_player(session) # regex matching IP addresses with wildcards, eg. 233.122.4.* diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 604b04b5b3..bf979e6ca2 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -1868,9 +1868,7 @@ class CmdExamine(ObjManipCommand): string = "\n{wName/key{n: {c%s{n (%s)" % (obj.name, obj.dbref) if hasattr(obj, "aliases") and obj.aliases.all(): string += "\n{wAliases{n: %s" % (", ".join(utils.make_iter(str(obj.aliases)))) - if hasattr(obj, "sessid") and obj.sessid.count(): - string += "\n{wsession{n: %s" % obj.sessid.get() - elif hasattr(obj, "sessions") and obj.sessions: + if hasattr(obj, "sessions") and obj.sessions: string += "\n{wsession(s){n: %s" % (", ".join(str(sess.sessid) for sess in obj.sessions)) if hasattr(obj, "has_player") and obj.has_player: @@ -1924,16 +1922,16 @@ class CmdExamine(ObjManipCommand): # if we merge on the object level. if hasattr(obj, "player") and obj.player: all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.player.cmdset.all()]) - if obj.sessid.count(): + if obj.sessions.count(): # if there are more sessions than one on objects it's because of multisession mode 3. # we only show the first session's cmdset here (it is -in principle- possible that # different sessions have different cmdsets but for admins who want such madness # it is better that they overload with their own CmdExamine to handle it). - all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.player.get_session(obj.sessid.get()[0]).cmdset.all()]) + all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.player.sessions.all()[0].cmdset.all()]) else: try: # we have to protect this since many objects don't have sessions. - all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.get_session(obj.sessid.get()).cmdset.all()]) + all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.get_session(obj.sessions.get()).cmdset.all()]) except (TypeError, AttributeError): pass all_cmdsets = [cmdset for cmdset in dict(all_cmdsets).values()] @@ -2043,7 +2041,7 @@ class CmdExamine(ObjManipCommand): # we are only interested in specific attributes caller.msg(self.format_attributes(obj, attrname, crop=False)) else: - if hasattr(obj, "sessid") and obj.sessid.count(): + if obj.sessions.count(): mergemode = "session" elif self.player_mode: mergemode = "player" diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index b0953a36ca..f159c4bbc8 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -188,6 +188,6 @@ class MuxPlayerCommand(MuxCommand): self.caller = self.caller.player elif utils.inherits_from(self.caller, "evennia.players.players.DefaultPlayer"): # caller was already a Player - self.character = self.caller.get_puppet(self.sessid) + self.character = self.caller.get_puppet(self.session) else: self.character = None diff --git a/evennia/commands/default/player.py b/evennia/commands/default/player.py index c2350cd319..98e6ff849e 100644 --- a/evennia/commands/default/player.py +++ b/evennia/commands/default/player.py @@ -101,7 +101,7 @@ class CmdOOCLook(MuxPlayerLookCommand): return # call on-player look helper method - self.msg(self.player.at_look(target=self.playable, sessid=self.sessid)) + self.msg(self.player.at_look(target=self.playable, session=self.session)) class CmdCharCreate(MuxPlayerCommand): @@ -188,7 +188,7 @@ class CmdIC(MuxPlayerCommand): Main puppet method """ player = self.player - sessid = self.sessid + session = self.session new_character = None if not self.args: @@ -205,7 +205,7 @@ class CmdIC(MuxPlayerCommand): self.msg("That is not a valid character choice.") return try: - player.puppet_object(sessid, new_character) + player.puppet_object(session, new_character) player.db._last_puppet = new_character except RuntimeError as exc: self.msg("{rYou cannot become {C%s{n: %s" % (new_character.name, exc)) @@ -234,9 +234,9 @@ class CmdOOC(MuxPlayerLookCommand): "Implement function" player = self.player - sessid = self.sessid + session = self.session - old_char = player.get_puppet(sessid) + old_char = player.get_puppet(session) if not old_char: string = "You are already OOC." self.msg(string) @@ -246,7 +246,7 @@ class CmdOOC(MuxPlayerLookCommand): # disconnect try: - player.unpuppet_object(sessid) + player.unpuppet_object(session) self.msg("\n{GYou go OOC.{n\n") if _MULTISESSION_MODE < 2: @@ -254,7 +254,7 @@ class CmdOOC(MuxPlayerLookCommand): self.msg("You are out-of-character (OOC).\nUse {w@ic{n to get back into the game.") return - self.msg(player.at_look(target=self.playable, sessid=sessid)) + self.msg(player.at_look(target=self.playable, session=session)) except RuntimeError as exc: self.msg("{rCould not unpuppet from {c%s{n: %s" % (old_char, exc)) @@ -480,19 +480,19 @@ class CmdQuit(MuxPlayerCommand): player = self.player if 'all' in self.switches: - player.msg("{RQuitting{n all sessions. Hope to see you soon again.", sessid=self.sessid) - for session in player.get_all_sessions(): - player.disconnect_session_from_player(session.sessid) + player.msg("{RQuitting{n all sessions. Hope to see you soon again.", session=self.session) + for session in player.sessions.all() + player.disconnect_session_from_player(session) else: - nsess = len(player.get_all_sessions()) + nsess = len(player.sessions.all()) if nsess == 2: - player.msg("{RQuitting{n. One session is still connected.", sessid=self.sessid) + player.msg("{RQuitting{n. One session is still connected.", session=self.session) elif nsess > 2: - player.msg("{RQuitting{n. %i session are still connected." % (nsess-1), sessid=self.sessid) + player.msg("{RQuitting{n. %i session are still connected." % (nsess-1), session=self.session) else: # we are quitting the last available session - player.msg("{RQuitting{n. Hope to see you again, soon.", sessid=self.sessid) - player.disconnect_session_from_player(self.sessid) + player.msg("{RQuitting{n. Hope to see you again, soon.", session=self.session) + player.disconnect_session_from_player(self.session) @@ -601,8 +601,8 @@ class CmdQuell(MuxPlayerCommand): def _recache_locks(self, player): "Helper method to reset the lockhandler on an already puppeted object" - if self.sessid: - char = player.get_puppet(self.sessid) + if self.session: + char = session.puppet if char: # we are already puppeting an object. We need to reset # the lock caches (otherwise the superuser status change @@ -625,7 +625,7 @@ class CmdQuell(MuxPlayerCommand): self.msg("Already quelling Player%s permissions." % permstr) return player.attributes.add('_quell', True) - puppet = player.get_puppet(self.sessid) + puppet = self.session.puppet if puppet: cpermstr = " (%s)" % ", ".join(puppet.permissions.all()) cpermstr = "Quelling to current puppet's permissions%s." % cpermstr diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index 0549cffbe4..3c27f486f6 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -178,7 +178,7 @@ class CmdPy(MuxCommand): 'inherits_from': utils.inherits_from} try: - self.msg(">>> %s" % pycode, raw=True, sessid=self.sessid) + self.msg(">>> %s" % pycode, raw=True, session=self.session) except TypeError: self.msg(">>> %s" % pycode, raw=True) @@ -209,7 +209,7 @@ class CmdPy(MuxCommand): ret = "\n".join("<<< %s" % line for line in errlist if line) try: - self.msg(ret, sessid=self.sessid, raw=True) + self.msg(ret, session=self.session, raw=True) except TypeError: self.msg(ret, raw=True) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index bd0a95d1b8..fc2778f2a4 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -55,7 +55,6 @@ class CommandTest(EvenniaTest): cmdobj.cmdstring = cmdobj.key cmdobj.args = args cmdobj.cmdset = cmdset - cmdobj.sessid = 1 cmdobj.session = SESSIONS.session_from_sessid(1) cmdobj.player = self.player cmdobj.raw_string = cmdobj.key + " " + args @@ -174,7 +173,7 @@ class TestPlayer(CommandTest): self.call(player.CmdOOC(), "", "You go OOC.", caller=self.player) def test_ic(self): - self.player.unpuppet_object(self.session.sessid) + self.player.unpuppet_object(self.session) self.call(player.CmdIC(), "Char", "You become Char.", caller=self.player, receiver=self.char1) def test_password(self): diff --git a/evennia/commands/default/unloggedin.py b/evennia/commands/default/unloggedin.py index cf50a433a0..051bab4f3b 100644 --- a/evennia/commands/default/unloggedin.py +++ b/evennia/commands/default/unloggedin.py @@ -237,7 +237,7 @@ class CmdUnconnectedConnect(MuxCommand): # player.at_init() # always called when object is loaded from disk # player.at_first_login() # only once, for player-centric setup # player.at_pre_login() - # player.at_post_login(sessid=sessid) + # player.at_post_login(session=session) session.sessionhandler.login(session, player) diff --git a/evennia/contrib/gendersub.py b/evennia/contrib/gendersub.py index 32ae88b20f..206d9c8b62 100644 --- a/evennia/contrib/gendersub.py +++ b/evennia/contrib/gendersub.py @@ -110,7 +110,7 @@ class GenderCharacter(DefaultCharacter): pronoun = _GENDER_PRONOUN_MAP[gender][typ.lower()] return pronoun.capitalize() if typ.isupper() else pronoun - def msg(self, text, from_obj=None, sessid=0, **kwargs): + def msg(self, text, from_obj=None, session=None, **kwargs): """ Emits something to a session attached to the object. Overloads the default msg() implementation to include @@ -120,8 +120,8 @@ class GenderCharacter(DefaultCharacter): text (str, optional): The message to send from_obj (obj, optional): object that is sending. If given, at_msg_send will be called - sessid (int or list, optional): sessid or list of - sessids to relay to, if any. If set, will + session (Session or list, optional): session or list of + sessions to relay to, if any. If set, will force send regardless of MULTISESSION_MODE. Notes: `at_msg_receive` will be called on this Object. @@ -130,4 +130,4 @@ class GenderCharacter(DefaultCharacter): """ # pre-process the text before continuing text = _RE_GENDER_PRONOUN.sub(self._get_pronoun, text) - super(GenderCharacter, self).msg(text, from_obj=from_obj, sessid=sessid, **kwargs) + super(GenderCharacter, self).msg(text, from_obj=from_obj, session=session, **kwargs) diff --git a/evennia/game_template/typeclasses/objects.py b/evennia/game_template/typeclasses/objects.py index 82c83453e4..c028278176 100644 --- a/evennia/game_template/typeclasses/objects.py +++ b/evennia/game_template/typeclasses/objects.py @@ -43,7 +43,8 @@ class Object(DefaultObject): player (Player) - controlling player (if any, only set together with sessid below) sessid (int, read-only) - session id (if any, only set together with - player above) + player above). Use `sessions` handler to get the + Sessions directly. location (Object) - current location. Is None if this is a room home (Object) - safety start-location sessions (list of Sessions, read-only) - returns all sessions connected @@ -66,6 +67,8 @@ class Object(DefaultObject): scripts - script-handler. Add new scripts to object with scripts.add() cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object nicks - nick-handler. New nicks with nicks.add(). + sessions - sessions-handler. Get Sessions connected to this + object with sessions.get() * Helper methods (see src.objects.objects.py for full headers) diff --git a/evennia/game_template/typeclasses/players.py b/evennia/game_template/typeclasses/players.py index 5c1d3beb17..d8a7d9a5df 100644 --- a/evennia/game_template/typeclasses/players.py +++ b/evennia/game_template/typeclasses/players.py @@ -65,7 +65,7 @@ class Player(DefaultPlayer): msg(text=None, **kwargs) swap_character(new_character, delete_old_character=False) - execute_cmd(raw_string, sessid=None) + execute_cmd(raw_string, session=None) search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False) is_typeclass(typeclass, exact=False) swap_typeclass(new_typeclass, clean_attributes=False, no_default=True) @@ -83,7 +83,7 @@ class Player(DefaultPlayer): at_init() at_cmdset_get(**kwargs) at_first_login() - at_post_login(sessid=None) + at_post_login(session=None) at_disconnect() at_message_receive() at_message_send() diff --git a/evennia/players/bots.py b/evennia/players/bots.py index 86643ade01..5ce4ba77bf 100644 --- a/evennia/players/bots.py +++ b/evennia/players/bots.py @@ -90,7 +90,7 @@ class CmdBotListen(Command): key = "bot_data_in" def func(self): "Relay to typeclass" - self.obj.execute_cmd(self.args.strip(), sessid=self.sessid) + self.obj.execute_cmd(self.args.strip(), session=self.session) class BotCmdSet(CmdSet): """ @@ -135,19 +135,19 @@ class Bot(DefaultPlayer): """ pass - def msg(self, text=None, from_obj=None, sessid=None, **kwargs): + def msg(self, text=None, from_obj=None, session=None, **kwargs): """ Evennia -> outgoing protocol """ - super(Bot, self).msg(text=text, from_obj=from_obj, sessid=sessid, **kwargs) + super(Bot, self).msg(text=text, from_obj=from_obj, session=session, **kwargs) - def execute_cmd(self, raw_string, sessid=None): + def execute_cmd(self, raw_string, session=None): """ Incoming protocol -> Evennia """ - super(Bot, self).msg(raw_string, sessid=sessid) + super(Bot, self).msg(raw_string, session=session) def at_server_shutdown(self): """ @@ -236,14 +236,14 @@ class IRCBot(Bot): text = "bot_data_out %s" % text super(IRCBot, self).msg(text=text) - def execute_cmd(self, text=None, sessid=None): + def execute_cmd(self, text=None, session=None): """ Take incoming data and send it to connected channel. This is triggered by the CmdListen command in the BotCmdSet. Args: text (str, optional): Command string. - sessid (int, optional): Session id responsible for this + session (Session, optional): Session responsible for this command. """ @@ -296,7 +296,7 @@ class RSSBot(Bot): "rate": self.db.rss_rate} _SESSIONS.start_bot_session("evennia.server.portal.rss.RSSBotFactory", configdict) - def execute_cmd(self, text=None, sessid=None): + def execute_cmd(self, text=None, session=None): """ Echo RSS input to connected channel @@ -386,13 +386,13 @@ class IMC2Bot(Bot): text = "bot_data_out %s" % text self.msg(text=text) - def execute_cmd(self, text=None, sessid=None): + def execute_cmd(self, text=None, session=None): """ Relay incoming data to connected channel. Args: text (str, optional): Command string. - sessid (int, optional): Session id responsible for this + session (Session, optional): Session responsible for this command. """ diff --git a/evennia/players/players.py b/evennia/players/players.py index 2c9e436887..cfd7de8d3d 100644 --- a/evennia/players/players.py +++ b/evennia/players/players.py @@ -430,8 +430,9 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)): raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_player=False) if not session and _MULTISESSION_MODE in (0, 1): - # for these modes we use the - session + # for these modes we use the first/only session + sessions = self.sessions.get() + session = sessions[0] if sessions else None return cmdhandler.cmdhandler(self, raw_string, callertype="player", session=session, **kwargs) @@ -679,7 +680,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)): the player loose. Args: - sessid (int, optional): Session logging in, if any. + session (Session, optional): Session logging in, if any. Notes: This is called *before* an eventual Character's @@ -786,7 +787,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)): # list of targets - make list characters = target - sessions = self.get_all_sessions() + sessions = self.sessions.all() is_su = self.is_superuser # text shown when looking in the ooc area @@ -845,7 +846,7 @@ class DefaultGuest(DefaultPlayer): MULTISESSION_MODE we're in. They don't get a choice. Args: - sessid (int, optional): Id of Session connecting. + session (Session, optional): Session connecting. """ self._send_to_connect_channel("{G%s connected{n" % self.key) diff --git a/evennia/server/amp.py b/evennia/server/amp.py index 9b3665ff5a..fa962d6fed 100644 --- a/evennia/server/amp.py +++ b/evennia/server/amp.py @@ -18,7 +18,7 @@ Server - (AMP server) Handles all mud operations. The server holds its own list from __future__ import print_function # imports needed on both server and portal side -import os, sys +import os from time import time from collections import defaultdict from itertools import count @@ -28,7 +28,7 @@ try: except ImportError: import pickle from twisted.protocols import amp -from twisted.internet import protocol, reactor +from twisted.internet import protocol from twisted.internet.defer import Deferred from evennia.utils import logger from evennia.utils.utils import to_str, variable_from_module @@ -416,10 +416,10 @@ class AMPProtocol(amp.AMP): """ sessid, kwargs = loads(packed_data) - self.factory.server.sessions.data_in(sessid, **kwargs) + self.factory.server.sessions.data_in(self.factory.server.sessions[sessid], **kwargs) return {} - def send_MsgPortal2Server(self, sessid, text="", **kwargs): + def send_MsgPortal2Server(self, session, text="", **kwargs): """ Access method called by the Portal and executed on the Portal. @@ -432,7 +432,7 @@ class AMPProtocol(amp.AMP): deferred (Deferred): Asynchronous return. """ - return self.send_data(MsgPortal2Server, sessid, text=text, **kwargs) + return self.send_data(MsgPortal2Server, session.sessid, text=text, **kwargs) # Server -> Portal message @@ -442,31 +442,26 @@ class AMPProtocol(amp.AMP): Receives message arriving to Portal from Server. This method is executed on the Portal. - Since AMP has a limit of 65355 bytes per message, it's - possible the data comes in multiple chunks; if so (nparts>1) - we buffer the data and wait for the remaining parts to arrive - before continuing. - Args: packed_data (str): Pickled data (sessid, kwargs) coming over the wire. """ sessid, kwargs = loads(packed_data) - self.factory.portal.sessions.data_out(sessid, **kwargs) + self.factory.portal.sessions.data_out(self.factory.portal.sessions[sessid], **kwargs) return {} - def send_MsgServer2Portal(self, sessid, text="", **kwargs): + def send_MsgServer2Portal(self, session, text="", **kwargs): """ Access method - executed on the Server for sending data to Portal. Args: - sessid (int): Unique Session id. + session (Session): Unique Session. msg (str, optional): Message to send over the wire. kwargs (any, optiona): Extra data. """ - return self.send_data(MsgServer2Portal, sessid, text=text, **kwargs) + return self.send_data(MsgServer2Portal, session.sessid, text=text, **kwargs) # Server administration from the Portal side @AdminPortal2Server.responder @@ -483,7 +478,7 @@ class AMPProtocol(amp.AMP): sessid, kwargs = loads(packed_data) operation = kwargs.pop("operation", "") server_sessionhandler = self.factory.server.sessions - + session = server_sessionhandler[sessid] if operation == PCONN: # portal_session_connect # create a new session and sync it @@ -494,7 +489,7 @@ class AMPProtocol(amp.AMP): elif operation == PDISCONN: # portal_session_disconnect # session closed from portal side - self.factory.server.sessions.portal_disconnect(sessid) + self.factory.server.sessions.portal_disconnect(session) elif operation == PSYNC: # portal_session_sync # force a resync of sessions when portal reconnects to @@ -507,20 +502,20 @@ class AMPProtocol(amp.AMP): raise Exception("operation %(op)s not recognized." % {'op': operation}) return {} - def send_AdminPortal2Server(self, sessid, operation="", **kwargs): + def send_AdminPortal2Server(self, session, operation="", **kwargs): """ Send Admin instructions from the Portal to the Server. Executed on the Portal. Args: - sessid (int): Session id. + session (Session): Session. operation (char, optional): Identifier for the server operation, as defined by the global variables in `evennia/server/amp.py`. data (str or dict, optional): Data used in the administrative operation. """ - return self.send_data(AdminPortal2Server, sessid, operation=operation, **kwargs) + return self.send_data(AdminPortal2Server, session.sessid, operation=operation, **kwargs) # Portal administraton from the Server side @@ -539,13 +534,15 @@ class AMPProtocol(amp.AMP): operation = kwargs.pop("operation") portal_sessionhandler = self.factory.portal.sessions + session = portal_sessionhandler[sessid] + if operation == SLOGIN: # server_session_login # a session has authenticated; sync it. - portal_sessionhandler.server_logged_in(sessid, kwargs.get("sessiondata")) + portal_sessionhandler.server_logged_in(session, kwargs.get("sessiondata")) elif operation == SDISCONN: # server_session_disconnect # the server is ordering to disconnect the session - portal_sessionhandler.server_disconnect(sessid, reason=kwargs.get("reason")) + portal_sessionhandler.server_disconnect(session, reason=kwargs.get("reason")) elif operation == SDISCONNALL: # server_session_disconnect_all # server orders all sessions to disconnect @@ -569,20 +566,20 @@ class AMPProtocol(amp.AMP): raise Exception("operation %(op)s not recognized." % {'op': operation}) return {} - def send_AdminServer2Portal(self, sessid, operation="", **kwargs): + def send_AdminServer2Portal(self, session, operation="", **kwargs): """ Administrative access method called by the Server to send an instruction to the Portal. Args: - sessid (int): Session id. + session (Session): Session. operation (char, optional): Identifier for the server operation, as defined by the global variables in `evennia/server/amp.py`. data (str or dict, optional): Data going into the adminstrative. """ - return self.send_data(AdminServer2Portal, sessid, operation=operation, **kwargs) + return self.send_data(AdminServer2Portal, session.sessid, operation=operation, **kwargs) # Extra functions diff --git a/evennia/server/oob_cmds.py b/evennia/server/oob_cmds.py index d65adbdab8..52e81d46f9 100644 --- a/evennia/server/oob_cmds.py +++ b/evennia/server/oob_cmds.py @@ -125,7 +125,7 @@ def oob_repeat(session, oobfuncname, interval, *args, **kwargs): interval = 20 if not interval else (max(5, interval)) obj = session.get_puppet_or_player() if obj and oobfuncname != "REPEAT": - OOB_HANDLER.add_repeater(obj, session.sessid, oobfuncname, interval, *args, **kwargs) + OOB_HANDLER.add_repeater(obj, session, oobfuncname, interval, *args, **kwargs) ##OOB{"UNREPEAT":10} @@ -146,7 +146,7 @@ def oob_unrepeat(session, oobfuncname, interval): """ obj = session.get_puppet_or_player() if obj: - OOB_HANDLER.remove_repeater(obj, session.sessid, oobfuncname, interval) + OOB_HANDLER.remove_repeater(obj, session, oobfuncname, interval) # @@ -251,10 +251,10 @@ def oob_report(session, *args, **kwargs): oob_error(session, "No Reportable property '%s'. Use LIST REPORTABLE_VARIABLES." % propname) # the field_monitors require an oob function as a callback when they report a change. elif propname.startswith("db_"): - OOB_HANDLER.add_field_monitor(obj, session.sessid, propname, "return_field_report") + OOB_HANDLER.add_field_monitor(obj, session, propname, "return_field_report") ret.append(to_str(_GA(obj, propname), force_string=True)) else: - OOB_HANDLER.add_attribute_monitor(obj, session.sessid, propname, "return_attribute_report") + OOB_HANDLER.add_attribute_monitor(obj, session, propname, "return_attribute_report") ret.append(_GA(obj, "db_value")) session.msg(oob=("MSDP_ARRAY", ret)) else: @@ -313,9 +313,9 @@ def oob_unreport(session, *args, **kwargs): if not propname: oob_error(session, "No Un-Reportable property '%s'. Use LIST REPORTABLE_VARIABLES." % propname) elif propname.startswith("db_"): - OOB_HANDLER.remove_field_monitor(obj, session.sessid, propname, "oob_return_field_report") + OOB_HANDLER.remove_field_monitor(obj, session, propname, "oob_return_field_report") else: # assume attribute - OOB_HANDLER.remove_attribute_monitor(obj, session.sessid, propname, "oob_return_attribute_report") + OOB_HANDLER.remove_attribute_monitor(obj, session, propname, "oob_return_attribute_report") else: oob_error(session, "You must log in first.") @@ -358,7 +358,7 @@ def oob_list(session, mode, *args, **kwargs): # we need to check so as to use the right return value depending on if it is # an Attribute (identified by tracking the db_value field) or a normal database field # reported is a list of tuples (obj, propname, args, kwargs) - reported = OOB_HANDLER.get_all_monitors(session.sessid) + reported = OOB_HANDLER.get_all_monitors(session) reported = [rep[0].key if rep[1] == "db_value" else rep[1] for rep in reported] session.msg(oob=("REPORTED_VARIABLES", reported)) elif mode == "SENDABLE_VARIABLES": diff --git a/evennia/server/oobhandler.py b/evennia/server/oobhandler.py index 97f17ee802..4dda804e1f 100644 --- a/evennia/server/oobhandler.py +++ b/evennia/server/oobhandler.py @@ -70,15 +70,17 @@ class OOBFieldMonitor(object): # oobtuples is a list [(oobfuncname, args, kwargs), ...], # a potential list of oob commands to call when this # field changes. - for (oobfuncname, args, kwargs) in oobtuples: - OOB_HANDLER.execute_cmd(sessid, oobfuncname, fieldname, self.obj, *args, **kwargs) + sessid = SESSIONS.get(sessid) + if sessid: + for (oobfuncname, args, kwargs) in oobtuples: + OOB_HANDLER.execute_cmd(sessid, oobfuncname, fieldname, self.obj, *args, **kwargs) - def add(self, sessid, oobfuncname, *args, **kwargs): + def add(self, session, oobfuncname, *args, **kwargs): """ Add a specific tracking callback to monitor Args: - sessid (int): Session id + session (int): Session. oobfuncname (str): oob command to call when field updates args,kwargs (any): arguments to pass to oob commjand @@ -88,9 +90,9 @@ class OOBFieldMonitor(object): field updates. """ - self.subscribers[sessid].append((oobfuncname, args, kwargs)) + self.subscribers[session.sessid].append((oobfuncname, args, kwargs)) - def remove(self, sessid, oobfuncname=None): + def remove(self, session, oobfuncname=None): """ Remove a subscribing session from the monitor @@ -101,10 +103,10 @@ class OOBFieldMonitor(object): """ if oobfuncname: - self.subscribers[sessid] = [item for item in self.subscribers[sessid] + self.subscribers[session.sessid] = [item for item in self.subscribers[session.sessid] if item[0] != oobfuncname] else: - self.subscribers.pop(sessid, None) + self.subscribers.pop(session.sessid, None) class OOBAtRepeater(object): @@ -268,22 +270,20 @@ class OOBHandler(TickerHandler): oobfuncname = kwargs["_oobfuncname"] self.add_repeater(obj, sessid, oobfuncname, interval, *args, **kwargs) - def add_repeater(self, obj, sessid, oobfuncname, interval=20, *args, **kwargs): + def add_repeater(self, obj, session, oobfuncname, interval=20, *args, **kwargs): """ Set an oob function to be repeatedly called. Args: - obj (Object) - the object on which to register the repeat - sessid (int) - session id of the session registering - oobfuncname (str) - oob function name to call every interval seconds - interval (int, optional) - interval to call oobfunc, in seconds + obj (Object); The object on which to register the repeat. + session (Session): Session of the session registering. + oobfuncname (str): Oob function name to call every interval seconds. + interval (int, optional): Interval to call oobfunc, in seconds. + Notes: *args, **kwargs are used as extra arguments to the oobfunc. """ - # check so we didn't get a session instead of a sessid - if not isinstance(sessid, int): - sessid = sessid.sessid - + sessid = session hook = OOBAtRepeater() hookname = self._get_repeater_hook_name(oobfuncname, interval, sessid) _SA(obj, hookname, hook) @@ -291,20 +291,18 @@ class OOBHandler(TickerHandler): kwargs.update({"_sessid":sessid, "_oobfuncname":oobfuncname}) super(OOBHandler, self).add(obj, int(interval), oobfuncname, hookname, *args, **kwargs) - def remove_repeater(self, obj, sessid, oobfuncname, interval=20): + def remove_repeater(self, obj, session, oobfuncname, interval=20): """ Remove the repeatedly calling oob function Args: obj (Object): The object on which the repeater sits - sessid (int): Session id of the Session that registered the repeater + sessid (Session): Session that registered the repeater oobfuncname (str): Name of oob function to call at repeat interval (int, optional): Number of seconds between repeats """ - # check so we didn't get a session instead of a sessid - if not isinstance(sessid, int): - sessid = sessid.sessid + sessid = session.sessid super(OOBHandler, self).remove(obj, interval, idstring=oobfuncname) hookname = self._get_repeater_hook_name(oobfuncname, interval, sessid) try: @@ -312,16 +310,16 @@ class OOBHandler(TickerHandler): except AttributeError: pass - def add_field_monitor(self, obj, sessid, field_name, oobfuncname, *args, **kwargs): + def add_field_monitor(self, obj, session, field_name, oobfuncname, *args, **kwargs): """ Add a monitor tracking a database field Args: - obj (Object): The object who'se field is to be monitored - sessid (int): Session if of the session monitoring + obj (Object): The object who'se field is to be monitored. + session (Session): Session monitoring. field_name (str): Name of database field to monitor. The db_* can optionally - be skipped (it will be automatically appended if missing) - oobfuncname (str): OOB function to call when field changes + be skipped (it will be automatically appended if missing). + oobfuncname (str): OOB function to call when field changes. Notes: When the field updates the given oobfunction will be called as @@ -333,23 +331,21 @@ class OOBHandler(TickerHandler): can also easily get the new field value if you want. """ - # check so we didn't get a session instead of a sessid - if not isinstance(sessid, int): - sessid = sessid.sessid + sessid = session.sessid # all database field names starts with db_* field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name self._add_monitor(obj, sessid, field_name, oobfuncname, *args, **kwargs) - def remove_field_monitor(self, obj, sessid, field_name, oobfuncname=None): + def remove_field_monitor(self, obj, session, field_name, oobfuncname=None): """ Un-tracks a database field Args: - obj (Object): Entity with the monitored field - sessid (int): Session id of session that monitors + obj (Object): Entity with the monitored field. + session (Session): Session that monitors. field_name (str): database field monitored (the db_* can optionally be - skipped (it will be auto-appended if missing) - oobfuncname (str, optional): OOB command to call on that field + skipped (it will be auto-appended if missing). + oobfuncname (str, optional): OOB command to call on that field. Notes: When the Attributes db_value updates the given oobfunction @@ -361,65 +357,57 @@ class OOBHandler(TickerHandler): `obj` is the object on which the field sits. From this you can also easily get the new field value if you want. """ - # check so we didn't get a session instead of a sessid - if not isinstance(sessid, int): - sessid = sessid.sessid + sessid = session.sessid field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name self._remove_monitor(obj, sessid, field_name, oobfuncname=oobfuncname) - def add_attribute_monitor(self, obj, sessid, attr_name, oobfuncname, *args, **kwargs): + def add_attribute_monitor(self, obj, session, attr_name, oobfuncname, *args, **kwargs): """ Monitor the changes of an Attribute on an object. Will trigger when the Attribute's `db_value` field updates. Args: obj (Object): Object with the Attribute to monitor. - sessid (int): Session id of monitoring Session. + session (Session): Session monitoring Session. attr_name (str): Name (key) of Attribute to monitor. oobfuncname (str): OOB function to call when Attribute updates. """ - # check so we didn't get a session instead of a sessid - if not isinstance(sessid, int): - sessid = sessid.sessid + sessid = session.sessid # get the attribute object if we can attrobj = obj.attributes.get(attr_name, return_obj=True) if attrobj: self._add_monitor(attrobj, sessid, "db_value", oobfuncname) - def remove_attribute_monitor(self, obj, sessid, attr_name, oobfuncname): + def remove_attribute_monitor(self, obj, session, attr_name, oobfuncname): """ Deactivate tracking for a given object's Attribute Args: obj (Object): Object monitored. - sessid (int): Session id of monitoring Session. + session (Session): Session monitoring. attr_name (str): Name of Attribute monitored. oobfuncname (str): OOB function name called when Attribute updates. """ - # check so we didn't get a session instead of a sessid - if not isinstance(sessid, int): - sessid = sessid.sessid + sessid = session.sessid attrobj = obj.attributes.get(attr_name, return_obj=True) if attrobj: self._remove_monitor(attrobj, sessid, "db_value", oobfuncname) - def get_all_monitors(self, sessid): + def get_all_monitors(self, session): """ Get the names of all variables this session is tracking. Args: - sessid (id): Session id of monitoring Session + session (Session): Session monitoring. Returns: stored monitors (tuple): A list of tuples - `(obj, fieldname, args, kwargs)` representing all - the monitoring the Session with the given sessid is doing. + `(obj, fieldname, args, kwargs)` representing all + the monitoring the Session with the given sessid is doing. """ - # check so we didn't get a session instead of a sessid - if not isinstance(sessid, int): - sessid = sessid.sessid + sessid = session.sessid # [(obj, fieldname, args, kwargs), ...] return [(unpack_dbobj(key[0]), key[2], stored[0], stored[1]) for key, stored in self.oob_monitor_storage.items() if key[1] == sessid] @@ -441,9 +429,6 @@ class OOBHandler(TickerHandler): `kwargs` are passed into the oob command. """ - if isinstance(session, int): - # a sessid. Convert to a session - session = SESSIONS.session_from_sessid(session) if not session: errmsg = "OOB Error: execute_cmd(%s,%s,%s,%s) - no valid session" % \ (session, oobfuncname, args, kwargs) diff --git a/evennia/server/portal/portalsessionhandler.py b/evennia/server/portal/portalsessionhandler.py index d0fd37a0e1..87b4477c65 100644 --- a/evennia/server/portal/portalsessionhandler.py +++ b/evennia/server/portal/portalsessionhandler.py @@ -195,7 +195,7 @@ class PortalSessionHandler(SessionHandler): protocol = cls(self, **config) protocol.start() - def server_disconnect(self, sessid, reason=""): + def server_disconnect(self, session, reason=""): """ Called by server to force a disconnect by sessid. @@ -204,12 +204,11 @@ class PortalSessionHandler(SessionHandler): reason (str, optional): Motivation for disconect. """ - session = self.get(sessid, None) if session: session.disconnect(reason) - if sessid in self: + if session.sessid in self: # in case sess.disconnect doesn't delete it - del self[sessid] + del self[session.sessid] del session def server_disconnect_all(self, reason=""): @@ -225,18 +224,17 @@ class PortalSessionHandler(SessionHandler): del session self = {} - def server_logged_in(self, sessid, data): + def server_logged_in(self, session, data): """ The server tells us that the session has been authenticated. Update it. Called by the Server. Args: - sessid (int): Session id logging in. + session (Session): Session logging in. data (dict): The session sync data. """ - sess = self.get_session(sessid) - sess.load_sync_data(data) + session.load_sync_data(data) def server_session_sync(self, serversessions): """ @@ -395,7 +393,7 @@ class PortalSessionHandler(SessionHandler): if self.command_overflow: reactor.callLater(1.0, self.data_in, None) if self.command_overflow: - self.data_out(session.sessid, text=_ERROR_COMMAND_OVERFLOW) + self.data_out(session, text=_ERROR_COMMAND_OVERFLOW) return # relay data to Server self.command_counter += 1 @@ -409,14 +407,14 @@ class PortalSessionHandler(SessionHandler): reactor.callLater(1.0, self.data_in, None) - def data_out(self, sessid, text=None, **kwargs): + def data_out(self, session, text=None, **kwargs): """ Called by server for having the portal relay messages and data to the correct session protocol. We also convert oob input to a generic form here. Args: - sessid (int): Session id sending data. + session (Session): Session sending data. Kwargs: text (str): Text from protocol. @@ -426,7 +424,6 @@ class PortalSessionHandler(SessionHandler): #from evennia.server.profiling.timetrace import timetrace #text = timetrace(text, "portalsessionhandler.data_out") - session = self.get(sessid, None) if session: # convert oob to the generic format if "oob" in kwargs: diff --git a/evennia/server/serversession.py b/evennia/server/serversession.py index 49163da565..73dbbd17da 100644 --- a/evennia/server/serversession.py +++ b/evennia/server/serversession.py @@ -202,7 +202,7 @@ class ServerSession(Session): # done in the default @ic command but without any # hooks, echoes or access checks. obj = _ObjectDB.objects.get(id=self.puid) - obj.sessid.add(self.sessid) + obj.sessions.add(self) obj.player = self.player self.puid = obj.id self.puppet = obj @@ -239,10 +239,9 @@ class ServerSession(Session): """ if self.logged_in: - sessid = self.sessid player = self.player if self.puppet: - player.unpuppet_object(sessid) + player.unpuppet_object(self) uaccount = player uaccount.last_login = timezone.now() uaccount.save() @@ -363,14 +362,14 @@ class ServerSession(Session): return if self.player: # nick replacement - puppet = self.player.get_puppet(self.sessid) + puppet = self.puppet if puppet: text = puppet.nicks.nickreplace(text, categories=("inputline", "channel"), include_player=True) else: text = self.player.nicks.nickreplace(text, categories=("inputline", "channels"), include_player=False) - cmdhandler(self, text, callertype="session", sessid=self.sessid) + cmdhandler(self, text, callertype="session", session=self) self.update_session_counters() execute_cmd = data_in # alias diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index 54e1945e5c..ea14998d02 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -33,6 +33,9 @@ _ServerConfig = None _ScriptDB = None _OOB_HANDLER = None +class DummySession(object): + sessid = 0 +DUMMYSESSION = DummySession() # AMP signals PCONN = chr(1) # portal session connect @@ -55,6 +58,7 @@ _IDLE_TIMEOUT = settings.IDLE_TIMEOUT _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. @@ -264,7 +268,7 @@ class ServerSessionHandler(SessionHandler): the Server. """ - self.server.amp_protocol.send_AdminServer2Portal(0, operation=SCONN, + self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, operation=SCONN, protocol_path=protocol_path, config=configdict) def portal_shutdown(self): @@ -272,7 +276,7 @@ class ServerSessionHandler(SessionHandler): Called by server when shutting down the portal. """ - self.server.amp_protocol.send_AdminServer2Portal(0, + self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, operation=SSHUTD) def login(self, session, player, testmode=False): @@ -319,7 +323,7 @@ class ServerSessionHandler(SessionHandler): session.logged_in = True # sync the portal to the session if not testmode: - self.server.amp_protocol.send_AdminServer2Portal(session.sessid, + self.server.amp_protocol.send_AdminServer2Portal(session, operation=SLOGIN, sessiondata={"logged_in": True}) player.at_post_login(sessid=session.sessid) @@ -349,7 +353,7 @@ class ServerSessionHandler(SessionHandler): sessid = session.sessid del self[sessid] # inform portal that session should be closed. - self.server.amp_protocol.send_AdminServer2Portal(sessid, + self.server.amp_protocol.send_AdminServer2Portal(session, operation=SDISCONN, reason=reason) @@ -360,7 +364,7 @@ class ServerSessionHandler(SessionHandler): """ sessdata = self.get_all_sync_data() - return self.server.amp_protocol.send_AdminServer2Portal(0, + return self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, operation=SSYNC, sessiondata=sessdata) @@ -376,7 +380,7 @@ class ServerSessionHandler(SessionHandler): for session in self: del session # tell portal to disconnect all sessions - self.server.amp_protocol.send_AdminServer2Portal(0, + self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, operation=SDISCONNALL, reason=reason) @@ -462,12 +466,9 @@ class ServerSessionHandler(SessionHandler): sessions (Session or list): Session(s) found. """ - if is_iter(sessid): - sessions = [self.get(sid) for sid in sessid] - s = [sess for sess in sessions if sess and sess.logged_in and player.uid == sess.uid] - return s - session = self.get(sessid) - return session and session.logged_in and player.uid == session.uid and session or None + sessions = [self[sid] for sid in make_iter(sessid) + if sid in self and self[sid].logged_in and player.uid == self[sid].uid] + return sessions[0] if len(sessions) == 1 else sessions def sessions_from_player(self, player): """ @@ -495,10 +496,8 @@ class ServerSessionHandler(SessionHandler): more than one Session (MULTISESSION_MODE > 1). """ - sessid = puppet.sessid.get() - if is_iter(sessid): - return [self.get(sid) for sid in sessid if sid in self] - return self.get(sessid) + sessions = puppet.sessid.get() + return sessions[0] if len(sessions) == 1 else sessions sessions_from_character = sessions_from_puppet def announce_all(self, message): @@ -527,17 +526,17 @@ class ServerSessionHandler(SessionHandler): text = text and to_str(to_unicode(text), encoding=session.encoding) # send across AMP - self.server.amp_protocol.send_MsgServer2Portal(sessid=session.sessid, + self.server.amp_protocol.send_MsgServer2Portal(session, text=text, **kwargs) - def data_in(self, sessid, text="", **kwargs): + def data_in(self, session, text="", **kwargs): """ Data Portal -> Server. We also intercept OOB communication here. Args: - sessid (int): Session id. + sessions (Session): Session. Kwargs: text (str): Text from protocol. @@ -546,7 +545,6 @@ class ServerSessionHandler(SessionHandler): """ #from evennia.server.profiling.timetrace import timetrace #text = timetrace(text, "ServerSessionHandler.data_in") - session = self.get(sessid, None) if session: text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding) if "oob" in kwargs: