Started work on #673; refactored the data flow. Still issues with correctly puppeting/unpuppeting in modes > 0.

This commit is contained in:
Griatch 2015-02-26 21:53:19 +01:00
parent c0aafe74ab
commit a87fbff366
5 changed files with 141 additions and 102 deletions

View file

@ -14,7 +14,7 @@ access the character when these commands are triggered with
a connected character (such as the case of the @ooc command), it a connected character (such as the case of the @ooc command), it
is None if we are OOC. is None if we are OOC.
Note that under MULTISESSION_MODE=2, Player- commands should use Note that under MULTISESSION_MODE > 2, Player- commands should use
self.msg() and similar methods to reroute returns to the correct self.msg() and similar methods to reroute returns to the correct
method. Otherwise all text will be returned to all connected sessions. method. Otherwise all text will be returned to all connected sessions.
@ -249,10 +249,11 @@ class CmdIC(MuxPlayerCommand):
else: else:
self.msg("That is not a valid character choice.") self.msg("That is not a valid character choice.")
return return
if player.puppet_object(sessid, new_character): try:
player.puppet_object(sessid, new_character)
player.db._last_puppet = new_character player.db._last_puppet = new_character
else: except RuntimeError, exc:
self.msg("{rYou cannot become {C%s{n." % new_character.name) self.msg("{rYou cannot become {C%s{n: %s" % (new_character.name, exc))
class CmdOOC(MuxPlayerCommand): class CmdOOC(MuxPlayerCommand):
@ -287,11 +288,12 @@ class CmdOOC(MuxPlayerCommand):
player.db._last_puppet = old_char player.db._last_puppet = old_char
# disconnect # disconnect
if player.unpuppet_object(sessid): try:
player.unpuppet_object(sessid)
self.msg("\n{GYou go OOC.{n\n") self.msg("\n{GYou go OOC.{n\n")
player.execute_cmd("look", sessid=sessid) player.execute_cmd("look", sessid=sessid)
else: except RuntimeError, exc:
raise RuntimeError("Could not unpuppet!") self.msg("{rCould not unpuppet from {c%s{n: %s" % (old_char, exc))
class CmdSessions(MuxPlayerCommand): class CmdSessions(MuxPlayerCommand):
""" """

View file

@ -501,23 +501,19 @@ class DefaultObject(ObjectDB):
""" """
Emits something to a session attached to the object. Emits something to a session attached to the object.
message (str): The message to send Args:
from_obj (obj): object that is sending. text (str, optional): The message to send
data (object): an optional data object that may or may not from_obj (obj, optional): object that is sending. If
be used by the protocol. given, at_msg_send will be called
sessid (int): sessid to relay to, if any. sessid (int or list, optional): sessid or list of
If set to 0 (default), use either from_obj.sessid (if set) or self.sessid automatically sessids to relay to, if any. If set, will
If None, echo to all connected sessions force send regardless of MULTISESSION_MODE.
Notes:
When this message is called, from_obj.at_msg_send and self.at_msg_receive are called. `at_msg_receive` will be called on this Object.
All extra kwargs will be passed on to the protocol.
""" """
global _SESSIONS
if not _SESSIONS:
from evennia.server.sessionhandler import SESSIONS as _SESSIONS
text = to_str(text, force_string=True) if text else "" text = to_str(text, force_string=True) if text else ""
if from_obj: if from_obj:
# call hook # call hook
try: try:
@ -531,9 +527,25 @@ class DefaultObject(ObjectDB):
except Exception: except Exception:
log_trace() log_trace()
sessions = _SESSIONS.session_from_sessid([sessid] if sessid else make_iter(self.sessid.get())) # session relay
for session in sessions:
session.msg(text=text, **kwargs) if self.player:
# for there to be a session there must be a Player.
if sessid:
# this could still be an iterable if sessid is.
sessions = self.player.get_session(sessid)
if sessions:
# this is a special instruction to ignore MULTISESSION_MODE
# and only relay to this given session.
kwargs["_nomulti"] = True
for session in make_iter(sessions):
session.msg(text=text, **kwargs)
return
# we only send to the first of any connected sessions - the sessionhandler
# will disperse this to the other sessions based on MULTISESSION_MODE.
sessions = self.player.get_all_sessions()
if sessions:
sessions[0].msg(text=text, **kwargs)
def msg_contents(self, message, exclude=None, from_obj=None, **kwargs): def msg_contents(self, message, exclude=None, from_obj=None, **kwargs):
""" """

View file

@ -166,26 +166,30 @@ class DefaultPlayer(PlayerDB):
Use the given session to control (puppet) the given object (usually Use the given session to control (puppet) the given object (usually
a Character type). a Character type).
sessid - session id of session to connect Args:
obj - the object to connect to sessid (int): session id of session to connect
normal_mode - trigger hooks and extra checks - this is turned off when obj (Object): the object to start puppeting
the server reloads, to quickly re-connect puppets. normal_mode (bool, optional): trigger hooks and extra
checks - this is turned off when the server reloads, to
quickly re-connect puppets.
Raises:
RuntimeError with message if puppeting is not possible
returns True if successful, False otherwise returns True if successful, False otherwise
""" """
# safety checks # safety checks
if not obj: if not obj:
return raise RuntimeError("Object not found")
session = self.get_session(sessid) session = self.get_session(sessid)
if not session: if not session:
return False raise RuntimeError("Session not found")
if self.get_puppet(sessid) == obj: if self.get_puppet(sessid) == obj:
# already puppeting this object # already puppeting this object
return raise RuntimeError("You are already puppeting this object.")
if not obj.access(self, 'puppet'): if not obj.access(self, 'puppet'):
# no access # no access
self.msg("You don't have permission to puppet '%s'." % obj.key) raise RuntimeError("You don't have permission to puppet '%s'." % obj.key)
return
if normal_mode and obj.player: if normal_mode and obj.player:
# object already puppeted # object already puppeted
if obj.player == self: if obj.player == self:
@ -198,13 +202,12 @@ class DefaultPlayer(PlayerDB):
else: else:
txt1 = "{c%s{n{R is now acted from another of your sessions.{n" txt1 = "{c%s{n{R is now acted from another of your sessions.{n"
txt2 = "Taking over {c%s{n from another of your sessions." txt2 = "Taking over {c%s{n from another of your sessions."
self.unpuppet_object(obj.sessid.get())
self.msg(txt1 % obj.name, sessid=obj.sessid.get()) self.msg(txt1 % obj.name, sessid=obj.sessid.get())
self.msg(txt2 % obj.name, sessid=sessid) self.msg(txt2 % obj.name, sessid=sessid)
self.unpuppet_object(obj.sessid.get())
elif obj.player.is_connected: elif obj.player.is_connected:
# controlled by another player # controlled by another player
self.msg("{R{c%s{R is already puppeted by another Player.") raise RuntimeError("{R{c%s{R is already puppeted by another Player.")
return
# do the puppeting # do the puppeting
if normal_mode and session.puppet: if normal_mode and session.puppet:
@ -227,34 +230,38 @@ class DefaultPlayer(PlayerDB):
obj.at_post_puppet() obj.at_post_puppet()
# re-cache locks to make sure superuser bypass is updated # re-cache locks to make sure superuser bypass is updated
obj.locks.cache_lock_bypass(obj) obj.locks.cache_lock_bypass(obj)
return True
def unpuppet_object(self, sessid): def unpuppet_object(self, sessid):
""" """
Disengage control over an object Disengage control over an object
sessid - the session id to disengage Args:
sessid(int): the session id to disengage
returns True if successful Raises:
RuntimeError with message about error.
""" """
session = self.get_session(sessid) if _MULTISESSION_MODE == 1:
if not session: sessions = self.get_all_sessions()
return False else:
session = make_iter(session)[0] sessions = self.get_session(sessid)
#print "unpuppet, session:", session, session.puppet if not sessions:
obj = hasattr(session, "puppet") and session.puppet or None raise RuntimeError("No session was found.")
#print "unpuppet, obj:", obj for session in make_iter(sessions):
if not obj: #print "unpuppet, session:", session, session.puppet
return False obj = hasattr(session, "puppet") and session.puppet or None
# do the disconnect, but only if we are the last session to puppet #print "unpuppet, obj:", obj
obj.at_pre_unpuppet() if not obj:
obj.sessid.remove(sessid) raise RuntimeError("No puppet was found to disconnect from.")
if not obj.sessid.count(): # do the disconnect, but only if we are the last session to puppet
del obj.player obj.at_pre_unpuppet()
obj.at_post_unpuppet(self, sessid=sessid) obj.sessid.remove(sessid)
session.puppet = None if not obj.sessid.count():
session.puid = None del obj.player
return True obj.at_post_unpuppet(self, sessid=sessid)
session.puppet = None
session.puid = None
def unpuppet_all(self): def unpuppet_all(self):
""" """
@ -329,15 +336,14 @@ class DefaultPlayer(PlayerDB):
This is the main route for sending data back to the user from the This is the main route for sending data back to the user from the
server. server.
outgoing_string (string) - text data to send Args:
from_obj (Object/Player) - source object of message to send. Its text (str, optional): text data to send
at_msg_send() hook will be called. from_obj (Object or Player, optional): object sending. If given,
sessid - the session id of the session to send to. If not given, return its at_msg_send() hook will be called.
to all sessions connected to this player. This is usually only sessid (int or list, optional): session id or ids to receive this
relevant when using msg() directly from a player-command (from send. If given, overrules MULTISESSION_MODE.
a command on a Character, the character automatically stores Notes:
and handles the sessid). Can also be a list of sessids. All other keywords are passed on to the protocol.
kwargs (dict) - All other keywords are parsed as extra data.
""" """
text = to_str(text, force_string=True) if text else "" text = to_str(text, force_string=True) if text else ""
if from_obj: if from_obj:
@ -346,18 +352,24 @@ class DefaultPlayer(PlayerDB):
from_obj.at_msg_send(text=text, to_obj=self, **kwargs) from_obj.at_msg_send(text=text, to_obj=self, **kwargs)
except Exception: except Exception:
pass pass
sessions = _MULTISESSION_MODE > 1 and sessid and self.get_session(sessid) or None
# session relay
if sessid:
# this could still be an iterable if sessid is an iterable
sessions = self.get_session(sessid)
if sessions:
# this is a special instruction to ignore MULTISESSION_MODE
# and only relay to this given session.
kwargs["_nomulti"] = True
for session in make_iter(sessions):
session.msg(text=text, **kwargs)
return
# we only send to the first of any connected sessions - the sessionhandler
# will disperse this to the other sessions based on MULTISESSION_MODE.
sessions = self.get_all_sessions()
if sessions: if sessions:
for session in make_iter(sessions): sessions[0].msg(text=text, **kwargs)
obj = session.puppet
if obj and not obj.at_msg_receive(text=text, **kwargs):
# if hook returns false, cancel send
continue
session.msg(text=text, **kwargs)
else:
# if no session was specified, send to them all
for sess in self.get_all_sessions():
sess.msg(text=text, **kwargs)
def execute_cmd(self, raw_string, sessid=None, **kwargs): def execute_cmd(self, raw_string, sessid=None, **kwargs):
""" """
@ -629,12 +641,8 @@ class DefaultPlayer(PlayerDB):
# try to auto-connect to our last conneted object, if any # try to auto-connect to our last conneted object, if any
self.puppet_object(sessid, self.db._last_puppet) self.puppet_object(sessid, self.db._last_puppet)
elif _MULTISESSION_MODE == 1: elif _MULTISESSION_MODE == 1:
# in this mode the first session to connect acts like mode 0, # in this mode all sessions connect to the same puppet.
# the following sessions "share" the same view and should self.puppet_object(sessid, self.db._last_puppet)
# not perform any actions
if not self.get_all_puppets():
# we are first. Connect.
self.puppet_object(sessid, self.db._last_puppet)
elif _MULTISESSION_MODE in (2, 3): elif _MULTISESSION_MODE in (2, 3):
# In this mode we by default end up at a character selection # In this mode we by default end up at a character selection
# screen. We execute look on the player. # screen. We execute look on the player.

View file

@ -224,6 +224,8 @@ class ServerSession(Session):
if INLINEFUNC_ENABLED and not "raw" in kwargs: if INLINEFUNC_ENABLED and not "raw" in kwargs:
text = parse_inlinefunc(text, strip="strip_inlinefunc" in kwargs, session=self) text = parse_inlinefunc(text, strip="strip_inlinefunc" in kwargs, session=self)
self.sessionhandler.data_out(self, text=text, **kwargs) self.sessionhandler.data_out(self, text=text, **kwargs)
# alias
msg = data_out
def __eq__(self, other): def __eq__(self, other):
return self.address == other.address return self.address == other.address
@ -251,18 +253,6 @@ class ServerSession(Session):
""" """
return u"%s" % str(self) return u"%s" % str(self)
# easy-access functions
#def login(self, player):
# "alias for at_login"
# self.session_login(player)
#def disconnect(self):
# "alias for session_disconnect"
# self.session_disconnect()
def msg(self, text='', **kwargs):
"alias for at_data_out"
self.data_out(text=text, **kwargs)
# Dummy API hooks for use during non-loggedin operation # Dummy API hooks for use during non-loggedin operation
def at_cmdset_get(self, **kwargs): def at_cmdset_get(self, **kwargs):

View file

@ -46,7 +46,7 @@ PCONNSYNC = chr(10) # portal post-syncing session
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
SERVERNAME = settings.SERVERNAME SERVERNAME = settings.SERVERNAME
MULTISESSION_MODE = settings.MULTISESSION_MODE _MULTISESSION_MODE = settings.MULTISESSION_MODE
IDLE_TIMEOUT = settings.IDLE_TIMEOUT IDLE_TIMEOUT = settings.IDLE_TIMEOUT
@ -278,7 +278,7 @@ class ServerSessionHandler(SessionHandler):
player.at_pre_login() player.at_pre_login()
if MULTISESSION_MODE == 0: if _MULTISESSION_MODE == 0:
# disconnect all previous sessions. # disconnect all previous sessions.
self.disconnect_duplicate_sessions(session) self.disconnect_duplicate_sessions(session)
@ -418,14 +418,15 @@ class ServerSessionHandler(SessionHandler):
uid = player.uid uid = player.uid
return [session for session in self.sessions.values() if session.logged_in and session.uid == uid] return [session for session in self.sessions.values() if session.logged_in and session.uid == uid]
def sessions_from_character(self, character): def sessions_from_puppet(self, puppet):
""" """
Given a game character, return any matching sessions. Given a puppeted object, return all controlling sessions.
""" """
sessid = character.sessid.get() sessid = puppet.sessid.get()
if is_iter(sessid): if is_iter(sessid):
return [self.sessions.get(sess) for sess in sessid if sessid in self.sessions] return [self.sessions.get(sid) for sid in sessid if sid in self.sessions]
return self.sessions.get(sessid) return self.sessions.get(sessid)
sessions_from_character = sessions_from_puppet
def announce_all(self, message): def announce_all(self, message):
""" """
@ -437,9 +438,35 @@ class ServerSessionHandler(SessionHandler):
def data_out(self, session, text="", **kwargs): def data_out(self, session, text="", **kwargs):
""" """
Sending data Server -> Portal Sending data Server -> Portal
Args:
session (Session): Session object
text (str, optional): text data to return
_nomulti (bool, optional): if given, only this
session will receive the rest of the data,
regardless of MULTISESSION_MODE. This is an
internal variable that will not be passed on.
""" """
text = text and to_str(to_unicode(text), encoding=session.encoding) text = text and to_str(to_unicode(text), encoding=session.encoding)
self.server.amp_protocol.call_remote_MsgServer2Portal(sessid=session.sessid, multi = not kwargs.pop("_nomulti", None)
sessions = [session]
if _MULTISESSION_MODE == 1:
if session.player:
sessions = self.sessions_from_player(session.player)
elif multi:
if _MULTISESSION_MODE == 2:
if session.player:
sessions = self.sessions_from_player(session.player)
elif _MULTISESSION_MODE == 3:
if session.puppet:
sessions = self.sessions_from_puppet(session.puppet)
elif session.player:
sessions = self.sessions_from_player(session.player)
# send to all found sessions
for session in sessions:
self.server.amp_protocol.call_remote_MsgServer2Portal(sessid=session.sessid,
msg=text, msg=text,
data=kwargs) data=kwargs)