mirror of
https://github.com/evennia/evennia.git
synced 2026-04-07 00:45:22 +02:00
704 lines
27 KiB
Python
704 lines
27 KiB
Python
"""
|
|
Typeclass for Player objects
|
|
|
|
Note that this object is primarily intended to
|
|
store OOC information, not game info! This
|
|
object represents the actual user (not their
|
|
character) and has NO actual precence in the
|
|
game world (this is handled by the associated
|
|
character object, so you should customize that
|
|
instead for most things).
|
|
|
|
"""
|
|
|
|
import datetime
|
|
from django.conf import settings
|
|
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, 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
|
|
|
|
class DefaultPlayer(PlayerDB):
|
|
"""
|
|
This is the base Typeclass for all Players. Players represent
|
|
the person playing the game and tracks account info, password
|
|
etc. They are OOC entities without presence in-game. A Player
|
|
can connect to a Character Object in order to "enter" the
|
|
game.
|
|
|
|
Player Typeclass API:
|
|
|
|
* Available properties (only available on initiated typeclass objects)
|
|
|
|
key (string) - name of player
|
|
name (string)- wrapper for user.username
|
|
aliases (list of strings) - aliases to the object. Will be saved to
|
|
database as AliasDB entries but returned as strings.
|
|
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
|
date_created (string) - time stamp of object creation
|
|
permissions (list of strings) - list of permission strings
|
|
|
|
user (User, read-only) - django User authorization object
|
|
obj (Object) - game object controlled by player. 'character' can also
|
|
be used.
|
|
sessions (list of Sessions) - sessions connected to this player
|
|
is_superuser (bool, read-only) - if the connected user is a superuser
|
|
|
|
* Handlers
|
|
|
|
locks - lock-handler: use locks.add() to add new lock strings
|
|
db - attribute-handler: store/retrieve database attributes on this
|
|
self.db.myattr=val, val=self.db.myattr
|
|
ndb - non-persistent attribute handler: same as db but does not
|
|
create a database entry when storing data
|
|
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().
|
|
|
|
* Helper methods
|
|
|
|
msg(outgoing_string, from_obj=None, **kwargs)
|
|
#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,
|
|
ignore_errors=False, player=False)
|
|
is_typeclass(typeclass, exact=False)
|
|
swap_typeclass(new_typeclass, clean_attributes=False, no_default=True)
|
|
access(accessing_obj, access_type='read', default=False)
|
|
check_permstring(permstring)
|
|
|
|
* Hook methods
|
|
|
|
basetype_setup()
|
|
at_player_creation()
|
|
|
|
- note that the following hooks are also found on Objects and are
|
|
usually handled on the character level:
|
|
|
|
at_init()
|
|
at_access()
|
|
at_cmdset_get(**kwargs)
|
|
at_first_login()
|
|
at_post_login(sessid=None)
|
|
at_disconnect()
|
|
at_message_receive()
|
|
at_message_send()
|
|
at_server_reload()
|
|
at_server_shutdown()
|
|
|
|
"""
|
|
|
|
__metaclass__ = TypeclassBase
|
|
objects = PlayerManager()
|
|
|
|
# properties
|
|
@lazy_property
|
|
def cmdset(self):
|
|
return CmdSetHandler(self, True)
|
|
|
|
@lazy_property
|
|
def scripts(self):
|
|
return ScriptHandler(self)
|
|
|
|
@lazy_property
|
|
def nicks(self):
|
|
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()
|
|
# re-cache locks to make sure superuser bypass is updated
|
|
obj.locks.cache_lock_bypass(obj)
|
|
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.sessid.remove(sessid)
|
|
if not obj.sessid.count():
|
|
del obj.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,
|
|
|
|
"""
|
|
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):
|
|
"""
|
|
Evennia -> User
|
|
This is the main route for sending data back to the user from the
|
|
server.
|
|
|
|
outgoing_string (string) - text data to send
|
|
from_obj (Object/Player) - source object of message to send. Its
|
|
at_msg_send() hook will be called.
|
|
sessid - the session id of the session to send to. If not given, return
|
|
to all sessions connected to this player. This is usually only
|
|
relevant when using msg() directly from a player-command (from
|
|
a command on a Character, the character automatically stores
|
|
and handles the sessid). Can also be a list of sessids.
|
|
kwargs (dict) - All other keywords are parsed as extra data.
|
|
"""
|
|
if "data" in kwargs:
|
|
# deprecation warning
|
|
logger.log_depmsg("PlayerDB:msg() 'data'-dict keyword is deprecated. Use **kwargs instead.")
|
|
data = kwargs.pop("data")
|
|
if isinstance(data, dict):
|
|
kwargs.update(data)
|
|
|
|
text = to_str(text, force_string=True) if text else ""
|
|
if from_obj:
|
|
# call hook
|
|
try:
|
|
from_obj.at_msg_send(text=text, to_obj=self, **kwargs)
|
|
except Exception:
|
|
pass
|
|
sessions = _MULTISESSION_MODE > 1 and sessid and self.get_session(sessid) or None
|
|
if sessions:
|
|
for session in make_iter(sessions):
|
|
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):
|
|
"""
|
|
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.
|
|
|
|
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.
|
|
"""
|
|
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 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
|
|
return_puppet - will try to return the object the player controls
|
|
instead of the Player object itself. If no
|
|
puppeted object exists (since Player is OOC), None will
|
|
be returned.
|
|
Extra keywords are ignored, but are allowed in call in order to make
|
|
API more consistent with objects.models.TypedObject.search.
|
|
"""
|
|
# handle me, self and *me, *self
|
|
if isinstance(searchdata, basestring):
|
|
# handle wrapping of common terms
|
|
if searchdata.lower() in ("me", "*me", "self", "*self",):
|
|
return self
|
|
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):
|
|
"""
|
|
Returns true if this object has this type
|
|
OR has a typeclass which is an subclass of
|
|
the given typeclass.
|
|
|
|
typeclass - can be a class object or the
|
|
python path to such an object to match against.
|
|
|
|
exact - returns true only if the object's
|
|
type is exactly this typeclass, ignoring
|
|
parents.
|
|
|
|
Returns: Boolean
|
|
"""
|
|
return super(DefaultPlayer, self).is_typeclass(typeclass, exact=exact)
|
|
|
|
def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True):
|
|
"""
|
|
This performs an in-situ swap of the typeclass. This means
|
|
that in-game, this object will suddenly be something else.
|
|
Player will not be affected. To 'move' a player to a different
|
|
object entirely (while retaining this object's type), use
|
|
self.player.swap_object().
|
|
|
|
Note that this might be an error prone operation if the
|
|
old/new typeclass was heavily customized - your code
|
|
might expect one and not the other, so be careful to
|
|
bug test your code if using this feature! Often its easiest
|
|
to create a new object and just swap the player over to
|
|
that one instead.
|
|
|
|
Arguments:
|
|
new_typeclass (path/classobj) - type to switch to
|
|
clean_attributes (bool/list) - will delete all attributes
|
|
stored on this object (but not any
|
|
of the database fields such as name or
|
|
location). You can't get attributes back,
|
|
but this is often the safest bet to make
|
|
sure nothing in the new typeclass clashes
|
|
with the old one. If you supply a list,
|
|
only those named attributes will be cleared.
|
|
no_default - if this is active, the swapper will not allow for
|
|
swapping to a default typeclass in case the given
|
|
one fails for some reason. Instead the old one
|
|
will be preserved.
|
|
Returns:
|
|
boolean True/False depending on if the swap worked or not.
|
|
|
|
"""
|
|
super(DefaultPlayer, self).swap_typeclass(new_typeclass,
|
|
clean_attributes=clean_attributes, no_default=no_default)
|
|
|
|
def access(self, accessing_obj, access_type='read', default=False, **kwargs):
|
|
"""
|
|
Determines if another object has permission to access this object
|
|
in whatever way.
|
|
|
|
accessing_obj (Object)- object trying to access this one
|
|
access_type (string) - type of access sought
|
|
default (bool) - what to return if no lock of access_type was found
|
|
**kwargs - passed to the at_access hook along with the result.
|
|
"""
|
|
result = super(DefaultPlayer, self).access(accessing_obj, access_type=access_type, default=default)
|
|
self.at_access(result, accessing_obj, access_type, **kwargs)
|
|
return result
|
|
|
|
def check_permstring(self, permstring):
|
|
"""
|
|
This explicitly checks the given string against this object's
|
|
'permissions' property without involving any locks.
|
|
|
|
permstring (string) - permission string that need to match a permission
|
|
on the object. (example: 'Builders')
|
|
Note that this method does -not- call the at_access hook.
|
|
"""
|
|
return super(DefaultPlayer, self).check_permstring(permstring)
|
|
|
|
## player hooks
|
|
|
|
def basetype_setup(self):
|
|
"""
|
|
This sets up the basic properties for a player.
|
|
Overload this with at_player_creation rather than
|
|
changing this method.
|
|
|
|
"""
|
|
# A basic security setup
|
|
lockstring = "examine:perm(Wizards);edit:perm(Wizards);delete:perm(Wizards);boot:perm(Wizards);msg:all()"
|
|
self.locks.add(lockstring)
|
|
|
|
# The ooc player cmdset
|
|
self.cmdset.add_default(_CMDSET_PLAYER, permanent=True)
|
|
|
|
def at_player_creation(self):
|
|
"""
|
|
This is called once, the very first time
|
|
the player is created (i.e. first time they
|
|
register with the game). It's a good place
|
|
to store attributes all players should have,
|
|
like configuration values etc.
|
|
"""
|
|
# set an (empty) attribute holding the characters this player has
|
|
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 --
|
|
that is, whenever it its typeclass is cached from memory. This
|
|
happens on-demand first time the object is used or activated
|
|
in some way after being created but also after each server
|
|
restart or reload. In the case of player objects, this usually
|
|
happens the moment the player logs in or reconnects after a
|
|
reload.
|
|
"""
|
|
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 cdict.get("locks"):
|
|
self.locks.add(cdict["locks"])
|
|
if cdict.get("permissions"):
|
|
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
|
|
any kwargs used for that call. The return of this method does
|
|
not affect the result of the lock check. It can be used e.g. to
|
|
customize error messages in a central location or other effects
|
|
based on the access result.
|
|
"""
|
|
pass
|
|
|
|
def at_cmdset_get(self, **kwargs):
|
|
"""
|
|
Called just before cmdsets on this player are requested by the
|
|
command handler. If changes need to be done on the fly to the
|
|
cmdset before passing them on to the cmdhandler, this is the
|
|
place to do it. This is called also if the player currently
|
|
have no cmdsets. kwargs are usually not used unless the
|
|
cmdset is generated dynamically.
|
|
"""
|
|
pass
|
|
|
|
def at_first_login(self):
|
|
"""
|
|
Called the very first time this player logs into the game.
|
|
"""
|
|
pass
|
|
|
|
def at_pre_login(self):
|
|
"""
|
|
Called every time the user logs in, just before the actual
|
|
login-state is set.
|
|
"""
|
|
pass
|
|
|
|
def _send_to_connect_channel(self, message):
|
|
"Helper method for loading the default comm channel"
|
|
global _CONNECT_CHANNEL
|
|
if not _CONNECT_CHANNEL:
|
|
try:
|
|
_CONNECT_CHANNEL = ChannelDB.objects.filter(db_key=settings.CHANNEL_CONNECTINFO[0])[0]
|
|
except Exception:
|
|
logger.log_trace()
|
|
now = datetime.datetime.now()
|
|
now = "%02i-%02i-%02i(%02i:%02i)" % (now.year, now.month,
|
|
now.day, now.hour, now.minute)
|
|
if _CONNECT_CHANNEL:
|
|
_CONNECT_CHANNEL.tempmsg("[%s, %s]: %s" % (_CONNECT_CHANNEL.key, now, message))
|
|
else:
|
|
logger.log_infomsg("[%s]: %s" % (now, message))
|
|
|
|
def at_post_login(self, sessid=None):
|
|
"""
|
|
Called at the end of the login process, just before letting
|
|
them loose. This is called before an eventual Character's
|
|
at_post_login hook.
|
|
"""
|
|
self._send_to_connect_channel("{G%s connected{n" % self.key)
|
|
if _MULTISESSION_MODE == 0:
|
|
# in this mode we should have only one character available. We
|
|
# try to auto-connect to it by calling the @ic command
|
|
# (this relies on player.db._last_puppet being set)
|
|
self.execute_cmd("@ic", sessid=sessid)
|
|
elif _MULTISESSION_MODE == 1:
|
|
# in this mode the first session to connect acts like mode 0,
|
|
# the following sessions "share" the same view and should
|
|
# not perform any actions
|
|
if not self.get_all_puppets():
|
|
self.execute_cmd("@ic", sessid=sessid)
|
|
elif _MULTISESSION_MODE in (2, 3):
|
|
# In this mode we by default end up at a character selection
|
|
# screen. We execute look on the player.
|
|
self.execute_cmd("look", sessid=sessid)
|
|
|
|
def at_disconnect(self, reason=None):
|
|
"""
|
|
Called just before user is disconnected.
|
|
"""
|
|
reason = reason and "(%s)" % reason or ""
|
|
self._send_to_connect_channel("{R%s disconnected %s{n" % (self.key, reason))
|
|
|
|
def at_post_disconnect(self):
|
|
"""
|
|
This is called after disconnection is complete. No messages
|
|
can be relayed to the player from here. After this call, the
|
|
player should not be accessed any more, making this a good
|
|
spot for deleting it (in the case of a guest player account,
|
|
for example).
|
|
"""
|
|
pass
|
|
|
|
def at_message_receive(self, message, from_obj=None):
|
|
"""
|
|
Called when any text is emitted to this
|
|
object. If it returns False, no text
|
|
will be sent automatically.
|
|
"""
|
|
return True
|
|
|
|
def at_message_send(self, message, to_object):
|
|
"""
|
|
Called whenever this object tries to send text
|
|
to another object. Only called if the object supplied
|
|
itself as a sender in the msg() call.
|
|
"""
|
|
pass
|
|
|
|
def at_server_reload(self):
|
|
"""
|
|
This hook is called whenever the server is shutting down for
|
|
restart/reboot. If you want to, for example, save non-persistent
|
|
properties across a restart, this is the place to do it.
|
|
"""
|
|
pass
|
|
|
|
def at_server_shutdown(self):
|
|
"""
|
|
This hook is called whenever the server is shutting down fully
|
|
(i.e. not for a restart).
|
|
"""
|
|
pass
|
|
|
|
|
|
class Guest(DefaultPlayer):
|
|
"""
|
|
This class is used for guest logins. Unlike Players, Guests and their
|
|
characters are deleted after disconnection.
|
|
"""
|
|
def at_post_login(self, sessid=None):
|
|
"""
|
|
In theory, guests only have one character regardless of which
|
|
MULTISESSION_MODE we're in. They don't get a choice.
|
|
"""
|
|
self._send_to_connect_channel("{G%s connected{n" % self.key)
|
|
self.execute_cmd("@ic", sessid=sessid)
|
|
|
|
def at_disconnect(self):
|
|
"""
|
|
A Guest's characters aren't meant to linger on the server. When a
|
|
Guest disconnects, we remove its character.
|
|
"""
|
|
super(Guest, self).at_disconnect()
|
|
characters = self.db._playable_characters
|
|
for character in filter(None, characters):
|
|
character.delete()
|
|
|
|
def at_server_shutdown(self):
|
|
"""
|
|
We repeat at_disconnect() here just to be on the safe side.
|
|
"""
|
|
super(Guest, self).at_server_shutdown()
|
|
characters = self.db._playable_characters
|
|
for character in filter(None, characters):
|
|
character.delete()
|
|
|
|
def at_post_disconnect(self):
|
|
"""
|
|
Guests aren't meant to linger on the server, either. We need to wait
|
|
until after the Guest disconnects to delete it, though.
|
|
"""
|
|
super(Guest, self).at_post_disconnect()
|
|
self.delete()
|