Moved Players over to the new proxy system, made the start-hook called by the save-signal system into at_first_save()
This commit is contained in:
parent
db512cbbf5
commit
9af9f94fa0
9 changed files with 465 additions and 584 deletions
|
|
@ -17,15 +17,23 @@ from src.typeclasses.models import TypeclassBase
|
|||
from src.players.manager import PlayerManager
|
||||
from src.players.models import PlayerDB
|
||||
from src.comms.models import ChannelDB
|
||||
from src.commands import cmdhandler
|
||||
from src.scripts.models import ScriptDB
|
||||
from src.utils import logger
|
||||
from src.utils.utils import lazy_property, to_str, make_iter
|
||||
from src.utils.utils import (lazy_property, to_str,
|
||||
make_iter, to_unicode,
|
||||
variable_from_module)
|
||||
from src.typeclasses.attributes import NickHandler
|
||||
from src.scripts.scripthandler import ScriptHandler
|
||||
from src.commands.cmdsethandler import CmdSetHandler
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
__all__ = ("DefaultPlayer",)
|
||||
|
||||
_SESSIONS = None
|
||||
|
||||
_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
_CMDSET_PLAYER = settings.CMDSET_PLAYER
|
||||
_CONNECT_CHANNEL = None
|
||||
|
|
@ -74,7 +82,7 @@ class DefaultPlayer(PlayerDB):
|
|||
* Helper methods
|
||||
|
||||
msg(outgoing_string, from_obj=None, **kwargs)
|
||||
swap_character(new_character, delete_old_character=False)
|
||||
#swap_character(new_character, delete_old_character=False)
|
||||
execute_cmd(raw_string)
|
||||
search(ostring, global_search=False, attribute_name=None,
|
||||
use_nicks=False, location=None,
|
||||
|
|
@ -122,6 +130,171 @@ class DefaultPlayer(PlayerDB):
|
|||
return NickHandler(self)
|
||||
|
||||
|
||||
# session-related methods
|
||||
|
||||
def get_session(self, sessid):
|
||||
"""
|
||||
Return session with given sessid connected to this player.
|
||||
note that the sessionhandler also accepts sessid as an iterable.
|
||||
"""
|
||||
global _SESSIONS
|
||||
if not _SESSIONS:
|
||||
from src.server.sessionhandler import SESSIONS as _SESSIONS
|
||||
return _SESSIONS.session_from_player(self, 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)
|
||||
sessions = property(get_all_sessions) # alias shortcut
|
||||
|
||||
def disconnect_session_from_player(self, sessid):
|
||||
"""
|
||||
Access method for disconnecting a given session from the player
|
||||
(connection happens automatically in the sessionhandler)
|
||||
"""
|
||||
# this should only be one value, loop just to make sure to
|
||||
# clean everything
|
||||
sessions = (session for session in self.get_all_sessions()
|
||||
if session.sessid == sessid)
|
||||
for session in sessions:
|
||||
# this will also trigger unpuppeting
|
||||
session.sessionhandler.disconnect(session)
|
||||
|
||||
# puppeting operations
|
||||
|
||||
def puppet_object(self, sessid, obj, normal_mode=True):
|
||||
"""
|
||||
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(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:
|
||||
obj.at_pre_puppet(self, sessid=sessid)
|
||||
# do the connection
|
||||
obj.sessid.add(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:
|
||||
obj.at_post_puppet()
|
||||
return True
|
||||
|
||||
def unpuppet_object(self, sessid):
|
||||
"""
|
||||
Disengage control over an object
|
||||
|
||||
sessid - the session id to disengage
|
||||
|
||||
returns True if successful
|
||||
"""
|
||||
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, but only if we are the last session to puppet
|
||||
obj.at_pre_unpuppet()
|
||||
obj.dbobj.sessid.remove(sessid)
|
||||
if not obj.dbobj.sessid.count():
|
||||
del obj.dbobj.player
|
||||
obj.at_post_unpuppet(self, sessid=sessid)
|
||||
session.puppet = None
|
||||
session.puid = None
|
||||
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.
|
||||
|
||||
"""
|
||||
session = self.get_session(sessid)
|
||||
if not session:
|
||||
return None
|
||||
if return_dbobj:
|
||||
return session.puppet
|
||||
return session.puppet and session.puppet or None
|
||||
|
||||
def get_all_puppets(self, return_dbobj=False):
|
||||
"""
|
||||
Get all currently puppeted objects as a list
|
||||
"""
|
||||
puppets = [session.puppet for session in self.get_all_sessions()
|
||||
if session.puppet]
|
||||
if return_dbobj:
|
||||
return puppets
|
||||
return [puppet for puppet in puppets]
|
||||
|
||||
def __get_single_puppet(self):
|
||||
"""
|
||||
This is a legacy convenience link for users of
|
||||
MULTISESSION_MODE 0 or 1. It will return
|
||||
only the first puppet. For mode 2, this returns
|
||||
a list of all characters.
|
||||
"""
|
||||
puppets = self.get_all_puppets()
|
||||
if _MULTISESSION_MODE in (0, 1):
|
||||
return puppets and puppets[0] or None
|
||||
return puppets
|
||||
character = property(__get_single_puppet)
|
||||
puppet = property(__get_single_puppet)
|
||||
|
||||
# utility methods
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
"""
|
||||
Deletes the player permanently.
|
||||
"""
|
||||
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."))
|
||||
self.scripts.stop()
|
||||
self.attributes.clear()
|
||||
self.nicks.clear()
|
||||
self.aliases.clear()
|
||||
super(PlayerDB, self).delete(*args, **kwargs)
|
||||
## methods inherited from database model
|
||||
|
||||
def msg(self, text=None, from_obj=None, sessid=None, **kwargs):
|
||||
|
|
@ -167,48 +340,38 @@ class DefaultPlayer(PlayerDB):
|
|||
for sess in self.get_all_sessions():
|
||||
sess.msg(text=text, **kwargs)
|
||||
|
||||
def swap_character(self, new_character, delete_old_character=False):
|
||||
"""
|
||||
Swaps the character controlled by this Player, if possible.
|
||||
|
||||
new_character (Object) - character/object to swap to
|
||||
delete_old_character (bool) - delete the old character when swapping
|
||||
|
||||
Returns: True/False depending on if swap suceeded or not.
|
||||
"""
|
||||
return super(DefaultPlayer, self).swap_character(new_character, delete_old_character=delete_old_character)
|
||||
|
||||
def execute_cmd(self, raw_string, sessid=None, **kwargs):
|
||||
"""
|
||||
Do something as this object. This command transparently
|
||||
lets its typeclass execute the command. This method
|
||||
is -not- called by Evennia normally, it is here to be
|
||||
called explicitly in code.
|
||||
Do something as this player. This method is never called normally,
|
||||
but only when the player object itself is supposed to execute the
|
||||
command. It takes player nicks into account, but not nicks of
|
||||
eventual puppets.
|
||||
|
||||
Argument:
|
||||
raw_string (string) - raw command input
|
||||
sessid (int) - id of session executing the command. This sets the
|
||||
sessid property on the command
|
||||
raw_string - raw command input coming from the command line.
|
||||
sessid - the optional session id to be responsible for the command-send
|
||||
**kwargs - other keyword arguments will be added to the found command
|
||||
object instace as variables before it executes. This is
|
||||
unused by default Evennia but may be used to set flags and
|
||||
change operating paramaters for commands at run-time.
|
||||
|
||||
Returns Deferred - this is an asynchronous Twisted object that will
|
||||
not fire until the command has actually finished executing. To
|
||||
overload this one needs to attach callback functions to it, with
|
||||
addCallback(function). This function will be called with an
|
||||
eventual return value from the command execution.
|
||||
|
||||
This return is not used at all by Evennia by default, but might
|
||||
be useful for coders intending to implement some sort of nested
|
||||
command structure.
|
||||
"""
|
||||
return super(DefaultPlayer, self).execute_cmd(raw_string, sessid=sessid, **kwargs)
|
||||
raw_string = to_unicode(raw_string)
|
||||
raw_string = self.nicks.nickreplace(raw_string,
|
||||
categories=("inputline", "channel"), include_player=False)
|
||||
if not sessid and _MULTISESSION_MODE in (0, 1):
|
||||
# in this case, we should either have only one sessid, or the sessid
|
||||
# should not matter (since the return goes to all of them we can
|
||||
# just use the first one as the source)
|
||||
try:
|
||||
sessid = self.get_all_sessions()[0].sessid
|
||||
except IndexError:
|
||||
# this can happen for bots
|
||||
sessid = None
|
||||
return cmdhandler.cmdhandler(self, raw_string,
|
||||
callertype="player", sessid=sessid, **kwargs)
|
||||
|
||||
def search(self, searchdata, return_puppet=False, **kwargs):
|
||||
"""
|
||||
This is similar to the Object search method but will search for
|
||||
This is similar to the ObjectDB search method but will search for
|
||||
Players only. Errors will be echoed, and None returned if no Player
|
||||
is found.
|
||||
searchdata - search criterion, the Player's key or dbref to search for
|
||||
|
|
@ -224,7 +387,14 @@ class DefaultPlayer(PlayerDB):
|
|||
# handle wrapping of common terms
|
||||
if searchdata.lower() in ("me", "*me", "self", "*self",):
|
||||
return self
|
||||
return super(DefaultPlayer, self).search(searchdata, return_puppet=return_puppet, **kwargs)
|
||||
matches = self.__class__.objects.player_search(searchdata)
|
||||
matches = _AT_SEARCH_RESULT(self, searchdata, matches, global_search=True)
|
||||
if matches and return_puppet:
|
||||
try:
|
||||
return matches.puppet
|
||||
except AttributeError:
|
||||
return None
|
||||
return matches
|
||||
|
||||
def is_typeclass(self, typeclass, exact=False):
|
||||
"""
|
||||
|
|
@ -332,6 +502,7 @@ class DefaultPlayer(PlayerDB):
|
|||
lockstring = "attrread:perm(Admins);attredit:perm(Admins);attrcreate:perm(Admins)"
|
||||
self.attributes.add("_playable_characters", [], lockstring=lockstring)
|
||||
|
||||
# TODO - handle this in __init__ instead.
|
||||
def at_init(self):
|
||||
"""
|
||||
This is always called whenever this object is initiated --
|
||||
|
|
@ -344,12 +515,35 @@ class DefaultPlayer(PlayerDB):
|
|||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Note that the hooks below also exist in the character object's
|
||||
# typeclass. You can often ignore these and rely on the character
|
||||
# ones instead, unless you are implementing a multi-character game
|
||||
# and have some things that should be done regardless of which
|
||||
# character is currently connected to this player.
|
||||
|
||||
def at_first_save(self):
|
||||
"""
|
||||
This is a generic hook called by Evennia when this object is
|
||||
saved to the database the very first time. You generally
|
||||
don't override this method but the hooks called by it.
|
||||
"""
|
||||
self.basetype_setup()
|
||||
self.at_player_creation()
|
||||
|
||||
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
||||
if hasattr(self, "_createdict"):
|
||||
# this will only be set if the utils.create_player
|
||||
# function was used to create the object.
|
||||
cdict = self._createdict
|
||||
if "locks" in cdict:
|
||||
self.locks.add(cdict["locks"])
|
||||
if "permissions" in cdict:
|
||||
permissions = cdict["permissions"]
|
||||
del self._createdict
|
||||
|
||||
self.permissions.add(permissions)
|
||||
|
||||
def at_access(self, result, accessing_obj, access_type, **kwargs):
|
||||
"""
|
||||
This is called with the result of an access call, along with
|
||||
|
|
@ -373,8 +567,7 @@ class DefaultPlayer(PlayerDB):
|
|||
|
||||
def at_first_login(self):
|
||||
"""
|
||||
Only called once, the very first
|
||||
time the user logs in.
|
||||
Called the very first time this player logs into the game.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -472,6 +665,7 @@ class DefaultPlayer(PlayerDB):
|
|||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Guest(DefaultPlayer):
|
||||
"""
|
||||
This class is used for guest logins. Unlike Players, Guests and their
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue