mirror of
https://github.com/evennia/evennia.git
synced 2026-04-01 05:27:17 +02:00
Heavily reworked the many-char system, cleaner and more consistent by not having any persistent links on the Object side once a player has unconnected.
This commit is contained in:
parent
5100a0561f
commit
26ced2cb90
8 changed files with 321 additions and 251 deletions
|
|
@ -403,6 +403,7 @@ class CmdQuit(MuxCommand):
|
|||
|
||||
def func(self):
|
||||
"hook function"
|
||||
# always operate on the player
|
||||
if hasattr(self.caller, "player"):
|
||||
player = self.caller.player
|
||||
else:
|
||||
|
|
@ -772,7 +773,7 @@ class CmdColorTest(MuxCommand):
|
|||
|
||||
|
||||
#------------------------------------------------------------
|
||||
# OOC commands
|
||||
# Player commands
|
||||
#
|
||||
# Note that in commands inheriting from MuxCommandOOC,
|
||||
# self.caller is always the Player object, not the Character.
|
||||
|
|
@ -803,15 +804,14 @@ class CmdOOCLook(MuxCommandOOC, CmdLook):
|
|||
|
||||
def look_target(self):
|
||||
"Hook method for when an argument is given."
|
||||
# caller is assumed to be a player object here.
|
||||
caller = self.caller
|
||||
player = self.caller
|
||||
key = self.args.lower()
|
||||
chars = dict((utils.to_str(char.key.lower()), char) for char in caller.db._playable_characters)
|
||||
chars = dict((utils.to_str(char.key.lower()), char) for char in player.db._playable_characters)
|
||||
looktarget = chars.get(key)
|
||||
if looktarget:
|
||||
caller.msg(looktarget.return_appearance(caller))
|
||||
self.msg(looktarget.return_appearance(caller))
|
||||
else:
|
||||
caller.msg("No such character.")
|
||||
self.msg("No such character.")
|
||||
return
|
||||
|
||||
def no_look_target(self):
|
||||
|
|
@ -887,7 +887,7 @@ class CmdCharCreate(MuxCommandOOC):
|
|||
"create the new character"
|
||||
player = self.caller
|
||||
if not self.args:
|
||||
player.msg("Usage: @charcreate <charname> [= description]")
|
||||
self.msg("Usage: @charcreate <charname> [= description]")
|
||||
return
|
||||
key = self.lhs
|
||||
desc = self.rhs
|
||||
|
|
@ -941,44 +941,43 @@ class CmdIC(MuxCommandOOC):
|
|||
"""
|
||||
Simple puppet method
|
||||
"""
|
||||
caller = self.caller
|
||||
player = self.caller
|
||||
sessid = self.sessid
|
||||
old_character = self.character
|
||||
|
||||
new_character = None
|
||||
if not self.args:
|
||||
new_character = caller.db._last_puppet
|
||||
new_character = player.db._last_puppet
|
||||
if not new_character:
|
||||
self.msg("Usage: @ic <character>")
|
||||
return
|
||||
if not new_character:
|
||||
# search for a matching character
|
||||
new_character = search.objects(self.args, caller)
|
||||
new_character = search.objects(self.args, player)
|
||||
if new_character:
|
||||
new_character = new_character[0]
|
||||
else:
|
||||
self.msg("That is not a valid character choice.")
|
||||
return
|
||||
# permission checks
|
||||
if caller.get_character(sessid=sessid, character=new_character):
|
||||
if player.get_puppet(sessid) == new_character:
|
||||
self.msg("{RYou already act as {c%s{n." % new_character.name)
|
||||
return
|
||||
if new_character.player:
|
||||
if new_character.sessid == sessid:
|
||||
self.msg("{RYou already act as {c%s{n." % new_character.name)
|
||||
return
|
||||
elif new_character.sessid and new_character.player == caller:
|
||||
# may not puppet an already puppeted character
|
||||
if new_character.sessid and new_character.player == player:
|
||||
self.msg("{RYou already act as {c%s{n in another session." % new_character.name)
|
||||
return
|
||||
elif not caller.get_character(character=new_character):
|
||||
elif new_character.player != player and new_character.player.is_connected:
|
||||
self.msg("{c%s{r is already acted by another player.{n" % new_character.name)
|
||||
return
|
||||
if not new_character.access(caller, "puppet"):
|
||||
if not new_character.access(player, "puppet"):
|
||||
# main acccess check
|
||||
self.msg("{rYou may not become %s.{n" % new_character.name)
|
||||
return
|
||||
if caller.connect_character(new_character, sessid=sessid):
|
||||
if player.puppet_object(sessid, new_character):
|
||||
self.msg("\n{gYou become {c%s{n.\n" % new_character.name)
|
||||
caller.db._last_puppet = old_character
|
||||
player.db._last_puppet = old_character
|
||||
if not new_character.location:
|
||||
# this might be due to being hidden away at logout; check
|
||||
loc = new_character.db.prelogout_location
|
||||
|
|
@ -1012,15 +1011,16 @@ class CmdOOC(MuxCommandOOC):
|
|||
def func(self):
|
||||
"Implement function"
|
||||
|
||||
caller = self.caller
|
||||
player = self.caller
|
||||
sessid = self.sessid
|
||||
|
||||
old_char = caller.get_character(sessid=self.sessid)
|
||||
old_char = player.get_puppet(sessid)
|
||||
if not old_char:
|
||||
string = "You are already OOC."
|
||||
self.msg(string)
|
||||
return
|
||||
|
||||
caller.db._last_puppet = old_char
|
||||
player.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])
|
||||
|
|
@ -1028,7 +1028,9 @@ class CmdOOC(MuxCommandOOC):
|
|||
old_char.location = None
|
||||
|
||||
# disconnect
|
||||
err = caller.disconnect_character(self.character)
|
||||
self.msg("\n{GYou go OOC.{n\n")
|
||||
caller.execute_cmd("look")
|
||||
if player.unpuppet_object(sessid):
|
||||
self.msg("\n{GYou go OOC.{n\n")
|
||||
player.execute_cmd("look")
|
||||
else:
|
||||
raise RuntimeError("Could not unpuppet!")
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,8 @@ class ObjectDB(TypedObject):
|
|||
ndb - non-persistent attribute storage
|
||||
|
||||
The ObjectDB adds the following properties:
|
||||
player - optional connected player
|
||||
player - optional connected player (always together with sessid)
|
||||
sessid - optional connection session id (always together with player)
|
||||
location - in-game location of object
|
||||
home - safety location for object (handler)
|
||||
|
||||
|
|
|
|||
|
|
@ -288,24 +288,6 @@ class PlayerDB(TypedObject):
|
|||
raise Exception("User id cannot be deleted!")
|
||||
uid = property(uid_get, uid_set, uid_del)
|
||||
|
||||
# sessions property
|
||||
#@property
|
||||
def sessions_get(self):
|
||||
"Getter. Retrieve sessions related to this player/user"
|
||||
global _SESSIONS
|
||||
if not _SESSIONS:
|
||||
from src.server.sessionhandler import SESSIONS as _SESSIONS
|
||||
return _SESSIONS.sessions_from_player(self)
|
||||
#@sessions.setter
|
||||
def sessions_set(self, value):
|
||||
"Setter. Protects the sessions property from adding things"
|
||||
raise Exception("Cannot set sessions manually!")
|
||||
#@sessions.deleter
|
||||
def sessions_del(self):
|
||||
"Deleter. Protects the sessions property from deletion"
|
||||
raise Exception("Cannot delete sessions manually!")
|
||||
sessions = property(sessions_get, sessions_set, sessions_del)
|
||||
|
||||
#@property
|
||||
def is_superuser_get(self):
|
||||
"Superusers have all permissions."
|
||||
|
|
@ -344,7 +326,7 @@ class PlayerDB(TypedObject):
|
|||
|
||||
session = _MULTISESSION_MODE == 2 and sessid and _GA(self, "get_session")(sessid) or None
|
||||
if session:
|
||||
char = _GA(self, "get_character")(sessid=sessid)
|
||||
obj = session.puppet
|
||||
if char and not char.at_msg_receive(outgoing_string, from_obj=from_obj, data=data):
|
||||
# if hook returns false, cancel send
|
||||
return
|
||||
|
|
@ -354,24 +336,29 @@ class PlayerDB(TypedObject):
|
|||
for sess in _GA(self, 'get_all_sessions')():
|
||||
sess.msg(outgoing_string, data)
|
||||
|
||||
def inmsg(self, ingoing_string, sessid):
|
||||
def inmsg(self, ingoing_string, session):
|
||||
"""
|
||||
User -> Evennia
|
||||
This is the reverse of msg - used by sessions to relay
|
||||
messages/data back into the game. It is normally not called
|
||||
messages/data back into the game. It is not called
|
||||
from inside game code but only by the serversessions directly.
|
||||
|
||||
ingoing_string - text string (i.e. command string)
|
||||
data - dictionary of optional data
|
||||
sessid - session sending this data
|
||||
session - session sending this data (no need to look it up again)
|
||||
"""
|
||||
character = _GA(self, "get_character")(sessid=sessid)
|
||||
if character:
|
||||
# execute command on character
|
||||
_GA(character, "execute_cmd")(ingoing_string, sessid=sessid)
|
||||
puppet = session.puppet
|
||||
if puppet:
|
||||
# execute command on the puppeted object (this will include
|
||||
# cmdsets both on object and on player)
|
||||
_GA(puppet, "execute_cmd")(ingoing_string, sessid=session.sessid)
|
||||
else:
|
||||
# a non-character session; this goes to player directly
|
||||
_GA(self, "execute_cmd")(ingoing_string, sessid=sessid)
|
||||
# a non-character session; this executes on player directly
|
||||
# (this will only include cmdsets on player)
|
||||
_GA(self, "execute_cmd")(ingoing_string, sessid=session.sessid)
|
||||
|
||||
|
||||
# session-related methods
|
||||
|
||||
def get_session(self, sessid):
|
||||
"""
|
||||
|
|
@ -380,7 +367,7 @@ class PlayerDB(TypedObject):
|
|||
global _SESSIONS
|
||||
if not _SESSIONS:
|
||||
from src.server.sessionhandler import SESSIONS as _SESSIONS
|
||||
return _SESSIONS.sessions_from_player(self, sessid=sessid)
|
||||
return _SESSIONS.session_from_player(self, sessid)
|
||||
|
||||
def get_all_sessions(self):
|
||||
"Return all sessions connected to this player"
|
||||
|
|
@ -388,208 +375,272 @@ class PlayerDB(TypedObject):
|
|||
if not _SESSIONS:
|
||||
from src.server.sessionhandler import SESSIONS as _SESSIONS
|
||||
return _SESSIONS.sessions_from_player(self)
|
||||
sessions = property(get_all_sessions) # alias shortcut
|
||||
|
||||
def get_session_from_sessid(self, sessid):
|
||||
"""
|
||||
Get the session object from sessid. If session with sessid is not
|
||||
connected to this player, return None.
|
||||
"""
|
||||
global _SESSIONS
|
||||
if not _SESSIONS:
|
||||
from src.server.sessionhandler import SESSIONS as _SESSIONS
|
||||
return _SESSIONS.sessions_from_player(self, sessid=sessid)
|
||||
|
||||
def disconnect_session_from_player(self, sessid):
|
||||
"""
|
||||
Access method for disconnecting a given session from the player.
|
||||
Access method for disconnecting a given session from the player
|
||||
(connection happens automatically in the sessionhandler)
|
||||
"""
|
||||
sessions = self.get_session_from_sessid(sessid)
|
||||
for session in make_iter(sessions):
|
||||
# this will also trigger disconnection of character(s)
|
||||
# this should only be one value, loop just to make sure to clean everything
|
||||
sessions = (session for session in self.get_all_sessions(sessid) if session.sessid == sessid)
|
||||
for session in sessions:
|
||||
# this will also trigger unpuppeting
|
||||
session.sessionhandler.disconnect(session)
|
||||
|
||||
def connect_session_to_character(self, sessid, character, force=False, call_hooks=True):
|
||||
"""
|
||||
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().
|
||||
# puppeting operations
|
||||
|
||||
force - drop existing connection to other character
|
||||
call_hooks - call puppet/unpuppet hooks. This is not wanted e.g. if
|
||||
server is reloading
|
||||
|
||||
Returns True if connection was successful, False otherwise
|
||||
def puppet_object(self, sessid, obj, normal_mode=True):
|
||||
"""
|
||||
# first check if we already have a character tied to this session
|
||||
char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
|
||||
if char:
|
||||
if force and char != character:
|
||||
_GA(self, "disconnect_session_from_character")(sessid)
|
||||
else:
|
||||
return
|
||||
# pre-puppet hook
|
||||
if call_hooks:
|
||||
# if e.g. server reloads we don't want to call any hooks anew
|
||||
_GA(character.typeclass, "at_pre_puppet")(self.typeclass)
|
||||
Use the given session to control (puppet) the given object (usually
|
||||
a Character type). Note that we make no puppet checks here, that must
|
||||
have been done before calling this method.
|
||||
|
||||
sessid - session id of session to connect
|
||||
obj - the object to connect to
|
||||
normal_mode - trigger hooks and extra checks - this is turned off when
|
||||
the server reloads, to quickly re-connect puppets.
|
||||
|
||||
returns True if successful, False otherwise
|
||||
"""
|
||||
session = self.get_session(sessid)
|
||||
if not session:
|
||||
return False
|
||||
if normal_mode and session.puppet:
|
||||
# cleanly unpuppet eventual previous object puppeted by this session
|
||||
self.unpuppet_object(self, sessid)
|
||||
if obj.player and obj.player.is_connected and obj.player != self:
|
||||
# we don't allow to puppet an object already controlled by an active
|
||||
# player. To kick a player, call unpuppet_object on them explicitly.
|
||||
return
|
||||
# if we get to this point the character is ready to puppet or it was left
|
||||
# with a lingering player/sessid reference from an unclean server kill or similar
|
||||
|
||||
if normal_mode:
|
||||
_GA(obj.typeclass, "at_pre_puppet")(self.typeclass)
|
||||
# do the connection
|
||||
character.sessid = sessid
|
||||
# update cache
|
||||
cache = get_prop_cache(self, "_characters") or {}
|
||||
cache[sessid] = character
|
||||
set_prop_cache(self, "_characters", cache)
|
||||
# start/validate (persistent) scripts on this object
|
||||
ScriptDB.objects.validate(obj=character)
|
||||
# post-puppet hook
|
||||
if call_hooks:
|
||||
_GA(character.typeclass, "at_post_puppet")()
|
||||
obj.sessid = sessid
|
||||
obj.player = self
|
||||
session.puid = obj.id
|
||||
session.puppet = obj
|
||||
# validate/start persistent scripts on object
|
||||
ScriptDB.objects.validate(obj=obj)
|
||||
if normal_mode:
|
||||
_GA(obj.typeclass, "at_post_puppet")()
|
||||
return True
|
||||
|
||||
def disconnect_session_from_character(self, sessid):
|
||||
def unpuppet_object(self, sessid):
|
||||
"""
|
||||
Disconnect a session from the characterm (still keeping the
|
||||
connection to the Player)
|
||||
returns the newly disconnected character, if it existed
|
||||
"""
|
||||
print "player disconnect_session_from_character", sessid
|
||||
if not sessid:
|
||||
return
|
||||
char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
|
||||
print char
|
||||
if char:
|
||||
# call hook before disconnecting
|
||||
_GA(char.typeclass, "at_pre_unpuppet")()
|
||||
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)
|
||||
# call post-unpuppet hook
|
||||
_GA(char.typeclass, "at_post_unpuppet")(self.typeclass)
|
||||
print "... leaving player disconnect_session_from_character", sessid
|
||||
return char
|
||||
Disengage control over an object
|
||||
|
||||
def server_reconnect_session_to_character(self, sessid):
|
||||
"""
|
||||
Auto-re-connect a session to a character. This is called by the sessionhandler
|
||||
during a server reload. It goes through the characters stored in this player's
|
||||
db_objs many2many fields and checks if any of those has the given sessid
|
||||
stored on themselves - if so they connect them. This should ONLY be called
|
||||
automatically by sessionhandler after a reload - after a portal shutdown
|
||||
the portal sessids will be out of sync with whatever is stored on character
|
||||
objects which could lead to a session being linked to the wrong character.
|
||||
"""
|
||||
char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
|
||||
if not char:
|
||||
return
|
||||
_GA(self, "connect_session_to_character")(sessid, char, force=True, call_hooks=False)
|
||||
sessid - the session id to disengage
|
||||
|
||||
|
||||
def get_character(self, sessid=None, character=None, return_dbobj=False):
|
||||
returns True if successful
|
||||
"""
|
||||
Get the character connected to this player and sessid. This is the main
|
||||
method for retrieving the character from the player's end.
|
||||
session = self.get_session(sessid)
|
||||
if not session:
|
||||
return False
|
||||
obj = hasattr(session, "puppet") and session.puppet or None
|
||||
if not obj:
|
||||
return False
|
||||
# do the disconnect
|
||||
_GA(obj.typeclass, "at_pre_unpuppet")()
|
||||
del obj.dbobj.sessid
|
||||
del obj.dbobj.player
|
||||
session.puppet = None
|
||||
session.puid = None
|
||||
_GA(obj.typeclass, "at_post_unpuppet")(self)
|
||||
return True
|
||||
|
||||
def unpuppet_all(self):
|
||||
"""
|
||||
Disconnect all puppets. This is called by server
|
||||
before a reset/shutdown.
|
||||
"""
|
||||
for session in self.get_all_sessions():
|
||||
self.unpuppet_object(session.sessid)
|
||||
|
||||
def get_puppet(self, sessid, return_dbobj=False):
|
||||
"""
|
||||
Get an object puppeted by this session through this player. This is the main
|
||||
method for retrieving the puppeted object from the player's end.
|
||||
|
||||
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 as a list.
|
||||
"""
|
||||
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 = _GA(self, "db_objs").filter(db_player=self, db_sessid=sessid) or None
|
||||
if char:
|
||||
char = char[0]
|
||||
cache[sessid] = char
|
||||
set_prop_cache(self, "_characters", cache)
|
||||
if character:
|
||||
return char and (char == character.dbobj and (return_dbobj and char or char.typeclass)) or None
|
||||
return char and (return_dbobj and char or char.typeclass) or None
|
||||
elif character:
|
||||
char = _GA(self, "db_objs").filter(id=_GA(character.dbobj, "id"))
|
||||
return char and (return_dbobj and char[0] or char[0].typeclass) or None
|
||||
else:
|
||||
# no sessid given - return all available characters
|
||||
chars = list(return_dbobj and o or o.typeclass for o in self.db_objs.all())
|
||||
return len(chars) == 1 and chars[0] or chars
|
||||
session = self.get_session(sessid)
|
||||
if not session:
|
||||
return None
|
||||
if return_dbobj:
|
||||
return session.puppet
|
||||
return session.puppet and session.puppet.typeclass or None
|
||||
|
||||
def get_all_characters(self):
|
||||
def get_all_puppets(self, return_dbobj=False):
|
||||
"""
|
||||
Readability-wrapper for getting all characters
|
||||
Get all currently puppeted objects as a list
|
||||
"""
|
||||
return _GA(self, "get_character")(sessid=None, character=None)
|
||||
puppets = [session.puppet for session in self.get_all_sessions() if session.puppet]
|
||||
if return_dbobj:
|
||||
return puppets
|
||||
return [puppet.typeclass for puppet in puppets]
|
||||
|
||||
def get_all_connected_characters(self):
|
||||
def has_puppet(self, obj):
|
||||
"""
|
||||
Return all characters with an active session connected
|
||||
to them through this player
|
||||
Checks of this player currently puppets this object or not
|
||||
"""
|
||||
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]
|
||||
return obj in self.get_all_puppets()
|
||||
|
||||
def connect_character(self, character, sessid=None):
|
||||
"""
|
||||
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.
|
||||
# def connect_session_to_character(self, sessid, character, force=False, call_hooks=True):
|
||||
# """
|
||||
# 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().
|
||||
#
|
||||
# force - drop existing connection to other character
|
||||
# call_hooks - call puppet/unpuppet hooks. This is not wanted e.g. if
|
||||
# server is reloading
|
||||
#
|
||||
# Returns True if connection was successful, False otherwise
|
||||
# """
|
||||
# # first check if we already have a character tied to this session
|
||||
# char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
|
||||
# if char:
|
||||
# if force and char != character:
|
||||
# _GA(self, "disconnect_session_from_character")(sessid)
|
||||
# else:
|
||||
# return
|
||||
# # pre-puppet hook
|
||||
# if call_hooks:
|
||||
# # if e.g. server reloads we don't want to call any hooks anew
|
||||
# _GA(character.typeclass, "at_pre_puppet")(self.typeclass)
|
||||
# # do the connection
|
||||
# character.sessid = sessid
|
||||
# # update cache
|
||||
# cache = get_prop_cache(self, "_characters") or {}
|
||||
# cache[sessid] = character
|
||||
# set_prop_cache(self, "_characters", cache)
|
||||
# # start/validate (persistent) scripts on this object
|
||||
# ScriptDB.objects.validate(obj=character)
|
||||
# # post-puppet hook
|
||||
# if call_hooks:
|
||||
# _GA(character.typeclass, "at_post_puppet")()
|
||||
# return True
|
||||
#
|
||||
# def disconnect_session_from_character(self, sessid):
|
||||
# """
|
||||
# Disconnect a session from the characterm (still keeping the
|
||||
# connection to the Player)
|
||||
# returns the newly disconnected character, if it existed
|
||||
# """
|
||||
# print "player disconnect_session_from_character", sessid
|
||||
# if not sessid:
|
||||
# return
|
||||
# char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
|
||||
# print char
|
||||
# if char:
|
||||
# # call hook before disconnecting
|
||||
# _GA(char.typeclass, "at_pre_unpuppet")()
|
||||
# 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)
|
||||
# # call post-unpuppet hook
|
||||
# _GA(char.typeclass, "at_post_unpuppet")(self.typeclass)
|
||||
# print "... leaving player disconnect_session_from_character", sessid
|
||||
# return char
|
||||
#
|
||||
# def server_reconnect_session_to_character(self, sessid):
|
||||
# """
|
||||
# Auto-re-connect a session to a character. This is called by the sessionhandler
|
||||
# during a server reload. It goes through the characters stored in this player's
|
||||
# db_objs many2many fields and checks if any of those has the given sessid
|
||||
# stored on themselves - if so they connect them. This should ONLY be called
|
||||
# automatically by sessionhandler after a reload - after a portal shutdown
|
||||
# the portal sessids will be out of sync with whatever is stored on character
|
||||
# objects which could lead to a session being linked to the wrong character.
|
||||
# """
|
||||
# char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
|
||||
# if not char:
|
||||
# return
|
||||
# _GA(self, "connect_session_to_character")(sessid, char, force=True, call_hooks=False)
|
||||
|
||||
if sessid is given, also connect the sessid to the character directly.
|
||||
"""
|
||||
# first disconnect any other character from this session
|
||||
char = character.dbobj
|
||||
_GA(self, "disconnect_character")(char)
|
||||
char.player = self
|
||||
_GA(self, "db_objs").add(char)
|
||||
_GA(self, "save")()
|
||||
if sessid:
|
||||
return _GA(self, "connect_session_to_character")(sessid=sessid, character=char)
|
||||
return True
|
||||
|
||||
def disconnect_character(self, character):
|
||||
"""
|
||||
Disconnect a character from this player, either based
|
||||
on sessid or by giving the character object directly
|
||||
|
||||
Returns newly disconnected character.
|
||||
"""
|
||||
if not character:
|
||||
return
|
||||
char = _GA(self, "get_character")(character=character, return_dbobj=True)
|
||||
if char:
|
||||
err = _GA(self, "disconnect_session_from_character")(char.sessid)
|
||||
_GA(self, "db_objs").remove(char)
|
||||
del char.player
|
||||
self.save()
|
||||
# clear cache
|
||||
cache = get_prop_cache(self, "_characters") or {}
|
||||
[cache.pop(sessid) for sessid,stored_char in cache.items() if stored_char==char]
|
||||
set_prop_cache(self, "_characters", cache)
|
||||
return char
|
||||
# def get_all_characters(self):
|
||||
# """
|
||||
# Readability-wrapper for getting all characters
|
||||
# """
|
||||
# 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 disconnect_all_characters(self):
|
||||
for char in self.db_objs.all():
|
||||
_GA(self, "disconnect_character")(char)
|
||||
# def connect_character(self, character, sessid=None):
|
||||
# """
|
||||
# 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 directly.
|
||||
# """
|
||||
# # first disconnect any other character from this session
|
||||
# char = character.dbobj
|
||||
# _GA(self, "disconnect_character")(char)
|
||||
# char.player = self
|
||||
# _GA(self, "db_objs").add(char)
|
||||
# _GA(self, "save")()
|
||||
# if sessid:
|
||||
# return _GA(self, "connect_session_to_character")(sessid=sessid, character=char)
|
||||
# return True
|
||||
#
|
||||
# def disconnect_character(self, character):
|
||||
# """
|
||||
# Disconnect a character from this player, either based
|
||||
# on sessid or by giving the character object directly
|
||||
#
|
||||
# Returns newly disconnected character.
|
||||
# """
|
||||
# if not character:
|
||||
# return
|
||||
# char = _GA(self, "get_character")(character=character, return_dbobj=True)
|
||||
# if char:
|
||||
# err = _GA(self, "disconnect_session_from_character")(char.sessid)
|
||||
# _GA(self, "db_objs").remove(char)
|
||||
# del char.player
|
||||
# self.save()
|
||||
# # clear cache
|
||||
# cache = get_prop_cache(self, "_characters") or {}
|
||||
# [cache.pop(sessid) for sessid,stored_char in cache.items() if stored_char==char]
|
||||
# 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)
|
||||
|
||||
def swap_character(self, old_character, new_character):
|
||||
"""
|
||||
Swaps character between sessions, if possible
|
||||
"""
|
||||
this_sessid = old_character.sessid
|
||||
other_sessd = new_character.sessid
|
||||
this_char = _GA(self, "disconnect_session_from_character")(this_sessid)
|
||||
other_char = _GA(self, "disconnect_session_from_character")(other_sessid)
|
||||
_GA(self, "connect_session_to_character")(this_sessid, other_char)
|
||||
_GA(self, "connect_session_to_character")(other_sessid, this_char)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
"Make sure to delete user also when deleting player - the two may never exist separately."
|
||||
"""
|
||||
Deletes the player permanently.
|
||||
Makes sure to delete user also when deleting player - the two may never exist separately.
|
||||
"""
|
||||
for session in self.get_all_sessions():
|
||||
# unpuppeting all objects and disconnecting the user, if any
|
||||
# sessions remain (should usually be handled from the deleting command)
|
||||
self.unpuppet_object(session.sessid)
|
||||
session.sessionhandler.disconnect(session, reason=_("Player being deleted."))
|
||||
|
||||
try:
|
||||
if _GA(self, "user"):
|
||||
_GA(_GA(self, "user"), "delete")()
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ class Player(TypeClass):
|
|||
print "player at_post_login", self
|
||||
self._send_to_connect_channel("{G%s connected{n" % self.key)
|
||||
|
||||
if _MULTISESSION_MODE == 2 or not self.get_all_characters():
|
||||
if _MULTISESSION_MODE == 2:
|
||||
# Character.at_post_login also looks around. Only use
|
||||
# this as a backup when logging in without a character
|
||||
self.execute_cmd("look")
|
||||
|
|
|
|||
|
|
@ -268,13 +268,13 @@ class Evennia(object):
|
|||
|
||||
else:
|
||||
if mode == 'reset':
|
||||
# don't call disconnect hooks on reset
|
||||
# don't unset the is_connected flag on reset, otherwise same as shutdown
|
||||
yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
else: # shutdown
|
||||
yield [_SA(p, "is_connected", False) for p in PlayerDB.get_all_cached_instances()]
|
||||
yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
|
||||
yield [(p.typeclass, p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()]
|
||||
yield [(p.typeclass, p.unpuppet_all(), p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()]
|
||||
yield [(s.typeclass, s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()]
|
||||
yield ObjectDB.objects.clear_all_sessids()
|
||||
ServerConfig.objects.conf("server_restart_mode", "reset")
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from src.server.session import Session
|
|||
|
||||
IDLE_COMMAND = settings.IDLE_COMMAND
|
||||
_GA = object.__getattribute__
|
||||
_ObjectDB = None
|
||||
|
||||
# load optional out-of-band function module
|
||||
OOB_FUNC_MODULE = settings.OOB_FUNC_MODULE
|
||||
|
|
@ -51,14 +52,19 @@ class ServerSession(Session):
|
|||
Since this is often called after a server restart we need to set up
|
||||
the session as it was.
|
||||
"""
|
||||
global _ObjectDB
|
||||
if not _ObjectDB:
|
||||
from src.objects.models import ObjectDB as _ObjectDB
|
||||
|
||||
if not self.logged_in:
|
||||
# assign the unloggedin-command set.
|
||||
self.cmdset = cmdsethandler.CmdSetHandler(self)
|
||||
self.cmdset_storage = [settings.CMDSET_UNLOGGEDIN]
|
||||
self.cmdset.update(init_mode=True)
|
||||
return
|
||||
else:
|
||||
self.player.server_reconnect_session_to_character(self.sessid)
|
||||
elif self.puid:
|
||||
self.puppet = None
|
||||
obj = _ObjectDB.objects.get(id=self.puid)
|
||||
self.player.puppet_object(self.sessid, obj, normal_mode=False)
|
||||
|
||||
def at_login(self, player):
|
||||
"""
|
||||
|
|
@ -72,6 +78,8 @@ class ServerSession(Session):
|
|||
self.uname = self.user.username
|
||||
self.logged_in = True
|
||||
self.conn_time = time.time()
|
||||
self.puid = None
|
||||
self.puppet = None
|
||||
|
||||
# Update account's last login time.
|
||||
self.user.last_login = datetime.now()
|
||||
|
|
@ -85,7 +93,7 @@ class ServerSession(Session):
|
|||
sessid = self.sessid
|
||||
player = self.player
|
||||
print "session at_disconnect", self
|
||||
_GA(player.dbobj, "disconnect_session_from_character")(sessid)
|
||||
_GA(player.dbobj, "unpuppet_object")(sessid)
|
||||
uaccount = _GA(player.dbobj, "user")
|
||||
uaccount.last_login = datetime.now()
|
||||
uaccount.save()
|
||||
|
|
@ -102,12 +110,13 @@ class ServerSession(Session):
|
|||
"""
|
||||
return self.logged_in and self.player
|
||||
|
||||
def get_character(self):
|
||||
def get_puppet(self):
|
||||
"""
|
||||
Returns the in-game character associated with this session.
|
||||
This returns the typeclass of the object.
|
||||
"""
|
||||
return self.logged_in and self.player.get_character(self.sessid) or None
|
||||
return self.logged_in and self.puppet
|
||||
get_character = get_puppet
|
||||
|
||||
def log(self, message, channel=True):
|
||||
"""
|
||||
|
|
@ -135,9 +144,11 @@ class ServerSession(Session):
|
|||
# Player-visible idle time, not used in idle timeout calcs.
|
||||
self.cmd_last_visible = time.time()
|
||||
|
||||
def execute_cmd(self, command_string):
|
||||
def data_in(self, command_string):
|
||||
"""
|
||||
Execute a command string on the server.
|
||||
Send Player->Evennia. This will in effect
|
||||
execute a command string on the server.
|
||||
Eventual extra data moves through oob_data_in
|
||||
"""
|
||||
# handle the 'idle' command
|
||||
if str(command_string).strip() == IDLE_COMMAND:
|
||||
|
|
@ -145,11 +156,12 @@ class ServerSession(Session):
|
|||
return
|
||||
if self.logged_in:
|
||||
# the inmsg handler will relay to the right place
|
||||
self.player.inmsg(command_string, sessid=self.sessid)
|
||||
self.player.inmsg(command_string, self)
|
||||
else:
|
||||
# we are not logged in. Use the session directly
|
||||
# we are not logged in. Execute cmd with the the session directly
|
||||
# (it uses the settings.UNLOGGEDIN cmdset)
|
||||
cmdhandler.cmdhandler(self, command_string, sessid=self.sessid)
|
||||
execute_cmd = data_in # alias
|
||||
|
||||
def data_out(self, msg, data=None):
|
||||
"""
|
||||
|
|
@ -157,6 +169,7 @@ class ServerSession(Session):
|
|||
"""
|
||||
self.sessionhandler.data_out(self, msg, data)
|
||||
|
||||
|
||||
def oob_data_in(self, data):
|
||||
"""
|
||||
This receives out-of-band data from the Portal.
|
||||
|
|
@ -249,7 +262,7 @@ class ServerSession(Session):
|
|||
self.data_out(string, data=data)
|
||||
|
||||
|
||||
# Dummy API hooks for use a non-loggedin operation
|
||||
# Dummy API hooks for use during non-loggedin operation
|
||||
|
||||
def at_cmdset_get(self):
|
||||
"dummy hook all objects with cmdsets need to have"
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ class Session(object):
|
|||
|
||||
# names of attributes that should be affected by syncing.
|
||||
_attrs_to_sync = ('protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname',
|
||||
'logged_in', 'cid', 'encoding',
|
||||
'logged_in', 'puid', 'encoding',
|
||||
'conn_time', 'cmd_last', 'cmd_last_visible', 'cmd_total',
|
||||
'server_data')
|
||||
'protocol_flags', 'server_data')
|
||||
|
||||
def init_session(self, protocol_key, address, sessionhandler):
|
||||
"""
|
||||
|
|
@ -63,15 +63,15 @@ class Session(object):
|
|||
# if user has authenticated already or not
|
||||
self.logged_in = False
|
||||
|
||||
# database id of character/object connected to this player session (if any)
|
||||
self.cid = None
|
||||
self.encoding = "utf-8"
|
||||
# database id of puppeted object (if any)
|
||||
self.puid = None
|
||||
|
||||
# session time statistics
|
||||
self.conn_time = time.time()
|
||||
self.cmd_last_visible = self.conn_time
|
||||
self.cmd_last = self.conn_time
|
||||
self.cmd_total = 0
|
||||
self.encoding = "utf-8"
|
||||
|
||||
self.protocol_flags = {}
|
||||
self.server_data = {}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
# validate all script
|
||||
_ScriptDB.objects.validate()
|
||||
self.sessions[sess.sessid] = sess
|
||||
sess.execute_cmd(CMD_LOGINSTART)
|
||||
sess.data_in(CMD_LOGINSTART)
|
||||
|
||||
def portal_disconnect(self, sessid):
|
||||
"""
|
||||
|
|
@ -196,7 +196,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
Called from server side to remove session and inform portal
|
||||
of this fact.
|
||||
"""
|
||||
session = self.sessions.get(session.sessid, None)
|
||||
session = self.sessions.get(session.sessid)
|
||||
if session:
|
||||
session.at_disconnect()
|
||||
sessid = session.sessid
|
||||
|
|
@ -305,16 +305,19 @@ class ServerSessionHandler(SessionHandler):
|
|||
"""
|
||||
return len(set(session.uid for session in self.sessions.values() if session.logged_in))
|
||||
|
||||
def sessions_from_player(self, player, sessid=None):
|
||||
def session_from_player(self, player, sessid):
|
||||
"""
|
||||
Given a player, return any matching sessions.
|
||||
Given a player and a session id, return the actual session object
|
||||
"""
|
||||
session = self.sessions.get(sessid)
|
||||
return session and session.logged_in and player.uid == session.uid and session or None
|
||||
|
||||
def sessions_from_player(self, player):
|
||||
"""
|
||||
Given a player, return all matching sessions.
|
||||
"""
|
||||
uid = player.uid
|
||||
if sessid:
|
||||
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]
|
||||
return [session for session in self.sessions.values() if session.logged_in and session.uid == uid]
|
||||
|
||||
def sessions_from_character(self, character):
|
||||
"""
|
||||
|
|
@ -345,7 +348,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
"""
|
||||
session = self.sessions.get(sessid, None)
|
||||
if session:
|
||||
session.execute_cmd(string)
|
||||
session.data_in(string)
|
||||
|
||||
# ignore 'data' argument for now; this is otherwise the place
|
||||
# to put custom effects on the server due to data input, e.g.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue