diff --git a/src/commands/default/general.py b/src/commands/default/general.py index b30376f6f3..b6b8243c8c 100644 --- a/src/commands/default/general.py +++ b/src/commands/default/general.py @@ -903,9 +903,14 @@ class CmdCharCreate(MuxCommandOOC): new_character = create.create_object(typeclass, key=key, location=default_home, home=default_home, permissions=permissions) + # only allow creator (and immortals) to puppet this char + new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" % + (new_character.id, player.id)) player.db._playable_characters.append(new_character) if desc: new_character.db.desc = desc + else: + new_character.db.desc = "This is a Player." self.msg("Created new character %s." % new_character.key) @@ -942,7 +947,7 @@ class CmdIC(MuxCommandOOC): new_character = None if not self.args: - new_character = caller.db.last_puppet + new_character = caller.db._last_puppet if not new_character: self.msg("Usage: @ic ") return @@ -960,7 +965,10 @@ class CmdIC(MuxCommandOOC): return if new_character.player: if new_character.sessid == sessid: - self.msg("{RYou already act as {c%s{n from another session." % new_character.name) + self.msg("{RYou already act as {c%s{n." % new_character.name) + return + elif new_character.player == caller: + self.msg("{RYou already act as {c%s{n in another session." % new_character.name) return elif not caller.get_character(character=new_character): self.msg("{c%s{r is already acted by another player.{n" % new_character.name) @@ -972,7 +980,7 @@ class CmdIC(MuxCommandOOC): self.msg("\n{gYou become {c%s{n.\n" % new_character.name) caller.db.last_puppet = old_character if not new_character.location: - # this might be due to being hidden away at logout; check + # this might be due to being hidden away at logout; check loc = new_character.db.prelogout_location if not loc: # still no location; use home loc = new_character.home @@ -1012,7 +1020,7 @@ class CmdOOC(MuxCommandOOC): self.msg(string) return - caller.db.last_puppet = old_char + caller.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]) diff --git a/src/commands/default/unloggedin.py b/src/commands/default/unloggedin.py index 59c791db76..98a8351f73 100644 --- a/src/commands/default/unloggedin.py +++ b/src/commands/default/unloggedin.py @@ -92,12 +92,11 @@ class CmdUnconnectedConnect(MuxCommand): return # actually do the login. This will call all other hooks: - # session.at_init() - # if character: - # at_first_login() # only once - # at_pre_login() - # player.at_post_login() - calls look if no character is set - # character.at_post_login() - this calls look command by default + # session.at_login() + # player.at_init() # always called when object is loaded from disk + # player.at_pre_login() + # player.at_first_login() # only once + # player.at_post_login() session.sessionhandler.login(session, player) class CmdUnconnectedCreate(MuxCommand): @@ -199,7 +198,8 @@ class CmdUnconnectedCreate(MuxCommand): # If no description is set, set a default description if not new_character.db.desc: new_character.db.desc = "This is a Player." - + # set flag for triggering first-time login hook + new_character.db._first_login = True # tell the caller everything went well. string = "A new account '%s' was created. Welcome!" diff --git a/src/objects/objects.py b/src/objects/objects.py index 48034ce787..ff7bdc9f2b 100644 --- a/src/objects/objects.py +++ b/src/objects/objects.py @@ -794,6 +794,7 @@ class Character(Object): """ This recovers the character again after having been "stoved away" at disconnect. """ + print "char:at_post_login", self if self.db.prelogout_location: # try to recover self.location = self.db.prelogout_location diff --git a/src/players/models.py b/src/players/models.py index 6e918082b1..aa167e2c64 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -36,7 +36,7 @@ from src.typeclasses.typeclass import TypeClass from src.commands.cmdsethandler import CmdSetHandler from src.commands import cmdhandler from src.utils import logger, utils -from src.utils.utils import inherits_from +from src.utils.utils import inherits_from, make_iter __all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB") @@ -182,50 +182,17 @@ class PlayerDB(TypedObject): #@property def objs_get(self): "Getter. Allows for value = self.obj" - return self.db_objs.all() - #return get_field_cache(self, "objs").all() - #@obj.setter + return list(self.db_objs.all()) + #@objs.setter def objs_set(self, value): - "Setter. Allows for self.obj = value" - global _TYPECLASS - if not _TYPECLASS: - from src.typeclasses.typeclass import TypeClass as _TYPECLASS - - if isinstance(value, _TYPECLASS): - value = value.dbobj - try: - self.db_objs.add(value) - self.save() - #set_field_cache(self, "obj", value) - except Exception: - logger.log_trace() - raise Exception("Cannot assign %s as a player object!" % value) + "Setter. Allows for self.objs = value" + raise Exception("Use access methods to add new characters instead.") #@obj.deleter def objs_del(self): "Deleter. Allows for del self.obj" - self.db_objs.clear() - self.save() - #del_field_cache(self, "obj") + raise Exception("Use access methods to delete new characters instead.") objs = property(objs_get, objs_set, objs_del) - - # whereas the name 'obj' is consistent with the rest of the code, - # 'character' is a more intuitive property name, so we - # define this too, as an alias to player.obj. - #@property - def character_get(self): - "Getter. Allows for value = self.character" - return get_field_cache(self, "obj") - #@character.setter - def character_set(self, character): - "Setter. Allows for self.character = value" - if inherits_from(character, TypeClass): - character = character.dbobj - set_field_cache(self, "obj", character) - #@character.deleter - def character_del(self): - "Deleter. Allows for del self.character" - del_field_cache(self, "obj") - character = property(character_get, character_set, character_del) + characters = property(objs_get, objs_set, objs_del) # cmdset_storage property # This seems very sensitive to caching, so leaving it be for now /Griatch @@ -407,6 +374,22 @@ class PlayerDB(TypedObject): # a non-character session; this goes to player directly _GA(self, "execute_cmd")(ingoing_string, sessid=sessid) + def get_session(self, sessid): + """ + Return session with given sessid connected to this player. + """ + global _SESSIONS + if not _SESSIONS: + from src.server.sessionhandler import SESSIONS as _SESSIONS + return _SESSIONS.sessions_from_player(self, sessid=sessid) + + def get_all_sessions(self): + "Return all sessions connected to this player" + global _SESSIONS + if not _SESSIONS: + from src.server.sessionhandler import SESSIONS as _SESSIONS + return _SESSIONS.sessions_from_player(self) + def get_session_from_sessid(self, sessid): """ Get the session object from sessid. If session with sessid is not @@ -421,8 +404,8 @@ class PlayerDB(TypedObject): """ Access method for disconnecting a given session from the player. """ - session = self.get_session_from_sessid(sessid) - if session: + sessions = self.get_session_from_sessid(sessid) + for session in make_iter(sessions): session.sessionhandler.disconnect(session) def connect_session_to_character(self, sessid, character, force=False): @@ -454,10 +437,11 @@ class PlayerDB(TypedObject): # start (persistent) scripts on this object #ScriptDB.objects.validate(obj=character) pass - if character.db.FIRST_LOGIN: + if character.db._first_login: character.at_first_login() - del character.db.FIRST_LOGIN + del character.db._first_login character.at_pre_login() + print "player: calling at_post_login on char" character.at_post_login() return True @@ -494,23 +478,8 @@ class PlayerDB(TypedObject): char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True) if not char: return - self.connect_session_to_character(sessid, char, force=True) + _GA(self, "connect_session_to_character")(sessid, char, force=True) - def get_session(self, sessid): - """ - Return session with given sessid connected to this player. - """ - global _SESSIONS - if not _SESSIONS: - from src.server.sessionhandler import SESSIONS as _SESSIONS - return _SESSIONS.sessions_from_player(self, sessid=sessid) - - def get_all_sessions(self): - "Return all sessions connected to this player" - global _SESSIONS - if not _SESSIONS: - from src.server.sessionhandler import SESSIONS as _SESSIONS - return _SESSIONS.sessions_from_player(self) def get_character(self, sessid=None, character=None, return_dbobj=False): """ @@ -550,14 +519,23 @@ class PlayerDB(TypedObject): """ 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 connect_character(self, character, sessid=None): """ - 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 + 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. + if sessid is given, also connect the sessid to the character directly. """ # first disconnect any other character from this session char = character.dbobj @@ -590,7 +568,6 @@ class PlayerDB(TypedObject): 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) diff --git a/src/players/player.py b/src/players/player.py index b73b1e536e..5a96c49e78 100644 --- a/src/players/player.py +++ b/src/players/player.py @@ -18,6 +18,7 @@ from src.comms.models import Channel from src.utils import logger __all__ = ("Player",) +_MULTISESSION_MODE = settings.MULTISESSION_MODE _CMDSET_OOC = settings.CMDSET_OOC _CONNECT_CHANNEL = None @@ -322,9 +323,11 @@ class Player(TypeClass): at_post_login hook. """ self._send_to_connect_channel("{G%s connected{n" % self.key) - # Character.at_post_login also looks around. Only use - # this as a backup when logging in without a character - self.execute_cmd("look") + + if _MULTISESSION_MODE == 2 or not self.get_all_characters(): + # Character.at_post_login also looks around. Only use + # this as a backup when logging in without a character + self.execute_cmd("look") def at_disconnect(self, reason=None): """ diff --git a/src/server/initial_setup.py b/src/server/initial_setup.py index 01717a3aed..5dde6350e0 100644 --- a/src/server/initial_setup.py +++ b/src/server/initial_setup.py @@ -61,6 +61,7 @@ def create_objects(): god_character.save() god_character.set_attribute("_superuser_character", True) + gor_character.set_attribute("_first_login", True) # Limbo is the default "nowhere" starting room diff --git a/src/server/serversession.py b/src/server/serversession.py index 26dbfac127..59f5953f0c 100644 --- a/src/server/serversession.py +++ b/src/server/serversession.py @@ -143,7 +143,7 @@ class ServerSession(Session): return if self.logged_in: # the inmsg handler will relay to the right place - self.player.inmsg(command_string, self.sessid) + self.player.inmsg(command_string, sessid=self.sessid) else: # we are not logged in. Use the session directly # (it uses the settings.UNLOGGEDIN cmdset) diff --git a/src/server/sessionhandler.py b/src/server/sessionhandler.py index ba9982b6ad..26c017d0a1 100644 --- a/src/server/sessionhandler.py +++ b/src/server/sessionhandler.py @@ -233,7 +233,7 @@ class ServerSessionHandler(SessionHandler): player.at_pre_login() - session.log(_('Logged in: %(self)s') % {'self': self}) + session.log(_('Logged in: %(self)s') % {'self': player}) # start (persistent) scripts on this object #ScriptDB.objects.validate(obj=self.player.character) @@ -275,12 +275,12 @@ class ServerSessionHandler(SessionHandler): def disconnect_duplicate_sessions(self, curr_session, reason = _("Logged in from elsewhere. Disconnecting.") ): """ - Disconnects any existing sessions with the same game object. + Disconnects any existing sessions with the same user. """ - curr_char = curr_session.get_character() + uid = curr_session.uid doublet_sessions = [sess for sess in self.sessions.values() if sess.logged_in - and sess.get_character() == curr_char + and sess.uid == uid and sess != curr_session] for session in doublet_sessions: self.disconnect(session, reason)