Changed player.search to only search for players explicitly.

Added a MuxCommandOOC class to handle the OOC commands in a more uniform way.
Fixed the @ic/@ooc and page commands. Resolves issue 233. Resolves issue 234.
This commit is contained in:
Griatch 2012-05-17 19:42:37 +02:00
parent 96e95ca525
commit 8ad4f4a9fc
12 changed files with 253 additions and 168 deletions

View file

@ -12,7 +12,7 @@ from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChan
from src.comms import irc, imc2, rss from src.comms import irc, imc2, rss
from src.comms.channelhandler import CHANNELHANDLER from src.comms.channelhandler import CHANNELHANDLER
from src.utils import create, utils from src.utils import create, utils
from src.commands.default.muxcommand import MuxCommand from src.commands.default.muxcommand import MuxCommand, MuxCommandOOC
# limit symbol import for API # limit symbol import for API
__all__ = ("CommCommand", "CmdAddCom", "CmdDelCom", "CmdAllCom", __all__ = ("CommCommand", "CmdAddCom", "CmdDelCom", "CmdAllCom",
@ -624,7 +624,7 @@ class CmdCdesc(MuxCommand):
channel.save() channel.save()
caller.msg("Description of channel '%s' set to '%s'." % (channel.key, self.rhs)) caller.msg("Description of channel '%s' set to '%s'." % (channel.key, self.rhs))
class CmdPage(MuxCommand): class CmdPage(MuxCommandOOC):
""" """
page - send private message page - send private message
@ -647,18 +647,17 @@ class CmdPage(MuxCommand):
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
"Implement function using the Msg methods" "Implement function using the Msg methods"
# this is a MuxCommandOOC, which means caller will be a Player.
caller = self.caller caller = self.caller
player = caller character = self.character
# get the messages we've sent # get the messages we've sent
messages_we_sent = list(Msg.objects.get_messages_by_sender(player)) messages_we_sent = list(Msg.objects.get_messages_by_sender(caller))
pages_we_sent = [msg for msg in messages_we_sent pages_we_sent = [msg for msg in messages_we_sent if msg.receivers]
if msg.receivers]
# get last messages we've got # get last messages we've got
pages_we_got = list(Msg.objects.get_messages_by_receiver(player)) pages_we_got = list(Msg.objects.get_messages_by_receiver(caller))
if 'last' in self.switches: if 'last' in self.switches:
if pages_we_sent: if pages_we_sent:
@ -718,9 +717,7 @@ class CmdPage(MuxCommand):
recobjs = [] recobjs = []
for receiver in set(receivers): for receiver in set(receivers):
if isinstance(receiver, basestring): if isinstance(receiver, basestring):
pobj = caller.search("*%s" % (receiver.lstrip('*')), global_search=True) pobj = caller.search(receiver)
if not pobj:
return
elif hasattr(receiver, 'character'): elif hasattr(receiver, 'character'):
pobj = receiver.character pobj = receiver.character
else: else:
@ -739,7 +736,7 @@ class CmdPage(MuxCommand):
message = "%s %s" % (caller.key, message.strip(':').strip()) message = "%s %s" % (caller.key, message.strip(':').strip())
# create the persistent message object # create the persistent message object
msg = create.create_message(player, message, msg = create.create_message(caller, message,
receivers=recobjs) receivers=recobjs)
# tell the players they got a message. # tell the players they got a message.

View file

@ -5,9 +5,9 @@ now.
import time import time
from django.conf import settings from django.conf import settings
from src.server.sessionhandler import SESSIONS from src.server.sessionhandler import SESSIONS
from src.utils import utils from src.utils import utils, search
from src.objects.models import ObjectNick as Nick from src.objects.models import ObjectNick as Nick
from src.commands.default.muxcommand import MuxCommand from src.commands.default.muxcommand import MuxCommand, MuxCommandOOC
# limit symbol import for API # limit symbol import for API
__all__ = ("CmdHome", "CmdLook", "CmdPassword", "CmdNick", __all__ = ("CmdHome", "CmdLook", "CmdPassword", "CmdNick",
@ -611,7 +611,7 @@ class CmdAccess(MuxCommand):
# OOC commands # OOC commands
class CmdOOCLook(CmdLook): class CmdOOCLook(MuxCommandOOC, CmdLook):
""" """
ooc look ooc look
@ -633,14 +633,6 @@ class CmdOOCLook(CmdLook):
def func(self): def func(self):
"implement the ooc look command" "implement the ooc look command"
self.character = None
if utils.inherits_from(self.caller, "src.objects.objects.Object"):
# An object of some type is calling. Convert to player.
#print self.caller, self.caller.__class__
self.character = self.caller
if hasattr(self.caller, "player"):
self.caller = self.caller.player
if not self.character: if not self.character:
string = "You are out-of-character (OOC). " string = "You are out-of-character (OOC). "
string += "Use {w@ic{n to get back to the game, {whelp{n for more info." string += "Use {w@ic{n to get back to the game, {whelp{n for more info."
@ -649,7 +641,7 @@ class CmdOOCLook(CmdLook):
self.caller = self.character # we have to put this back for normal look to work. self.caller = self.character # we have to put this back for normal look to work.
super(CmdOOCLook, self).func() super(CmdOOCLook, self).func()
class CmdIC(MuxCommand): class CmdIC(MuxCommandOOC):
""" """
Switch control to an object Switch control to an object
@ -674,8 +666,7 @@ class CmdIC(MuxCommand):
Simple puppet method Simple puppet method
""" """
caller = self.caller caller = self.caller
if utils.inherits_from(caller, "src.objects.objects.Object"): old_character = self.character
caller = caller.player
new_character = None new_character = None
if not self.args: if not self.args:
@ -685,10 +676,12 @@ class CmdIC(MuxCommand):
return return
if not new_character: if not new_character:
# search for a matching character # search for a matching character
new_character = caller.search(self.args, global_search=True) new_character = search.objects(self.args, caller, global_search=True, single_result=True)
if not new_character: if new_character:
# the search method handles error messages etc. new_character = new_character[0]
return else:
# the search method handles error messages etc.
return
if new_character.player: if new_character.player:
if new_character.player == caller: if new_character.player == caller:
caller.msg("{RYou already are {c%s{n." % new_character.name) caller.msg("{RYou already are {c%s{n." % new_character.name)
@ -698,13 +691,9 @@ class CmdIC(MuxCommand):
if not new_character.access(caller, "puppet"): if not new_character.access(caller, "puppet"):
caller.msg("{rYou may not become %s.{n" % new_character.name) caller.msg("{rYou may not become %s.{n" % new_character.name)
return return
old_char = None
if caller.character:
# save the old character. We only assign this to last_puppet if swap is successful.
old_char = caller.character
if caller.swap_character(new_character): if caller.swap_character(new_character):
new_character.msg("\n{gYou become {c%s{n.\n" % new_character.name) new_character.msg("\n{gYou become {c%s{n.\n" % new_character.name)
caller.db.last_puppet = old_char caller.db.last_puppet = old_character
if not new_character.location: if not new_character.location:
# this might be due to being hidden away at logout; check # this might be due to being hidden away at logout; check
loc = new_character.db.prelogout_location loc = new_character.db.prelogout_location
@ -718,7 +707,7 @@ class CmdIC(MuxCommand):
else: else:
caller.msg("{rYou cannot become {C%s{n." % new_character.name) caller.msg("{rYou cannot become {C%s{n." % new_character.name)
class CmdOOC(MuxCommand): class CmdOOC(MuxCommandOOC):
""" """
@ooc - go ooc @ooc - go ooc

View file

@ -1,5 +1,6 @@
""" """
The command template for the default MUX-style command set The command template for the default MUX-style command set. There
is also an OOC version that makes sure caller is a Player object.
""" """
from src.utils import utils from src.utils import utils
@ -11,7 +12,7 @@ __all__ = ("MuxCommand",)
class MuxCommand(Command): class MuxCommand(Command):
""" """
This sets up the basis for a MUX command. The idea This sets up the basis for a MUX command. The idea
is that most other Mux-related commands should just is tkhat most other Mux-related commands should just
inherit from this and don't have to implement much inherit from this and don't have to implement much
parsing of their own unless they do something particularly parsing of their own unless they do something particularly
advanced. advanced.
@ -161,3 +162,33 @@ class MuxCommand(Command):
string += "rhs, comma separated (self.rhslist): {w%s{n\n" % self.rhslist string += "rhs, comma separated (self.rhslist): {w%s{n\n" % self.rhslist
string += "-" * 50 string += "-" * 50
self.caller.msg(string) self.caller.msg(string)
class MuxCommandOOC(MuxCommand):
"""
This is an OOC version of the MuxCommand. Since OOC commands sit
on Players rather than on Characters/Objects, we need to check
this in the parser.
OOC commands are strictly speaking also available when IC, it's
just that they are applied with a lower priority and are always
available, also when disconnected from a character (i.e. "ooc").
This class makes sure that caller is always a Player object, while
creating a new property "character" that is set only if a
character is actually attached to the Player.
"""
def parse(self):
"""
We run the parent parser as usual, then fix the result
"""
super(MuxCommandOOC, self).parse()
if utils.inherits_from(self.caller, "src.objects.objects.Object"):
# caller is an Object/Character
self.character = self.caller
self.caller = self.caller.player
elif hasattr(self.caller, "character"):
# caller was already a Player
self.character = self.caller.character
else:
self.character = None

View file

@ -33,10 +33,11 @@ from src.objects.models import ObjectDB
# ------------------------------------------------------------ # ------------------------------------------------------------
# print all feedback from test commands (can become very verbose!) # print all feedback from test commands (can become very verbose!)
VERBOSE = False VERBOSE = True
NOMANGLE = True # mangle command input for extra testing
def cleanup(): def cleanup():
print "cleaning test database ..."
User.objects.all().delete() User.objects.all().delete()
PlayerDB.objects.all().delete() PlayerDB.objects.all().delete()
ObjectDB.objects.all().delete() ObjectDB.objects.all().delete()
@ -45,6 +46,7 @@ def cleanup():
PlayerChannelConnection.objects.all().delete() PlayerChannelConnection.objects.all().delete()
ExternalChannelConnection.objects.all().delete() ExternalChannelConnection.objects.all().delete()
ServerConfig.objects.all().delete() ServerConfig.objects.all().delete()
cleanup()
class FakeSessionHandler(sessionhandler.ServerSessionHandler): class FakeSessionHandler(sessionhandler.ServerSessionHandler):
""" """
@ -82,6 +84,7 @@ class FakeSession(serversession.ServerSession):
def lineReceived(self, raw_string): def lineReceived(self, raw_string):
pass pass
def msg(self, message, data=None): def msg(self, message, data=None):
global VERBOSE
if message.startswith("Traceback (most recent call last):"): if message.startswith("Traceback (most recent call last):"):
#retval = "Traceback last line: %s" % message.split('\n')[-4:] #retval = "Traceback last line: %s" % message.split('\n')[-4:]
raise AssertionError(message) raise AssertionError(message)
@ -103,6 +106,7 @@ class FakeSession(serversession.ServerSession):
raise AssertionError(retval) raise AssertionError(retval)
if VERBOSE: if VERBOSE:
print message print message
# setting up objects
class CommandTest(TestCase): class CommandTest(TestCase):
""" """
@ -111,53 +115,77 @@ class CommandTest(TestCase):
Inherit new tests from this. Inherit new tests from this.
""" """
print "creating command testing objects ..."
NOMANGLE = True # mangle command input for extra testing ROOM1 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room1")
ROOM2 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room2")
# create a faux player/character for testing.
CHAR1 = create.create_player("TestChar", "testplayer@test.com", "testpassword", character_location=ROOM1)
CHAR1.player.user.is_superuser = True
CHAR1.lock_storage = ""
CHAR1.locks = LockHandler(CHAR1)
CHAR1.ndb.return_string = None
sess = FakeSession()
sess.connectionMade()
sess.session_login(CHAR1.player)
# create second player
CHAR2 = create.create_player("TestChar2", "testplayer2@test.com", "testpassword2", character_location=ROOM1)
CHAR2.player.user.is_superuser = False
CHAR2.lock_storage = ""
CHAR2.locks = LockHandler(CHAR2)
CHAR2.ndb.return_string = None
sess2 = FakeSession()
sess2.connectionMade()
sess2.session_login(CHAR2.player)
# A non-player-controlled character
CHAR3 = create.create_object(settings.BASE_CHARACTER_TYPECLASS, key="TestChar3", location=ROOM1)
# create some objects
OBJ1 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj1", location=ROOM1)
OBJ2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2", location=ROOM1)
EXIT1 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit1", location=ROOM1)
EXIT2 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit2", location=ROOM2)
def setUp(self): def setUp(self):
"sets up the testing environment" "sets up the testing environment"
#ServerConfig.objects.conf("default_home", 2) #ServerConfig.objects.conf("default_home", 2)
self.addCleanup(cleanup) self.room1 = self.ROOM1
self.room2 = self.ROOM2
self.char1 = self.CHAR1
self.char2 = self.CHAR2
self.char3 = self.CHAR3
self.obj1 = self.OBJ1
self.obj2 = self.OBJ2
self.exit1 = self.EXIT1
self.exit2 = self.EXIT2
self.room1 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room1") #self.addCleanup(cleanup)
self.room2 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room2") #self.room1 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room1")
# create a faux player/character for testing. #self.room2 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room2")
self.char1 = create.create_player("TestChar", "testplayer@test.com", "testpassword", character_location=self.room1) ## create a faux player/character for testing.
self.char1.player.user.is_superuser = True #self.char1 = create.create_player("TestChar", "testplayer@test.com", "testpassword", character_location=self.room1)
self.char1.lock_storage = "" #self.char1.player.user.is_superuser = True
self.char1.locks = LockHandler(self.char1) #self.char1.lock_storage = ""
self.char1.ndb.return_string = None #self.char1.locks = LockHandler(self.char1)
sess = FakeSession() #self.char1.ndb.return_string = None
sess.connectionMade() #sess = FakeSession()
sess.session_login(self.char1.player) #sess.connectionMade()
# create second player #sess.session_login(self.char1.player)
self.char2 = create.create_player("TestChar2", "testplayer2@test.com", "testpassword2", character_location=self.room1) ## create second player
self.char2.player.user.is_superuser = False #self.char2 = create.create_player("TestChar2", "testplayer2@test.com", "testpassword2", character_location=self.room1)
self.char2.lock_storage = "" #self.char2.player.user.is_superuser = False
self.char2.locks = LockHandler(self.char2) #self.char2.lock_storage = ""
self.char2.ndb.return_string = None #self.char2.locks = LockHandler(self.char2)
sess2 = FakeSession() #self.char2.ndb.return_string = None
sess2.connectionMade() #sess2 = FakeSession()
sess2.session_login(self.char2.player) #sess2.connectionMade()
# A non-player-controlled character #sess2.session_login(self.char2.player)
self.char3 = create.create_object(settings.BASE_CHARACTER_TYPECLASS, key="TestChar3", location=self.room1) ## A non-player-controlled character
# create some objects #self.char3 = create.create_object(settings.BASE_CHARACTER_TYPECLASS, key="TestChar3", location=self.room1)
self.obj1 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj1", location=self.room1) ## create some objects
self.obj2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2", location=self.room1) #self.obj1 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj1", location=self.room1)
self.exit1 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit1", location=self.room1) #self.obj2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2", location=self.room1)
self.exit2 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit2", location=self.room2) #self.exit1 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit1", location=self.room1)
#self.exit2 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit2", location=self.room2)
def tearDown(self):
"Cleans up testing environment after test has run."
User.objects.all().delete()
PlayerDB.objects.all().delete()
ObjectDB.objects.all().delete()
Channel.objects.all().delete()
Msg.objects.all().delete()
PlayerChannelConnection.objects.all().delete()
ExternalChannelConnection.objects.all().delete()
ServerConfig.objects.all().delete()
def get_cmd(self, cmd_class, argument_string=""): def get_cmd(self, cmd_class, argument_string=""):
""" """
@ -178,7 +206,7 @@ class CommandTest(TestCase):
This also mangles the input in various ways to test if the command This also mangles the input in various ways to test if the command
will be fooled. will be fooled.
""" """
if not nomangle and not VERBOSE and not self.NOMANGLE: if not nomangle and not VERBOSE and not NOMANGLE:
# only mangle if not VERBOSE, to make fewer return lines # only mangle if not VERBOSE, to make fewer return lines
test1 = re.sub(r'\s', '', raw_string) # remove all whitespace inside it test1 = re.sub(r'\s', '', raw_string) # remove all whitespace inside it
test2 = "%s/åäö öäö;-:$£@*~^' 'test" % raw_string # inserting weird characters in call test2 = "%s/åäö öäö;-:$£@*~^' 'test" % raw_string # inserting weird characters in call
@ -202,7 +230,6 @@ class BuildTest(CommandTest):
NOMANGLE = True NOMANGLE = True
#------------------------------------------------------------ #------------------------------------------------------------
# Default set Command testing # Default set Command testing
#------------------------------------------------------------ #------------------------------------------------------------
@ -352,7 +379,7 @@ class TestSet(BuildTest):
class TestCpAttr(BuildTest): class TestCpAttr(BuildTest):
def test_call(self): def test_call(self):
self.execute_cmd("@set obj1/test = value") self.execute_cmd("@set obj1/test = value")
self.execute_cmd("@set me/test2 = value2") self.execute_cmd("@set obj2/test2 = value2")
self.execute_cmd("@cpattr obj1/test = obj2/test") self.execute_cmd("@cpattr obj1/test = obj2/test")
self.execute_cmd("@cpattr test2 = obj2") self.execute_cmd("@cpattr test2 = obj2")
self.assertEqual(self.obj2.db.test, u"value") self.assertEqual(self.obj2.db.test, u"value")
@ -408,7 +435,7 @@ class TestCmdSets(BuildTest):
def test_call(self): def test_call(self):
self.execute_cmd("@cmdsets") self.execute_cmd("@cmdsets")
self.execute_cmd("@cmdsets obj1") self.execute_cmd("@cmdsets obj1")
class TestDesc(BuildTest): class TestName(BuildTest):
def test_call(self): def test_call(self):
self.execute_cmd("@name obj1 = Test object", "Object's name changed to 'Test object'.") self.execute_cmd("@name obj1 = Test object", "Object's name changed to 'Test object'.")
self.assertEqual(self.obj1.key, u"Test object") self.assertEqual(self.obj1.key, u"Test object")

View file

@ -203,7 +203,7 @@ class ObjectManager(TypedObjectManager):
@returns_typeclass_list @returns_typeclass_list
def object_search(self, ostring, caller=None, def object_search(self, ostring, caller=None,
global_search=False, global_search=False,
attribute_name=None, location=None): attribute_name=None, location=None, single_result=False):
""" """
Search as an object and return results. The result is always an Object. Search as an object and return results. The result is always an Object.
If * is appended (player search, a Character controlled by this Player If * is appended (player search, a Character controlled by this Player
@ -313,7 +313,7 @@ class ObjectManager(TypedObjectManager):
matches = [matches[match_number]] matches = [matches[match_number]]
except IndexError: except IndexError:
pass pass
# This is always a list. # We always have a (possibly empty) list at this point.
return matches return matches
# #

View file

@ -28,7 +28,7 @@ from src.commands.cmdsethandler import CmdSetHandler
from src.commands import cmdhandler from src.commands import cmdhandler
from src.scripts.scripthandler import ScriptHandler from src.scripts.scripthandler import ScriptHandler
from src.utils import logger from src.utils import logger
from src.utils.utils import make_iter, to_unicode, variable_from_module from src.utils.utils import make_iter, to_unicode, variable_from_module, inherits_from
#__all__ = ("ObjAttribute", "Alias", "ObjectNick", "ObjectDB") #__all__ = ("ObjAttribute", "Alias", "ObjectNick", "ObjectDB")
@ -245,7 +245,7 @@ class ObjectDB(TypedObject):
#@player.setter #@player.setter
def __player_set(self, player): def __player_set(self, player):
"Setter. Allows for self.player = value" "Setter. Allows for self.player = value"
if isinstance(player, TypeClass): if inherits_from(player, TypeClass):
player = player.dbobj player = player.dbobj
_set_cache(self, "player", player) _set_cache(self, "player", player)
#@player.deleter #@player.deleter

View file

@ -4,7 +4,6 @@ The managers for the custom Player object and permissions.
import datetime import datetime
from functools import update_wrapper from functools import update_wrapper
from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager
from src.utils import logger from src.utils import logger
@ -181,6 +180,7 @@ class PlayerManager(TypedObjectManager):
return matches return matches
return self.filter(user__username__iexact=ostring) return self.filter(user__username__iexact=ostring)
def swap_character(self, player, new_character, delete_old_character=False): def swap_character(self, player, new_character, delete_old_character=False):
""" """
This disconnects a player from the current character (if any) and connects This disconnects a player from the current character (if any) and connects
@ -201,9 +201,10 @@ class PlayerManager(TypedObjectManager):
new_character.player = player new_character.player = player
except Exception: except Exception:
# recover old setup # recover old setup
old_character.player = player if old_character:
player.character = old_character old_character.player = player
player.character = old_character
return False return False
if delete_old_character: if old_character and delete_old_character:
old_character.delete() old_character.delete()
return True return True

View file

@ -50,9 +50,11 @@ from src.typeclasses.models import _get_cache, _set_cache, _del_cache
from src.server.sessionhandler import SESSIONS from src.server.sessionhandler import SESSIONS
from src.players import manager from src.players import manager
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
from src.utils import logger, utils from src.typeclasses.typeclass import TypeClass
from src.commands.cmdsethandler import CmdSetHandler from src.commands.cmdsethandler import CmdSetHandler
from src.commands import cmdhandler from src.commands import cmdhandler
from src.utils import logger, utils
from src.utils.utils import inherits_from
__all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB") __all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB")
@ -217,9 +219,11 @@ class PlayerDB(TypedObject):
"Getter. Allows for value = self.character" "Getter. Allows for value = self.character"
return _get_cache(self, "obj") return _get_cache(self, "obj")
#@character.setter #@character.setter
def character_set(self, value): def character_set(self, character):
"Setter. Allows for self.character = value" "Setter. Allows for self.character = value"
_set_cache(self, "obj", value) if inherits_from(character, TypeClass):
character = character.dbobj
_set_cache(self, "obj", character)
#@character.deleter #@character.deleter
def character_del(self): def character_del(self):
"Deleter. Allows for del self.character" "Deleter. Allows for del self.character"
@ -382,25 +386,17 @@ class PlayerDB(TypedObject):
break break
return cmdhandler.cmdhandler(self.typeclass, raw_string) return cmdhandler.cmdhandler(self.typeclass, raw_string)
def search(self, ostring, global_search=False, attribute_name=None, use_nicks=False, def search(self, ostring, return_character=False):
location=None, ignore_errors=False, player=False):
"""
A shell method mimicking the ObjectDB equivalent, for easy inclusion from
commands regardless of if the command is run by a Player or an Object.
""" """
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.
if self.character: return_character - will try to return the character the player controls instead of
# run the normal search the Player object itself. If no Character exists (since Player is
return self.character.search(ostring, global_search=global_search, attribute_name=attribute_name, OOC), None will be returned.
use_nicks=use_nicks, location=location, """
ignore_errors=ignore_errors, player=player) matches = self.__class__.objects.player_search(ostring)
if player: matches = _AT_SEARCH_RESULT(self, ostring, matches, global_search=True)
# seach for players if matches and return_character and hasattr(matches, "character"):
matches = self.__class__.objects.player_search(ostring) return matches.character
else:
# more limited player-only search. Still returns an Object.
ObjectDB = ContentType.objects.get(app_label="objects", model="objectdb").model_class()
matches = ObjectDB.objects.object_search(ostring, caller=self, global_search=global_search)
# deal with results
matches = _AT_SEARCH_RESULT(self, ostring, matches, global_search=global_search)
return matches return matches

View file

@ -129,16 +129,14 @@ class Player(TypeClass):
This return is not used at all by Evennia by default, but might be useful 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. for coders intending to implement some sort of nested command structure.
""" """
self.dbobj.execute_cmd(raw_string) return self.dbobj.execute_cmd(raw_string)
def search(self, ostring, global_search=False, attribute_name=None, use_nicks=False, def search(self, ostring, return_character=False):
location=None, ignore_errors=False, player=False):
""" """
This method mimicks object.search if self.character is set. Otherwise only This method mimicks object.search if self.character is set. Otherwise only
other Players can be searched with this method. other Players can be searched with this method.
""" """
self.dbobj.search(ostring, global_search=global_search, attribute_name=attribute_name, use_nicks=use_nicks, return self.dbobj.search(ostring, return_character=return_character)
location=location, ignore_errors=ignore_errors, player=player)
def is_typeclass(self, typeclass, exact=False): def is_typeclass(self, typeclass, exact=False):
""" """

View file

@ -15,7 +15,12 @@ from django.conf import settings
from src.utils.utils import make_iter, mod_import from src.utils.utils import make_iter, mod_import
from src.utils import logger from src.utils import logger
# custom functions
OOC_MODULE = mod_import(settings.OOB_FUNC_MODULE) OOC_MODULE = mod_import(settings.OOB_FUNC_MODULE)
OOC_FUNCS = dict((key.upper(), var) for key, var in OOC_MODULE if not key.startswith('__') and callable(var))
# MSDP commands supported by Evennia
MSDP_COMMANDS = ("LIST", "REPORT", "RESET", "SEND", "UNREPORT")
# MSDP-relevant telnet cmd/opt-codes # MSDP-relevant telnet cmd/opt-codes
MSDP = chr(69) MSDP = chr(69)
@ -99,46 +104,75 @@ class Msdp(object):
msdp_string = MSDP_VAR + cmdname + MSDP_VAL + data msdp_string = MSDP_VAR + cmdname + MSDP_VAL + data
return msdp_string return msdp_string
# MSDP commands supported by Evennia
MSDP_COMMANDS = {"LIST": "msdp_list",
"REPORT":"mspd_report",
"RESET":"mspd_reset",
"SEND":"mspd_send",
"UNREPORT":"mspd_unreport"}
def msdp_to_func(self, data): def msdp_to_func(self, data):
""" """
Handle a client's requested negotiation, converting Handle a client's requested negotiation, converting
it into a function mapping. it into a function mapping - either one of the MSDP
default functions (LIST, SEND etc) or a custom one
in OOC_FUNCS dictionary. command names are case-insensitive.
varname, var --> mapped to function varname(var)
arrayname, array --> mapped to function arrayname(*array)
tablename, table --> mapped to function tablename(**table)
Note: Combinations of args/kwargs to one function is not supported
in this implementation (it complicates the code for limited
gain - arrayname(*array) is usually as complex as anyone should
ever need to go anyway (I hope!).
This does not support receiving nested tables from the client
(and there is no real reason why it should).
""" """
tables = {} tables = {}
arrays = {} arrays = {}
variables = {} variables = {}
for table in regex_table.findall(data): for table in regex_table.findall(data):
tables[table[0]] = dict(regex_varval(table[1])) tables[table[0].upper()] = dict(regex_varval(table[1]))
for array in regex_array.findall(data): for array in regex_array.findall(data):
arrays[array[0]] = dict(regex_varval(array[1])) arrays[array[0].upper()] = dict(regex_varval(array[1]))
variables = dict(regex_varval(regex_array.sub("", regex_table.sub("", data)))) variables = dict((key.upper(), val) for key, val in regex_varval(regex_array.sub("", regex_table.sub("", data))))
ret = ""
if "LIST" in variables:
ret = self.msdp_cmd_list(variables["LIST"])
if "REPORT" in variables:
ret = self.msdp_cmd_report(*(variables["REPORT"],))
if "REPORT" in arrays:
ret = self.msdp_cmd_report(*arrays["REPORT"])
if "RESET" in variables:
ret = self.msdp_cmd_reset(*(variables["RESET"],))
if "RESET" in arrays:
ret = self.msdp_cmd_reset(*arrays["RESET"])
if "SEND" in variables:
ret = self.msdp_cmd_send((*variables["SEND"],))
if "SEND" in arrays:
ret = self.msdp_cmd_send(*arrays["SEND"])
ret = ""
# default MSDP functions
if "LIST" in variables:
ret += self.func_to_msdp("LIST", self.msdp_cmd_list(variables["LIST"]))
del variables["LIST"]
if "REPORT" in variables:
ret += self.func_to_msdp("REPORT", self.msdp_cmd_report(*(variables["REPORT"],)))
del variables["REPORT"]
if "REPORT" in arrays:
ret += self.func_to_msdp("REPORT", self.msdp_cmd_report(*arrays["REPORT"]))
del arrays["REPORT"]
if "RESET" in variables:
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*(variables["RESET"],)))
del variables["RESET"]
if "RESET" in arrays:
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*arrays["RESET"]))
del arrays["RESET"]
if "SEND" in variables:
ret += self.func_to_msdp("SEND", self.msdp_cmd_send((*variables["SEND"],)))
del variables["SEND"]
if "SEND" in arrays:
ret += self.func_to_msdp("SEND",self.msdp_cmd_send(*arrays["SEND"]))
del arrays["SEND"]
# if there are anything left we look for a custom function
for varname, var in variables.items():
# a simple function + argument
ooc_func = OOC_FUNCS.get(varname.upper())
if ooc_func:
ret += self.func_to_msdp(varname, ooc_func(var))
for arrayname, array in arrays.items():
# we assume the array are multiple arguments to the function
ooc_func = OOC_FUNCS.get(arrayname.upper())
if ooc_func:
ret += self.func_to_msdp(arrayname, ooc_func(*array))
for tablename, table in tables.items():
# we assume tables are keyword arguments to the function
ooc_func = OOC_FUNCS.get(arrayname.upper())
if ooc_func:
ret += self.func_to_msdp(tablename, ooc_func(**table))
return ret
# MSDP Commands # MSDP Commands
# Some given MSDP (varname, value) pairs can also be treated as command + argument. # Some given MSDP (varname, value) pairs can also be treated as command + argument.
@ -150,10 +184,9 @@ class Msdp(object):
The List command allows for retrieving various info about the server/client The List command allows for retrieving various info about the server/client
""" """
if arg == 'COMMANDS': if arg == 'COMMANDS':
return self.func_to_msdp(arg, self.MSDP_COMMANDS.keys()) return self.func_to_msdp(arg, MSDP_COMMANDS))
elif arg == 'LISTS': elif arg == 'LISTS':
return self.func_to_msdp(arg, ("COMMANDS", "LISTS", return self.func_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES",
"CONFIGURABLE_VARIABLES",
"REPORTED_VARIABLES", "SENDABLE_VARIABLES")) "REPORTED_VARIABLES", "SENDABLE_VARIABLES"))
elif arg == 'CONFIGURABLE_VARIABLES': elif arg == 'CONFIGURABLE_VARIABLES':
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID")) return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
@ -210,7 +243,6 @@ class Msdp(object):
return ret return ret
# MSDP_MAP is a standard suggestions for making it easy to create generic guis. # MSDP_MAP is a standard suggestions for making it easy to create generic guis.
# this maps MSDP command names to Evennia commands found in OOB_FUNC_MODULE. It # this maps MSDP command names to Evennia commands found in OOB_FUNC_MODULE. It
# is up to these commands to return data on proper form. # is up to these commands to return data on proper form.

View file

@ -28,6 +28,13 @@ TELNET_ENABLED = True
TELNET_PORTS = [4000] TELNET_PORTS = [4000]
# Interface addresses to listen to. If 0.0.0.0, listen to all. # Interface addresses to listen to. If 0.0.0.0, listen to all.
TELNET_INTERFACES = ['0.0.0.0'] TELNET_INTERFACES = ['0.0.0.0']
# OOB (out-of-band) telnet communication allows Evennia to communicate
# special commands and data with enabled Telnet clients. This is used
# to create custom client interfaces over a telnet connection. To make
# full use of OOB, you need to prepare functions to handle the data
# server-side (see OOB_FUNC_MODULE). TELNET_ENABLED is required for this
# to work.
TELNET_OOB_ENABLED = False # OBS - currently not fully implemented - do not use!
# Start the evennia django+twisted webserver so you can # Start the evennia django+twisted webserver so you can
# browse the evennia website and the admin interface # browse the evennia website and the admin interface
# (Obs - further web configuration can be found below # (Obs - further web configuration can be found below
@ -108,6 +115,7 @@ AMP_PORT = 5000
# memory. So every now and then Evennia checks the size of this cache and resets # memory. So every now and then Evennia checks the size of this cache and resets
# it if it's too big. This variable sets the maximum size (in MB). # it if it's too big. This variable sets the maximum size (in MB).
ATTRIBUTE_CACHE_MAXSIZE = 100 ATTRIBUTE_CACHE_MAXSIZE = 100
# OOB (Out-of-band
###################################################################### ######################################################################
# Evennia Database config # Evennia Database config
@ -146,7 +154,8 @@ DATABASE_PORT = ''
# Evennia pluggable modules # Evennia pluggable modules
###################################################################### ######################################################################
# An alternate command parser module to use # The command parser module to use. See the default module for which
# functions it must implement.
COMMAND_PARSER = "src.commands.cmdparser.cmdparser" COMMAND_PARSER = "src.commands.cmdparser.cmdparser"
# The handler that outputs errors when searching # The handler that outputs errors when searching
# objects using object.search(). # objects using object.search().
@ -159,29 +168,34 @@ SEARCH_AT_MULTIMATCH_INPUT = "src.commands.cmdparser.at_multimatch_input"
# This module should contain one or more variables # This module should contain one or more variables
# with strings defining the look of the screen. # with strings defining the look of the screen.
CONNECTION_SCREEN_MODULE = "src.commands.connection_screen" CONNECTION_SCREEN_MODULE = "src.commands.connection_screen"
# An option al module that, if existing, must hold a function # An optional module that, if existing, must hold a function
# named at_initial_setup(). This hook method can be used to customize # named at_initial_setup(). This hook method can be used to customize
# the server's initial setup sequence (the very first startup of the system). # the server's initial setup sequence (the very first startup of the system).
# The check will fail quietly if module doesn't exist or fails to load. # The check will fail quietly if module doesn't exist or fails to load.
AT_INITIAL_SETUP_HOOK_MODULE = "" AT_INITIAL_SETUP_HOOK_MODULE = ""
# Module holding at_server_start(), at_server_reload() and # Module containing your custom at_server_start(), at_server_reload() and
# at_server_stop() methods. These methods will be called every time # at_server_stop() methods. These methods will be called every time
# the server starts, reloads and resets/stops. # the server starts, reloads and resets/stops respectively.
AT_SERVER_STARTSTOP_MODULE = "" AT_SERVER_STARTSTOP_MODULE = ""
# Module holding server-side functions for out-of-band protocols to call. # Module holding MSSP meta data. This is used by MUD-crawlers to determine
OOB_FUNC_MODULE = "" # what type of game you are running, how many players you have etc.
# Module holding MSSP meta data
MSSP_META_MODULE = "" MSSP_META_MODULE = ""
# Module holding server-side custom functions for out-of-band protocols to call.
# Note that OOB_ENABLED must be True for this to be used.
OOB_FUNC_MODULE = "" # Not yet available in Evennia - do not use!
###################################################################### ######################################################################
# Default command sets # Default command sets
###################################################################### ######################################################################
# Note that with the exception of the unloggedin set (which is not # Note that with the exception of the unloggedin set (which is not
# stored anywhere), changing these paths will only affect NEW created # stored anywhere in the databse), changing these paths will only affect NEW created
# characters, not those already in play. So if you plan to change # characters/objects, not those already in play. So if you plan to change
# this, it's recommended you do it on a pristine setup only. To # this, it's recommended you do it before having created a lot of objects
# dynamically add new commands to a running server, extend/overload # (or simply reset the database after the change for simplicity). Remember
# these existing sets instead. # that you should never edit things in src/. Instead copy out the examples
# in game/gamesrc/commands/examples up one level and re-point these settings
# to point to these copies instead - these you can then change as you please
# (or copy/paste from the default modules in src/ if you prefer).
# Command set used before player has logged in # Command set used before player has logged in
CMDSET_UNLOGGEDIN = "src.commands.default.cmdset_unloggedin.UnloggedinCmdSet" CMDSET_UNLOGGEDIN = "src.commands.default.cmdset_unloggedin.UnloggedinCmdSet"