Run Migrate. Implemented a full separation between Player and Character - Players (OOC entities) can now also hold cmdsets and execute commands. This means that "disconnecting" from a Character becomes possible, putting the Player in an "OOC" state outside the game. This overall makes the game much more stable since there used to be issues if the character was destroyed. Having an OOC set also avoids the previous problem of @puppeting into an object that didn't have any cmdset of its own - you couldn't get back out! A new default OOC-Cmdset handles commands available to a player while OOC. Commands in this set are applied with a low priority, allowing "IC" mode to give precedence if desired.

This change meant several changes to the lock and permission functionality, since it becomes important if permissions are assigned on the Player or on their Character (lock functions pperm() and pid() etc check on Player rather than Character). This has the boon of allowing Admins to switch and play/test the game as a "Low access" character as they like.

Plenty of bug fixes and adjustments. Migrations should make sure to move over all data properly.
This commit is contained in:
Griatch 2011-04-23 11:54:08 +00:00
parent ce2a8e9ffe
commit 28fe2ad3f4
37 changed files with 1622 additions and 555 deletions

View file

@ -19,7 +19,7 @@ new cmdset class.
""" """
from src.commands.cmdset import CmdSet from src.commands.cmdset import CmdSet
from src.commands.default import cmdset_default, cmdset_unloggedin from src.commands.default import cmdset_default, cmdset_unloggedin, cmdset_ooc
from game.gamesrc.commands.basecommand import Command from game.gamesrc.commands.basecommand import Command
@ -70,6 +70,24 @@ class UnloggedinCmdSet(cmdset_unloggedin.UnloggedinCmdSet):
# #
class OOCCmdSet(cmdset_ooc.OOCCmdSet):
"""
This is set is available to the player when they have no
character connected to them (i.e. they are out-of-character, ooc).
"""
key = "OOC"
def at_cmdset_creation(self):
"""
Populates the cmdset
"""
super(OOCCmdSet, self).at_cmdset_creation()
#
# any commands you add below will overload the default ones.
#
class BaseCmdSet(CmdSet): class BaseCmdSet(CmdSet):
""" """
Implements an empty, example cmdset. Implements an empty, example cmdset.

View file

@ -44,12 +44,6 @@ class Object(BaseObject):
Hooks (these are class methods, so their arguments should also start with self): Hooks (these are class methods, so their arguments should also start with self):
basetype_setup() - only called once, when object is first created, before
at_object_creation(). Sets up semi-engine-specific details such as
the default lock policy, what defines a Room, Exit etc.
Usually not modified unless you want to overload the default
security restriction policies.
at_object_creation() - only called once, when object is first created. at_object_creation() - only called once, when object is first created.
Almost all object customizations go here. Almost all object customizations go here.
at_first_login() - only called once, the very first time user logs in. at_first_login() - only called once, the very first time user logs in.

View file

@ -54,14 +54,11 @@ from django.conf import settings
from src.comms.channelhandler import CHANNELHANDLER from src.comms.channelhandler import CHANNELHANDLER
from src.commands.cmdsethandler import import_cmdset from src.commands.cmdsethandler import import_cmdset
from src.objects.exithandler import EXITHANDLER from src.objects.exithandler import EXITHANDLER
from src.utils import logger from src.utils import logger, utils
#This switches the command parser to a user-defined one. #This switches the command parser to a user-defined one.
# You have to restart the server for this to take effect. # You have to restart the server for this to take effect.
try: COMMAND_PARSER = utils.mod_import(*settings.COMMAND_PARSER.rsplit('.', 1))
CMDPARSER = __import__(settings.ALTERNATE_PARSER, fromlist=[True]).cmdparser
except Exception:
from src.commands.cmdparser import cmdparser as CMDPARSER
# There are a few system-hardcoded command names. These # There are a few system-hardcoded command names. These
# allow for custom behaviour when the command handler hits # allow for custom behaviour when the command handler hits
@ -101,13 +98,21 @@ def get_and_merge_cmdsets(caller):
exit_cmdset = None exit_cmdset = None
local_objects_cmdsets = [None] local_objects_cmdsets = [None]
# Player object's commandsets
try:
player_cmdset = caller.player.cmdset.current
except AttributeError:
player_cmdset = None
if not caller_cmdset.no_channels: if not caller_cmdset.no_channels:
# Make cmdsets out of all valid channels # Make cmdsets out of all valid channels
channel_cmdset = CHANNELHANDLER.get_cmdset(caller) channel_cmdset = CHANNELHANDLER.get_cmdset(caller)
if not caller_cmdset.no_exits: if not caller_cmdset.no_exits:
# Make cmdsets out of all valid exits in the room # Make cmdsets out of all valid exits in the room
exit_cmdset = EXITHANDLER.get_cmdset(caller) exit_cmdset = EXITHANDLER.get_cmdset(caller)
location = caller.location location = None
if hasattr(caller, "location"):
location = caller.location
if location and not caller_cmdset.no_objs: if location and not caller_cmdset.no_objs:
# Gather all cmdsets stored on objects in the room and # Gather all cmdsets stored on objects in the room and
# also in the caller's inventory # also in the caller's inventory
@ -138,6 +143,12 @@ def get_and_merge_cmdsets(caller):
cmdset = channel_cmdset + cmdset cmdset = channel_cmdset + cmdset
except TypeError: except TypeError:
pass pass
# finally merge on the player cmdset. This should have a low priority
try:
cmdset = player_cmdset + cmdset
except TypeError:
pass
return cmdset return cmdset
def match_command(cmd_candidates, cmdset, logged_caller=None): def match_command(cmd_candidates, cmdset, logged_caller=None):
@ -295,7 +306,7 @@ def cmdhandler(caller, raw_string, unloggedin=False, testing=False):
raise ExecSystemCommand(syscmd, sysarg) raise ExecSystemCommand(syscmd, sysarg)
# Parse the input string into command candidates # Parse the input string into command candidates
cmd_candidates = CMDPARSER(raw_string) cmd_candidates = COMMAND_PARSER(raw_string)
#string ="Command candidates" #string ="Command candidates"
#for cand in cmd_candidates: #for cand in cmd_candidates:

View file

@ -174,3 +174,128 @@ def cmdparser(raw_string):
wordlist = raw_string.split(" ") wordlist = raw_string.split(" ")
candidates.extend(produce_candidates(nr_candidates, wordlist)) candidates.extend(produce_candidates(nr_candidates, wordlist))
return candidates return candidates
#------------------------------------------------------------
# Search parsers and support methods
#------------------------------------------------------------
#
# Default functions for formatting and processing searches.
#
# This is in its own module due to them being possible to
# replace from the settings file by setting the variables
#
# SEARCH_AT_RESULTERROR_HANDLER
# SEARCH_MULTIMATCH_PARSER
#
# The the replacing modules must have the same inputs and outputs as
# those in this module.
#
def at_search_result(msg_obj, ostring, results, global_search=False):
"""
Called by search methods after a result of any type has been found.
Takes a search result (a list) and
formats eventual errors.
msg_obj - object to receive feedback.
ostring - original search string
results - list of found matches (0, 1 or more)
global_search - if this was a global_search or not
(if it is, there might be an idea of supplying
dbrefs instead of only numbers)
Multiple matches are returned to the searching object
as
1-object
2-object
3-object
etc
"""
string = ""
if not results:
# no results.
string = "Could not find '%s'." % ostring
results = None
elif len(results) > 1:
# we have more than one match. We will display a
# list of the form 1-objname, 2-objname etc.
# check if the msg_object may se dbrefs
show_dbref = global_search
string += "More than one match for '%s'" % ostring
string += " (please narrow target):"
for num, result in enumerate(results):
invtext = ""
dbreftext = ""
if hasattr(result, "location") and result.location == msg_obj:
invtext = " (carried)"
if show_dbref:
dbreftext = "(#%i)" % result.id
string += "\n %i-%s%s%s" % (num+1, result.name,
dbreftext, invtext)
results = None
else:
# we have exactly one match.
results = results[0]
if string:
msg_obj.msg(string.strip())
return results
def at_multimatch_input(ostring):
"""
Parse number-identifiers.
This parser will be called by the engine when a user supplies
a search term. The search term must be analyzed to determine
if the user wants to differentiate between multiple matches
(usually found during a previous search).
This method should separate out any identifiers from the search
string used to differentiate between same-named objects. The
result should be a tuple (index, search_string) where the index
gives which match among multiple matches should be used (1 being
the lowest number, rather than 0 as in Python).
This parser version will identify search strings on the following
forms
2-object
This will be parsed to (2, "object") and, if applicable, will tell
the engine to pick the second from a list of same-named matches of
objects called "object".
Ex for use in a game session:
> look
You see: ball, ball, ball and ball.
> get ball
There where multiple matches for ball:
1-ball
2-ball
3-ball
4-ball
> get 3-ball
You get the ball.
"""
if not isinstance(ostring, basestring):
return (None, ostring)
if not '-' in ostring:
return (None, ostring)
try:
index = ostring.find('-')
number = int(ostring[:index])-1
return (number, ostring[index+1:])
except ValueError:
#not a number; this is not an identifier.
return (None, ostring)
except IndexError:
return (None, ostring)

View file

@ -15,6 +15,9 @@ together to create interesting in-game effects.
""" """
import copy import copy
from src.utils.utils import inherits_from, is_iter
RECURSIVE_PROTECTION = False
class CmdSetMeta(type): class CmdSetMeta(type):
""" """
@ -204,16 +207,40 @@ class CmdSet(object):
def add(self, cmd): def add(self, cmd):
""" """
Add a command to this cmdset. Add a command, a list of commands or a cmdset to this cmdset.
Note that if cmd already exists in set, Note that if cmd already exists in set,
it will replace the old one (no priority checking etc it will replace the old one (no priority checking etc
at this point; this is often used to overload at this point; this is often used to overload
default commands). default commands).
"""
cmd = instantiate(cmd)
if cmd: If cmd is another cmdset class or -instance, the commands
of that command set is added to this one, as if they were part
of the original cmdset definition. No merging or priority checks
are made, rather later added commands will simply replace
existing ones to make a unique set.
"""
if inherits_from(cmd, "src.commands.cmdset.CmdSet"):
# this is a command set so merge all commands in that set
# to this one. We are not protecting against recursive
# loops (adding a cmdset that itself adds this cmdset here
# since such an error will be very visible and lead to a
# traceback at startup anyway.
try:
cmd = instantiate(cmd)
except RuntimeError, e:
string = "Adding cmdset %s to %s lead to an infinite loop. When adding a cmdset to another, "
string += "make sure they are not themself cyclically added to the new cmdset somewhere in the chain."
raise RuntimeError(string % (cmd, self.__class__))
cmds = cmd.commands
elif not is_iter(cmd):
cmds = [instantiate(cmd)]
else:
cmds = instantiate(cmd)
for cmd in cmds:
# add all commands
if not hasattr(cmd, 'obj'): if not hasattr(cmd, 'obj'):
cmd.obj = self.cmdsetobj cmd.obj = self.cmdsetobj
try: try:

View file

@ -179,7 +179,7 @@ class CmdSetHandler(object):
string += "\n" string += "\n"
merged = True merged = True
# Display the currently active cmdset # Display the currently active cmdset, limited by self.obj's permissions
mergetype = self.mergetype_stack[-1] mergetype = self.mergetype_stack[-1]
if mergetype != self.current.mergetype: if mergetype != self.current.mergetype:
merged_on = self.cmdset_stack[-2].key merged_on = self.cmdset_stack[-2].key
@ -187,7 +187,8 @@ class CmdSetHandler(object):
if merged: if merged:
string += " <Merged (%s)>: %s" % (mergetype, self.current) string += " <Merged (%s)>: %s" % (mergetype, self.current)
else: else:
string += " <%s (%s)>: %s" % (self.current.key, mergetype, self.current) string += " <%s (%s)>: %s" % (self.current.key, mergetype,
", ".join(cmd.key for cmd in self.current if cmd.access(self.obj, "cmd")))
return string.strip() return string.strip()
def update(self, init_mode=False): def update(self, init_mode=False):

View file

@ -58,7 +58,7 @@ class CmdBoot(MuxCommand):
break break
else: else:
# Boot by player object # Boot by player object
pobj = caller.search("*%s" % args, global_search=True) pobj = caller.search("*%s" % args, global_search=True, player=True)
if not pobj: if not pobj:
return return
if pobj.character.has_player: if pobj.character.has_player:
@ -118,6 +118,9 @@ class CmdDelPlayer(MuxCommand):
caller = self.caller caller = self.caller
args = self.args args = self.args
if hasattr(caller, 'player'):
caller = caller.player
if not args: if not args:
caller.msg("Usage: @delplayer[/delobj] <player/user name or #id> [: reason]") caller.msg("Usage: @delplayer[/delobj] <player/user name or #id> [: reason]")
return return
@ -128,7 +131,7 @@ class CmdDelPlayer(MuxCommand):
# We use player_search since we want to be sure to find also players # We use player_search since we want to be sure to find also players
# that lack characters. # that lack characters.
players = caller.search("*%s" % args) players = caller.search("*%s" % args, player=True)
if not players: if not players:
try: try:
players = PlayerDB.objects.filter(id=args) players = PlayerDB.objects.filter(id=args)
@ -304,7 +307,7 @@ class CmdNewPassword(MuxCommand):
return return
# the player search also matches 'me' etc. # the player search also matches 'me' etc.
player = caller.search("*%s" % self.lhs, global_search=True) player = caller.search("*%s" % self.lhs, global_search=True, player=True)
if not player: if not player:
return return
player.user.set_password(self.rhs) player.user.set_password(self.rhs)
@ -320,12 +323,14 @@ class CmdPerm(MuxCommand):
Usage: Usage:
@perm[/switch] <object> [= <permission>[,<permission>,...]] @perm[/switch] <object> [= <permission>[,<permission>,...]]
@perm[/switch] *<player> [= <permission>[,<permission>,...]]
Switches: Switches:
del : delete the given permission from <object>. del : delete the given permission from <object> or <player>.
player : set permission on a player (same as adding * to name)
This command sets/clears individual permission strings on an object. This command sets/clears individual permission strings on an object
If no permission is given, list all permissions on <object> or player. If no permission is given, list all permissions on <object>.
""" """
key = "@perm" key = "@perm"
aliases = "@setperm" aliases = "@setperm"
@ -340,17 +345,18 @@ class CmdPerm(MuxCommand):
lhs, rhs = self.lhs, self.rhs lhs, rhs = self.lhs, self.rhs
if not self.args: if not self.args:
string = "Usage: @perm[/switch] object [ = permission, permission, ...]\n" string = "Usage: @perm[/switch] object [ = permission, permission, ...]"
caller.msg(string) caller.msg(string)
return return
playermode = 'player' in self.switches or lhs.startswith('*')
# locate the object # locate the object
obj = caller.search(lhs, global_search=True) obj = caller.search(lhs, global_search=True, player=playermode)
if not obj: if not obj:
return return
if not rhs: if not rhs:
if not obj.access(caller, 'examine'): if not obj.access(caller, 'examine'):
caller.msg("You are not allowed to examine this object.") caller.msg("You are not allowed to examine this object.")
return return
@ -397,11 +403,9 @@ class CmdPerm(MuxCommand):
for perm in self.rhslist: for perm in self.rhslist:
perm = perm.lower()
# don't allow to set a permission higher in the hierarchy than the one the # don't allow to set a permission higher in the hierarchy than the one the
# caller has (to prevent self-escalation) # caller has (to prevent self-escalation)
if perm in PERMISSION_HIERARCHY and not obj.locks.check_lockstring(caller, "dummy:perm(%s)" % perm): if perm.lower() in PERMISSION_HIERARCHY and not obj.locks.check_lockstring(caller, "dummy:perm(%s)" % perm):
caller.msg("You cannot assign a permission higher than the one you have yourself.") caller.msg("You cannot assign a permission higher than the one you have yourself.")
return return
@ -416,45 +420,6 @@ class CmdPerm(MuxCommand):
if tstring: if tstring:
obj.msg(tstring.strip()) obj.msg(tstring.strip())
class CmdPuppet(MuxCommand):
"""
Switch control to an object
Usage:
@puppet <character object>
This will attempt to "become" a different character. Note that this command does not check so that
the target object has the appropriate cmdset. You cannot puppet a character that is already "taken".
"""
key = "@puppet"
locks = "cmd:perm(puppet) or perm(Builders)"
help_category = "Admin"
def func(self):
"""
Simple puppet method (does not check permissions)
"""
caller = self.caller
if not self.args:
caller.msg("Usage: @puppet <character>")
return
player = caller.player
new_character = caller.search(self.args)
if not new_character:
return
if not utils.inherits_from(new_character, settings.BASE_CHARACTER_TYPECLASS):
caller.msg("%s is not a Character." % self.args)
return
if new_character.player:
caller.msg("This character is already under the control of a player.")
if player.swap_character(new_character):
new_character.msg("You now control %s." % new_character.name)
else:
caller.msg("You cannot control %s." % new_character.name)
class CmdWall(MuxCommand): class CmdWall(MuxCommand):
""" """
@wall @wall

View file

@ -6,6 +6,7 @@ Building and world design commands
from django.conf import settings from django.conf import settings
from src.objects.models import ObjectDB, ObjAttribute from src.objects.models import ObjectDB, ObjAttribute
from src.players.models import PlayerAttribute
from src.utils import create, utils, debug from src.utils import create, utils, debug
from src.commands.default.muxcommand import MuxCommand from src.commands.default.muxcommand import MuxCommand
@ -122,7 +123,7 @@ class CmdSetObjAlias(MuxCommand):
old_aliases = obj.aliases old_aliases = obj.aliases
if old_aliases: if old_aliases:
caller.msg("Cleared aliases from %s: %s" % (obj.key, ", ".join(old_aliases))) caller.msg("Cleared aliases from %s: %s" % (obj.key, ", ".join(old_aliases)))
del obj.dbobj.aliases # TODO: del does not understand indirect typeclass reference! del obj.dbobj.aliases
else: else:
caller.msg("No aliases to clear.") caller.msg("No aliases to clear.")
return return
@ -1389,16 +1390,25 @@ class CmdExamine(ObjManipCommand):
Usage: Usage:
examine [<object>[/attrname]] examine [<object>[/attrname]]
examine [*<player>[/attrname]]
Switch:
player - examine a Player (same as adding *)
The examine command shows detailed game info about an The examine command shows detailed game info about an
object and optionally a specific attribute on it. object and optionally a specific attribute on it.
If object is not specified, the current location is examined. If object is not specified, the current location is examined.
Append a * before the search string to examine a player.
""" """
key = "@examine" key = "@examine"
aliases = ["@ex","ex", "exam", "examine"] aliases = ["@ex","ex", "exam", "examine"]
locks = "cmd:perm(examine) or perm(Builders)" locks = "cmd:perm(examine) or perm(Builders)"
help_category = "Building" help_category = "Building"
player_mode = False
def format_attributes(self, obj, attrname=None, crop=True): def format_attributes(self, obj, attrname=None, crop=True):
""" """
Helper function that returns info about attributes and/or Helper function that returns info about attributes and/or
@ -1411,7 +1421,10 @@ class CmdExamine(ObjManipCommand):
except Exception: except Exception:
ndb_attr = None ndb_attr = None
else: else:
db_attr = [(attr.key, attr.value) for attr in ObjAttribute.objects.filter(db_obj=obj)] if player_mode:
db_attr = [(attr.key, attr.value) for attr in PlayerAttribute.objects.filter(db_obj=obj)]
else:
db_attr = [(attr.key, attr.value) for attr in ObjAttribute.objects.filter(db_obj=obj)]
try: try:
ndb_attr = [(aname, avalue) for aname, avalue in obj.ndb.__dict__.items()] ndb_attr = [(aname, avalue) for aname, avalue in obj.ndb.__dict__.items()]
except Exception: except Exception:
@ -1439,13 +1452,13 @@ class CmdExamine(ObjManipCommand):
returns a string. returns a string.
""" """
if obj.has_player: if hasattr(obj, "has_player") and obj.has_player:
string = "\n{wName/key{n: {c%s{n (%s)" % (obj.name, obj.dbref) string = "\n{wName/key{n: {c%s{n (%s)" % (obj.name, obj.dbref)
else: else:
string = "\n{wName/key{n: {C%s{n (%s)" % (obj.name, obj.dbref) string = "\n{wName/key{n: {C%s{n (%s)" % (obj.name, obj.dbref)
if obj.aliases: if hasattr(obj, "aliases") and obj.aliases:
string += "\n{wAliases{n: %s" % (", ".join(obj.aliases)) string += "\n{wAliases{n: %s" % (", ".join(obj.aliases))
if obj.has_player: if hasattr(obj, "has_player") and obj.has_player:
string += "\n{wPlayer{n: {c%s{n" % obj.player.name string += "\n{wPlayer{n: {c%s{n" % obj.player.name
perms = obj.player.permissions perms = obj.player.permissions
if obj.player.is_superuser: if obj.player.is_superuser:
@ -1453,10 +1466,11 @@ class CmdExamine(ObjManipCommand):
elif not perms: elif not perms:
perms = ["<None>"] perms = ["<None>"]
string += "\n{wPlayer Perms{n: %s" % (", ".join(perms)) string += "\n{wPlayer Perms{n: %s" % (", ".join(perms))
string += "\n{wTypeclass{n: %s (%s)" % (obj.typeclass, obj.typeclass_path) string += "\n{wTypeclass{n: %s (%s)" % (obj.typeclass, obj.typeclass_path)
string += "\n{wLocation{n: %s" % obj.location
if obj.destination: if hasattr(obj, "location"):
string += "\n{wLocation{n: %s" % obj.location
if hasattr(obj, "destination") and obj.destination:
string += "\n{wDestination{n: %s" % obj.destination string += "\n{wDestination{n: %s" % obj.destination
perms = obj.permissions perms = obj.permissions
if perms: if perms:
@ -1467,8 +1481,8 @@ class CmdExamine(ObjManipCommand):
if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "Empty"): if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "Empty"):
cmdsetstr = "\n".join([utils.fill(cmdset, indent=2) for cmdset in str(obj.cmdset).split("\n")]) cmdsetstr = "\n".join([utils.fill(cmdset, indent=2) for cmdset in str(obj.cmdset).split("\n")])
string += "\n{wCurrent Cmdset (before permission checks){n:\n %s" % cmdsetstr string += "\n{wCurrent Cmdset (including permission checks){n:\n %s" % cmdsetstr
if obj.scripts.all(): if hasattr(obj, "scripts") and hasattr(obj.scripts, "all") and obj.scripts.all():
string += "\n{wScripts{n:\n %s" % obj.scripts string += "\n{wScripts{n:\n %s" % obj.scripts
# add the attributes # add the attributes
string += self.format_attributes(obj) string += self.format_attributes(obj)
@ -1476,21 +1490,21 @@ class CmdExamine(ObjManipCommand):
exits = [] exits = []
pobjs = [] pobjs = []
things = [] things = []
for content in obj.contents: if hasattr(obj, "contents"):
if content.destination: for content in obj.contents:
# an exit if content.destination:
exits.append(content) exits.append(content)
elif content.player: elif content.player:
pobjs.append(content) pobjs.append(content)
else: else:
things.append(content) things.append(content)
if exits: if exits:
string += "\n{wExits{n: " + ", ".join([exit.name for exit in exits]) string += "\n{wExits{n: " + ", ".join([exit.name for exit in exits])
if pobjs: if pobjs:
string += "\n{wCharacters{n: " + ", ".join(["{c%s{n" % pobj.name for pobj in pobjs]) string += "\n{wCharacters{n: " + ", ".join(["{c%s{n" % pobj.name for pobj in pobjs])
if things: if things:
string += "\n{wContents{n: " + ", ".join([cont.name for cont in obj.contents string += "\n{wContents{n: " + ", ".join([cont.name for cont in obj.contents
if cont not in exits and cont not in pobjs]) if cont not in exits and cont not in pobjs])
#output info #output info
return "-"*78 + '\n' + string.strip() + "\n" + '-'*78 return "-"*78 + '\n' + string.strip() + "\n" + '-'*78
@ -1506,36 +1520,35 @@ class CmdExamine(ObjManipCommand):
caller.execute_cmd('look %s' % obj.name) caller.execute_cmd('look %s' % obj.name)
return return
string = self.format_output(obj) string = self.format_output(obj)
else:
# we have given a specific target object
string = ""
for objdef in self.lhs_objattr:
obj_name = objdef['name']
obj_attrs = objdef['attrs']
obj = caller.search(obj_name)
if not obj:
continue
if not obj.access(caller, 'examine'):
#If we don't have special info access, just look at the object instead.
caller.execute_cmd('look %s' % obj_name)
continue
if obj_attrs:
for attrname in obj_attrs:
# we are only interested in specific attributes
string += self.format_attributes(obj, attrname, crop=False)
else:
string += self.format_output(obj)
string = string.strip()
# Send it all
if string:
caller.msg(string.strip()) caller.msg(string.strip())
return
# we have given a specific target object
string = ""
for objdef in self.lhs_objattr:
obj_name = objdef['name']
obj_attrs = objdef['attrs']
global player_mode
player_mode = "player" in self.switches or obj_name.startswith('*')
obj = caller.search(obj_name, player=player_mode)
if not obj:
continue
if not obj.access(caller, 'examine'):
#If we don't have special info access, just look at the object instead.
caller.execute_cmd('look %s' % obj_name)
continue
if obj_attrs:
for attrname in obj_attrs:
# we are only interested in specific attributes
string += self.format_attributes(obj, attrname, crop=False)
else:
string += self.format_output(obj)
caller.msg(string.strip())
class CmdFind(MuxCommand): class CmdFind(MuxCommand):

View file

@ -18,17 +18,14 @@ class DefaultCmdSet(CmdSet):
# The general commands # The general commands
self.add(general.CmdLook()) self.add(general.CmdLook())
self.add(general.CmdHome()) self.add(general.CmdHome())
self.add(general.CmdPassword()) self.add(general.CmdWho())
self.add(general.CmdInventory()) self.add(general.CmdInventory())
self.add(general.CmdQuit())
self.add(general.CmdPose()) self.add(general.CmdPose())
self.add(general.CmdNick()) self.add(general.CmdNick())
self.add(general.CmdGet()) self.add(general.CmdGet())
self.add(general.CmdDrop()) self.add(general.CmdDrop())
self.add(general.CmdWho())
self.add(general.CmdSay()) self.add(general.CmdSay())
self.add(general.CmdAccess()) self.add(general.CmdAccess())
self.add(general.CmdEncoding())
# The help system # The help system
self.add(help.CmdHelp()) self.add(help.CmdHelp())
@ -52,7 +49,6 @@ class DefaultCmdSet(CmdSet):
self.add(admin.CmdEmit()) self.add(admin.CmdEmit())
self.add(admin.CmdNewPassword()) self.add(admin.CmdNewPassword())
self.add(admin.CmdPerm()) self.add(admin.CmdPerm())
self.add(admin.CmdPuppet())
self.add(admin.CmdWall()) self.add(admin.CmdWall())
# Building and world manipulation # Building and world manipulation
@ -80,24 +76,6 @@ class DefaultCmdSet(CmdSet):
self.add(building.CmdScript()) self.add(building.CmdScript())
self.add(building.CmdHome()) self.add(building.CmdHome())
# Comm commands
self.add(comms.CmdAddCom())
self.add(comms.CmdDelCom())
self.add(comms.CmdAllCom())
self.add(comms.CmdChannels())
self.add(comms.CmdCdestroy())
self.add(comms.CmdChannelCreate())
self.add(comms.CmdCset())
self.add(comms.CmdCBoot())
self.add(comms.CmdCemit())
self.add(comms.CmdCWho())
self.add(comms.CmdCdesc())
self.add(comms.CmdPage())
self.add(comms.CmdIRC2Chan())
self.add(comms.CmdIMC2Chan())
self.add(comms.CmdIMCInfo())
self.add(comms.CmdIMCTell())
# Batchprocessor commands # Batchprocessor commands
self.add(batchprocess.CmdBatchCommands()) self.add(batchprocess.CmdBatchCommands())
self.add(batchprocess.CmdBatchCode()) self.add(batchprocess.CmdBatchCode())

View file

@ -0,0 +1,55 @@
"""
This is the cmdset for OutOfCharacter (OOC) commands.
These are stored on the Player object and should
thus be able to handle getting a Player object
as caller rather than a Character.
"""
from src.commands.cmdset import CmdSet
from src.commands.default import help, comms, general, admin
class OOCCmdSet(CmdSet):
"""
Implements the player command set.
"""
key = "DefaultOOC"
priority = -5
def at_cmdset_creation(self):
"Populates the cmdset"
# general commands
self.add(general.CmdOOCLook())
self.add(general.CmdIC())
self.add(general.CmdOOC())
self.add(general.CmdEncoding())
self.add(general.CmdQuit())
self.add(general.CmdPassword())
# help command
self.add(help.CmdHelp())
# admin commands
self.add(admin.CmdBoot())
self.add(admin.CmdDelPlayer())
self.add(admin.CmdNewPassword())
# Comm commands
self.add(comms.CmdAddCom())
self.add(comms.CmdDelCom())
self.add(comms.CmdAllCom())
self.add(comms.CmdChannels())
self.add(comms.CmdCdestroy())
self.add(comms.CmdChannelCreate())
self.add(comms.CmdCset())
self.add(comms.CmdCBoot())
self.add(comms.CmdCemit())
self.add(comms.CmdCWho())
self.add(comms.CmdCdesc())
self.add(comms.CmdPage())
self.add(comms.CmdIRC2Chan())
self.add(comms.CmdIMC2Chan())
self.add(comms.CmdIMCInfo())
self.add(comms.CmdIMCTell())

View file

@ -1,5 +1,11 @@
""" """
Comsys command module. Comsystem command module.
Comm commands are OOC commands and intended to be made available to
the Player at all times (they go into the PlayerCmdSet). So we
make sure to homogenize self.caller to always be the player object
for easy handling.
""" """
from django.conf import settings from django.conf import settings
from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection
@ -30,6 +36,25 @@ def find_channel(caller, channelname, silent=False, noaliases=False):
return None return None
return channels[0] return channels[0]
class CommCommand(MuxCommand):
"""
This is a parent for comm-commands. Since
These commands are to be available to the
Player, we make sure to homogenize the caller
here, so it's always seen as a player to the
command body.
"""
def parse(self):
"overload parts of parse"
# run parent
super(CommCommand, self).parse()
# fix obj->player
if utils.inherits_from(self.caller, "src.objects.objects.Object"):
# an object. Convert it to its player.
self.caller = self.caller.player
class CmdAddCom(MuxCommand): class CmdAddCom(MuxCommand):
""" """
addcom - subscribe to a channel with optional alias addcom - subscribe to a channel with optional alias
@ -46,14 +71,14 @@ class CmdAddCom(MuxCommand):
key = "addcom" key = "addcom"
aliases = ["aliaschan","chanalias"] aliases = ["aliaschan","chanalias"]
help_category = "Comms" help_category = "Comms"
locks = "cmd:not perm(channel_banned)" locks = "cmd:not pperm(channel_banned)"
def func(self): def func(self):
"Implement the command" "Implement the command"
caller = self.caller caller = self.caller
args = self.args args = self.args
player = caller.player player = caller
if not args: if not args:
caller.msg("Usage: addcom [alias =] channelname.") caller.msg("Usage: addcom [alias =] channelname.")
@ -120,7 +145,7 @@ class CmdDelCom(MuxCommand):
"Implementing the command. " "Implementing the command. "
caller = self.caller caller = self.caller
player = caller.player player = caller
if not self.args: if not self.args:
caller.msg("Usage: delcom <alias or channel>") caller.msg("Usage: delcom <alias or channel>")
@ -169,7 +194,7 @@ class CmdAllCom(MuxCommand):
""" """
key = "allcom" key = "allcom"
locks = "cmd: not perm(channel_banned)" locks = "cmd: not pperm(channel_banned)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -189,7 +214,7 @@ class CmdAllCom(MuxCommand):
caller.execute_cmd("addcom %s" % channel.key) caller.execute_cmd("addcom %s" % channel.key)
elif args == "off": elif args == "off":
#get names all subscribed channels and disconnect from them all #get names all subscribed channels and disconnect from them all
channels = [conn.channel for conn in PlayerChannelConnection.objects.get_all_player_connections(caller.player)] channels = [conn.channel for conn in PlayerChannelConnection.objects.get_all_player_connections(caller)]
for channel in channels: for channel in channels:
caller.execute_cmd("delcom %s" % channel.key) caller.execute_cmd("delcom %s" % channel.key)
elif args == "destroy": elif args == "destroy":
@ -230,7 +255,7 @@ class CmdChannels(MuxCommand):
key = "@channels" key = "@channels"
aliases = ["@clist", "channels", "comlist", "chanlist", "channellist", "all channels"] aliases = ["@clist", "channels", "comlist", "chanlist", "channellist", "all channels"]
help_category = "Comms" help_category = "Comms"
locks = "cmd: not perm(channel_banned)" locks = "cmd: not pperm(channel_banned)"
def func(self): def func(self):
"Implement function" "Implement function"
@ -243,7 +268,7 @@ class CmdChannels(MuxCommand):
caller.msg("No channels available.") caller.msg("No channels available.")
return return
# all channel we are already subscribed to # all channel we are already subscribed to
subs = [conn.channel for conn in PlayerChannelConnection.objects.get_all_player_connections(caller.player)] subs = [conn.channel for conn in PlayerChannelConnection.objects.get_all_player_connections(caller)]
if self.cmdstring != "comlist": if self.cmdstring != "comlist":
@ -298,7 +323,7 @@ class CmdCdestroy(MuxCommand):
key = "@cdestroy" key = "@cdestroy"
help_category = "Comms" help_category = "Comms"
locks = "cmd: not perm(channel_banned)" locks = "cmd: not pperm(channel_banned)"
def func(self): def func(self):
"Destroy objects cleanly." "Destroy objects cleanly."
@ -337,7 +362,7 @@ class CmdCBoot(MuxCommand):
""" """
key = "@cboot" key = "@cboot"
locks = "cmd: not perm(channel_banned)" locks = "cmd: not pperm(channel_banned)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -352,12 +377,12 @@ class CmdCBoot(MuxCommand):
if not channel: if not channel:
return return
reason = "" reason = ""
player = None
if ":" in self.rhs: if ":" in self.rhs:
playername, reason = self.rhs.rsplit(":", 1) playername, reason = self.rhs.rsplit(":", 1)
player = self.caller.search("*%s" % playername.lstrip('*')) searchstring = playername.lstrip('*')
if not player: else:
player = self.caller.search("*%s" % self.rhs.lstrip('*')) searchstring = self.rhs.lstrip('*')
player = self.caller.search(searchstring, player=True)
if not player: if not player:
return return
if reason: if reason:
@ -400,7 +425,7 @@ class CmdCemit(MuxCommand):
key = "@cemit" key = "@cemit"
aliases = ["@cmsg"] aliases = ["@cmsg"]
locks = "cmd: not perm(channel_banned)" locks = "cmd: not pperm(channel_banned)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -437,7 +462,7 @@ class CmdCWho(MuxCommand):
List who is connected to a given channel you have access to. List who is connected to a given channel you have access to.
""" """
key = "@cwho" key = "@cwho"
locks = "cmd: not perm(channel_banned)" locks = "cmd: not pperm(channel_banned)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -475,7 +500,7 @@ class CmdChannelCreate(MuxCommand):
key = "@ccreate" key = "@ccreate"
aliases = "channelcreate" aliases = "channelcreate"
locks = "cmd:not perm(channel_banned)" locks = "cmd:not pperm(channel_banned)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -522,7 +547,7 @@ class CmdCset(MuxCommand):
""" """
key = "@cset" key = "@cset"
locks = "cmd:not perm(channel_banned)" locks = "cmd:not pperm(channel_banned)"
aliases = ["@cclock"] aliases = ["@cclock"]
help_category = "Comms" help_category = "Comms"
@ -568,7 +593,7 @@ class CmdCdesc(MuxCommand):
""" """
key = "@cdesc" key = "@cdesc"
locks = "cmd:not perm(channel_banned)" locks = "cmd:not pperm(channel_banned)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -611,7 +636,7 @@ class CmdPage(MuxCommand):
key = "page" key = "page"
aliases = ['tell'] aliases = ['tell']
locks = "cmd:not perm(page_banned)" locks = "cmd:not pperm(page_banned)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -619,7 +644,7 @@ class CmdPage(MuxCommand):
"Implement function using the Msg methods" "Implement function using the Msg methods"
caller = self.caller caller = self.caller
player = caller.player player = caller
# 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(player))
@ -751,7 +776,7 @@ class CmdIRC2Chan(MuxCommand):
""" """
key = "@irc2chan" key = "@irc2chan"
locks = "cmd:serversetting(IRC_ENABLED) and perm(Immortals)" locks = "cmd:serversetting(IRC_ENABLED) and pperm(Immortals)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -840,7 +865,7 @@ class CmdIMC2Chan(MuxCommand):
""" """
key = "@imc2chan" key = "@imc2chan"
locks = "cmd:serversetting(IMC2_ENABLED) and perm(Immortals)" locks = "cmd:serversetting(IMC2_ENABLED) and pperm(Immortals)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -923,7 +948,7 @@ class CmdIMCInfo(MuxCommand):
key = "@imcinfo" key = "@imcinfo"
aliases = ["@imcchanlist", "@imclist", "@imcwhois"] aliases = ["@imcchanlist", "@imclist", "@imcwhois"]
locks = "cmd: serversetting(IMC2_ENABLED) and perm(Wizards)" locks = "cmd: serversetting(IMC2_ENABLED) and pperm(Wizards)"
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -982,7 +1007,7 @@ class CmdIMCInfo(MuxCommand):
return return
from src.comms.imc2 import IMC2_CLIENT from src.comms.imc2 import IMC2_CLIENT
self.caller.msg("Sending IMC whois request. If you receive no response, no matches were found.") self.caller.msg("Sending IMC whois request. If you receive no response, no matches were found.")
IMC2_CLIENT.msg_imc2(None, from_obj=self.caller.player, packet_type="imcwhois", data={"target":self.args}) IMC2_CLIENT.msg_imc2(None, from_obj=self.caller, packet_type="imcwhois", data={"target":self.args})
elif not self.switches or "channels" in self.switches or self.cmdstring == "@imcchanlist": elif not self.switches or "channels" in self.switches or self.cmdstring == "@imcchanlist":
# show channels # show channels
@ -1051,6 +1076,6 @@ class CmdIMCTell(MuxCommand):
data = {"target":target, "destination":destination} data = {"target":target, "destination":destination}
# send to imc2 # send to imc2
IMC2_CLIENT.msg_imc2(message, from_obj=self.caller.player, packet_type="imctell", data=data) IMC2_CLIENT.msg_imc2(message, from_obj=self.caller, packet_type="imctell", data=data)
self.caller.msg("You paged {c%s@%s{n (over IMC): '%s'." % (target, destination, message)) self.caller.msg("You paged {c%s@%s{n (over IMC): '%s'." % (target, destination, message))

View file

@ -5,11 +5,13 @@ 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.objects.models import HANDLE_SEARCH_ERRORS
from src.utils import utils from src.utils import utils
from src.objects.models import Nick from src.objects.models import ObjectNick as Nick
from src.commands.default.muxcommand import MuxCommand from src.commands.default.muxcommand import MuxCommand
AT_SEARCH_RESULT = utils.mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
BASE_PLAYER_TYPECLASS = settings.BASE_PLAYER_TYPECLASS
class CmdHome(MuxCommand): class CmdHome(MuxCommand):
""" """
home home
@ -45,7 +47,7 @@ class CmdLook(MuxCommand):
Observes your location or objects in your vicinity. Observes your location or objects in your vicinity.
""" """
key = "look" key = "look"
aliases = ["l"] aliases = ["l", "ls"]
locks = "cmd:all()" locks = "cmd:all()"
def func(self): def func(self):
@ -53,7 +55,7 @@ class CmdLook(MuxCommand):
Handle the looking. Handle the looking.
""" """
caller = self.caller caller = self.caller
args = self.args # caller.msg(inp) args = self.args
if args: if args:
# Use search to handle duplicate/nonexistant results. # Use search to handle duplicate/nonexistant results.
@ -63,8 +65,9 @@ class CmdLook(MuxCommand):
else: else:
looking_at_obj = caller.location looking_at_obj = caller.location
if not looking_at_obj: if not looking_at_obj:
caller.msg("Location: None") caller.msg("You have no location to look at!")
return return
if not hasattr(looking_at_obj, 'return_appearance'): if not hasattr(looking_at_obj, 'return_appearance'):
# this is likely due to us having a player instead # this is likely due to us having a player instead
looking_at_obj = looking_at_obj.character looking_at_obj = looking_at_obj.character
@ -89,6 +92,8 @@ class CmdPassword(MuxCommand):
"hook function." "hook function."
caller = self.caller caller = self.caller
if hasattr(caller, "player"):
caller = caller.player
if not self.rhs: if not self.rhs:
caller.msg("Usage: @password <oldpass> = <newpass>") caller.msg("Usage: @password <oldpass> = <newpass>")
@ -96,7 +101,7 @@ class CmdPassword(MuxCommand):
oldpass = self.lhslist[0] # this is already stripped by parse() oldpass = self.lhslist[0] # this is already stripped by parse()
newpass = self.rhslist[0] # '' newpass = self.rhslist[0] # ''
try: try:
uaccount = caller.player.user uaccount = caller.user
except AttributeError: except AttributeError:
caller.msg("This is only applicable for players.") caller.msg("This is only applicable for players.")
return return
@ -309,7 +314,7 @@ class CmdDrop(MuxCommand):
# those in our inventory. # those in our inventory.
results = [obj for obj in results if obj in caller.contents] results = [obj for obj in results if obj in caller.contents]
# now we send it into the handler. # now we send it into the handler.
obj = HANDLE_SEARCH_ERRORS(caller, self.args, results, False) obj = AT_SEARCH_RESULT(caller, self.args, results, False)
if not obj: if not obj:
return return
@ -348,13 +353,13 @@ class CmdWho(MuxCommand):
who who
doing doing
Shows who is currently online. Doing is an Shows who is currently online. Doing is an alias that limits info
alias that limits info also for those with also for those with all permissions.
all permissions.
""" """
key = "who" key = "who"
aliases = "doing" aliases = "doing"
locks = "cmd:all()"
def func(self): def func(self):
""" """
@ -518,17 +523,20 @@ class CmdEncoding(MuxCommand):
Sets the encoding. Sets the encoding.
""" """
caller = self.caller caller = self.caller
if hasattr(caller, 'player'):
caller = caller.player
if 'clear' in self.switches: if 'clear' in self.switches:
# remove customization # remove customization
old_encoding = caller.player.db.encoding old_encoding = caller.db.encoding
if old_encoding: if old_encoding:
string = "Your custom text encoding ('%s') was cleared." % old_encoding string = "Your custom text encoding ('%s') was cleared." % old_encoding
else: else:
string = "No custom encoding was set." string = "No custom encoding was set."
del caller.player.db.encoding del caller.db.encoding
elif not self.args: elif not self.args:
# just list the encodings supported # just list the encodings supported
pencoding = caller.player.db.encoding pencoding = caller.db.encoding
string = "" string = ""
if pencoding: if pencoding:
string += "Default encoding: {g%s{n (change with {w@encoding <encoding>{n)" % pencoding string += "Default encoding: {g%s{n (change with {w@encoding <encoding>{n)" % pencoding
@ -539,9 +547,9 @@ class CmdEncoding(MuxCommand):
string = "No encodings found." string = "No encodings found."
else: else:
# change encoding # change encoding
old_encoding = caller.player.db.encoding old_encoding = caller.db.encoding
encoding = self.args encoding = self.args
caller.player.db.encoding = encoding caller.db.encoding = encoding
string = "Your custom text encoding was changed from '%s' to '%s'." % (old_encoding, encoding) string = "Your custom text encoding was changed from '%s' to '%s'." % (old_encoding, encoding)
caller.msg(string.strip()) caller.msg(string.strip())
@ -579,3 +587,141 @@ class CmdAccess(MuxCommand):
if hasattr(caller, 'player'): if hasattr(caller, 'player'):
string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms) string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms)
caller.msg(string) caller.msg(string)
# OOC commands
class CmdOOCLook(CmdLook):
"""
ooc look
Usage:
look
This is an OOC version of the look command. Since a
Player doesn't have an in-game existence, there is no
concept of location or "self". If we are controlling
a character, pass control over to normal look.
"""
key = "look"
aliases = ["l", "ls"]
locks = "cmd:all()"
help_cateogory = "General"
def func(self):
"implement the command"
self.character = None
if utils.inherits_from(self.caller, "src.objects.objects.Object"):
# An object of some type is calling. Convert to player.
self.character = self.caller
self.caller = self.caller.player
if not self.character:
string = "You are out-of-character (OOC). "
string += "Use {w@ic{n to get back to the game, {whelp{n for more info."
self.caller.msg(string)
else:
self.caller = self.character # we have to put this back for normal look to work.
super(CmdLook, self).func()
class CmdIC(MuxCommand):
"""
Switch control to an object
Usage:
@ic <character>
Go in-character (IC) as a given Character.
This will attempt to "become" a different object assuming you have
the right to do so. You cannot become an object that is already
controlled by another player. In principle <character> can be
any in-game object as long as you have access right to puppet it.
"""
key = "@ic"
locks = "cmd:all()" # must be all() or different puppeted objects won't be able to access it.
aliases = "@puppet"
help_category = "General"
def func(self):
"""
Simple puppet method
"""
caller = self.caller
if utils.inherits_from(caller, "src.objects.objects.Object"):
caller = caller.player
new_character = None
if not self.args:
new_character = caller.db.last_puppet
if not new_character:
caller.msg("Usage: @ic <character>")
return
if not new_character:
# search for a matching character
new_character = caller.search(self.args)
if not new_character:
# the search method handles error messages etc.
return
if new_character.player:
if new_character.player == caller:
caller.msg("{RYou already are {c%s{n." % new_character.name)
else:
caller.msg("{c%s{r is already acted by another player.{n" % new_character.name)
return
if not new_character.access(caller, "puppet"):
caller.msg("{rYou may not become %s.{n" % new_character.name)
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):
new_character.msg("\n{gYou become {c%s{n.\n" % new_character.name)
caller.db.last_puppet = old_char
new_character.execute_cmd("look")
else:
caller.msg("{rYou cannot become {C%s{n." % new_character.name)
class CmdOOC(MuxCommand):
"""
@ooc - go ooc
Usage:
@ooc
Go out-of-character (OOC).
This will leave your current character and put you in a incorporeal OOC state.
"""
key = "@ooc"
locks = "cmd:all()" # this must be all(), or different puppeted objects won't be able to access it.
aliases = "@unpuppet"
help_category = "General"
def func(self):
"Implement function"
caller = self.caller
if utils.inherits_from(caller, "src.objects.objects.Object"):
caller = self.caller.player
if not caller.character:
string = "You are already OOC."
caller.msg(string)
return
caller.db.last_puppet = caller.character
# disconnect
caller.character.player = None
caller.character = None
caller.msg("\n{GYou go OOC.{n\n")
caller.execute_cmd("look")

View file

@ -441,5 +441,12 @@ class TestCwho(CommandTest):
self.execute_cmd("@ccreate testchannel1;testchan1;testchan1b = This is a test channel") self.execute_cmd("@ccreate testchannel1;testchan1;testchan1b = This is a test channel")
self.execute_cmd("@cwho testchan1b", "Channel subscriptions") self.execute_cmd("@cwho testchan1b", "Channel subscriptions")
# OOC commands
#class TestOOC_and_IC(CommandTest): # can't be tested it seems, causes errors in other commands (?)
# def test_call(self):
# self.execute_cmd("@ooc", "\nYou go OOC.")
# self.execute_cmd("@ic", "\nYou become TestChar")
# Unloggedin commands # Unloggedin commands
# these cannot be tested from here. # these cannot be tested from here.

View file

@ -62,49 +62,81 @@ class CmdConnect(MuxCommand):
# We are logging in, get/setup the player object controlled by player # We are logging in, get/setup the player object controlled by player
character = player.character
if not character:
# Create a new character object to tie the player to. This should
# usually not be needed unless the old character object was manually
# deleted.
default_home_id = ServerConfig.objects.conf("default_home")
default_home = ObjectDB.objects.get_id(default_home_id)
typeclass = settings.BASE_CHARACTER_TYPECLASS
character = create.create_object(typeclass=typeclass,
key=player.name,
location=default_home,
home=default_home,
player=player)
character.db.FIRST_LOGIN = "True"
# Getting ready to log the player in.
# Check if this is the first time the # Check if this is the first time the
# *player* connects # *player* connects
if player.db.FIRST_LOGIN: if player.db.FIRST_LOGIN:
player.at_first_login() player.at_first_login()
del player.db.FIRST_LOGIN del player.db.FIRST_LOGIN
# check if this is the first time the *character*
# character (needs not be the first time the player
# does so, e.g. if the player has several characters)
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# actually do the login, calling
# customization hooks before and after.
player.at_pre_login() player.at_pre_login()
character.at_pre_login()
character = player.character
if character:
# this player has a character. Check if it's the
# first time *this character* logs in
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# run character login hook
character.at_pre_login()
# actually do the login
session.session_login(player) session.session_login(player)
# post-login hooks
player.at_post_login() player.at_post_login()
character.at_post_login() if character:
character.at_post_login()
character.execute_cmd('look')
else:
player.execute_cmd('look')
# run look # run look
#print "character:", character, character.scripts.all(), character.cmdset.current #print "character:", character, character.scripts.all(), character.cmdset.current
character.execute_cmd('look')
#
# character = player.character
# if not character:
# # Create a new character object to tie the player to. This should
# # usually not be needed unless the old character object was manually
# # deleted.
# default_home_id = ServerConfig.objects.conf("default_home")
# default_home = ObjectDB.objects.get_id(default_home_id)
# typeclass = settings.BASE_CHARACTER_TYPECLASS
# character = create.create_object(typeclass=typeclass,
# key=player.name,
# location=default_home,
# home=default_home,
# player=player)
# character.db.FIRST_LOGIN = "True"
# # Getting ready to log the player in.
# # Check if this is the first time the
# # *player* connects
# if player.db.FIRST_LOGIN:
# player.at_first_login()
# del player.db.FIRST_LOGIN
# # check if this is the first time the *character*
# # character (needs not be the first time the player
# # does so, e.g. if the player has several characters)
# if character.db.FIRST_LOGIN:
# character.at_first_login()
# del character.db.FIRST_LOGIN
# # actually do the login, calling
# # customization hooks before and after.
# player.at_pre_login()
# character.at_pre_login()
# session.session_login(player)
# player.at_post_login()
# character.at_post_login()
# # run look
# #print "character:", character, character.scripts.all(), character.cmdset.current
# character.execute_cmd('look')
class CmdCreate(MuxCommand): class CmdCreate(MuxCommand):
""" """
@ -173,57 +205,65 @@ class CmdCreate(MuxCommand):
if PlayerDB.objects.get_player_from_name(playername) or User.objects.filter(username=playername): if PlayerDB.objects.get_player_from_name(playername) or User.objects.filter(username=playername):
# player already exists # player already exists
session.msg("Sorry, there is already a player with the name '%s'." % playername) session.msg("Sorry, there is already a player with the name '%s'." % playername)
elif PlayerDB.objects.get_player_from_email(email): return
if PlayerDB.objects.get_player_from_email(email):
# email already set on a player # email already set on a player
session.msg("Sorry, there is already a player with that email address.") session.msg("Sorry, there is already a player with that email address.")
elif len(password) < 3: return
if len(password) < 3:
# too short password # too short password
string = "Your password must be at least 3 characters or longer." string = "Your password must be at least 3 characters or longer."
string += "\n\rFor best security, make it at least 8 characters long, " string += "\n\rFor best security, make it at least 8 characters long, "
string += "avoid making it a real word and mix numbers into it." string += "avoid making it a real word and mix numbers into it."
session.msg(string) session.msg(string)
else: return
# everything's ok. Create the new player account
try:
default_home_id = ServerConfig.objects.conf("default_home")
default_home = ObjectDB.objects.get_id(default_home_id)
typeclass = settings.BASE_CHARACTER_TYPECLASS # everything's ok. Create the new player account.
permissions = settings.PERMISSION_PLAYER_DEFAULT try:
default_home_id = ServerConfig.objects.conf("default_home")
default_home = ObjectDB.objects.get_id(default_home_id)
new_character = create.create_player(playername, email, password, typeclass = settings.BASE_CHARACTER_TYPECLASS
permissions=permissions, permissions = settings.PERMISSION_PLAYER_DEFAULT
location=default_home,
typeclass=typeclass,
home=default_home)
# character safety features
new_character.locks.delete("get")
new_character.locks.add("get:perm(Wizards)")
# set a default description new_character = create.create_player(playername, email, password,
new_character.db.desc = "This is a Player." permissions=permissions,
location=default_home,
typeclass=typeclass,
home=default_home)
new_player = new_character.player
new_character.db.FIRST_LOGIN = True # character safety features
new_player = new_character.player new_character.locks.delete("get")
new_player.db.FIRST_LOGIN = True new_character.locks.add("get:perm(Wizards)")
# allow the character itself and the player to puppet this character.
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
(new_character.id, new_player.id))
# join the new player to the public channel # set a default description
pchanneldef = settings.CHANNEL_PUBLIC new_character.db.desc = "This is a Player."
if pchanneldef:
pchannel = Channel.objects.get_channel(pchanneldef[0])
if not pchannel.connect_to(new_player):
string = "New player '%s' could not connect to public channel!" % new_player.key
logger.log_errmsg(string)
string = "A new account '%s' was created with the email address %s. Welcome!" new_character.db.FIRST_LOGIN = True
string += "\n\nYou can now log with the command 'connect %s <your password>'." new_player = new_character.player
session.msg(string % (playername, email, email)) new_player.db.FIRST_LOGIN = True
except Exception:
# we have to handle traceback ourselves at this point, if # join the new player to the public channel
# we don't, errors will give no feedback. pchanneldef = settings.CHANNEL_PUBLIC
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists." if pchanneldef:
session.msg(string % (traceback.format_exc())) pchannel = Channel.objects.get_channel(pchanneldef[0])
logger.log_errmsg(traceback.format_exc()) if not pchannel.connect_to(new_player):
string = "New player '%s' could not connect to public channel!" % new_player.key
logger.log_errmsg(string)
string = "A new account '%s' was created with the email address %s. Welcome!"
string += "\n\nYou can now log with the command 'connect %s <your password>'."
session.msg(string % (playername, email, email))
except Exception:
# We are in the middle between logged in and -not, so we have to handle tracebacks
# ourselves at this point. If we don't, we won't see any errors at all.
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
session.msg(string % (traceback.format_exc()))
logger.log_errmsg(traceback.format_exc())
class CmdQuit(MuxCommand): class CmdQuit(MuxCommand):
""" """

View file

@ -80,11 +80,16 @@ class ChannelCommand(command.Command):
msg = "[%s] %s: %s" % (channel.key, caller.name, msg) msg = "[%s] %s: %s" % (channel.key, caller.name, msg)
# we can't use the utils.create function to make the Msg, # we can't use the utils.create function to make the Msg,
# since that creates an import recursive loop. # since that creates an import recursive loop.
msgobj = Msg(db_sender=caller.player, db_message=msg) try:
sender = caller.player
except AttributeError:
# this could happen if a player is calling directly.
sender = caller.dbobj
msgobj = Msg(db_sender=sender, db_message=msg)
msgobj.save() msgobj.save()
msgobj.channels = channel msgobj.channels = channel
# send new message object to channel # send new message object to channel
channel.msg(msgobj, from_obj=caller.player) channel.msg(msgobj, from_obj=sender)
class ChannelHandler(object): class ChannelHandler(object):
""" """

View file

@ -106,6 +106,16 @@ from src.utils import utils
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY] PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
def _to_player(accessing_obj):
"Helper function. Makes sure an accessing object is a player object"
if utils.inherits_from(accessing_obj, "src.objects.objects.Object"):
# an object. Convert to player.
accessing_obj = accessing_obj.player
return accessing_obj
# lock functions
def true(*args, **kwargs): def true(*args, **kwargs):
"Always returns True." "Always returns True."
return True return True
@ -155,6 +165,29 @@ def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
return any(True for hpos, hperm in enumerate(PERMISSION_HIERARCHY) return any(True for hpos, hperm in enumerate(PERMISSION_HIERARCHY)
if hperm in [p.lower() for p in accessing_obj.permissions] and hpos > ppos) if hperm in [p.lower() for p in accessing_obj.permissions] and hpos > ppos)
def pperm(accessing_obj, accessed_obj, *args, **kwargs):
"""
The basic permission-checker for Player objects. Ignores case.
Usage:
pperm(<permission>)
where <permission> is the permission accessing_obj must
have in order to pass the lock. If the given permission
is part of PERMISSION_HIERARCHY, permission is also granted
to all ranks higher up in the hierarchy.
"""
return perm(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
def pperm_above(accessing_obj, accessed_obj, *args, **kwargs):
"""
Only allow Player objects with a permission *higher* in the permission
hierarchy than the one given. If there is no such higher rank,
it's assumed we refer to superuser. If no hierarchy is defined,
this function has no meaning and returns False.
"""
return perm_above(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
def dbref(accessing_obj, accessed_obj, *args, **kwargs): def dbref(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
@ -175,10 +208,21 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs):
return dbref == accessing_obj.id return dbref == accessing_obj.id
return False return False
def pdbref(accessing_obj, accessed_obj, *args, **kwargs):
"""
Same as dbref, but making sure accessing_obj is a player.
"""
return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
def id(accessing_obj, accessed_obj, *args, **kwargs): def id(accessing_obj, accessed_obj, *args, **kwargs):
"Alias to dbref" "Alias to dbref"
return dbref(accessing_obj, accessed_obj, *args, **kwargs) return dbref(accessing_obj, accessed_obj, *args, **kwargs)
def pid(accessing_obj, accessed_obj, *args, **kwargs):
"Alias to dbref, for Players"
return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
def attr(accessing_obj, accessed_obj, *args, **kwargs): def attr(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:

View file

@ -223,7 +223,8 @@ class LockHandler(object):
wlist.append("Lock: access type '%s' changed from '%s' to '%s' " % \ wlist.append("Lock: access type '%s' changed from '%s' to '%s' " % \
(access_type, locks[access_type][2], raw_lockstring)) (access_type, locks[access_type][2], raw_lockstring))
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring) locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
if wlist: if wlist and self.log_obj:
# not an error, so only report if log_obj is available.
self._log_error("\n".join(wlist)) self._log_error("\n".join(wlist))
if elist: if elist:
raise LockException("\n".join(elist)) raise LockException("\n".join(elist))
@ -343,10 +344,11 @@ class LockHandler(object):
to None if the lock functions called don't access it). atype can also be to None if the lock functions called don't access it). atype can also be
put to a dummy value since no lock selection is made. put to a dummy value since no lock selection is made.
""" """
if (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'user') if ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser)
and hasattr(accessing_obj.player.user, 'is_superuser') or (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser)
and accessing_obj.player.user.is_superuser): or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser))):
return True # always grant access to the superuser. return True
locks = self. _parse_lockstring(lockstring) locks = self. _parse_lockstring(lockstring)
for access_type in locks: for access_type in locks:
evalstring, func_tup, raw_string = locks[access_type] evalstring, func_tup, raw_string = locks[access_type]

View file

@ -9,11 +9,8 @@ from src.typeclasses.managers import returns_typeclass, returns_typeclass_list
from src.utils import utils from src.utils import utils
# Try to use a custom way to parse id-tagged multimatches. # Try to use a custom way to parse id-tagged multimatches.
IDPARSER_PATH = getattr(settings, 'ALTERNATE_OBJECT_SEARCH_MULTIMATCH_PARSER', 'src.objects.object_search_funcs')
if not IDPARSER_PATH: AT_MULTIMATCH_INPUT = utils.mod_import(*settings.SEARCH_AT_MULTIMATCH_INPUT.rsplit('.', 1))
# can happen if variable is set to "" in settings
IDPARSER_PATH = 'src.objects.object_search_funcs'
exec("from %s import object_multimatch_parser as IDPARSER" % IDPARSER_PATH)
class ObjectManager(TypedObjectManager): class ObjectManager(TypedObjectManager):
""" """
@ -172,7 +169,10 @@ class ObjectManager(TypedObjectManager):
global_search=False, global_search=False,
attribute_name=None, location=None): attribute_name=None, location=None):
""" """
Search as an object and return results. Search as an object and return results. The result is always an Object.
If * is appended (player search, a Character controlled by this Player
is looked for. The Character is returned, not the Player. Use player_search
to find Player objects.
character: (Object) The object performing the search. character: (Object) The object performing the search.
ostring: (string) The string to compare names against. ostring: (string) The string to compare names against.
@ -187,7 +187,7 @@ class ObjectManager(TypedObjectManager):
if not ostring or not character: if not ostring or not character:
return None return None
if not location: if not location and hasattr(character, "location"):
location = character.location location = character.location
# Easiest case - dbref matching (always exact) # Easiest case - dbref matching (always exact)
@ -204,15 +204,16 @@ class ObjectManager(TypedObjectManager):
if character and ostring in ['me', 'self']: if character and ostring in ['me', 'self']:
return [character] return [character]
if character and ostring in ['*me', '*self']: if character and ostring in ['*me', '*self']:
return [character.player] return [character]
# Test if we are looking for a player object # Test if we are looking for an object controlled by a
# specific player
if utils.to_unicode(ostring).startswith("*"): if utils.to_unicode(ostring).startswith("*"):
# Player search - try to find obj by its player's name # Player search - try to find obj by its player's name
player_match = self.get_object_with_player(ostring) player_match = self.get_object_with_player(ostring)
if player_match is not None: if player_match is not None:
return [player_match.player] return [player_match]
# Search for keys, aliases or other attributes # Search for keys, aliases or other attributes
@ -246,7 +247,7 @@ class ObjectManager(TypedObjectManager):
matches = local_and_global_search(ostring, exact=True) matches = local_and_global_search(ostring, exact=True)
if not matches: if not matches:
# if we have no match, check if we are dealing with an "N-keyword" query - if so, strip it. # if we have no match, check if we are dealing with an "N-keyword" query - if so, strip it.
match_number, ostring = IDPARSER(ostring) match_number, ostring = AT_MULTIMATCH_INPUT(ostring)
if match_number != None and ostring: if match_number != None and ostring:
# Run search again, without match number: # Run search again, without match number:
matches = local_and_global_search(ostring, exact=True) matches = local_and_global_search(ostring, exact=True)

View file

@ -0,0 +1,124 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models, utils
class Migration(SchemaMigration):
def forwards(self, orm):
try:
# if we migrate, we just rename the table. This will move over all values too.
db.rename_table("objects_nick", "objects_objectnick")
except utils.DatabaseError:
# this happens if we start from scratch. In that case the old
# database table doesn't exist, so we just create the new one.
# Adding model 'ObjectNick'
db.create_table('objects_objectnick', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('db_nick', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
('db_real', self.gf('django.db.models.fields.TextField')()),
('db_type', self.gf('django.db.models.fields.CharField')(default='inputline', max_length=16, null=True, blank=True)),
('db_obj', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['objects.ObjectDB'])),
))
db.send_create_signal('objects', ['ObjectNick'])
# Adding unique constraint on 'ObjectNick', fields ['db_nick', 'db_type', 'db_obj']
db.create_unique('objects_objectnick', ['db_nick', 'db_type', 'db_obj_id'])
def backwards(self, orm):
raise RuntimeError("This migration cannot be reversed.")
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.alias': {
'Meta': {'object_name': 'Alias'},
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objattribute': {
'Meta': {'object_name': 'ObjAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectnick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'ObjectNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['objects']

View file

@ -0,0 +1,121 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models, utils
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
# we need to add a default lock string to all objects, then a separate set to Characters.
lockstring1 = 'control:id(1);get:all();edit:perm(Wizards);examine:perm(Builders);call:true();puppet:id(#4) or perm(Immortals) or pperm(Immortals);delete:id(1) or perm(Wizards)'
lockstring2 = 'control:id(#3) or perm(Immortals);get:perm(Wizards);edit:perm(Wizards);examine:perm(Builders);call:false();puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals);delete:perm(Wizards)'
try:
for obj in orm.ObjectDB.objects.all().exclude(db_player__isnull=False):
obj.db_lock_storage = lockstring1
obj.save()
for obj in orm.ObjectDB.objects.filter(db_player__isnull=False):
obj.db_lock_storage = lockstring2 % (obj.id, obj.db_player.id)
obj.save()
except utils.DatabaseError:
# running from scatch. In this case we just ignore this.
pass
def backwards(self, orm):
"Write your backwards methods here."
raise RuntimeError("You cannot reverse this migration.")
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.alias': {
'Meta': {'object_name': 'Alias'},
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objattribute': {
'Meta': {'object_name': 'ObjAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectnick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'ObjectNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['objects']

View file

@ -16,25 +16,24 @@ transparently through the decorating TypeClass.
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from src.utils.idmapper.models import SharedMemoryModel from src.utils.idmapper.models import SharedMemoryModel
from src.typeclasses.models import Attribute, TypedObject from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
from src.typeclasses.typeclass import TypeClass from src.typeclasses.typeclass import TypeClass
from src.objects.manager import ObjectManager from src.objects.manager import ObjectManager
from src.players.models import PlayerDB
from src.server.models import ServerConfig from src.server.models import ServerConfig
from src.commands.cmdsethandler import CmdSetHandler from src.commands.cmdsethandler import CmdSetHandler
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 is_iter, to_unicode from src.utils.utils import is_iter, to_unicode, to_str, mod_import
#PlayerDB = ContentType.objects.get(app_label="players", model="playerdb").model_class()
FULL_PERSISTENCE = settings.FULL_PERSISTENCE FULL_PERSISTENCE = settings.FULL_PERSISTENCE
AT_SEARCH_RESULT = mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
try:
HANDLE_SEARCH_ERRORS = __import__(
settings.ALTERNATE_OBJECT_SEARCH_ERROR_HANDLER).handle_search_errors, fromlist=[None]
except Exception:
from src.objects.object_search_funcs \
import handle_search_errors as HANDLE_SEARCH_ERRORS
#------------------------------------------------------------ #------------------------------------------------------------
# #
@ -80,80 +79,33 @@ class Alias(SharedMemoryModel):
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Nick # Object Nicks
# #
#------------------------------------------------------------ #------------------------------------------------------------
class Nick(SharedMemoryModel): class ObjectNick(TypeNick):
""" """
This model holds whichever alternate names this object
has for OTHER objects, but also for arbitrary strings,
channels, players etc. Setting a nick does not affect
the nicknamed object at all (as opposed to Aliases above),
and only this object will be able to refer to the nicknamed
object by the given nick.
The default nick types used by Evennia are: The default nick types used by Evennia are:
inputline (default) - match against all input inputline (default) - match against all input
player - match against player searches player - match against player searches
obj - match against object searches obj - match against object searches
channel - used to store own names for channels channel - used to store own names for channels
""" """
db_nick = models.CharField(max_length=255, db_index=True) # the nick
db_real = models.TextField() # the aliased string
db_type = models.CharField(default="inputline", max_length=16, null=True, blank=True) # the type of nick
db_obj = models.ForeignKey("ObjectDB") db_obj = models.ForeignKey("ObjectDB")
class Meta: class Meta:
"Define Django meta options" "Define Django meta options"
verbose_name = "Nickname" verbose_name = "Nickname for Objects"
verbose_name_plural = "Nicknames" verbose_name_plural = "Nicknames Objects"
unique_together = ("db_nick", "db_type", "db_obj") unique_together = ("db_nick", "db_type", "db_obj")
class NickHandler(object): class ObjectNickHandler(TypeNickHandler):
""" """
Handles nick access and setting. Accessed through ObjectDB.nicks Handles nick access and setting. Accessed through ObjectDB.nicks
""" """
NickClass = ObjectNick
def __init__(self, obj):
"Setup"
self.obj = obj
def add(self, nick, realname, nick_type="inputline"):
"We want to assign a new nick"
if not nick or not nick.strip():
return
nick = nick.strip()
real = realname.strip()
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
if query.count():
old_nick = query[0]
old_nick.db_real = real
old_nick.save()
else:
new_nick = Nick(db_nick=nick, db_real=real, db_type=nick_type, db_obj=self.obj)
new_nick.save()
def delete(self, nick, nick_type="inputline"):
"Removes a nick"
nick = nick.strip()
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
if query.count():
# remove the found nick(s)
query.delete()
def get(self, nick=None, nick_type="inputline"):
if nick:
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
query = query.values_list("db_real", flat=True)
if query.count():
return query[0]
else:
return nick
else:
return Nick.objects.filter(db_obj=self.obj)
def has(self, nick, nick_type="inputline"):
"Returns true/false if this nick is defined or not"
return Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type).count()
#------------------------------------------------------------ #------------------------------------------------------------
# #
@ -239,7 +191,7 @@ class ObjectDB(TypedObject):
self.cmdset.update(init_mode=True) self.cmdset.update(init_mode=True)
self.scripts = ScriptHandler(self) self.scripts = ScriptHandler(self)
self.scripts.validate(init_mode=True) self.scripts.validate(init_mode=True)
self.nicks = NickHandler(self) self.nicks = ObjectNickHandler(self)
# Wrapper properties to easily set database fields. These are # Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using # @property decorators that allows to access these fields using
@ -312,7 +264,7 @@ class ObjectDB(TypedObject):
loc = location loc = location
elif ObjectDB.objects.dbref(location): elif ObjectDB.objects.dbref(location):
# location is a dbref; search # location is a dbref; search
loc = ObjectDB.objects.dbref_search(location) loc = ObjectDB.objects.dbref_search(ocation)
if loc and hasattr(loc,'dbobj'): if loc and hasattr(loc,'dbobj'):
loc = loc.dbobj loc = loc.dbobj
else: else:
@ -524,14 +476,11 @@ class ObjectDB(TypedObject):
global_search=False, global_search=False,
attribute_name=None, attribute_name=None,
use_nicks=False, location=None, use_nicks=False, location=None,
ignore_errors=False): ignore_errors=False, player=False):
""" """
Perform a standard object search in the database, handling Perform a standard object search in the database, handling
multiple results and lack thereof gracefully. multiple results and lack thereof gracefully.
if local_only AND search_self are both false, a global
search is done instead.
ostring: (str) The string to match object names against. ostring: (str) The string to match object names against.
Obs - To find a player, append * to the Obs - To find a player, append * to the
start of ostring. start of ostring.
@ -544,6 +493,15 @@ class ObjectDB(TypedObject):
ignore_errors : Don't display any error messages even ignore_errors : Don't display any error messages even
if there are none/multiple matches - if there are none/multiple matches -
just return the result as a list. just return the result as a list.
player : Don't search for an Object but a Player.
This will also find players that don't
currently have a character.
Use *<string> to search for objects controlled by a specific
player. Note that the object controlled by the player will be
returned, not the player object itself. This also means that
this will not find Players without a character. Use the keyword
player=True to find player objects.
Note - for multiple matches, the engine accepts a number Note - for multiple matches, the engine accepts a number
linked to the key in order to separate the matches from linked to the key in order to separate the matches from
@ -554,22 +512,30 @@ class ObjectDB(TypedObject):
etc. etc.
""" """
if use_nicks: if use_nicks:
if ostring.startswith('*'): if ostring.startswith('*') or player:
# player nick replace # player nick replace
ostring = "*%s" % self.nicks.get(ostring.lstrip('*'), nick_type="player") ostring = self.nicks.get(ostring.lstrip('*'), nick_type="player")
if not player:
ostring = "*%s" % ostring
else: else:
# object nick replace # object nick replace
ostring = self.nicks.get(ostring, nick_type="object") ostring = self.nicks.get(ostring, nick_type="object")
results = ObjectDB.objects.object_search(self, ostring, if player:
global_search=global_search, if ostring in ("me", "self", "*me", "*self"):
attribute_name=attribute_name, results = [self.player]
location=location) else:
results = PlayerDB.objects.player_search(ostring.lstrip('*'))
else:
results = ObjectDB.objects.object_search(self, ostring,
global_search=global_search,
attribute_name=attribute_name,
location=location)
if ignore_errors: if ignore_errors:
return results return results
return HANDLE_SEARCH_ERRORS(self, ostring, results, global_search) # this import is cache after the first call.
return AT_SEARCH_RESULT(self, ostring, results, global_search)
# #
# Execution/action methods # Execution/action methods
@ -588,7 +554,7 @@ class ObjectDB(TypedObject):
raw_list = raw_string.split(None) raw_list = raw_string.split(None)
raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]] raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]]
for nick in Nick.objects.filter(db_obj=self, db_type__in=("inputline","channel")): for nick in ObjectNick.objects.filter(db_obj=self, db_type__in=("inputline","channel")):
if nick.db_nick in raw_list: if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1) raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break break
@ -796,9 +762,15 @@ class ObjectDB(TypedObject):
return False return False
# See if we need to kick the player off. # See if we need to kick the player off.
for session in self.sessions: for session in self.sessions:
session.msg("Your character %s has been destroyed. Goodbye." % self.name) session.msg("Your character %s has been destroyed." % self.name)
session.session_disconnect() #session.session_disconnect()
# sever the connection (important!)
if object.__getattribute__(self, 'player') and self.player:
self.player.character = None
self.player = None
# if self.player: # if self.player:
# self.player.user.is_active = False # self.player.user.is_active = False
@ -811,6 +783,3 @@ class ObjectDB(TypedObject):
# Perform the deletion of the object # Perform the deletion of the object
super(ObjectDB, self).delete() super(ObjectDB, self).delete()
return True return True
# Deferred import to avoid circular import errors.
from src.commands import cmdhandler

View file

@ -1,102 +0,0 @@
"""
Default functions for formatting and processing object searches.
This is in its own module due to them being possible to
replace from the settings file by use of setting the variables
ALTERNATE_OBJECT_SEARCH_ERROR_HANDLER
ALTERNATE_OBJECT_SEARCH_MULTIMATCH_PARSER
Both the replacing functions must have the same name and same input/output
as the ones in this module.
"""
def handle_search_errors(emit_to_obj, ostring, results, global_search=False):
"""
Takes a search result (a list) and
formats eventual errors.
emit_to_obj - object to receive feedback.
ostring - original search string
results - list of object matches, if any
global_search - if this was a global_search or not
(if it is, there might be an idea of supplying
dbrefs instead of only numbers)
"""
if not results:
emit_to_obj.msg("Could not find '%s'." % ostring)
return None
if len(results) > 1:
# we have more than one match. We will display a
# list of the form 1-objname, 2-objname etc.
# check if the emit_to_object may se dbrefs
show_dbref = global_search and \
emit_to_obj.check_permstring('Builders')
string = "More than one match for '%s'" % ostring
string += " (please narrow target):"
for num, result in enumerate(results):
invtext = ""
dbreftext = ""
if result.location == emit_to_obj:
invtext = " (carried)"
if show_dbref:
dbreftext = "(#%i)" % result.id
string += "\n %i-%s%s%s" % (num+1, result.name,
dbreftext, invtext)
emit_to_obj.msg(string.strip())
return None
else:
return results[0]
def object_multimatch_parser(ostring):
"""
Parse number-identifiers.
Sometimes it can happen that there are several objects in the room
all with exactly the same key/identifier. Showing dbrefs to
separate them is not suitable for all types of games since it's
unique to that object (and e.g. in rp-games the object might not
want to be identified like that). Instead Evennia allows for
dbref-free matching by letting the user number which of the
objects in a multi-match they want.
Ex for use in game session:
> look
You see: ball, ball, ball and ball.
> get ball
There where multiple matches for ball:
1-ball
2-ball
3-ball
4-ball
> get 3-ball
You get the ball.
The actual feedback upon multiple matches has to be
handled by the searching command. The syntax shown above is the
default.
For replacing, the method must be named the same and
take the searchstring as argument and
return a tuple (int, string) where int is the identifier
matching which of the results (in order) should be used to
pick out the right match from the multimatch). Note
that the engine assumes this number to start with 1 (i.e. not
zero as in normal Python).
"""
if not isinstance(ostring, basestring):
return (None, ostring)
if not '-' in ostring:
return (None, ostring)
try:
index = ostring.find('-')
number = int(ostring[:index])-1
return (number, ostring[index+1:])
except ValueError:
#not a number; this is not an identifier.
return (None, ostring)
except IndexError:
return (None, ostring)

View file

@ -49,6 +49,9 @@ class Object(TypeClass):
""" """
This sets up the default properties of an Object, This sets up the default properties of an Object,
just before the more general at_object_creation. just before the more general at_object_creation.
Don't change this, instead edit at_object_creation() to
overload the defaults (it is called after this one).
""" """
# the default security setup fallback for a generic # the default security setup fallback for a generic
# object. Overload in child for a custom setup. Also creation # object. Overload in child for a custom setup. Also creation
@ -63,6 +66,7 @@ class Object(TypeClass):
self.locks.add("delete:perm(Wizards)") # delete object self.locks.add("delete:perm(Wizards)") # delete object
self.locks.add("get:all()") # pick up object self.locks.add("get:all()") # pick up object
self.locks.add("call:true()") # allow to call commands on this object self.locks.add("call:true()") # allow to call commands on this object
self.locks.add("puppet:id(%s) or perm(Immortals) or pperm(Immortals)" % dbref) # restricts puppeting of this object
def at_object_creation(self): def at_object_creation(self):
""" """
@ -310,9 +314,11 @@ class Character(Object):
def basetype_setup(self): def basetype_setup(self):
""" """
Setup character-specific security Setup character-specific security
Don't change this, instead edit at_object_creation() to
overload the defaults (it is called after this one).
""" """
super(Character, self).basetype_setup() super(Character, self).basetype_setup()
self.locks.add("puppet:id(%s) or perm(Immortals)" % self.dbobj.dbref) # who may become this object's player
self.locks.add("get:false()") # noone can pick up the character self.locks.add("get:false()") # noone can pick up the character
self.locks.add("call:false()") # no commands can be called on character self.locks.add("call:false()") # no commands can be called on character
@ -348,9 +354,13 @@ class Room(Object):
""" """
Simple setup, shown as an example Simple setup, shown as an example
(since default is None anyway) (since default is None anyway)
Don't change this, instead edit at_object_creation() to
overload the defaults (it is called after this one).
""" """
super(Room, self).basetype_setup() super(Room, self).basetype_setup()
self.locks.add("puppet:false()") # would be weird to puppet a room ...
self.locks.add("get:false()") self.locks.add("get:false()")
super(Room, self).basetype_setup() super(Room, self).basetype_setup()
@ -371,9 +381,13 @@ class Exit(Object):
def basetype_setup(self): def basetype_setup(self):
""" """
Setup exit-security Setup exit-security
Don't change this, instead edit at_object_creation() to
overload the defaults (it is called after this one).
""" """
# the lock is open to all by default # the lock is open to all by default
super(Exit, self).basetype_setup() super(Exit, self).basetype_setup()
self.locks.add("puppet:false()") # would be weird to puppet an exit ...
self.locks.add("traverse:all()") # who can pass through exit self.locks.add("traverse:all()") # who can pass through exit
self.locks.add("get:false()") # noone can pick up the exit self.locks.add("get:false()") # noone can pick up the exit

View file

@ -146,17 +146,13 @@ class PlayerManager(TypedObjectManager):
ostring = a string or database id. ostring = a string or database id.
""" """
players = [] ostring = ostring.lstrip("*")
try: dbref = self.dbref(ostring)
# try dbref match if dbref:
dbref = int(ostring.strip('#')) matches = self.filter(id=dbref)
players = self.filter(id=dbref) if matches:
except Exception: return matches
pass return self.filter(user__username__iexact=ostring)
if not players:
players = self.filter(user__username=ostring)
return players
def swap_character(self, player, new_character, delete_old_character=False): def swap_character(self, player, new_character, delete_old_character=False):
""" """

View file

@ -0,0 +1,95 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'PlayerDB.db_cmdset_storage'
db.add_column('players_playerdb', 'db_cmdset_storage', self.gf('django.db.models.fields.TextField')(null=True), keep_default=False)
def backwards(self, orm):
# Deleting field 'PlayerDB.db_cmdset_storage'
db.delete_column('players_playerdb', 'db_cmdset_storage')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerattribute': {
'Meta': {'object_name': 'PlayerAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['players']

View file

@ -0,0 +1,116 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'PlayerNick'
db.create_table('players_playernick', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('db_nick', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
('db_real', self.gf('django.db.models.fields.TextField')()),
('db_type', self.gf('django.db.models.fields.CharField')(default='inputline', max_length=16, null=True, blank=True)),
('db_obj', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['players.PlayerDB'])),
))
db.send_create_signal('players', ['PlayerNick'])
# Adding unique constraint on 'PlayerNick', fields ['db_nick', 'db_type', 'db_obj']
db.create_unique('players_playernick', ['db_nick', 'db_type', 'db_obj_id'])
def backwards(self, orm):
# Removing unique constraint on 'PlayerNick', fields ['db_nick', 'db_type', 'db_obj']
db.delete_unique('players_playernick', ['db_nick', 'db_type', 'db_obj_id'])
# Deleting model 'PlayerNick'
db.delete_table('players_playernick')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerattribute': {
'Meta': {'object_name': 'PlayerAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
},
'players.playernick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'PlayerNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
complete_apps = ['players']

View file

@ -44,10 +44,16 @@ from django.conf import settings
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.contrib.contenttypes.models import ContentType
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 from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
from src.utils import logger from src.utils import logger, utils
from src.commands.cmdsethandler import CmdSetHandler
from src.commands import cmdhandler
AT_SEARCH_RESULT = utils.mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
#------------------------------------------------------------ #------------------------------------------------------------
# #
@ -68,6 +74,36 @@ class PlayerAttribute(Attribute):
verbose_name = "Player Attribute" verbose_name = "Player Attribute"
verbose_name_plural = "Player Attributes" verbose_name_plural = "Player Attributes"
#------------------------------------------------------------
#
# Player Nicks
#
#------------------------------------------------------------
class PlayerNick(TypeNick):
"""
The default nick types used by Evennia are:
inputline (default) - match against all input
player - match against player searches
obj - match against object searches
channel - used to store own names for channels
"""
db_obj = models.ForeignKey("PlayerDB")
class Meta:
"Define Django meta options"
verbose_name = "Nickname for Players"
verbose_name_plural = "Nicknames Players"
unique_together = ("db_nick", "db_type", "db_obj")
class PlayerNickHandler(TypeNickHandler):
"""
Handles nick access and setting. Accessed through ObjectDB.nicks
"""
NickClass = PlayerNick
#------------------------------------------------------------ #------------------------------------------------------------
# #
# PlayerDB # PlayerDB
@ -117,12 +153,23 @@ class PlayerDB(TypedObject):
# Use the property 'obj' to access. # Use the property 'obj' to access.
db_obj = models.ForeignKey("objects.ObjectDB", null=True) db_obj = models.ForeignKey("objects.ObjectDB", null=True)
# database storage of persistant cmdsets.
db_cmdset_storage = models.TextField(null=True)
# Database manager # Database manager
objects = manager.PlayerManager() objects = manager.PlayerManager()
class Meta: class Meta:
app_label = 'players' app_label = 'players'
def __init__(self, *args, **kwargs):
"Parent must be initiated first"
TypedObject.__init__(self, *args, **kwargs)
# handlers
self.cmdset = CmdSetHandler(self)
self.cmdset.update(init_mode=True)
self.nicks = PlayerNickHandler(self)
# Wrapper properties to easily set database fields. These are # Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using # @property decorators that allows to access these fields using
# normal python operations (without having to remember to save() # normal python operations (without having to remember to save()
@ -172,6 +219,26 @@ class PlayerDB(TypedObject):
self.db_obj = None self.db_obj = None
self.save() self.save()
character = property(character_get, character_set, character_del) character = property(character_get, character_set, character_del)
# cmdset_storage property
#@property
def cmdset_storage_get(self):
"Getter. Allows for value = self.name. Returns a list of cmdset_storage."
if self.db_cmdset_storage:
return [path.strip() for path in self.db_cmdset_storage.split(',')]
return []
#@cmdset_storage.setter
def cmdset_storage_set(self, value):
"Setter. Allows for self.name = value. Stores as a comma-separated string."
if utils.is_iter(value):
value = ",".join([str(val).strip() for val in value])
self.db_cmdset_storage = value
self.save()
#@cmdset_storage.deleter
def cmdset_storage_del(self):
"Deleter. Allows for del self.name"
self.db_cmdset_storage = ""
self.save()
cmdset_storage = property(cmdset_storage_get, cmdset_storage_set, cmdset_storage_del)
class Meta: class Meta:
"Define Django meta options" "Define Django meta options"
@ -245,15 +312,23 @@ class PlayerDB(TypedObject):
Evennia -> User Evennia -> User
This is the main route for sending data back to the user from the server. This is the main route for sending data back to the user from the server.
""" """
if from_obj: if from_obj:
try: try:
from_obj.at_msg_send(outgoing_string, to_obj=self, data=data) from_obj.at_msg_send(outgoing_string, to_obj=self, data=data)
except Exception: except Exception:
pass pass
if object.__getattribute__(self, "character"):
if self.character.at_msg_receive(outgoing_string, from_obj=from_obj, data=data): if (object.__getattribute__(self, "character")
for session in object.__getattribute__(self, 'sessions'): and not self.character.at_msg_receive(outgoing_string, from_obj=from_obj, data=data)):
session.msg(outgoing_string, data) # the at_msg_receive() hook may block receiving of certain messages
return
outgoing_string = utils.to_str(outgoing_string, force_string=True)
for session in object.__getattribute__(self, 'sessions'):
session.msg(outgoing_string, data)
def swap_character(self, new_character, delete_old_character=False): def swap_character(self, new_character, delete_old_character=False):
""" """
@ -261,3 +336,49 @@ class PlayerDB(TypedObject):
""" """
return self.__class__.objects.swap_character(self, new_character, delete_old_character=delete_old_character) return self.__class__.objects.swap_character(self, new_character, delete_old_character=delete_old_character)
#
# Execution/action methods
#
def execute_cmd(self, raw_string):
"""
Do something as this playe. This command transparently
lets its typeclass execute the command.
raw_string - raw command input coming from the command line.
"""
# nick replacement - we require full-word matching.
raw_string = utils.to_unicode(raw_string)
raw_list = raw_string.split(None)
raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]]
for nick in PlayerNick.objects.filter(db_obj=self, db_type__in=("inputline","channel")):
if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break
cmdhandler.cmdhandler(self.typeclass(self), raw_string)
def search(self, ostring, global_search=False, attribute_name=None, use_nicks=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.
"""
if self.character:
# run the normal search
return self.character.search(ostring, global_search=global_search, attribute_name=attribute_name,
use_nicks=use_nicks, location=location,
ignore_errors=ignore_errors, player=player)
if player:
# seach for players
matches = self.__class__.objects.player_search(ostring)
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(self, ostring, global_search=global_search)
# deal with results
matches = AT_SEARCH_RESULT(self, ostring, matches, global_search=global_search)
return matches

View file

@ -12,18 +12,19 @@ instead for most things).
""" """
from src.typeclasses.typeclass import TypeClass from src.typeclasses.typeclass import TypeClass
from settings import CMDSET_OOC
class Player(TypeClass): class Player(TypeClass):
""" """
Base typeclass for all Players. Base typeclass for all Players.
""" """
def at_player_creation(self): def basetype_setup(self):
""" """
This is called once, the very first time This sets up the basic properties for a player.
the player is created (i.e. first time they Overload this with at_player_creation rather than
register with the game). It's a good place changing this method.
to store attributes all players should have,
like configuration values etc.
""" """
# the text encoding to use. # the text encoding to use.
self.db.encoding = "utf-8" self.db.encoding = "utf-8"
@ -35,6 +36,21 @@ class Player(TypeClass):
self.locks.add("boot:perm(Wizards)") self.locks.add("boot:perm(Wizards)")
self.locks.add("msg:all()") self.locks.add("msg:all()")
# The ooc player cmdset
self.cmdset.add_default(CMDSET_OOC, permanent=True)
self.cmdset.outside_access = False
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.
"""
pass
# Note that the hooks below also exist # Note that the hooks below also exist
# in the character object's typeclass. You # in the character object's typeclass. You
# can often ignore these and rely on the # can often ignore these and rely on the

View file

@ -1,10 +1,12 @@
""" """
This module implements the main Evennia server process, the core of This module implements the main Evennia server process, the core of
the game engine. Only import this once! the game engine. Don't import this module! If you need to access the
server processes from code, instead import sessionhandler.SESSIONS
and use its 'server' property.
This module should be started with the 'twistd' executable since it This module should be started with the 'twistd' executable since it
sets up all the networking features. (this is done by sets up all the networking features. (this is done by automatically
game/evennia.py). by game/evennia.py).
""" """
import time import time

View file

@ -145,13 +145,12 @@ class SessionBase(object):
and have to be done right after this function! and have to be done right after this function!
""" """
if self.logged_in: if self.logged_in:
character = self.get_character() player = self.get_player()
if character: uaccount = player.user
uaccount = character.player.user uaccount.last_login = datetime.now()
uaccount.last_login = datetime.now() uaccount.save()
uaccount.save() self.at_disconnect()
self.at_disconnect() self.logged_in = False
self.logged_in = False
SESSIONS.remove_session(self) SESSIONS.remove_session(self)
def session_validate(self): def session_validate(self):
@ -230,13 +229,15 @@ class SessionBase(object):
character = self.get_character() character = self.get_character()
if character: if character:
#print "loggedin _execute_cmd: '%s' __ %s" % (command_string, character)
# normal operation. # normal operation.
character.execute_cmd(command_string) character.execute_cmd(command_string)
else: else:
#print "unloggedin _execute_cmd: '%s' __ %s" % (command_string, character) if self.logged_in:
# we are not logged in yet; call cmdhandler directly # there is no character, but we are logged in. Use player instead.
cmdhandler.cmdhandler(self, command_string, unloggedin=True) self.get_player().execute_cmd(command_string)
else:
# we are not logged in. Use special unlogged-in call.
cmdhandler.cmdhandler(self, command_string, unloggedin=True)
self.update_session_counters() self.update_session_counters()
def get_data_obj(self, **kwargs): def get_data_obj(self, **kwargs):

View file

@ -136,21 +136,18 @@ DATABASE_PORT = ''
################################################### ###################################################
# An alternate command parser module to use # An alternate command parser module to use
# (if not set, uses 'src.commands.cmdparser') COMMAND_PARSER = "src.commands.cmdparser.cmdparser"
ALTERNATE_PARSER = ""
# How many space-separated words a command name may have # How many space-separated words a command name may have
# and still be identified as one single command # and still be identified as one single command
# (e.g. 'push button' instead of 'pushbutton') # (e.g. 'push button' instead of 'pushbutton')
COMMAND_MAXLEN = 3 COMMAND_MAXLEN = 3
# The handler that outputs errors when searching # The handler that outputs errors when searching
# objects using object.search(). (If not set, uses # objects using object.search().
# src.objects.object_search_funcs.handle_search_errors) SEARCH_AT_RESULT = "src.commands.cmdparser.at_search_result"
ALTERNATE_OBJECT_SEARCH_ERROR_HANDLER = ""
# The parser used in order to separate multiple # The parser used in order to separate multiple
# object matches (so you can separate between same-named # object matches (so you can separate between same-named
# objects without using dbrefs). (If not set, uses # objects without using dbrefs).
# src.objects.object_search_funcs.object_multimatch_parser). SEARCH_AT_MULTIMATCH_INPUT = "src.commands.cmdparser.at_multimatch_input"
ALTERNATE_OBJECT_SEARCH_MULTIMATCH_PARSER = ""
# The module holding text strings for the connection screen. # The module holding text strings for the connection screen.
# 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.
@ -162,8 +159,10 @@ CONNECTION_SCREEN_MODULE = "game.gamesrc.world.connection_screens"
# Command set used before player has logged in # Command set used before player has logged in
CMDSET_UNLOGGEDIN = "game.gamesrc.commands.basecmdset.UnloggedinCmdSet" CMDSET_UNLOGGEDIN = "game.gamesrc.commands.basecmdset.UnloggedinCmdSet"
# Default set for logged in players (fallback) # Default set for logged in player with characters (fallback)
CMDSET_DEFAULT = "game.gamesrc.commands.basecmdset.DefaultCmdSet" CMDSET_DEFAULT = "game.gamesrc.commands.basecmdset.DefaultCmdSet"
# Command set for players without a character (ooc)
CMDSET_OOC = "game.gamesrc.commands.basecmdset.OOCCmdSet"
################################################### ###################################################
# Default Object typeclasses # Default Object typeclasses

View file

@ -294,6 +294,87 @@ class Attribute(SharedMemoryModel):
return self.locks.check(accessing_obj, access_type=access_type, default=default) return self.locks.check(accessing_obj, access_type=access_type, default=default)
#------------------------------------------------------------
#
# Nicks
#
#------------------------------------------------------------
class TypeNick(SharedMemoryModel):
"""
This model holds whichever alternate names this object
has for OTHER objects, but also for arbitrary strings,
channels, players etc. Setting a nick does not affect
the nicknamed object at all (as opposed to Aliases above),
and only this object will be able to refer to the nicknamed
object by the given nick.
The default nick types used by Evennia are:
inputline (default) - match against all input
player - match against player searches
obj - match against object searches
channel - used to store own names for channels
"""
db_nick = models.CharField(max_length=255, db_index=True) # the nick
db_real = models.TextField() # the aliased string
db_type = models.CharField(default="inputline", max_length=16, null=True, blank=True) # the type of nick
db_obj = None #models.ForeignKey("ObjectDB")
class Meta:
"Define Django meta options"
abstract = True
verbose_name = "Nickname"
verbose_name_plural = "Nicknames"
unique_together = ("db_nick", "db_type", "db_obj")
class TypeNickHandler(object):
"""
Handles nick access and setting. Accessed through ObjectDB.nicks
"""
NickClass = TypeNick
def __init__(self, obj):
"Setup"
self.obj = obj
def add(self, nick, realname, nick_type="inputline"):
"We want to assign a new nick"
if not nick or not nick.strip():
return
nick = nick.strip()
real = realname.strip()
query = self.NickClass.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
if query.count():
old_nick = query[0]
old_nick.db_real = real
old_nick.save()
else:
new_nick = self.NickClass(db_nick=nick, db_real=real, db_type=nick_type, db_obj=self.obj)
new_nick.save()
def delete(self, nick, nick_type="inputline"):
"Removes a nick"
nick = nick.strip()
query = self.NickClass.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
if query.count():
# remove the found nick(s)
query.delete()
def get(self, nick=None, nick_type="inputline"):
if nick:
query = self.NickClass.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
query = query.values_list("db_real", flat=True)
if query.count():
return query[0]
else:
return nick
else:
return self.NickClass.objects.filter(db_obj=self.obj)
def has(self, nick, nick_type="inputline"):
"Returns true/false if this nick is defined or not"
return self.NickClass.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type).count()
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Typed Objects # Typed Objects
@ -992,9 +1073,12 @@ class TypedObject(SharedMemoryModel):
def check_permstring(self, permstring): def check_permstring(self, permstring):
""" """
This explicitly checks for we hold particular permission without involving This explicitly checks if we hold particular permission without involving
any locks. any locks.
""" """
if self.player and self.player.is_superuser:
return True
if not permstring: if not permstring:
return False return False
perm = permstring.lower() perm = permstring.lower()

View file

@ -390,6 +390,7 @@ def create_player(name, email, password,
new_player = PlayerDB(db_key=name, user=new_user) new_player = PlayerDB(db_key=name, user=new_user)
new_player.save() new_player.save()
new_player.basetype_setup() # setup the basic locks and cmdset
# call hook method (may override default permissions) # call hook method (may override default permissions)
new_player.at_player_creation() new_player.at_player_creation()

View file

@ -173,8 +173,8 @@ def reset_loop():
[m.locks.reset() for m in Msg.objects.all()] [m.locks.reset() for m in Msg.objects.all()]
[c.locks.reset() for c in Channel.objects.all()] [c.locks.reset() for c in Channel.objects.all()]
[s.locks.reset() for s in ScriptDB.objects.all()] [s.locks.reset() for s in ScriptDB.objects.all()]
[p.locks.reset() for p in PlayerDB.objects.all()]
[(o.typeclass(o), o.cmdset.reset(), o.locks.reset()) for o in ObjectDB.get_all_cached_instances()] [(o.typeclass(o), o.cmdset.reset(), o.locks.reset()) for o in ObjectDB.get_all_cached_instances()]
[(p.typeclass(p), p.cmdset.reset(), p.locks.reset()) for p in PlayerDB.get_all_cached_instances()]
t2 = time.time() t2 = time.time()
cemit_info(" ... Loop finished in %g seconds." % (t2-t1)) cemit_info(" ... Loop finished in %g seconds." % (t2-t1))

View file

@ -27,11 +27,15 @@ Example: To reach the search method 'get_object_with_user'
# Import the manager methods to be wrapped # Import the manager methods to be wrapped
from src.objects.models import ObjectDB from django.contrib.contenttypes.models import ContentType
from src.players.models import PlayerDB
from src.scripts.models import ScriptDB # import objects this way to avoid circular import problems
from src.comms.models import Msg, Channel ObjectDB = ContentType.objects.get(app_label="objects", model="objectdb").model_class()
from src.help.models import HelpEntry PlayerDB = ContentType.objects.get(app_label="players", model="playerdb").model_class()
ScriptDB = ContentType.objects.get(app_label="scripts", model="scriptdb").model_class()
Msg = ContentType.objects.get(app_label="comms", model="msg").model_class()
Channel = ContentType.objects.get(app_label="comms", model="channel").model_class()
HelpEntry = ContentType.objects.get(app_label="help", model="helpentry").model_class()
# #
# Search objects as a character # Search objects as a character

View file

@ -20,3 +20,5 @@ class EvenniaTestSuiteRunner(DjangoTestSuiteRunner):
test_labels = [applabel.rsplit('.', 1)[1] for applabel in settings.INSTALLED_APPS test_labels = [applabel.rsplit('.', 1)[1] for applabel in settings.INSTALLED_APPS
if (applabel.startswith('src.') or applabel.startswith('game.'))] if (applabel.startswith('src.') or applabel.startswith('game.'))]
return super(EvenniaTestSuiteRunner, self).build_suite(test_labels, extra_tests=extra_tests, **kwargs) return super(EvenniaTestSuiteRunner, self).build_suite(test_labels, extra_tests=extra_tests, **kwargs)

View file

@ -235,14 +235,28 @@ def dbref(dbref):
except Exception: except Exception:
return None return None
return dbref return dbref
return None
def to_unicode(obj, encoding='utf-8'): def to_unicode(obj, encoding='utf-8', force_string=False):
""" """
This decodes a suitable object to This decodes a suitable object to the unicode format. Note that
the unicode format. Note that one one needs to encode it back to utf-8 before writing to disk or
needs to encode it back to utf-8 printing. Note that non-string objects are let through without
before writing to disk or printing. conversion - this is important for e.g. Attributes. Use
force_string to enforce conversion of objects to string. .
""" """
if force_string and not isinstance(obj, basestring):
# some sort of other object. Try to
# convert it to a string representation.
if hasattr(obj, '__str__'):
obj = obj.__str__()
elif hasattr(obj, '__unicode__'):
obj = obj.__unicode__()
else:
# last resort
obj = str(obj)
if isinstance(obj, basestring) and not isinstance(obj, unicode): if isinstance(obj, basestring) and not isinstance(obj, unicode):
try: try:
obj = unicode(obj, encoding) obj = unicode(obj, encoding)
@ -257,11 +271,26 @@ def to_unicode(obj, encoding='utf-8'):
raise Exception("Error: '%s' contains invalid character(s) not in %s." % (obj, encoding)) raise Exception("Error: '%s' contains invalid character(s) not in %s." % (obj, encoding))
return obj return obj
def to_str(obj, encoding='utf-8'): def to_str(obj, encoding='utf-8', force_string=False):
""" """
This encodes a unicode string back to byte-representation, This encodes a unicode string back to byte-representation,
for printing, writing to disk etc. for printing, writing to disk etc. Note that non-string
objects are let through without modification - this is
required e.g. for Attributes. Use force_string to force
conversion of objects to strings.
""" """
if force_string and not isinstance(obj, basestring):
# some sort of other object. Try to
# convert it to a string representation.
if hasattr(obj, '__str__'):
obj = obj.__str__()
elif hasattr(obj, '__unicode__'):
obj = obj.__unicode__()
else:
# last resort
obj = str(obj)
if isinstance(obj, basestring) and isinstance(obj, unicode): if isinstance(obj, basestring) and isinstance(obj, unicode):
try: try:
obj = obj.encode(encoding) obj = obj.encode(encoding)
@ -321,7 +350,7 @@ def inherits_from(obj, parent):
Takes an object and tries to determine if it inherits at any distance Takes an object and tries to determine if it inherits at any distance
from parent. What differs this function from e.g. isinstance() from parent. What differs this function from e.g. isinstance()
is that obj may be both an instance and a class, and parent is that obj may be both an instance and a class, and parent
may be an instance, a class, or the python path to a class (counting < may be an instance, a class, or the python path to a class (counting
from the evennia root directory). from the evennia root directory).
""" """
@ -480,10 +509,11 @@ def has_parent(basepath, obj):
# instance. Not sure if one should defend against this. # instance. Not sure if one should defend against this.
return False return False
def mod_import(mod_path): def mod_import(mod_path, propname=None):
""" """
Takes filename of a module, converts it to a python path Takes filename of a module, converts it to a python path
and imports it. and imports it. If property is given, return the named
property from this module instead of the module itself.
""" """
def log_trace(errmsg=None): def log_trace(errmsg=None):
@ -494,6 +524,7 @@ def mod_import(mod_path):
""" """
from traceback import format_exc from traceback import format_exc
from twisted.python import log from twisted.python import log
print errmsg
tracestring = format_exc() tracestring = format_exc()
if tracestring: if tracestring:
@ -507,23 +538,39 @@ def mod_import(mod_path):
for line in errmsg.splitlines(): for line in errmsg.splitlines():
log.msg('[EE] %s' % line) log.msg('[EE] %s' % line)
if not os.path.isabs(mod_path): # first try to import as a python path
mod_path = os.path.abspath(mod_path) try:
path, filename = mod_path.rsplit(os.path.sep, 1) mod = __import__(mod_path, fromlist=["None"])
modname = filename.rstrip('.py') except ImportError:
try: # try absolute path import instead
result = imp.find_module(modname, [path])
except ImportError: if not os.path.isabs(mod_path):
log_trace("Could not find module '%s' (%s.py) at path '%s'" % (modname, modname, path)) mod_path = os.path.abspath(mod_path)
return path, filename = mod_path.rsplit(os.path.sep, 1)
try: modname = filename.rstrip('.py')
mod = imp.load_module(modname, *result)
except ImportError: try:
log_trace("Could not find or import module %s at path '%s'" % (modname, path)) result = imp.find_module(modname, [path])
mod = None except ImportError:
# we have to close the file handle manually log_trace("Could not find module '%s' (%s.py) at path '%s'" % (modname, modname, path))
result[0].close() return
try:
mod = imp.load_module(modname, *result)
except ImportError:
log_trace("Could not find or import module %s at path '%s'" % (modname, path))
mod = None
# we have to close the file handle manually
result[0].close()
if mod and propname:
# we have a module, extract the sought property from it.
try:
mod_prop = mod.__dict__[to_str(propname)]
except KeyError:
log_trace("Could not import property '%s' from module %s." % (propname, mod_path))
return None
return mod_prop
return mod return mod
def string_from_module(modpath, variable=None): def string_from_module(modpath, variable=None):