mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
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
|
|
@ -840,7 +840,7 @@ class DefaultObject(ObjectDB):
|
|||
except AttributeError:
|
||||
return False
|
||||
|
||||
def at_instance_creation(self):
|
||||
def at_first_save(self):
|
||||
"""
|
||||
This is called by the typeclass system whenever an instance of
|
||||
this class is saved for the first time. It is a generic hook
|
||||
|
|
@ -874,6 +874,7 @@ class DefaultObject(ObjectDB):
|
|||
updates.append("db_destination")
|
||||
if updates:
|
||||
self.save(update_fields=updates)
|
||||
|
||||
if cdict["permissions"]:
|
||||
self.permissions.add(cdict["permissions"])
|
||||
if cdict["locks"]:
|
||||
|
|
@ -883,6 +884,7 @@ class DefaultObject(ObjectDB):
|
|||
if cdict["location"]:
|
||||
cdict["location"].at_object_receive(self, None)
|
||||
self.at_after_move(None)
|
||||
del self._createdict
|
||||
|
||||
self.basetype_posthook_setup()
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class PlayerDBManager(TypedObjectManager, UserManager):
|
|||
get_player_from_uid
|
||||
get_player_from_name
|
||||
player_search (equivalent to ev.search_player)
|
||||
swap_character
|
||||
#swap_character
|
||||
|
||||
"""
|
||||
def num_total_players(self):
|
||||
|
|
@ -123,33 +123,33 @@ class PlayerDBManager(TypedObjectManager, UserManager):
|
|||
else:
|
||||
return self.filter(username__icontains=ostring)
|
||||
|
||||
def swap_character(self, player, new_character, delete_old_character=False):
|
||||
"""
|
||||
This disconnects a player from the current character (if any) and
|
||||
connects to a new character object.
|
||||
|
||||
"""
|
||||
|
||||
if new_character.player:
|
||||
# the new character is already linked to a player!
|
||||
return False
|
||||
|
||||
# do the swap
|
||||
old_character = player.character
|
||||
if old_character:
|
||||
old_character.player = None
|
||||
try:
|
||||
player.character = new_character
|
||||
new_character.player = player
|
||||
except Exception:
|
||||
# recover old setup
|
||||
if old_character:
|
||||
old_character.player = player
|
||||
player.character = old_character
|
||||
return False
|
||||
if old_character and delete_old_character:
|
||||
old_character.delete()
|
||||
return True
|
||||
# def swap_character(self, player, new_character, delete_old_character=False):
|
||||
# """
|
||||
# This disconnects a player from the current character (if any) and
|
||||
# connects to a new character object.
|
||||
#
|
||||
# """
|
||||
#
|
||||
# if new_character.player:
|
||||
# # the new character is already linked to a player!
|
||||
# return False
|
||||
#
|
||||
# # do the swap
|
||||
# old_character = player.character
|
||||
# if old_character:
|
||||
# old_character.player = None
|
||||
# try:
|
||||
# player.character = new_character
|
||||
# new_character.player = player
|
||||
# except Exception:
|
||||
# # recover old setup
|
||||
# if old_character:
|
||||
# old_character.player = player
|
||||
# player.character = old_character
|
||||
# return False
|
||||
# if old_character and delete_old_character:
|
||||
# old_character.delete()
|
||||
# return True
|
||||
|
||||
class PlayerManager(PlayerDBManager, TypeclassManager):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -22,21 +22,14 @@ from django.contrib.auth.models import AbstractUser
|
|||
from django.utils.encoding import smart_str
|
||||
|
||||
from src.players.manager import PlayerDBManager
|
||||
from src.scripts.models import ScriptDB
|
||||
from src.typeclasses.models import TypedObject
|
||||
from src.commands import cmdhandler
|
||||
from src.utils import utils, logger
|
||||
from src.utils.utils import to_str, make_iter
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from src.utils.utils import make_iter
|
||||
|
||||
__all__ = ("PlayerDB",)
|
||||
|
||||
#_ME = _("me")
|
||||
#_SELF = _("self")
|
||||
|
||||
_SESSIONS = None
|
||||
_AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
|
||||
_GA = object.__getattribute__
|
||||
|
|
@ -147,7 +140,7 @@ class PlayerDB(TypedObject, AbstractUser):
|
|||
cmdset_storage = property(cmdset_storage_get, cmdset_storage_set, cmdset_storage_del)
|
||||
|
||||
#
|
||||
# PlayerDB main class properties and methods
|
||||
# property/field access
|
||||
#
|
||||
|
||||
def __str__(self):
|
||||
|
|
@ -182,230 +175,3 @@ class PlayerDB(TypedObject, AbstractUser):
|
|||
def __uid_del(self):
|
||||
raise Exception("User id cannot be deleted!")
|
||||
uid = property(__uid_get, __uid_set, __uid_del)
|
||||
|
||||
#
|
||||
# PlayerDB class access methods
|
||||
#
|
||||
|
||||
# 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()
|
||||
_GA(self, "attributes").clear()
|
||||
_GA(self, "nicks").clear()
|
||||
_GA(self, "aliases").clear()
|
||||
super(PlayerDB, self).delete(*args, **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 = utils.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.
|
||||
"""
|
||||
#TODO deprecation
|
||||
if "return_character" in kwargs:
|
||||
logger.log_depmsg("Player.search's 'return_character' keyword is deprecated. Use the return_puppet keyword instead.")
|
||||
return_puppet = kwargs.get("return_character")
|
||||
|
||||
matches = _GA(self, "__class__").objects.player_search(searchdata)
|
||||
matches = _AT_SEARCH_RESULT(self, searchdata, matches, global_search=True)
|
||||
if matches and return_puppet:
|
||||
try:
|
||||
return _GA(matches, "puppet")
|
||||
except AttributeError:
|
||||
return None
|
||||
return matches
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -158,23 +158,23 @@ class ScriptDB(TypedObject):
|
|||
object = property(__get_obj, __set_obj)
|
||||
|
||||
|
||||
def at_typeclass_error(self):
|
||||
"""
|
||||
If this is called, it means the typeclass has a critical
|
||||
error and cannot even be loaded. We don't allow a script
|
||||
to be created under those circumstances. Already created,
|
||||
permanent scripts are set to already be active so they
|
||||
won't get activated now (next reboot the bug might be fixed)
|
||||
"""
|
||||
# By setting is_active=True, we trick the script not to run "again".
|
||||
self.is_active = True
|
||||
return super(ScriptDB, self).at_typeclass_error()
|
||||
|
||||
delete_iter = 0
|
||||
def delete(self):
|
||||
"Delete script"
|
||||
if self.delete_iter > 0:
|
||||
return
|
||||
self.delete_iter += 1
|
||||
_GA(self, "attributes").clear()
|
||||
super(ScriptDB, self).delete()
|
||||
# def at_typeclass_error(self):
|
||||
# """
|
||||
# If this is called, it means the typeclass has a critical
|
||||
# error and cannot even be loaded. We don't allow a script
|
||||
# to be created under those circumstances. Already created,
|
||||
# permanent scripts are set to already be active so they
|
||||
# won't get activated now (next reboot the bug might be fixed)
|
||||
# """
|
||||
# # By setting is_active=True, we trick the script not to run "again".
|
||||
# self.is_active = True
|
||||
# return super(ScriptDB, self).at_typeclass_error()
|
||||
#
|
||||
# delete_iter = 0
|
||||
# def delete(self):
|
||||
# "Delete script"
|
||||
# if self.delete_iter > 0:
|
||||
# return
|
||||
# self.delete_iter += 1
|
||||
# _GA(self, "attributes").clear()
|
||||
# super(ScriptDB, self).delete()
|
||||
|
|
|
|||
|
|
@ -351,109 +351,146 @@ class ScriptBase(ScriptDB):
|
|||
|
||||
class Script(ScriptBase):
|
||||
"""
|
||||
This is the class you should inherit from, it implements
|
||||
the hooks called by the script machinery.
|
||||
This is the base TypeClass for all Scripts. Scripts describe events,
|
||||
timers and states in game, they can have a time component or describe
|
||||
a state that changes under certain conditions.
|
||||
|
||||
Script API:
|
||||
|
||||
* Available properties (only available on initiated Typeclass objects)
|
||||
|
||||
key (string) - name of object
|
||||
name (string)- same as key
|
||||
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.
|
||||
dbobj (Object, read-only) - link to database model. dbobj.typeclass
|
||||
points back to this class
|
||||
typeclass (Object, read-only) - this links back to this class as an
|
||||
identified only. Use self.swap_typeclass() to switch.
|
||||
date_created (string) - time stamp of object creation
|
||||
permissions (list of strings) - list of permission strings
|
||||
|
||||
desc (string) - optional description of script, shown in listings
|
||||
obj (Object) - optional object that this script is connected to
|
||||
and acts on (set automatically
|
||||
by obj.scripts.add())
|
||||
interval (int) - how often script should run, in seconds.
|
||||
<=0 turns off ticker
|
||||
start_delay (bool) - if the script should start repeating right
|
||||
away or wait self.interval seconds
|
||||
repeats (int) - how many times the script should repeat before
|
||||
stopping. <=0 means infinite repeats
|
||||
persistent (bool) - if script should survive a server shutdown or not
|
||||
is_active (bool) - if script is currently running
|
||||
|
||||
* 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
|
||||
|
||||
* Helper methods
|
||||
|
||||
start() - start script (this usually happens automatically at creation
|
||||
and obj.script.add() etc)
|
||||
stop() - stop script, and delete it
|
||||
pause() - put the script on hold, until unpause() is called. If script
|
||||
is persistent, the pause state will survive a shutdown.
|
||||
unpause() - restart a previously paused script. The script will
|
||||
continue as if it was never paused.
|
||||
force_repeat() - force-step the script, regardless of how much remains
|
||||
until next step. This counts like a normal firing in all ways.
|
||||
time_until_next_repeat() - if a timed script (interval>0), returns
|
||||
time until next tick
|
||||
remaining_repeats() - number of repeats remaining, if limited
|
||||
|
||||
* Hook methods
|
||||
|
||||
at_script_creation() - called only once, when an object of this
|
||||
class is first created.
|
||||
is_valid() - is called to check if the script is valid to be running
|
||||
at the current time. If is_valid() returns False, the
|
||||
running script is stopped and removed from the game. You
|
||||
can use this to check state changes (i.e. an script
|
||||
tracking some combat stats at regular intervals is only
|
||||
valid to run while there is actual combat going on).
|
||||
at_start() - Called every time the script is started, which for
|
||||
persistent scripts is at least once every server start.
|
||||
Note that this is unaffected by self.delay_start, which
|
||||
only delays the first call to at_repeat(). It will also
|
||||
be called after a pause, to allow for setting up the script.
|
||||
at_repeat() - Called every self.interval seconds. It will be called
|
||||
immediately upon launch unless self.delay_start is True,
|
||||
which will delay the first call of this method by
|
||||
self.interval seconds. If self.interval<=0, this method
|
||||
will never be called.
|
||||
at_stop() - Called as the script object is stopped and is about to
|
||||
be removed from the game, e.g. because is_valid()
|
||||
returned False or self.stop() was called manually.
|
||||
at_server_reload() - Called when server reloads. Can be used to save
|
||||
temporary variables you want should survive a reload.
|
||||
at_server_shutdown() - called at a full server shutdown.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def at_first_save(self):
|
||||
"""
|
||||
This is the base TypeClass for all Scripts. Scripts describe events,
|
||||
timers and states in game, they can have a time component or describe
|
||||
a state that changes under certain conditions.
|
||||
This is called after very first time this object is saved.
|
||||
Generally, you don't need to overload this, but only the hooks
|
||||
called by this method.
|
||||
"""
|
||||
self.at_script_creation(self)
|
||||
|
||||
Script API:
|
||||
if hasattr(self, "_createdict"):
|
||||
# this will only be set if the utils.create_script
|
||||
# function was used to create the object. We want
|
||||
# the create call's kwargs to override the values
|
||||
# set by hooks.
|
||||
cdict = self._createdict
|
||||
updates = []
|
||||
if not cdict["key"]:
|
||||
self.db_key = "#%i" % self.dbid
|
||||
updates.append("db_key")
|
||||
elif self.key != cdict["db_key"]:
|
||||
self.db_key = cdict["key"]
|
||||
updates.append("db_key")
|
||||
if cdict["interval"] and self.interval != cdict["interval"]:
|
||||
self.db_interval = cdict["interval"]
|
||||
updates.append("db_interval")
|
||||
if cdict["start_delay"] and self.start_delay != cdict["start_delay"]:
|
||||
self.db_start_delay = cdict["start_delay"]
|
||||
updates.append("db_start_delay")
|
||||
if cdict["repeats"] and self.repeats != cdict["repeats"]:
|
||||
self.db_repeats = cdict["repeats"]
|
||||
updates.append("db_repeats")
|
||||
if cdict["persistent"] and self.persistent != cdict["persistent"]:
|
||||
self.db_persistent = cdict["persistent"]
|
||||
updates.append("db_persistent")
|
||||
if updates:
|
||||
self.save(update_fields=updates)
|
||||
|
||||
* Available properties (only available on initiated Typeclass objects)
|
||||
if cdict["permissions"]:
|
||||
self.permissions.add(cdict["permissions"])
|
||||
if cdict["locks"]:
|
||||
self.locks.add(cdict["locks"])
|
||||
if cdict["aliases"]:
|
||||
self.aliases.add(cdict["aliases"])
|
||||
|
||||
key (string) - name of object
|
||||
name (string)- same as key
|
||||
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.
|
||||
dbobj (Object, read-only) - link to database model. dbobj.typeclass
|
||||
points back to this class
|
||||
typeclass (Object, read-only) - this links back to this class as an
|
||||
identified only. Use self.swap_typeclass() to switch.
|
||||
date_created (string) - time stamp of object creation
|
||||
permissions (list of strings) - list of permission strings
|
||||
if not cdict["autostart"]:
|
||||
# don't auto-start the script
|
||||
return
|
||||
|
||||
desc (string) - optional description of script, shown in listings
|
||||
obj (Object) - optional object that this script is connected to
|
||||
and acts on (set automatically
|
||||
by obj.scripts.add())
|
||||
interval (int) - how often script should run, in seconds.
|
||||
<=0 turns off ticker
|
||||
start_delay (bool) - if the script should start repeating right
|
||||
away or wait self.interval seconds
|
||||
repeats (int) - how many times the script should repeat before
|
||||
stopping. <=0 means infinite repeats
|
||||
persistent (bool) - if script should survive a server shutdown or not
|
||||
is_active (bool) - if script is currently running
|
||||
# auto-start script (default)
|
||||
self.start()
|
||||
|
||||
* 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
|
||||
|
||||
* Helper methods
|
||||
|
||||
start() - start script (this usually happens automatically at creation
|
||||
and obj.script.add() etc)
|
||||
stop() - stop script, and delete it
|
||||
pause() - put the script on hold, until unpause() is called. If script
|
||||
is persistent, the pause state will survive a shutdown.
|
||||
unpause() - restart a previously paused script. The script will
|
||||
continue as if it was never paused.
|
||||
force_repeat() - force-step the script, regardless of how much remains
|
||||
until next step. This counts like a normal firing in all ways.
|
||||
time_until_next_repeat() - if a timed script (interval>0), returns
|
||||
time until next tick
|
||||
remaining_repeats() - number of repeats remaining, if limited
|
||||
|
||||
* Hook methods
|
||||
|
||||
at_script_creation() - called only once, when an object of this
|
||||
class is first created.
|
||||
is_valid() - is called to check if the script is valid to be running
|
||||
at the current time. If is_valid() returns False, the
|
||||
running script is stopped and removed from the game. You
|
||||
can use this to check state changes (i.e. an script
|
||||
tracking some combat stats at regular intervals is only
|
||||
valid to run while there is actual combat going on).
|
||||
at_start() - Called every time the script is started, which for
|
||||
persistent scripts is at least once every server start.
|
||||
Note that this is unaffected by self.delay_start, which
|
||||
only delays the first call to at_repeat(). It will also
|
||||
be called after a pause, to allow for setting up the script.
|
||||
at_repeat() - Called every self.interval seconds. It will be called
|
||||
immediately upon launch unless self.delay_start is True,
|
||||
which will delay the first call of this method by
|
||||
self.interval seconds. If self.interval<=0, this method
|
||||
will never be called.
|
||||
at_stop() - Called as the script object is stopped and is about to
|
||||
be removed from the game, e.g. because is_valid()
|
||||
returned False or self.stop() was called manually.
|
||||
at_server_reload() - Called when server reloads. Can be used to save
|
||||
temporary variables you want should survive a reload.
|
||||
at_server_shutdown() - called at a full server shutdown.
|
||||
|
||||
|
||||
"""
|
||||
super(Script, self).__init__(*args, **kwargs)
|
||||
|
||||
def at_script_creation(self):
|
||||
"""
|
||||
Only called once, by the create function.
|
||||
"""
|
||||
self.key = "<unnamed>"
|
||||
self.desc = ""
|
||||
self.interval = 0 # infinite
|
||||
self.start_delay = False
|
||||
self.repeats = 0 # infinite
|
||||
self.persistent = False
|
||||
pass
|
||||
|
||||
def is_valid(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -74,10 +74,10 @@ _SA = object.__setattr__
|
|||
# signal receivers. Assigned in __new__
|
||||
def post_save(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Is called Receive a signal just after the object is saved.
|
||||
Receives a signal just after the object is saved.
|
||||
"""
|
||||
if created:
|
||||
instance.at_instance_creation()
|
||||
instance.at_first_save()
|
||||
#TODO - put OOB handler here?
|
||||
|
||||
|
||||
|
|
@ -363,7 +363,8 @@ class TypedObject(SharedMemoryModel):
|
|||
self.nattributes.clear()
|
||||
|
||||
if run_start_hooks:
|
||||
self.at_instance_creation()
|
||||
# fake this call to mimic the first save
|
||||
self.at_first_save()
|
||||
|
||||
#
|
||||
# Lock / permission methods
|
||||
|
|
|
|||
|
|
@ -48,33 +48,6 @@ __all__ = ("create_object", "create_script", "create_help_entry",
|
|||
|
||||
_GA = object.__getattribute__
|
||||
|
||||
# Helper function
|
||||
|
||||
def handle_dbref(inp, objclass, raise_errors=True):
|
||||
"""
|
||||
Convert a #dbid to a valid object of objclass. objclass
|
||||
should be a valid object class to filter against (objclass.filter ...)
|
||||
If not raise_errors is set, this will swallow errors of non-existing
|
||||
objects.
|
||||
"""
|
||||
if not (isinstance(inp, basestring) and inp.startswith("#")):
|
||||
return inp
|
||||
# a string, analyze it
|
||||
inp = inp.lstrip('#')
|
||||
try:
|
||||
if int(inp) < 0:
|
||||
return None
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
# if we get to this point, inp is an integer dbref; get the matching object
|
||||
try:
|
||||
return objclass.objects.get(id=inp)
|
||||
except Exception:
|
||||
if raise_errors:
|
||||
raise
|
||||
return inp
|
||||
|
||||
#
|
||||
# Game Object creation
|
||||
#
|
||||
|
|
@ -128,7 +101,7 @@ def create_object(typeclass=None, key=None, location=None,
|
|||
"locks":locks, "aliases":aliases, "destination":destination,
|
||||
"report_to":report_to, "nohome":nohome}
|
||||
# this will trigger the save signal which in turn calls the
|
||||
# at_instance_creation hook on the typeclass, where the _createdict can be
|
||||
# at_first_save hook on the typeclass, where the _createdict can be
|
||||
# used.
|
||||
new_object.save()
|
||||
return new_object
|
||||
|
|
@ -168,83 +141,33 @@ def create_script(typeclass, key=None, obj=None, player=None, locks=None,
|
|||
error will be raised. If set, this method will
|
||||
return None upon errors.
|
||||
"""
|
||||
global _Script, _ScriptDB
|
||||
if not _Script:
|
||||
from src.scripts.scripts import Script as _Script
|
||||
if not _ScriptDB:
|
||||
from src.scripts.models import ScriptDB as _ScriptDB
|
||||
typeclass = typeclass if typeclass else settings.BASE_SCRIPT_TYPECLASS
|
||||
|
||||
if not typeclass:
|
||||
typeclass = settings.BASE_SCRIPT_TYPECLASS
|
||||
elif isinstance(typeclass, _ScriptDB):
|
||||
# this is already an scriptdb instance, extract its typeclass
|
||||
typeclass = typeclass.typeclass.path
|
||||
elif isinstance(typeclass, _Script) or utils.inherits_from(typeclass, _Script):
|
||||
# this is already an object typeclass, extract its path
|
||||
typeclass = typeclass.path
|
||||
if isinstance(typeclass, basestring):
|
||||
# a path is given. Load the actual typeclass
|
||||
typeclass = class_from_module(typeclass, settings.SCRIPT_TYPECLASS_PATHS)
|
||||
|
||||
# create new database script
|
||||
new_db_script = _ScriptDB()
|
||||
# validate input
|
||||
player = dbid_to_obj(player)
|
||||
obj = dbid_to_obj(obj)
|
||||
|
||||
# assign the typeclass
|
||||
typeclass = utils.to_unicode(typeclass)
|
||||
new_db_script.typeclass_path = typeclass
|
||||
|
||||
# the name/key is often set later in the typeclass. This
|
||||
# is set here as a failsafe.
|
||||
if key:
|
||||
new_db_script.key = key
|
||||
else:
|
||||
new_db_script.key = "#%i" % new_db_script.id
|
||||
|
||||
# this will either load the typeclass or the default one
|
||||
new_script = new_db_script.typeclass
|
||||
|
||||
if not _GA(new_db_script, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input and it still
|
||||
# gave us a default
|
||||
SharedMemoryModel.delete(new_db_script)
|
||||
if report_to:
|
||||
_GA(report_to, "msg")("Error creating %s (%s): %s" % (new_db_script.key, typeclass,
|
||||
_GA(new_db_script, "typeclass_last_errmsg")))
|
||||
return None
|
||||
else:
|
||||
raise Exception(_GA(new_db_script, "typeclass_last_errmsg"))
|
||||
|
||||
if obj:
|
||||
new_script.obj = obj
|
||||
if player:
|
||||
new_script.player = player
|
||||
|
||||
# call the hook method. This is where all at_creation
|
||||
# customization happens as the typeclass stores custom
|
||||
# things on its database object.
|
||||
new_script.at_script_creation()
|
||||
|
||||
# custom-given variables override the hook
|
||||
if key:
|
||||
new_script.key = key
|
||||
if locks:
|
||||
new_script.locks.add(locks)
|
||||
if interval is not None:
|
||||
new_script.interval = interval
|
||||
if start_delay is not None:
|
||||
new_script.start_delay = start_delay
|
||||
if repeats is not None:
|
||||
new_script.repeats = repeats
|
||||
if persistent is not None:
|
||||
new_script.persistent = persistent
|
||||
|
||||
# must do this before starting the script since some
|
||||
# scripts may otherwise run for a very short time and
|
||||
# try to delete itself before we have a time to save it.
|
||||
new_db_script.save()
|
||||
|
||||
# a new created script should usually be started.
|
||||
if autostart:
|
||||
new_script.start()
|
||||
# create new instance
|
||||
new_script = typeclass(db_key=key, db_obj=obj, db_player=player,
|
||||
db_interval=interval, db_start_delay=start_delay,
|
||||
db_repeats=repeats, db_peristent=persistent)
|
||||
# store the call signature for the signal
|
||||
new_script._createdict = {"key":key, "obj":obj, "player":player,
|
||||
"locks":locks, "interval":interval,
|
||||
"start_delay":start_delay, "repeats":repeats,
|
||||
"persistent":persistent, "autostart":autostart,
|
||||
"report_to":report_to}
|
||||
|
||||
# this will trigger the save signal which in turn calls the
|
||||
# at_first_save hook on the tyepclass, where the _createdict
|
||||
# can be used.
|
||||
new_script.save()
|
||||
return new_script
|
||||
|
||||
#alias
|
||||
script = create_script
|
||||
|
||||
|
|
@ -413,11 +336,16 @@ def create_player(key, email, password,
|
|||
operations and is thus not suitable for play-testing the game.
|
||||
|
||||
"""
|
||||
global _PlayerDB, _Player
|
||||
if not _PlayerDB:
|
||||
from src.players.models import PlayerDB as _PlayerDB
|
||||
if not _Player:
|
||||
from src.players.player import Player as _Player
|
||||
typeclass = typeclass if typeclass else settings.BASE_PLAYER_TYPECLASS
|
||||
|
||||
if isinstance(typeclass, basestring):
|
||||
# a path is given. Load the actual typeclass.
|
||||
typeclass = class_from_module(typeclass, settings.OBJECT_TYPECLASS_PATHS)
|
||||
typeclass_path = typeclass.path
|
||||
|
||||
# setup input for the create command. We use PlayerDB as baseclass
|
||||
# here to give us maximum freedom (the typeclasses will load
|
||||
# correctly when each object is recovered).
|
||||
|
||||
if not email:
|
||||
email = "dummy@dummy.com"
|
||||
|
|
@ -425,69 +353,23 @@ def create_player(key, email, password,
|
|||
raise ValueError("A Player with the name '%s' already exists." % key)
|
||||
|
||||
# this handles a given dbref-relocate to a player.
|
||||
report_to = handle_dbref(report_to, _PlayerDB)
|
||||
report_to = dbid_to_obj(report_to, _PlayerDB)
|
||||
|
||||
try:
|
||||
# create the correct player object
|
||||
if is_superuser:
|
||||
new_player = _PlayerDB.objects.create_superuser(key, email, password)
|
||||
else:
|
||||
new_player = _PlayerDB.objects.create_user(key, email, password)
|
||||
new_player.db_typeclass_path = typeclass_path
|
||||
# store the call signature for the signal
|
||||
new_player._createdict = {"locks":locks, "permissions":permissions,
|
||||
"report_to":report_to}
|
||||
|
||||
# create the correct Player object
|
||||
if is_superuser:
|
||||
new_db_player = _PlayerDB.objects.create_superuser(key, email, password)
|
||||
else:
|
||||
new_db_player = _PlayerDB.objects.create_user(key, email, password)
|
||||
|
||||
if not typeclass:
|
||||
typeclass = settings.BASE_PLAYER_TYPECLASS
|
||||
elif isinstance(typeclass, _PlayerDB):
|
||||
# this is an PlayerDB instance, extract its typeclass path
|
||||
typeclass = typeclass.typeclass.path
|
||||
elif isinstance(typeclass, _Player) or utils.inherits_from(typeclass, _Player):
|
||||
# this is Player object typeclass, extract its path
|
||||
typeclass = typeclass.path
|
||||
|
||||
# assign the typeclass
|
||||
typeclass = utils.to_unicode(typeclass)
|
||||
new_db_player.typeclass_path = typeclass
|
||||
|
||||
# this will either load the typeclass or the default one
|
||||
new_player = new_db_player.typeclass
|
||||
|
||||
if not _GA(new_db_player, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input
|
||||
# and it still gave us a default
|
||||
SharedMemoryModel.delete(new_db_player)
|
||||
if report_to:
|
||||
_GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_player.key, typeclass,
|
||||
_GA(new_db_player, "typeclass_last_errmsg")))
|
||||
return None
|
||||
else:
|
||||
raise Exception(_GA(new_db_player, "typeclass_last_errmsg"))
|
||||
|
||||
new_player.basetype_setup() # setup the basic locks and cmdset
|
||||
# call hook method (may override default permissions)
|
||||
new_player.at_player_creation()
|
||||
|
||||
# custom given arguments potentially overrides the hook
|
||||
if permissions:
|
||||
new_player.permissions.add(permissions)
|
||||
elif not new_player.permissions:
|
||||
new_player.permissions.add(settings.PERMISSION_PLAYER_DEFAULT)
|
||||
if locks:
|
||||
new_player.locks.add(locks)
|
||||
return new_player
|
||||
|
||||
except Exception:
|
||||
# a failure in creating the player; we try to clean
|
||||
# up as much as we can
|
||||
logger.log_trace()
|
||||
try:
|
||||
new_player.delete()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
del new_player
|
||||
except Exception:
|
||||
pass
|
||||
raise
|
||||
# saving will trigger the signal that calls the
|
||||
# at_first_save hook on the typeclass, where the _createdict
|
||||
# can be used.
|
||||
new_player.save()
|
||||
return new_player
|
||||
|
||||
# alias
|
||||
player = create_player
|
||||
|
|
|
|||
|
|
@ -76,12 +76,11 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings'
|
|||
from django.conf import settings
|
||||
from random import randint
|
||||
from src.objects.models import ObjectDB
|
||||
from src.utils.create import handle_dbref
|
||||
from src.utils.utils import make_iter, all_from_module
|
||||
from src.utils.utils import make_iter, all_from_module, dbid_to_obj
|
||||
|
||||
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
|
||||
|
||||
_handle_dbref = lambda inp: handle_dbref(inp, ObjectDB)
|
||||
_handle_dbref = lambda inp: dbid_to_obj(inp, ObjectDB)
|
||||
|
||||
|
||||
def _validate_prototype(key, prototype, protparents, visited):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue