Largely rewrote and refactored the help system.

The help entry database structure has changed! You have to resync or purge
your database or your will get problems!

New features:
* Help entry access now fully controlled by evennia permissions
* Categories for each help entry
* All entries are created dynamically, with a See also: footer calculated
  after the current state of the database.
* Indexes and topic list calculated on the fly (alphabetically/after category)
* Added auto-help help entries for all default commands.
* Only shows commands _actually implemented_ - MUX help db moved into 'MUX' category
  which is not shown by default.
* More powerful auto-help markup - supports categories and permissions (and inheritance).
* Global on/off switch for auto-help, when entering production
* Auto_help_override switch for selectively activating auto-help when developing
  new commands (like the old system).
* Refactored State help system; no more risk of overwriting global help entries.
* State help now defers to main help db when no match found; makes system more transparent.
* State help entries also support categories/permissions (state categories are not
  used much though).

Other updates:
* Added more commands to the batch processor
* Many bug-fixes.

/Griatch
This commit is contained in:
Griatch 2009-10-14 18:15:15 +00:00
parent 46e2cd3ecb
commit 8074617285
27 changed files with 1995 additions and 1072 deletions

View file

@ -291,7 +291,8 @@ def match_exits(command,test=False):
raise ExitCommandHandler
def command_table_lookup(command, command_table, eval_perms=True,test=False,neighbor=None):
def command_table_lookup(command, command_table, eval_perms=True,
test=False, neighbor=None):
"""
Performs a command table lookup on the specified command table. Also
evaluates the permissions tuple.
@ -329,7 +330,7 @@ def match_neighbor_ctables(command,test=False):
Looks through the command tables of neighboring objects for command
matches.
test mode just checks if the command is a match, without manipulating
any commands.
any commands.
"""
source_object = command.source_object
if source_object.location != None:
@ -349,7 +350,7 @@ def match_neighbor_ctables(command,test=False):
#no matches
return False
def handle(command):
def handle(command, ignore_state=False):
"""
Use the spliced (list) uinput variable to retrieve the correct
command, or return an invalid command error.
@ -357,6 +358,8 @@ def handle(command):
We're basically grabbing the player's command by tacking
their input on to 'cmd_' and looking it up in the GenCommands
class.
ignore_state : ignore eventual statetable lookups completely.
"""
try:
# TODO: Protect against non-standard characters.
@ -379,11 +382,11 @@ def handle(command):
state = command.source_object.get_state()
state_cmd_table = statetable.GLOBAL_STATE_TABLE.get_cmd_table(state)
if state and state_cmd_table:
if state and state_cmd_table and not ignore_state:
# Caller is in a special state.
state_allow_exits, state_allow_obj_cmds = \
statetable.GLOBAL_STATE_TABLE.get_state_flags(state)
statetable.GLOBAL_STATE_TABLE.get_exec_rights(state)
state_lookup = True
if match_channel(command):

View file

@ -14,8 +14,8 @@ based on the value of settings.COMMAND_MODULES and
settings.CUSTOM_COMMAND_MODULES. Each module imports cmdtable.py and runs
add_command on the command table each command belongs to.
"""
from src.helpsys.management.commands.edit_helpfiles import add_help
from django.conf import settings
from src.helpsys import helpsystem
class CommandTable(object):
"""
@ -28,7 +28,8 @@ class CommandTable(object):
self.ctable = {}
def add_command(self, command_string, function, priv_tuple=None,
extra_vals=None, auto_help=False, staff_help=False):
extra_vals=None, help_category="", priv_help_tuple=None,
auto_help_override=None):
"""
Adds a command to the command table.
@ -36,29 +37,45 @@ class CommandTable(object):
function: (reference) The command's function.
priv_tuple: (tuple) String tuple of permissions required for command.
extra_vals: (dict) Dictionary to add to the Command object.
Auto-help system:
auto_help (bool): If true, automatically creates/replaces a help topic with the
same name as the command_string, using the functions's __doc__ property
for help text.
staff_help (bool): Only relevant if auto_help is activated; If True, makes the
help topic (and all eventual subtopics) only visible to staff.
Note: the auto_help system also supports limited markup. If you divide your __doc__
with markers of the form <<TOPIC:MyTopic>>, the system will automatically create
separate help topics for each topic. Your initial text (if you define no TOPIC)
will still default to the name of your command.
You can also custon-set the staff_only flag for individual subtopics by
using the markup <<TOPIC:STAFF:MyTopic>> and <<TOPIC:NOSTAFF:MyTopic>>.
Auto-help system: (this is only used if settings.HELP_AUTO_ENABLED is active)
help_category (str): An overall help category where auto-help will place
the help entry. If not given, 'General' is assumed.
priv_help_tuple (tuple) String tuple of permissions required to view this
help entry. If nothing is given, priv_tuple is used.
auto_help_override (bool): Override the value in settings.AUTO_HELP_ENABLED with the
value given. Use None to not override.
This can be useful when developing a new routine and
has made manual changes to help entries of other
commands in the database (and so do not want to use global
auto-help). It is also used by e.g. the state system
to selectively deactive auto-help.
Note: the auto_help system also supports limited markup. You can divide your __doc__
with markers of any combinations of the forms
[[Title]]
[[Title, category]]
[[Title, (priv_tuple)]]
[[Title, category, (priv_tuple)]],
If such markers are found, the system will automatically create
separate help topics for each topic. Your main help entry will
default to the name of your command.
"""
self.ctable[command_string] = (function, priv_tuple, extra_vals)
if auto_help:
if auto_help_override == None:
auto_help_override = settings.HELP_AUTO_ENABLED
if auto_help_override:
#add automatic help text from the command's doc string
topicstr = command_string
entrytext = function.__doc__
add_help(topicstr, entrytext, staff_only=staff_help,
force_create=True, auto_help=True)
if not help_category:
help_category = "General"
if not priv_help_tuple:
priv_help_tuple = priv_tuple
helpsystem.edithelp.add_help_auto(topicstr, help_category,
entrytext, priv_help_tuple)
def get_command_tuple(self, func_name):
"""
@ -67,10 +84,6 @@ class CommandTable(object):
"""
return self.ctable.get(func_name, False)
"""
Command tables
"""
# Global command table, for authenticated users.
GLOBAL_CMD_TABLE = CommandTable()
# Global unconnected command table, for unauthenticated users.

View file

@ -55,7 +55,7 @@ from src.statetable import GLOBAL_STATE_TABLE
#global defines for storage
STATENAME="_interactive_batch_processor"
STATENAME="interactive batch processor"
CMDSTACKS={} # user:cmdstack pairs (for interactive)
STACKPTRS={} # user:stackpointer pairs (for interactive)
FILENAMES={} # user:filename pairs (for interactive/reload)
@ -161,6 +161,8 @@ def batch_process(source_object, commands):
def cmd_batchprocess(command):
"""
@batchprocess - build from batch file
Usage:
@batchprocess[/interactive] <filename with full path>
@ -198,16 +200,16 @@ def cmd_batchprocess(command):
CMDSTACKS[source_object] = commands
STACKPTRS[source_object] = 0
FILENAMES[source_object] = filename
source_object.emit_to("Interactive mode (h for help).")
source_object.emit_to("\nBatch processor - Interactive mode ...")
show_curr(source_object)
else:
source_object.emit_to("Running Batch processor - Automatic mode ...")
source_object.clear_state()
batch_process(source_object, commands)
source_object.emit_to("%s== Batchfile '%s' applied." % (cgreen,filename))
GLOBAL_CMD_TABLE.add_command("@batchprocess", cmd_batchprocess,
auto_help=True, staff_help=True,
priv_tuple=("genperms.process_control"))
priv_tuple=("genperms.process_control",), help_category="Building")
#interactive state commands
@ -271,43 +273,71 @@ def reload_stack(source_object):
source_object.emit_to("Commands in file could not be reloaded. Was it moved?")
def move_in_stack(source_object, step=1):
"store data in stack"
global CMDSTACKS, STACKPTRS
N = len(CMDSTACKS[source_object])
currpos = STACKPTRS[source_object]
STACKPTRS[source_object] = max(0,min(N-1,currpos+step))
def exit_state(source_object):
"Quit the state"
global CMDSTACKS,STACKPTRS,FILENAMES
del CMDSTACKS[source_object]
del STACKPTRS[source_object]
del FILENAMES[source_object]
try:
del CMDSTACKS[source_object]
del STACKPTRS[source_object]
del FILENAMES[source_object]
except KeyError:
logger.log_errmsg("Batchprocessor quit error: all state vars could not be deleted.")
source_object.clear_state()
def cmd_state_l(command):
"l-ook at current command definition"
def cmd_state_ll(command):
"""
ll
Look at the full source for the current
command definition.
"""
show_curr(command.source_object,showall=True)
def cmd_state_p(command):
"p-rocess current command definition"
def cmd_state_pp(command):
"""
pp
Process the currently shown command definition.
"""
process_commands(command.source_object)
command.source_object.emit_to(printfooter())
def cmd_state_r(command):
"r-eload file, keep current stack position"
def cmd_state_rr(command):
"""
rr
Reload the batch file, keeping the current
position in it.
"""
reload_stack(command.source_object)
command.source_object.emit_to("\nFile reloaded. Staying on same command.\n")
show_curr(command.source_object)
def cmd_state_rr(command):
"r-eload file, start over"
def cmd_state_rrr(command):
"""
rrr
Reload the batch file, starting over
from the beginning.
"""
global STACKPTRS
reload_stack(command.source_object)
STACKPTRS[command.source_object] = 0
command.source_object.emit_to("\nFile reloaded. Restarting from top.\n")
show_curr(command.source_object)
def cmd_state_n(command):
"n-ext command (no exec)"
def cmd_state_nn(command):
"""
nn
Go to next command. No commands are executed.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
@ -317,8 +347,29 @@ def cmd_state_n(command):
move_in_stack(source_object, step)
show_curr(source_object)
def cmd_state_b(command):
"b-ackwards to previous command (no exec)"
def cmd_state_nl(command):
"""
nl
Go to next command, viewing its full source.
No commands are executed.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = int(command.command_argument)
else:
step = 1
move_in_stack(source_object, step)
show_curr(source_object, showall=True)
def cmd_state_bb(command):
"""
bb
Backwards to previous command. No commands
are executed.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
@ -328,8 +379,30 @@ def cmd_state_b(command):
move_in_stack(source_object, step)
show_curr(source_object)
def cmd_state_s(command):
"s-tep to next command (exec)"
def cmd_state_bl(command):
"""
bl
Backwards to previous command, viewing its full
source. No commands are executed.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = -int(command.command_argument)
else:
step = -1
move_in_stack(source_object, step)
show_curr(source_object, showall=True)
def cmd_state_ss(command):
"""
ss [steps]
Process current command, then step to the next
one. If steps is given,
process this many commands.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
@ -339,8 +412,30 @@ def cmd_state_s(command):
process_commands(source_object,step)
show_curr(source_object)
def cmd_state_c(command):
"c-ontinue to process remaining"
def cmd_state_sl(command):
"""
sl [steps]
Process current command, then step to the next
one, viewing its full source. If steps is given,
process this many commands.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = int(command.command_argument)
else:
step = 1
process_commands(source_object,step)
show_curr(source_object, showall=True)
def cmd_state_cc(command):
"""
cc
Continue to process all remaining
commands.
"""
global CMDSTACKS,STACKPTRS
source_object = command.source_object
N = len(CMDSTACKS[source_object])
@ -350,8 +445,12 @@ def cmd_state_c(command):
exit_state(source_object)
source_object.emit_to("Finished processing batch file.")
def cmd_state_j(command):
"j-ump to specific command index"
def cmd_state_jj(command):
"""
j <command number>
Jump to specific command number
"""
global STACKPTRS
source_object = command.source_object
arg = command.command_argument
@ -365,18 +464,44 @@ def cmd_state_j(command):
move_in_stack(source_object, step)
show_curr(source_object)
def cmd_state_q(command):
"q-uit state."
def cmd_state_jl(command):
"""
jl <command number>
Jump to specific command number and view its full source.
"""
global STACKPTRS
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
no = int(command.command_argument)-1
else:
source_object.emit_to("You must give a number index.")
return
ptr = STACKPTRS[source_object]
step = no - ptr
move_in_stack(source_object, step)
show_curr(source_object, showall=True)
def cmd_state_qq(command):
"""
qq
Quit the batchprocessor.
"""
exit_state(command.source_object)
command.source_object.emit_to("Aborted interactive batch mode.")
def cmd_state_h(command):
def cmd_state_hh(command):
"Help command"
s = """
Interactive batch processing commands:
nn [steps] - next command (no processing)
nl [steps] - next & look
bb [steps] - back to previous command (no processing)
bl [steps] - back & look
jj <N> - jump to command no N (no processing)
jl <N> - jump & look
pp - process currently shown command (no step)
ss [steps] - process & step
ll - look at full definition of current command
@ -384,8 +509,8 @@ def cmd_state_h(command):
rrr - reload batch file (start from first)
hh - this help list
cc - continue processing to end and quit.
qq - quit (abort all remaining)
cc - continue processing to end, then quit.
qq - quit (abort all remaining commands)
"""
command.source_object.emit_to(s)
@ -394,14 +519,18 @@ def cmd_state_h(command):
GLOBAL_STATE_TABLE.add_state(STATENAME,global_cmds='all',
allow_exits=True,allow_obj_cmds=True)
#add state commands
GLOBAL_STATE_TABLE.add_command(STATENAME,"nn",cmd_state_n)
GLOBAL_STATE_TABLE.add_command(STATENAME,"bb",cmd_state_b)
GLOBAL_STATE_TABLE.add_command(STATENAME,"jj",cmd_state_j)
GLOBAL_STATE_TABLE.add_command(STATENAME,"pp",cmd_state_p)
GLOBAL_STATE_TABLE.add_command(STATENAME,"ss",cmd_state_s)
GLOBAL_STATE_TABLE.add_command(STATENAME,"cc",cmd_state_c)
GLOBAL_STATE_TABLE.add_command(STATENAME,"ll",cmd_state_l)
GLOBAL_STATE_TABLE.add_command(STATENAME,"rr",cmd_state_r)
GLOBAL_STATE_TABLE.add_command(STATENAME,"rrr",cmd_state_rr)
GLOBAL_STATE_TABLE.add_command(STATENAME,"hh",cmd_state_h)
GLOBAL_STATE_TABLE.add_command(STATENAME,"qq",cmd_state_q)
GLOBAL_STATE_TABLE.add_command(STATENAME,"nn",cmd_state_nn)
GLOBAL_STATE_TABLE.add_command(STATENAME,"nl",cmd_state_nl)
GLOBAL_STATE_TABLE.add_command(STATENAME,"bb",cmd_state_bb)
GLOBAL_STATE_TABLE.add_command(STATENAME,"bl",cmd_state_bl)
GLOBAL_STATE_TABLE.add_command(STATENAME,"jj",cmd_state_jj)
GLOBAL_STATE_TABLE.add_command(STATENAME,"jl",cmd_state_jl)
GLOBAL_STATE_TABLE.add_command(STATENAME,"pp",cmd_state_pp)
GLOBAL_STATE_TABLE.add_command(STATENAME,"ss",cmd_state_ss)
GLOBAL_STATE_TABLE.add_command(STATENAME,"sl",cmd_state_sl)
GLOBAL_STATE_TABLE.add_command(STATENAME,"cc",cmd_state_cc)
GLOBAL_STATE_TABLE.add_command(STATENAME,"ll",cmd_state_ll)
GLOBAL_STATE_TABLE.add_command(STATENAME,"rr",cmd_state_rr)
GLOBAL_STATE_TABLE.add_command(STATENAME,"rrr",cmd_state_rrr)
GLOBAL_STATE_TABLE.add_command(STATENAME,"hh",cmd_state_hh)
GLOBAL_STATE_TABLE.add_command(STATENAME,"qq",cmd_state_qq)

View file

@ -1,26 +1,22 @@
"""
Comsys command module.
"""
import time
from django.conf import settings
from src import comsys
from src.channels.models import CommChannelMembership, CommChannel
from src import defines_global
from src import ansi
from src.util import functions_general
from src.objects.models import Object
from src.cmdtable import GLOBAL_CMD_TABLE
def cmd_addcom(command):
"""
addcom
addcom - join a channel with alias
Usage:
addcom [alias=] <channel>
Joins a channel. Allows adding an alias for it to make it
easier and faster to use. Subsequent calls of this command
can be used to add multiple aliases.
Allows adding an alias for a channel to make is easier and
faster to use. Subsequent calls of this command can
be used to add multiple aliases.
"""
source_object = command.source_object
command_argument = command.command_argument
@ -81,11 +77,11 @@ def cmd_addcom(command):
except CommChannel.DoesNotExist:
# Failed to match iexact on channel's 'name' attribute.
source_object.emit_to("Could not find channel %s." % chan_name)
GLOBAL_CMD_TABLE.add_command("addcom", cmd_addcom)
GLOBAL_CMD_TABLE.add_command("addcom", cmd_addcom, help_category="Comms")
def cmd_delcom(command):
"""
delcom
delcom - remove a channel alias
Usage:
delcom <alias>
@ -113,10 +109,15 @@ def cmd_delcom(command):
leave_msg = "%s has left the channel." % \
(source_object.get_name(show_dbref=False),)
comsys.send_cmessage(chan_name, leave_msg)
GLOBAL_CMD_TABLE.add_command("delcom", cmd_delcom),
GLOBAL_CMD_TABLE.add_command("delcom", cmd_delcom,help_category="Comms")
def cmd_comlist(command):
"""
comlist - list channel memberships
Usage:
comlist
Lists the channels a user is subscribed to.
"""
source_object = command.source_object
@ -137,11 +138,14 @@ def cmd_comlist(command):
chan.get_name(), chan_on)
s = s[:-1]
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("comlist", cmd_comlist)
GLOBAL_CMD_TABLE.add_command("comlist", cmd_comlist,help_category="Comms")
def cmd_allcom(command):
"""
allcom [on|off|who|clear]
allcom - operate on all channels
Usage:
allcom [on | off | who | clear]
Allows the user to universally turn off or on all channels they are on,
as well as perform a 'who' for all channels they are on. Clear deletes
@ -197,14 +201,18 @@ def cmd_allcom(command):
s = s[:-2] + "\n"
s = s[:-1]
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("allcom", cmd_allcom)
GLOBAL_CMD_TABLE.add_command("allcom", cmd_allcom, help_category="Comms")
def cmd_clearcom(command):
"""
clearcom
clearcom - removes all channels
Effectively runs delcom on all channels the user is on. It will remove their aliases,
remove them from the channel, and clear any titles they have set.
Usage:
clearcom
Effectively runs delcom on all channels the user is on. It will remove
their aliases, remove them from the channel, and clear any titles they
have set.
"""
source_object = command.source_object
#get aall subscribed channel memberships
@ -227,7 +235,10 @@ def cmd_clist(command):
"""
@clist
Lists all available channels on the game.
Usage:
@clist
Lists all available channels in the game.
"""
session = command.session
source_object = command.source_object
@ -248,13 +259,17 @@ def cmd_clist(command):
s = s[:-1]
#s += "** End of Channel List **"
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("@clist", cmd_clist),
GLOBAL_CMD_TABLE.add_command("@clist", cmd_clist, help_category="Comms")
def cmd_cdestroy(command):
"""
@cdestroy
Destroys a channel.
Usage:
@cdestroy <channel>
Destroys a channel that you control.
"""
source_object = command.source_object
cname = command.command_argument
@ -275,7 +290,7 @@ def cmd_cdestroy(command):
else:
source_object.emit_to("Permission denied.")
return
GLOBAL_CMD_TABLE.add_command("@cdestroy", cmd_cdestroy)
GLOBAL_CMD_TABLE.add_command("@cdestroy", cmd_cdestroy, help_category="Comms")
def cmd_cset(command):
"""
@ -297,9 +312,12 @@ def cmd_ccharge(command):
def cmd_cboot(command):
"""
@cboot[/quiet] <channel>=<object>
@cboot
Kicks a player or object from the channel
Usage:
@cboot[/quiet] <channel> = <player or object>
Kicks a player or object from a channel you control.
"""
source_object = command.source_object
args = command.command_argument
@ -352,14 +370,17 @@ def cmd_cboot(command):
for mship in membership:
comsys.plr_del_channel(bootobj, mship.user_alias)
GLOBAL_CMD_TABLE.add_command("@cboot", cmd_cboot)
GLOBAL_CMD_TABLE.add_command("@cboot", cmd_cboot, help_category="Comms")
def cmd_cemit(command):
"""
@cemit <channel>=<message>
@cemit/noheader <channel>=<message>
@cemit/sendername <channel>=<message>
@cemit - send a message to channel
Usage:
@cemit <channel>=<message>
@cemit/noheader <channel>=<message>
@cemit/sendername <channel>=<message>
Allows the user to send a message over a channel as long as
they own or control it. It does not show the user's name unless they
@ -425,12 +446,12 @@ def cmd_cemit(command):
#pipe to external channels (IRC, IMC) eventually mapped to this channel
comsys.send_cexternal(cname_parsed, cmessage, caller=source_object)
GLOBAL_CMD_TABLE.add_command("@cemit", cmd_cemit,priv_tuple=("channels.emit_commchannel",))
GLOBAL_CMD_TABLE.add_command("@cemit", cmd_cemit,priv_tuple=("channels.emit_commchannel",),
help_category="Comms")
def cmd_cwho(command):
"""
@cwho
list
Usage:
@cwho channel[/all]
@ -468,15 +489,17 @@ def cmd_cwho(command):
else:
source_object.emit_to("No channel with that name was found.")
return
GLOBAL_CMD_TABLE.add_command("@cwho", cmd_cwho),
GLOBAL_CMD_TABLE.add_command("@cwho", cmd_cwho, help_category="Comms")
def cmd_ccreate(command):
"""
@ccreate
Creates a new channel with the invoker being the default owner.
Usage:
@ccreate <new channel>
Creates a new channel owned by you.
"""
# TODO: Implement cmd_ccreate
source_object = command.source_object
cname = command.command_argument
@ -496,11 +519,14 @@ def cmd_ccreate(command):
# Create and set the object up.
new_chan = comsys.create_channel(cname, source_object)
source_object.emit_to("Channel %s created." % (new_chan.get_name(),))
GLOBAL_CMD_TABLE.add_command("@ccreate", cmd_ccreate)
GLOBAL_CMD_TABLE.add_command("@ccreate", cmd_ccreate, help_category="Comms")
def cmd_cchown(command):
"""
@cchown <channel>=<player>
@cchown
Usage:
@cchown <channel> = <player>
Changes the owner of a channel.
"""
@ -535,4 +561,4 @@ def cmd_cchown(command):
channel.set_owner(new_owner)
source_object.emit_to("Owner of %s changed from %s to %s." % (cname, old_pname, pname))
new_owner.emit_to("%s transfered ownership of channel '%s' to you." % (old_pname, cname))
GLOBAL_CMD_TABLE.add_command("@cchown", cmd_cchown)
GLOBAL_CMD_TABLE.add_command("@cchown", cmd_cchown, help_category="Comms")

View file

@ -7,17 +7,19 @@ from django.contrib.auth.models import User
from src.config.models import ConfigValue
from src.helpsys.models import HelpEntry
from src.ansi import ANSITable
from src import defines_global
from src import session_mgr
from src.util import functions_general
import src.helpsys.management.commands.edit_helpfiles as edit_help
from src.helpsys import helpsystem
from src.cmdtable import GLOBAL_CMD_TABLE
def cmd_password(command):
"""
Changes your own password.
@password <Oldpass>=<Newpass>
@password - set your password
Usage:
@paassword <old password> = <new password>
Changes your password. Make sure to pick a safe one.
"""
source_object = command.source_object
@ -54,18 +56,27 @@ def cmd_password(command):
uaccount.set_password(newpass)
uaccount.save()
source_object.emit_to("Password changed.")
GLOBAL_CMD_TABLE.add_command("@password", cmd_password)
GLOBAL_CMD_TABLE.add_command("@password", cmd_password, help_category="System")
def cmd_pemit(command):
"""
@pemit
Emits something to a player.
(Not yet implemented)
"""
# TODO: Implement cmd_pemit
#GLOBAL_CMD_TABLE.add_command("@pemit", cmd_pemit)
def cmd_emit(command):
"""
Emits something to your location.
@emit
Usage:
@emit <message>
Emits a message to your immediate surroundings.
"""
message = command.command_argument
@ -74,10 +85,15 @@ def cmd_emit(command):
else:
command.source_object.emit_to("Emit what?")
GLOBAL_CMD_TABLE.add_command("@emit", cmd_emit,
priv_tuple=("genperms.announce")),
priv_tuple=("genperms.announce",),help_category="Comms"),
def cmd_wall(command):
"""
@wall
Usage:
@wall <message>
Announces a message to all connected players.
"""
wallstring = command.command_argument
@ -90,18 +106,29 @@ def cmd_wall(command):
command.source_object.get_name(show_dbref=False), wallstring)
session_mgr.announce_all(message)
GLOBAL_CMD_TABLE.add_command("@wall", cmd_wall,
priv_tuple=("genperms.announce"))
priv_tuple=("genperms.announce",),help_category="Comms")
def cmd_idle(command):
"""
Returns nothing, this lets the player set an idle timer without spamming
his screen.
idle
Usage:
idle
Returns and does nothing. You can use this to send idle
messages to the game, in order to avoid getting timed out.
"""
pass
GLOBAL_CMD_TABLE.add_command("idle", cmd_idle)
GLOBAL_CMD_TABLE.add_command("idle", cmd_idle, help_category="System")
def cmd_inventory(command):
"""
inventory
Usage:
inventory
inv
Shows a player's inventory.
"""
source_object = command.source_object
@ -121,7 +148,13 @@ GLOBAL_CMD_TABLE.add_command("inventory", cmd_inventory)
def cmd_look(command):
"""
Handle looking at objects.
look
Usage:
look
look <obj>
Observers your location or objects in your vicinity.
"""
source_object = command.source_object
@ -144,7 +177,13 @@ GLOBAL_CMD_TABLE.add_command("look", cmd_look)
def cmd_get(command):
"""
Get an object and put it in a player's inventory.
get
Usage:
get <obj>
Picks up an object from your location and puts it in
your inventory.
"""
source_object = command.source_object
obj_is_staff = source_object.is_staff()
@ -193,11 +232,15 @@ GLOBAL_CMD_TABLE.add_command("get", cmd_get)
def cmd_drop(command):
"""
Drop an object from a player's inventory into their current location.
drop
Usage:
drop <obj>
Has you drop an object from your inventory into the
location you are currently in.
"""
source_object = command.source_object
obj_is_staff = source_object.is_staff()
if not command.command_argument:
source_object.emit_to("Drop what?")
return
@ -219,153 +262,40 @@ def cmd_drop(command):
target_obj.get_name(show_dbref=False)),
exclude=source_object)
# SCRIPT: Call the object's script's a_drop() method.
# SCRIPT: Call the object script's a_drop() method.
target_obj.scriptlink.at_drop(source_object)
GLOBAL_CMD_TABLE.add_command("drop", cmd_drop),
def cmd_examine(command):
"""
Detailed object examine command
"""
source_object = command.source_object
attr_search = False
if not command.command_argument:
# If no arguments are provided, examine the invoker's location.
target_obj = source_object.get_location()
else:
# Look for a slash in the input, indicating an attribute search.
attr_split = command.command_argument.split("/", 1)
# If the splitting by the "/" character returns a list with more than 1
# entry, it's an attribute match.
if len(attr_split) > 1:
attr_search = True
# Strip the object search string from the input with the
# object/attribute pair.
obj_searchstr = attr_split[0]
attr_searchstr = attr_split[1].strip()
# Protect against stuff like: ex me/
if attr_searchstr == '':
source_object.emit_to('No attribute name provided.')
return
else:
# No slash in argument, just examine an object.
obj_searchstr = command.command_argument
# Resolve the target object.
target_obj = source_object.search_for_object(obj_searchstr)
# Use search_for_object to handle duplicate/nonexistant results.
if not target_obj:
return
# If the user doesn't control the object, just look at it instead.
if not source_object.controls_other(target_obj, builder_override=True):
command.command_string = 'look'
cmd_look(command)
return
if attr_search:
"""
Player did something like: examine me/* or examine me/TE*. Return
each matching attribute with its value.
"""
attr_matches = target_obj.attribute_namesearch(attr_searchstr)
if attr_matches:
for attribute in attr_matches:
source_object.emit_to(attribute.get_attrline())
else:
source_object.emit_to("No matching attributes found.")
else:
"""
Player is examining an object. Return a full readout of attributes,
along with detailed information about said object.
"""
s = ""
newl = "\r\n"
# Format the examine header area with general flag/type info.
s += str(target_obj.get_name(fullname=True)) + newl
s += str("Type: %s Flags: %s" % (target_obj.get_type(),
target_obj.get_flags())) + newl
s += str("Owner: %s " % target_obj.get_owner()) + newl
s += str("Zone: %s" % target_obj.get_zone()) + newl
s += str("Parent: %s " % target_obj.get_script_parent()) + newl
locks = target_obj.get_attribute_value("LOCKS")
if locks and "%s" % locks:
s += str("Locks: %s" % locks) + newl
# Contents container lists for sorting by type.
con_players = []
con_things = []
con_exits = []
# Break each object out into their own list.
for obj in target_obj.get_contents():
if obj.is_player():
con_players.append(obj)
elif obj.is_exit():
con_exits.append(obj)
elif obj.is_thing():
con_things.append(obj)
# Render the object's home or destination (for exits).
if not target_obj.is_room():
if target_obj.is_exit():
# The Home attribute on an exit is really its destination.
s += str("Destination: %s" % target_obj.get_home()) + newl
else:
# For everything else, home is home.
s += str("Home: %s" % target_obj.get_home()) + newl
# This obviously isn't valid for rooms.
s += str("Location: %s" % target_obj.get_location()) + newl
# Render other attributes
for attribute in target_obj.get_all_attributes():
s += str(attribute.get_attrline()) + newl
# Render Contents display.
if con_players or con_things:
s += str("%sContents:%s" % (ANSITable.ansi["hilite"],
ANSITable.ansi["normal"]))
for player in con_players:
s += str(' %s' % newl + player.get_name(fullname=True))
for thing in con_things:
s += str(' %s' % newl + thing.get_name(fullname=True))
# Render Exists display.
if con_exits:
s += str("%sExits:%s" % (newl + ANSITable.ansi["hilite"],
ANSITable.ansi["normal"]))
for exit in con_exits:
s += str(' %s' % newl + exit.get_name(fullname=True))
# Send it all
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("examine", cmd_examine,priv_tuple=("objects.info",))
def cmd_quit(command):
"""
Gracefully disconnect the user as per his own request.
quit
Usage:
quit
Gracefully disconnect from the game.
"""
if command.session:
session = command.session
session.msg("Quitting. Hope to see you soon again.")
session.handle_close()
GLOBAL_CMD_TABLE.add_command("quit", cmd_quit)
GLOBAL_CMD_TABLE.add_command("quit", cmd_quit, help_category="System")
def cmd_who(command):
"""
Generic WHO command.
who
Usage:
who
Shows who is currently online.
"""
session_list = session_mgr.get_session_list()
source_object = command.source_object
# In the case of the DOING command, don't show session data regardless.
if command.extra_vars and command.extra_vars.get("show_session_data", None) == False:
if command.extra_vars and \
command.extra_vars.get("show_session_data", None) == False:
show_session_data = False
else:
show_session_data = source_object.has_perm("genperms.see_session_data")
@ -412,12 +342,17 @@ def cmd_who(command):
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("doing", cmd_who,
extra_vals={"show_session_data": False})
GLOBAL_CMD_TABLE.add_command("who", cmd_who)
extra_vals={"show_session_data": False}, help_category="System")
GLOBAL_CMD_TABLE.add_command("who", cmd_who,help_category="System")
def cmd_say(command):
"""
Room-based speech command.
say
Usage:
say <message>
Talk to those in your current location.
"""
source_object = command.source_object
@ -441,7 +376,18 @@ GLOBAL_CMD_TABLE.add_command("say", cmd_say)
def cmd_pose(command):
"""
Pose/emote command.
pose - strike a pose
Usage:
pose <pose text>
Example:
pose is standing by the wall, smiling.
-> others will see:
Tom is standing by the wall, smiling.
Describe an action being taken. The pose text will
automatically begin with your name.
"""
source_object = command.source_object
@ -464,189 +410,134 @@ def cmd_pose(command):
GLOBAL_CMD_TABLE.add_command("pose", cmd_pose)
def cmd_group(command):
"""@group
"""
@group - show your groups
Usage:
@group
This command shows you which user permission groups you are a member of, if any.
This command shows you which user permission groups
you are a member of, if any.
"""
source_object = command.source_object
user = User.objects.get(username=source_object.get_name(show_dbref=False,no_ansi=True))
s = ""
user = User.objects.get(username=source_object.get_name(show_dbref=False, no_ansi=True))
string = ""
if source_object.is_superuser():
s += "\n This is a SUPERUSER account! Group membership does not matter."
string += "\n This is a SUPERUSER account! Group membership does not matter."
if not user.is_active:
s += "\n ACCOUNT NOT ACTIVE."
for g in user.groups.all():
s += "\n -- %s" % g
for p in g.permissions.all():
s += "\n --- %s" % p.name
if not s:
s = "You are not a member of any groups." % source_object.get_name(show_dbref=False)
string += "\n ACCOUNT NOT ACTIVE."
for group in user.groups.all():
string += "\n -- %s" % group
for perm in group.permissions.all():
string += "\n --- %s" % perm.name
if not string:
string = "You are not a member of any groups." % source_object.get_name(show_dbref=False)
else:
s = "\nYour (%s's) group memberships: %s" % (source_object.get_name(show_dbref=False),s)
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("@group", cmd_group,auto_help=True)
string = "\nYour (%s's) group memberships: %s" % (source_object.get_name(show_dbref=False), string)
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("@group", cmd_group)
GLOBAL_CMD_TABLE.add_command("@groups", cmd_group, help_category="System")
def cmd_help(command):
"""
Help command
Usage: help <topic>
help - view help database
Usage:
help <topic>
Examples: help index
help topic
help 2
help 345
Shows the available help on <topic>. Use without <topic> to
get the help index. If more than one topic match your query, you will get a
Shows the available help on <topic>. Use without <topic> to get the help
index. If more than one topic match your query, you will get a
list of topics to choose between. You can also supply a help entry number
directly if you know it.
<<TOPIC:STAFF:help_staff>>
Help command extra functions for staff:
help index - the normal index
help index_staff - show only help files unique to staff
help index_player - show only help files visible to all players
The help command has a range of staff-only switches for manipulating the
help data base:
help/add <topic>:<text> - add/replace help topic with text (staff only)
help/append <topic>:<text> - add text to the end of a topic (staff only)
(use the /newline switch to add a new paragraph
to your help entry.)
help/delete <topic> - delete help topic (staff only)
Note: further switches are /force and /staff. /force is used together with /add to
always create a help entry, also when they partially match a previous entry. /staff
makes the help file visible to staff only. The /append switch can be used to change the
/staff setting of an existing help file if required.
The <text> entry supports markup to automatically divide the help text into
sub-entries. These are started by the markup < <TOPIC:MyTopic> > (with no spaces
between the << >>), which will create a new subsectioned entry 'MyTopic' for all
text to follow it. All subsections to be added this way are automatically
referred to in the footer of each help entry. Normally the subsections inherit the
staff_only flag from the main entry (so if this is a staff-only help, all subentries
will also be staff-only and vice versa). You can override this behaviour using the
alternate forms < <TOPIC:STAFF:MyTopic> > and < <TOPIC:ALL:MyTopic> >.
"""
source_object = command.source_object
topicstr = command.command_argument
switches = command.command_switches
topicstr = command.command_argument
if not command.command_argument:
#display topic index if just help command is given
if not switches:
topicstr = "topic"
else:
#avoid applying things to "topic" by mistake
source_object.emit_to("You have to supply a topic.")
return
elif len(topicstr) < 2 and not topicstr.isdigit():
topicstr = "index"
if len(topicstr) < 2 and not topicstr.isdigit():
#check valid query
source_object.emit_to("Your search query must be at least two letters long.")
return
#speciel help index names. These entries are dynamically
#created upon request.
if topicstr == 'index':
#the normal index, affected by permissions
edit_help.get_help_index(source_object)
# speciel help index names. These entries are dynamically
# created upon request.
if topicstr in ['topic','topics']:
# the full index, affected by permissions
text = helpsystem.viewhelp.index_full(source_object)
text = " \nHELP TOPICS (By Category):\n\r%s" % text
source_object.emit_to(text)
return
elif topicstr == 'index_staff':
#allows staff to view only staff-specific help
edit_help.get_help_index(source_object,filter='staff')
return
elif topicstr == 'index_player':
#allows staff to view only the help files a player sees
edit_help.get_help_index(source_object,filter='player')
return
#handle special switches
force_create = 'for' in switches or 'force' in switches
staff_only = 'sta' in switches or 'staff' in switches
elif 'index' in topicstr:
# view the category index
text = helpsystem.viewhelp.index_categories()
text = " \nHELP CATEGORIES (try 'help <category>' or 'help topics'):\n\r\n\r%s" % text
source_object.emit_to(text)
return
if 'add' in switches:
#try to add/replace help text for a command
if not source_object.has_perm("helpsys.add_help"):
source_object.emit_to(defines_global.NOPERMS_MSG)
return
spl = (topicstr.split(':',1))
if len(spl) != 2:
source_object.emit_to("Format is help/add <topic>:<helptext>")
return
topicstr = spl[0]
text = spl[1]
topics = edit_help.add_help(topicstr,text,staff_only,force_create,source_object)
if not topics:
source_object.emit_to("No topic(s) added due to errors. Check syntax and that you don't have duplicate subtopics with the same name defined.")
# not a special help index entry. Do a search for the help entry.
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
# display help entry or handle no/multiple matches
string = ""
if not topics:
# no matches.
# try to see if it is matching the name of a category. If so,
# show the topics for this category.
text = helpsystem.viewhelp.index_category(source_object, topicstr)
if text:
# We have category matches, display the index and exit.
string = "\n%s%s%s\n\r\n\r%s" % ("---", " Help topics in category %s: " % \
topicstr.capitalize(), "-"* (30-len(topicstr)), text)
source_object.emit_to(string)
return
elif len(topics)>1:
source_object.emit_to("Added or replaced multiple help entries.")
# at this point we just give a not-found error and give suggestions.
topics = HelpEntry.objects.find_topicsuggestions(source_object,
topicstr)
if topics:
if len(topics) > 3:
topics = topics[:3]
string += "\n\rMatching similarly named topics (use name or number to refine search):"
for entry in topics:
string += "\n %i.%s" % (entry.id, entry.topicname)
else:
source_object.emit_to("Added or replaced help entry for %s." % topicstr )
string += "No matching topics found, please refine your search."
elif 'append' in switches or 'app' in switches:
#append text to a help entry
if not source_object.has_perm("helpsys.add_help"):
source_object.emit_to(defines_global.NOPERMS_MSG)
return
spl = (topicstr.split(':',1))
if len(spl) != 2:
source_object.emit_to("""Format is help/append <topic>:<text to add>
Use the /newline switch to make a new paragraph.""")
return
topicstr = spl[0]
text = spl[1]
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
if len(topics) == 1:
newtext = topics[0].get_entrytext_ingame()
if 'newl' in switches or 'newline' in switches:
newtext += "\n\r\n\r%s" % text
else:
newtext += "\n\r%s" % text
topics = edit_help.add_help(topicstr,newtext,staff_only,force_create,source_object)
if topics:
source_object.emit_to("Appended text to help entry for %s." % topicstr)
elif 'del' in switches or 'delete' in switches:
#delete a help entry
if not source_object.has_perm("helpsys.del_help"):
source_object.emit_to(defines_global.NOPERMS_MSG)
return
topics = edit_help.del_help(source_object,topicstr)
if type(topics) != type(list()):
source_object.emit_to("Help entry '%s' deleted." % topicstr)
return
else:
#no switch; just try to get the help as normal
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
#display help entry or handle no/multiple matches
if len(topics) == 0:
source_object.emit_to("No matching topics found, please refine your search.")
suggestions = HelpEntry.objects.find_topicsuggestions(source_object,
topicstr)
if len(suggestions) > 0:
source_object.emit_to("Matching similarly named topics:")
for result in suggestions:
source_object.emit_to(" %s" % (result,))
source_object.emit_to("You may type 'help <#>' to see any of these topics.")
elif len(topics) > 1:
source_object.emit_to("More than one match found:")
# multiple matches found
string += "More than one match found:"
for result in topics:
source_object.emit_to(" %3d. %s" % (result.id, result.get_topicname()))
source_object.emit_to("You may type 'help <#>' to see any of these topics.")
string += " %3d. %s" % (result.id, result.get_topicname())
else:
# a single match found
topic = topics[0]
source_object.emit_to("\n\r "+ topic.get_entrytext_ingame())
GLOBAL_CMD_TABLE.add_command("help", cmd_help, auto_help=True)
header = "--- Help entry for '%s' (%s category) " % (topic.get_topicname(),
topic.get_category())
header = "%s%s" % (header, "-" * (80-len(header)))
string += "\n\r%s\n\r\n\r%s" % (header, topic.get_entrytext_ingame())
# add the 'See also:' footer
topics = HelpEntry.objects.find_topicsuggestions(source_object,
topicstr)
if topics:
if len(topics) > 5:
topics = topics[:5]
topics = [str(topic.topicname) for topic in topics ]
string += "\n\r\n\r" + " " * helpsystem.viewhelp.indent + \
"See also: " + ", ".join(topics)
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("help", cmd_help)

View file

@ -1,14 +1,8 @@
"""
IMC2 user and administrative commands.
"""
from time import time
from django.conf import settings
from src.config.models import ConfigValue
from src.objects.models import Object
from src import defines_global
from src import ansi
from src import comsys
from src.util import functions_general
from src.cmdtable import GLOBAL_CMD_TABLE
from src.ansi import parse_ansi
from src.imc2.imc_ansi import IMCANSIParser
@ -20,7 +14,12 @@ from src.channels.models import CommChannel
def cmd_imcwhois(command):
"""
Shows a player's inventory.
imcwhois
Usage:
imcwhois
IMC2 command. Shows a player's inventory.
"""
source_object = command.source_object
if not command.command_argument:
@ -30,10 +29,15 @@ def cmd_imcwhois(command):
source_object.emit_to("Sending IMC whois request. If you receive no response, no matches were found.")
packet = IMC2PacketWhois(source_object, command.command_argument)
imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(packet)
GLOBAL_CMD_TABLE.add_command("imcwhois", cmd_imcwhois)
GLOBAL_CMD_TABLE.add_command("imcwhois", cmd_imcwhois, help_category="Comms")
def cmd_imcansi(command):
"""
imcansi
Usage:
imcansi <string>
Test IMC ANSI conversion.
"""
source_object = command.source_object
@ -43,20 +47,30 @@ def cmd_imcansi(command):
else:
retval = parse_ansi(command.command_argument, parser=IMCANSIParser())
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imcansi", cmd_imcansi)
GLOBAL_CMD_TABLE.add_command("imcansi", cmd_imcansi, help_category="Comms")
def cmd_imcicerefresh(command):
"""
Semds an ice-refresh packet.
imcicerefresh
Usage:
imcicerefresh
IMC2: Semds an ice-refresh packet.
"""
source_object = command.source_object
packet = IMC2PacketIceRefresh()
imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(packet)
source_object.emit_to("Sent")
GLOBAL_CMD_TABLE.add_command("imcicerefresh", cmd_imcicerefresh)
GLOBAL_CMD_TABLE.add_command("imcicerefresh", cmd_imcicerefresh, help_category="Comms")
def cmd_imcchanlist(command):
"""
imcchanlist
Usage:
imcchanlist
Shows the list of cached channels from the IMC2 Channel list.
"""
source_object = command.source_object
@ -73,10 +87,15 @@ def cmd_imcchanlist(command):
channel.policy)
retval += '%s channels found.' % len(IMC2_CHANLIST.chan_list)
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imcchanlist", cmd_imcchanlist)
GLOBAL_CMD_TABLE.add_command("imcchanlist", cmd_imcchanlist, help_category="Comms")
def cmd_imclist(command):
"""
imclist
Usage:
imclist
Shows the list of cached games from the IMC2 Mud list.
"""
source_object = command.source_object
@ -88,10 +107,15 @@ def cmd_imclist(command):
retval += '%s\n\r' % mudline[:78]
retval += '%s active MUDs found.' % len(IMC2_MUDLIST.mud_list)
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imclist", cmd_imclist)
GLOBAL_CMD_TABLE.add_command("imclist", cmd_imclist, help_category="Comms")
def cmd_imcstatus(command):
"""
imcstatus
Usage:
imcstatus
Shows some status information for your IMC2 connection.
"""
source_object = command.source_object
@ -118,21 +142,24 @@ def cmd_imcstatus(command):
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imcstatus", cmd_imcstatus,
priv_tuple=('imc2.admin_imc_channels',))
priv_tuple=('imc2.admin_imc_channels',), help_category="Comms")
def cmd_IMC2chan(command):
"""
@imc2chan IMCServer:IMCchannel channel
@imc2chan
Links an IMC channel to an existing
evennia channel. You can link as many existing
Usage:
@imc2chan <IMCServer> : <IMCchannel> <channel>
Links an IMC channel to an existing evennia
channel. You can link as many existing
evennia channels as you like to the
IMC channel this way. Running the command with an
existing mapping will re-map the channels.
Use 'imcchanlist' to get a list of IMC channels and servers.
Note that both are case sensitive.
Use 'imcchanlist' to get a list of IMC channels and
servers. Note that both are case sensitive.
"""
source_object = command.source_object
if not settings.IMC2_ENABLED:
@ -179,6 +206,6 @@ def cmd_IMC2chan(command):
outstring += "Mapping set: %s." % mapping
source_object.emit_to(outstring)
GLOBAL_CMD_TABLE.add_command("@imc2chan",cmd_IMC2chan,auto_help=True,staff_help=True,
priv_tuple=("imc2.admin_imc_channels",))
GLOBAL_CMD_TABLE.add_command("@imc2chan",cmd_IMC2chan,
priv_tuple=("imc2.admin_imc_channels",), help_category="Comms")

View file

@ -17,25 +17,41 @@ from src.cmdtable import GLOBAL_CMD_TABLE
def cmd_version(command):
"""
Version info command.
@version - game version
Usage:
@version
Display the game version info
"""
retval = "-"*50 +"\n\r"
retval += " Evennia %s\n\r" % (defines_global.EVENNIA_VERSION,)
retval += " Django %s\n\r" % (django.get_version())
retval += "-"*50
command.source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("version", cmd_version),
GLOBAL_CMD_TABLE.add_command("@version", cmd_version, help_category="Admin"),
def cmd_time(command):
"""
@time
Usage:
@time
Server local time.
"""
command.source_object.emit_to('Current server time : %s' %
(time.strftime('%a %b %d %H:%M:%S %Y (%Z)', time.localtime(),)))
GLOBAL_CMD_TABLE.add_command("time", cmd_time),
GLOBAL_CMD_TABLE.add_command("@time", cmd_time, priv_tuple=("genperms.game_info",),
help_category="Admin")
def cmd_uptime(command):
"""
@uptime
Usage:
@uptime
Server uptime and stats.
"""
source_object = command.source_object
@ -54,11 +70,18 @@ def cmd_uptime(command):
loadavg = os.getloadavg()
source_object.emit_to('Server load (1 min) : %.2f' %
loadavg[0])
GLOBAL_CMD_TABLE.add_command("uptime", cmd_uptime),
GLOBAL_CMD_TABLE.add_command("@uptime", cmd_uptime, priv_tuple=("genperms.game_info",),
help_category="Admin")
def cmd_list(command):
"""
Shows some game related information.
"""
@list - list info
Usage:
@list commands | flags | process
Shows game related information depending
on which argument is given.
"""
server = command.session.server
source_object = command.source_object
@ -99,10 +122,15 @@ def cmd_list(command):
source_object.emit_to("Flags: "+" ".join(flags.SERVER_FLAGS))
else:
source_object.emit_to(msg_invalid)
GLOBAL_CMD_TABLE.add_command("@list", cmd_list,priv_tuple=("genperms.game_info",)),
GLOBAL_CMD_TABLE.add_command("@list", cmd_list,priv_tuple=("genperms.game_info",), help_category="Admin")
def cmd_ps(command):
"""
@ps - list processes
Usage
@ps
Shows the process/event table.
"""
source_object = command.source_object
@ -115,13 +143,23 @@ def cmd_ps(command):
event.description))
source_object.emit_to("Totals: %d interval events" % (len(scheduler.schedule),))
GLOBAL_CMD_TABLE.add_command("@ps", cmd_ps,
priv_tuple=("genperms.process_control")),
priv_tuple=("genperms.process_control",), help_category="Admin")
def cmd_stats(command):
"""
@stats - show object stats
Usage:
@stats
Example:
@stats
->
4012 objects = 144 rooms, 212 exits, 613 things, 1878 players. (1165 garbage)
Shows stats about the database.
4012 objects = 144 rooms, 212 exits, 613 things, 1878 players. (1165 garbage)
"""
stats_dict = Object.objects.object_totals()
command.source_object.emit_to(
"%d objects = %d rooms, %d exits, %d things, %d players. (%d garbage)" %
@ -131,4 +169,4 @@ def cmd_stats(command):
stats_dict["things"],
stats_dict["players"],
stats_dict["garbage"]))
GLOBAL_CMD_TABLE.add_command("@stats", cmd_stats, priv_tuple=("genperms.game_info",)),
GLOBAL_CMD_TABLE.add_command("@stats", cmd_stats, priv_tuple=("genperms.game_info",), help_category="Admin"),

View file

@ -11,7 +11,10 @@ from src.channels.models import CommChannel
def cmd_IRC2chan(command):
"""
@irc2chan IRCchannel channel
@irc2chan - link irc to ingame channel
Usage:
@irc2chan <#IRCchannel> <local channel>
Links an IRC channel (including #) to an existing
evennia channel. You can link as many existing
@ -55,12 +58,16 @@ def cmd_IRC2chan(command):
outstring += "Mapping set: %s." % mapping
source_object.emit_to(outstring)
GLOBAL_CMD_TABLE.add_command("@irc2chan",cmd_IRC2chan,auto_help=True,staff_help=True,
priv_tuple=("irc.admin_irc_channels",))
GLOBAL_CMD_TABLE.add_command("@irc2chan",cmd_IRC2chan,
priv_tuple=("irc.admin_irc_channels",),
help_category="Comms")
def cmd_IRCjoin(command):
"""
@ircjoin IRCchannel
@ircjoin - join a new irc channel
Usage:
@ircjoin <#IRCchannel>
Attempts to connect a bot to a new IRC channel (don't forget that
IRC channels begin with a #).
@ -99,19 +106,25 @@ def cmd_IRCjoin(command):
# irc.setName("%s:%s" % ("IRC",channel))
# irc.setServiceParent(mud_service.service_collection)
GLOBAL_CMD_TABLE.add_command("@ircjoin",cmd_IRCjoin,auto_help=True,
staff_help=True,
priv_tuple=("irc.admin_irc_channels",))
GLOBAL_CMD_TABLE.add_command("@ircjoin",cmd_IRCjoin,
priv_tuple=("irc.admin_irc_channels",),
help_category="Comms")
def cmd_IRCchanlist(command):
"""
ircchanlist
Usage:
ircchanlist
Lists all externally available IRC channels.
"""
source_object = command.source_object
s = "Available IRC channels:"
for c in IRC_CHANNELS:
s += "\n %s \t(nick '%s') on %s" % (c.factory.channel,c.factory.nickname,c.factory.network,)
s += "\n %s \t(nick '%s') on %s" % (c.factory.channel,
c.factory.nickname,
c.factory.network,)
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("ircchanlist", cmd_IRCchanlist, auto_help=True)
GLOBAL_CMD_TABLE.add_command("ircchanlist", cmd_IRCchanlist,
help_category="Comms")

View file

@ -9,10 +9,21 @@ from src import locks
from src import ansi
from src.cmdtable import GLOBAL_CMD_TABLE
from src import defines_global
from src.ansi import ANSITable
def cmd_teleport(command):
"""
Teleports an object somewhere.
teleport
Usage:
teleport/switch [<object> = <location>]
Switches:
quiet - don't inform the source and target
locations about the move.
Teleports an object somewhere. If no object is
given we are teleporting ourselves.
"""
source_object = command.source_object
@ -66,10 +77,15 @@ def cmd_teleport(command):
source_object.move_to(target_obj, quiet=tel_quietly)
GLOBAL_CMD_TABLE.add_command("@teleport", cmd_teleport,
priv_tuple=("objects.teleport",))
priv_tuple=("objects.teleport",), help_category="Building")
def cmd_alias(command):
"""
@alias
Usage:
@alias <player> = <alias>
Assigns an alias to a player object for ease of paging, etc.
"""
source_object = command.source_object
@ -118,8 +134,17 @@ GLOBAL_CMD_TABLE.add_command("@alias", cmd_alias)
def cmd_wipe(command):
"""
Wipes an object's attributes, or optionally only those matching a search
string.
@wipe - clears attributes
Usage:
@wipe <object> [/attribute-wildcard]
Example:
@wipe box
@wipe box/colour
Wipes all of an object's attributes, or optionally only those
matching the given attribute-wildcard search string.
"""
source_object = command.source_object
attr_search = False
@ -166,11 +191,21 @@ def cmd_wipe(command):
target_obj.clear_attribute(attr.get_name())
source_object.emit_to("%s - %d attributes wiped." % (target_obj.get_name(),
len(attr_matches)))
GLOBAL_CMD_TABLE.add_command("@wipe", cmd_wipe,priv_tuple=("objects.wipe",))
GLOBAL_CMD_TABLE.add_command("@wipe", cmd_wipe,priv_tuple=("objects.wipe",),
help_category="Building")
def cmd_set(command):
"""
Sets flags or attributes on objects.
@set - set attributes and flags
Usage:
@set <obj> = <flag>
@set <obj> = <attr> : <value>
@set <obj> = !<flag>
@set <obj> = <attr> :
Sets flags or attributes on objects. The two last forms
above unsets the flag and clears the attribute, respectively.
"""
source_object = command.source_object
args = command.command_argument
@ -251,16 +286,26 @@ def cmd_set(command):
s += '\nFlag %s=%s set.' % (target_name, flag.upper())
target.set_flag(flag, True)
source_object.emit_to(s[1:])
GLOBAL_CMD_TABLE.add_command("@set", cmd_set, priv_tuple=("objects.modify_attributes",))
GLOBAL_CMD_TABLE.add_command("@set", cmd_set, priv_tuple=("objects.modify_attributes",),
help_category="Building")
def cmd_cpattr(command):
"""
copy an attribute to another object
@cpattr <obj>/<attr> = <obj1>/<attr1> [,<obj2>/<attr2>,<obj3>/<attr3>,...]
@cpattr <obj>/<attr> = <obj1> [,<obj2>,<obj3>,...]
@cpattr <attr> = <obj1>/<attr1> [,<obj2>/<attr2>,<obj3>/<attr3>,...]
@cpattr <attr> = <obj1>[,<obj2>,<obj3>,...]
@cpattr - copy attributes
Usage:
@cpattr <obj>/<attr> = <obj1>/<attr1> [,<obj2>/<attr2>,<obj3>/<attr3>,...]
@cpattr <obj>/<attr> = <obj1> [,<obj2>,<obj3>,...]
@cpattr <attr> = <obj1>/<attr1> [,<obj2>/<attr2>,<obj3>/<attr3>,...]
@cpattr <attr> = <obj1>[,<obj2>,<obj3>,...]
Example:
@cpattr coolness = Anna/chillout, Anna/nicety, Tom/nicety
->
copies the coolness attribute (defined on yourself), to attributes
on Anna and Tom.
Copy the attribute one object to one or more attributes on another object.
"""
source_object = command.source_object
args = command.command_argument
@ -333,14 +378,17 @@ def cmd_cpattr(command):
to_objname, to_attr)
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("@cpattr", cmd_cpattr,
priv_tuple=("objects.modify_attributes",))
priv_tuple=("objects.modify_attributes",), help_category="Building")
def cmd_mvattr(command):
"""
@mvattr <object>=<old>,<new>[,<copy1>[, <copy2 ...]]
@mvattr - move attributes
Move attributes around on an object
Usage:
@mvattr <object>=<old>,<new>[,<copy1>[, <copy2 ...]]
Move attributes around on the same object.
"""
source_object = command.source_object
arg = command.command_argument
@ -395,10 +443,16 @@ def cmd_mvattr(command):
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("@mvattr", cmd_mvattr,
priv_tuple=("objects.modify_attributes",))
priv_tuple=("objects.modify_attributes",),
help_category="Building")
def cmd_find(command):
"""
find
Usage:
find <searchname>
Searches for an object of a particular name.
"""
source_object = command.source_object
@ -421,13 +475,14 @@ def cmd_find(command):
else:
source_object.emit_to("No name matches found for: %s" % (searchstring,))
GLOBAL_CMD_TABLE.add_command("@find", cmd_find,
priv_tuple=("objects.info",))
priv_tuple=("objects.info",), help_category="Building")
def cmd_create(command):
"""
@create
@create - create new objects
Usage: @create[/drop] objname [:parent]
Usage:
@create[/drop] objname [:parent]
switch:
drop - automatically drop the new object into your current location (this is not echoed)
@ -477,15 +532,20 @@ def cmd_create(command):
GLOBAL_CMD_TABLE.add_command("@create", cmd_create,
priv_tuple=("objects.create",),auto_help=True,staff_help=True)
priv_tuple=("objects.create",),
help_category="Building")
def cmd_copy(command):
"""Usage:
@copy[/reset] <original obj> [new_name] [, new_location]
"""
@copy - copy objects
Usage:
@copy[/reset] <original obj> [new_name] [, new_location]
switch:
reset - make a 'clean' copy, without any changes that might have happened to the
original since it was first created.
reset - make a 'clean' copy off the object's script parent, thus
removing any changes that might have been made to the original
since it was first created.
Create an identical copy of an object.
"""
@ -542,12 +602,13 @@ def cmd_copy(command):
reset_text = " (using default attrs/flags)"
source_object.emit_to("Copied object '%s'%s%s%s." % (objname,name_text,loc_text,reset_text))
GLOBAL_CMD_TABLE.add_command("@copy", cmd_copy,
priv_tuple=("objects.create",),auto_help=True,staff_help=True)
priv_tuple=("objects.create",), help_category="Building")
def cmd_nextfree(command):
"""Usage:
"""
@nextfree
Usage:
@nextfree
Returns the next free object number.
@ -555,10 +616,12 @@ def cmd_nextfree(command):
nextfree = Object.objects.get_nextfree_dbnum()
command.source_object.emit_to("Next free object number: #%s" % nextfree)
GLOBAL_CMD_TABLE.add_command("@nextfree", cmd_nextfree,
priv_tuple=("objects.info",),auto_help=True,staff_help=True)
priv_tuple=("objects.info",), help_category="Building")
def cmd_open(command):
"""@open
"""
@open - create new exit
Usage:
@open <new exit> [:parent] [= <destination> [,<return exit> [:parent]]]
@ -670,15 +733,17 @@ def cmd_open(command):
source_object.emit_to("Created exit%s back from %s named %s." % \
(ptext, destination, new_object))
GLOBAL_CMD_TABLE.add_command("@open", cmd_open,
priv_tuple=("objects.dig",),auto_help=True,staff_help=True)
priv_tuple=("objects.dig",), help_category="Building")
def cmd_chown(command):
"""
Changes the ownership of an object. The new owner specified must be a
player object.
@chown - change ownerships
Forms:
@chown <Object>=<NewOwner>
Usage:
@chown <Object> = <NewOwner>
Changes the ownership of an object. The new owner specified must be a
player object.
"""
source_object = command.source_object
@ -721,15 +786,19 @@ def cmd_chown(command):
# We haven't provided a target.
source_object.emit_to("Who should be the new owner of the object?")
return
GLOBAL_CMD_TABLE.add_command("@chown", cmd_chown, priv_tuple=("objects.modify_attributes","objects.admin_ownership"))
GLOBAL_CMD_TABLE.add_command("@chown", cmd_chown, priv_tuple=("objects.modify_attributes",
"objects.admin_ownership"),
help_category="Building" )
def cmd_chzone(command):
"""
@chzone - set zones
Usage:
@chzone <Object> = <NewZone>
Changes an object's zone. The specified zone may be of any object type, but
will typically be a THING.
Forms:
@chzone <Object>=<NewZone>
"""
source_object = command.source_object
@ -773,10 +842,13 @@ def cmd_chzone(command):
# We haven't provided a target zone.
source_object.emit_to("What should the object's zone be set to?")
return
GLOBAL_CMD_TABLE.add_command("@chzone", cmd_chzone, priv_tuple=("objects.dig",))
GLOBAL_CMD_TABLE.add_command("@chzone", cmd_chzone, priv_tuple=("objects.dig",),
help_category="Building" )
def cmd_link(command):
"""@link
"""
@link - connect objects
Usage:
@link <object> = <target>
@link <object> =
@ -864,13 +936,17 @@ def cmd_link(command):
else:
source_object.emit_to("You set the home location of %s to %s%s." % (obj, destination, ohome_text))
GLOBAL_CMD_TABLE.add_command("@link", cmd_link,
priv_tuple=("objects.dig",), auto_help=True, staff_help=True)
priv_tuple=("objects.dig",), help_category="Building")
def cmd_unlink(command):
"""
Unlinks an object.
@unlink <Object>
@unlink - unconnect objects
Usage:
@unlink <Object>
Unlinks an object, for example an exit, disconnecting
it from whatever it was connected to.
"""
source_object = command.source_object
@ -890,15 +966,19 @@ def cmd_unlink(command):
target_obj.set_home(None)
source_object.emit_to("You have unlinked %s." % target_obj.get_name())
GLOBAL_CMD_TABLE.add_command("@unlink", cmd_unlink,
priv_tuple=("objects.dig",))
priv_tuple=("objects.dig",), help_category="Building")
def cmd_dig(command):
"""@dig
"""
@dig - build and connect new rooms
Usage:
@dig[/switches] roomname [:parent] [= exit_to_there [: parent][;alias]] [, exit_to_here [: parent][;alias]]
switches:
Switches:
teleport - move yourself to the new room
example:
Example:
@dig kitchen = north; n, south; s
This command is a convenient way to build rooms quickly; it creates the new room and you can optionally
@ -1018,13 +1098,16 @@ def cmd_dig(command):
source_object.move_to(new_room)
GLOBAL_CMD_TABLE.add_command("@dig", cmd_dig,
priv_tuple=("objects.dig",), auto_help=True, staff_help=True)
priv_tuple=("objects.dig",), help_category="Building")
def cmd_name(command):
"""
Handle naming an object.
@name <Object>=<Value>
@name - name objects
Usage:
@name <Object> = <Value>
Handle naming an object.
"""
source_object = command.source_object
@ -1060,10 +1143,16 @@ def cmd_name(command):
source_object.emit_to("You have renamed %s to %s." % (target_obj,
ansi_name))
target_obj.set_name(new_name)
GLOBAL_CMD_TABLE.add_command("@name", cmd_name)
GLOBAL_CMD_TABLE.add_command("@name", cmd_name, priv_tuple=("objects.create",),
help_category="Building")
def cmd_description(command):
"""
@desc
Usage:
@desc [obj =] <descriptive text>
Set an object's description.
"""
source_object = command.source_object
@ -1099,20 +1188,21 @@ def cmd_description(command):
else:
source_object.emit_to("%s - description set." % target_obj)
target_obj.set_attribute('desc', new_desc)
GLOBAL_CMD_TABLE.add_command("@describe", cmd_description)
GLOBAL_CMD_TABLE.add_command("@describe", cmd_description, priv_tuple=("objects.create",),
help_category="Building")
def cmd_recover(command):
"""
@recover
Recovers @destroyed non-player objects.
@recover - undo object deletion
Usage:
@recover[/switches] [obj [,obj2, ...]]
@recover[/switches] [obj [,obj2, ...]]
switches:
ROOM - recover as ROOM type instead of THING
EXIT - recover as EXIT type instead of THING
ROOM - recover as ROOM type instead of THING
EXIT - recover as EXIT type instead of THING
Recovers @destroyed non-player objects.
If no argument is given, a list of all recoverable objects will be given.
@ -1165,14 +1255,12 @@ def cmd_recover(command):
GLOBAL_CMD_TABLE.add_command("@recover", cmd_recover,
priv_tuple=("objects.create",),auto_help=True,staff_help=True)
priv_tuple=("objects.create",), help_category="Building")
def cmd_destroy(command):
"""
@destroy
Destroys one or many objects.
@destroy - send objects to trashbin
Usage:
@destroy[/<switches>] obj [,obj2, obj3, ...]
@ -1182,6 +1270,7 @@ def cmd_destroy(command):
switch overrides this safety.
instant|now - Destroy the object immediately, without delay.
Destroys one or many objects.
The objects are set to GOING and will be permanently destroyed next time the system
does cleanup. Until then non-player objects can still be saved by using the
@recover command. The contents of a room will be moved out before it is destroyed,
@ -1244,19 +1333,23 @@ def cmd_destroy(command):
source_object.emit_to("You schedule %s for destruction." % target_obj.get_name())
GLOBAL_CMD_TABLE.add_command("@destroy", cmd_destroy,
priv_tuple=("objects.create",),auto_help=True,staff_help=True)
priv_tuple=("objects.create",), help_category="Building")
def cmd_lock(command):
"""@lock
"""
@lock - limit use of objects
Usage:
@lock[/switch] <obj> [:type] [= <key>[,key2,key3,...]]
switches:
Switches:
add - add a lock (default) from object
del - remove a lock from object
list - view all locks on object (default)
type:
DefaultLock - the default lock type (default)
UseLock - prevents usage of objects' commands
EnterLock - blocking objects from entering the object
Locks an object for everyone except those matching the keys.
The keys can be of the following types (and searched in this order):
@ -1433,4 +1526,137 @@ def cmd_lock(command):
source_object.emit_to("Added lock '%s' to %s with keys%s." % (ltype, obj.get_name(), kstring))
obj.set_attribute("LOCKS",obj_locks)
GLOBAL_CMD_TABLE.add_command("@lock", cmd_lock, priv_tuple=("objects.create",),auto_help=True, staff_help=True)
GLOBAL_CMD_TABLE.add_command("@lock", cmd_lock, priv_tuple=("objects.create",), help_category="Building")
def cmd_examine(command):
"""
examine - detailed info on objects
Usage:
examine [<object>]
The examine command shows detailed game info about an
object; which attributes/flags it has and what it
contains. If object is not specified, the current
location is examined.
"""
source_object = command.source_object
attr_search = False
if not command.command_argument:
# If no arguments are provided, examine the invoker's location.
target_obj = source_object.get_location()
else:
# Look for a slash in the input, indicating an attribute search.
attr_split = command.command_argument.split("/", 1)
# If the splitting by the "/" character returns a list with more than 1
# entry, it's an attribute match.
if len(attr_split) > 1:
attr_search = True
# Strip the object search string from the input with the
# object/attribute pair.
obj_searchstr = attr_split[0]
attr_searchstr = attr_split[1].strip()
# Protect against stuff like: ex me/
if attr_searchstr == '':
source_object.emit_to('No attribute name provided.')
return
else:
# No slash in argument, just examine an object.
obj_searchstr = command.command_argument
# Resolve the target object.
target_obj = source_object.search_for_object(obj_searchstr)
# Use search_for_object to handle duplicate/nonexistant results.
if not target_obj:
return
# If the user doesn't control the object, just look at it instead.
if not source_object.controls_other(target_obj, builder_override=True):
command.command_string = 'look'
cmd_look(command)
return
if attr_search:
# Player did something like: examine me/* or examine me/TE*. Return
# each matching attribute with its value.
attr_matches = target_obj.attribute_namesearch(attr_searchstr)
if attr_matches:
for attribute in attr_matches:
source_object.emit_to(attribute.get_attrline())
else:
source_object.emit_to("No matching attributes found.")
else:
# Player is examining an object. Return a full readout of attributes,
# along with detailed information about said object.
string = ""
newl = "\r\n"
# Format the examine header area with general flag/type info.
string += str(target_obj.get_name(fullname=True)) + newl
string += str("Type: %s Flags: %s" % (target_obj.get_type(),
target_obj.get_flags())) + newl
string += str("Owner: %s " % target_obj.get_owner()) + newl
string += str("Zone: %s" % target_obj.get_zone()) + newl
string += str("Parent: %s " % target_obj.get_script_parent()) + newl
locks = target_obj.get_attribute_value("LOCKS")
if locks and "%s" % locks:
string += str("Locks: %s" % locks) + newl
# Contents container lists for sorting by type.
con_players = []
con_things = []
con_exits = []
# Break each object out into their own list.
for obj in target_obj.get_contents():
if obj.is_player():
con_players.append(obj)
elif obj.is_exit():
con_exits.append(obj)
elif obj.is_thing():
con_things.append(obj)
# Render the object's home or destination (for exits).
if not target_obj.is_room():
if target_obj.is_exit():
# The Home attribute on an exit is really its destination.
string += str("Destination: %s" % target_obj.get_home()) + newl
else:
# For everything else, home is home.
string += str("Home: %s" % target_obj.get_home()) + newl
# This obviously isn't valid for rooms.
string += str("Location: %s" % target_obj.get_location()) + newl
# Render other attributes
for attribute in target_obj.get_all_attributes():
string += str(attribute.get_attrline()) + newl
# Render Contents display.
if con_players or con_things:
string += str("%sContents:%s" % (ANSITable.ansi["hilite"],
ANSITable.ansi["normal"]))
for player in con_players:
string += str(' %s' % newl + player.get_name(fullname=True))
for thing in con_things:
string += str(' %s' % newl + thing.get_name(fullname=True))
# Render Exists display.
if con_exits:
string += str("%sExits:%s" % (newl + ANSITable.ansi["hilite"],
ANSITable.ansi["normal"]))
for exit in con_exits:
string += str(' %s' % newl + exit.get_name(fullname=True))
# Send it all
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("examine", cmd_examine, priv_tuple=("objects.info",))

View file

@ -2,7 +2,6 @@
Paging command and support functions.
"""
from src.objects.models import Object
from src import defines_global
from src.cmdtable import GLOBAL_CMD_TABLE
def get_last_paged_objects(source_object):
@ -31,7 +30,14 @@ def get_last_paged_objects(source_object):
def cmd_page(command):
"""
Send a message to target user (if online).
page - send private message
Usage:
page [<user> = <message>]
Send a message to target user (if online). If no
argument is given, you will instead see who was the last
person you paged to.
"""
source_object = command.source_object
# Get the last paged person(s)
@ -127,4 +133,4 @@ def cmd_page(command):
# Now set the LASTPAGED attribute
source_object.set_attribute("LASTPAGED", ','.join(
["#%d" % (x.id) for x in targets]))
GLOBAL_CMD_TABLE.add_command("page", cmd_page, priv_tuple=('channels.page',))
GLOBAL_CMD_TABLE.add_command("page", cmd_page, priv_tuple=('channels.page',), help_category="Comms")

View file

@ -2,10 +2,14 @@
Contains commands for managing script parents.
"""
from src import scripthandler
from src import defines_global
from src.cmdtable import GLOBAL_CMD_TABLE
def cmd_scriptcache(command):
"""Usage
"""
@scriptcache
Usage
@scriptcache
Shows the contents of the script cache.
@ -20,12 +24,21 @@ def cmd_scriptcache(command):
retval += "%d cached parents" % len(cache_dict)
command.source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("@scriptcache", cmd_scriptcache,
priv_tuple=("genperms.builder"),
auto_help=True,staff_help=True)
priv_tuple=("genperms.builder",), help_category="Admin")
def cmd_parent(command):
"""
Sets an object's script parent.
@parent - set script parent
Usage:
@parent <object> = <parent>
Example:
@parent button = examples.red_button
Sets an object's script parent. The parent must be identified
by its location using dot-notation pointing to the script
parent module.
"""
source_object = command.source_object
@ -80,5 +93,5 @@ def cmd_parent(command):
(target_obj,current_parent))
GLOBAL_CMD_TABLE.add_command("@parent", cmd_parent,
priv_tuple=("genperms.builder"))
priv_tuple=("genperms.builder",), help_category="Building" )

View file

@ -3,21 +3,30 @@ This file contains commands that require special permissions to use. These
are generally @-prefixed commands, but there are exceptions.
"""
from django.contrib.auth.models import Permission, Group, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Permission, Group
from django.conf import settings
from src.objects.models import Object
from src import defines_global
from src import ansi
from src import session_mgr
from src import comsys
from src.scripthandler import rebuild_cache
from src.util import functions_general
from src.cmdtable import GLOBAL_CMD_TABLE
from src.helpsys.models import HelpEntry
from src.helpsys import helpsystem
def cmd_reload(command):
"""
Reloads all modules.
@reload - reload game subsystems
Usage:
@reload/switches
Switches:
aliases - alias definitions
commands - the command modules
scripts, parents - the script parent modules
all
Reloads all the identified subsystems.
"""
source_object = command.source_object
switches = command.command_switches
@ -44,12 +53,17 @@ def cmd_reload(command):
comsys.cemit_mudinfo("... all Command modules were reloaded.")
GLOBAL_CMD_TABLE.add_command("@reload", cmd_reload,
priv_tuple=("genperms.process_control",)),
priv_tuple=("genperms.process_control",), help_category="Admin")
GLOBAL_CMD_TABLE.add_command("@restart", cmd_reload,
priv_tuple=("genperms.process_control",)),
priv_tuple=("genperms.process_control",), help_category="Admin")
def cmd_boot(command):
"""
@boot
Usage
@boot <player obj>
Boot a player object from the server.
"""
source_object = command.source_object
@ -119,10 +133,16 @@ def cmd_boot(command):
session_mgr.remove_session(boot)
return
GLOBAL_CMD_TABLE.add_command("@boot", cmd_boot,
priv_tuple=("genperms.manage_players",))
priv_tuple=("genperms.manage_players",),
help_category="Admin")
def cmd_newpassword(command):
"""
@newpassword
Usage:
@newpassword <user obj> = <new password>
Set a player's password.
"""
source_object = command.source_object
@ -157,10 +177,16 @@ def cmd_newpassword(command):
target_obj.emit_to("%s has changed your password." %
(source_object.get_name(show_dbref=False),))
GLOBAL_CMD_TABLE.add_command("@newpassword", cmd_newpassword,
priv_tuple=("genperms.manage_players",))
priv_tuple=("genperms.manage_players",),
help_category="Admin")
def cmd_home(command):
"""
home
Usage:
home
Teleport the player to their home.
"""
pobject = command.source_object
@ -174,8 +200,18 @@ GLOBAL_CMD_TABLE.add_command("home", cmd_home,
def cmd_service(command):
"""
Service management system. Allows for the listing, starting, and stopping
of services.
@service - manage services
Usage:
@service[/switch] <service>
Switches:
start - activates a service
stop - stops a service
list - shows all available services
Service management system. Allows for the listing,
starting, and stopping of services.
"""
source_object = command.source_object
switches = command.command_switches
@ -242,17 +278,25 @@ def cmd_service(command):
return
GLOBAL_CMD_TABLE.add_command("@service", cmd_service,
priv_tuple=("genperms.process_control",))
priv_tuple=("genperms.process_control",),
help_category="Admin")
def cmd_shutdown(command):
"""
Shut the server down gracefully.
@shutdown
Usage:
@shutdown
Shut the game server down gracefully.
"""
command.source_object.emit_to('Shutting down...')
print 'Server shutdown by %s' % (command.source_object.get_name(show_dbref=False),)
command.session.server.shutdown()
GLOBAL_CMD_TABLE.add_command("@shutdown", cmd_shutdown,
priv_tuple=("genperms.process_control",))
priv_tuple=("genperms.process_control",),
help_category="Admin")
# permission administration
@ -260,6 +304,7 @@ GLOBAL_CMD_TABLE.add_command("@shutdown", cmd_shutdown,
# mess with, but which are not very useful from inside the game. While these
# permissions are ok to use, we only show the permissions that we have defined
# in our settings file in order to give better control.
APPS_NOSHOW = ("news","admin","auth","config","contentypes",
"flatpages","news","sessions","sites")
SETTINGS_PERM_NAMES = []
@ -268,7 +313,9 @@ for apps in settings.PERM_ALL_DEFAULTS + settings.PERM_ALL_CUSTOM:
SETTINGS_PERM_NAMES.append(permtuples[1])
def cmd_setperm(command):
"""@setperm
"""
@setperm - set permissions
Usage:
@setperm[/switch] [<user>] = [<permission>]
@ -277,9 +324,9 @@ def cmd_setperm(command):
del : delete a permission from <user>
list : list all permissions, or those set on <user>
This command sets/clears individual permission bits on a user.
Use /list without any arguments to see all available permissions or those
defined on the <user> argument.
This command sets/clears individual permission bits on a user.
Use /list without any arguments to see all available permissions or those
defined on the <user> argument.
"""
source_object = command.source_object
args = command.command_argument
@ -380,17 +427,21 @@ def cmd_setperm(command):
obj.emit_to("%s removed your permission '%s'." % (source_object.get_name(show_dbref=False,no_ansi=True),
permission.name))
GLOBAL_CMD_TABLE.add_command("@setperm", cmd_setperm,
priv_tuple=("auth.change_permission","genperms.admin_perm"), auto_help=True, staff_help=True)
priv_tuple=("auth.change_permission",
"genperms.admin_perm"),
help_category="Admin")
def cmd_setgroup(command):
"""@setgroup
"""
@setgroup - manage group memberships
Usage:
@setgroup[/switch] [<user>] [= <group>]
switches
add : add user to a group
del : remove user from a group
list : list all groups a user is part of, or list all available groups if no user is given
Switches:
add - add user to a group
del - remove user from a group
list - list all groups a user is part of, or list all available groups if no user is given
Changes and views the group membership of a user.
"""
@ -410,7 +461,8 @@ def cmd_setgroup(command):
for p in g.permissions.all():
app = p.content_type.app_label
if app not in APPS_NOSHOW:
s += "\n --- %s.%s%s\t%s" % (app, p.codename, (35 - len(app) - len(p.codename)) * " ", p.name)
s += "\n --- %s.%s%s\t%s" % (app, p.codename,
(35 - len(app) - len(p.codename)) * " ", p.name)
source_object.emit_to(s)
return
#we have command arguments.
@ -484,5 +536,155 @@ def cmd_setgroup(command):
obj.emit_to("%s removed you from group '%s'." % (source_object.get_name(show_dbref=False,no_ansi=True),
group.name))
GLOBAL_CMD_TABLE.add_command("@setgroup", cmd_setgroup,
priv_tuple=("auth.change_group","genperms.admin_group"), auto_help=True, staff_help=True)
priv_tuple=("auth.change_group",
"genperms.admin_group"),
help_category="Admin")
def cmd_sethelp(command):
"""
@sethelp - edit the help database
Usage:
@sethelp[/switches] <topic>[,category][(permissions)][:<text>]
Switches:
add - add or replace a new topic with text.
append - add text to the end of topic.
delete - remove help topic.
force - (used with add) create help topic also if the topic
already exists.
newl - (used with append) add a newline between the old
text and the appended text.
Examples:
@sethelp/add throw : This throws something at ...
@sethelp/add throw, General (genperms.throwing) : This throws ...
@sethelp/add throw : 1st help entry
[[@sethelp_markup]]
@sethelp Help markup
The <text> entry in @sethelp supports markup to automatically divide the help text into
several sub-entries. The beginning of each new entry is marked in the form
[ [Title, category, (privtuple)] ] (with no spaces between the square brackets)
In the markup header, Title is mandatory, the other parts are optional. A new
help entry named Title will be created for each occurence. It is recommended
that the help entries should begin similarly since the system will then identify
them and better handle a list of recommended topics.
"""
source_object = command.source_object
arg = command.command_argument
switches = command.command_switches
if not arg or not switches:
source_object.emit_to("Usage: @sethelp/[add|del|append] <topic>[,category][:<text>]")
return
topicstr = ""
category = ""
text = ""
permtuple = ()
# analyze the argument
arg = arg.split(':', 1)
if len(arg) < 2:
# no : detected; this means we are deleting something.
topicstr = arg[0].strip()
else:
text = arg[1].strip()
# we have 4 possibilities:
# topicstr
# topicstr, category
# topicstr (perm1,perm2,...)
# topicstr, category, (perm1,perm2,...)
arg = arg[0].split('(',1)
if len(arg) > 1:
# we have a perm tuple
arg, permtuple = arg
try:
permtuple = permtuple.strip()[:-1] # cut last ')'
except IndexError:
source_object.emit_to("Malformed permission tuple. %s" % permtuple)
return
permtuple = tuple(permtuple.split(','))
else:
# no perm tuple
arg = arg[0]
arg = arg.split(',', 1)
if len(arg) > 1:
# we have a category
category = arg[1].strip()
topicstr = arg[0].strip()
if 'add' in switches:
# add a new help entry.
if not topicstr or not text:
source_object.emit_to("Usage: @sethelp/add <topic>[,category]:<text>")
return
force_create = ('for' in switches) or ('force' in switches)
topics = helpsystem.edithelp.add_help_manual(source_object, topicstr,
category, text,
permissions=permtuple,
force=force_create)
if not topics:
return
if len(topics) == 1:
string = "The topic already exists. Use /force to overwrite it."
elif len(topics)>1:
string = "The following results are similar to '%s'."
string += " Make sure you are not misspelling, then "
string += "use the /force flag to create a new entry."
string += "\n ".join(topics)
source_object.emit_to(string)
elif 'append' in switches or 'app' in switches:
# add text to the end of a help topic
if not topicstr or not text:
source_object.emit_to("Usage: @sethelp/append <topic>:<text>")
return
# find the topic to append to
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
if not topics:
source_object.emit_to("Help topic '%s' not found." % topicstr)
elif len(topics) > 1:
string = "Multiple matches to this topic. Refine your search."
string += "\n ".join(topics)
else:
# we have exactly one match. Extract all info from it,
# append the text and feed it back into the system.
newtext = topics[0].get_entrytext_ingame()
category = topics[0].category
perm_tuple = topics[0].canview
if perm_tuple:
perm_tuple = tuple(perm for perm in perm_tuple.split(','))
newl = "\n"
if 'newl' in switches or 'newline' in switches:
newl = "\n\n"
newtext += "%s%s" % (newl, text)
topics = helpsystem.edithelp.add_help_manual(source_object,
topicstr,
category,
newtext,
perm_tuple,
force=True)
elif 'del' in switches or 'delete' in switches:
#delete a help entry
topics = helpsystem.edithelp.del_help_manual(source_object, topicstr)
if not topics:
return
else:
string = "Multiple matches for '%s'. Please specify:" % topicstr
string += "\n ".join(topics)
GLOBAL_CMD_TABLE.add_command("@sethelp", cmd_sethelp,
priv_tuple=("helpsys.add_help",
"helpsys.del_help",
"helpsys.admin_heelp"),
help_category="Admin")

View file

@ -146,6 +146,11 @@ def build_query(source_object, search_query, search_player, search_type,
def cmd_search(command):
"""
search
Usage:
search <name>
Searches for owned objects as per MUX2.
"""
source_object = command.source_object
@ -232,4 +237,5 @@ def cmd_search(command):
display_results(source_object, search_query)
GLOBAL_CMD_TABLE.add_command("@search", cmd_search,
priv_tuple=("objects.info")),
priv_tuple=("objects.info",),
help_category="Building")

View file

@ -3,7 +3,7 @@ Commands that are available from the connect screen.
"""
import traceback
from django.contrib.auth.models import User
from src.objects.models import Attribute, Object
from src.objects.models import Object
from src import defines_global
from src.util import functions_general
from src.cmdtable import GLOBAL_UNCON_CMD_TABLE
@ -47,7 +47,7 @@ def cmd_connect(command):
else:
uname = user.username
session.login(user)
GLOBAL_UNCON_CMD_TABLE.add_command("connect", cmd_connect)
GLOBAL_UNCON_CMD_TABLE.add_command("connect", cmd_connect, auto_help_override=False)
def cmd_create(command):
"""
@ -113,7 +113,7 @@ def cmd_create(command):
log_errmsg(traceback.format_exc())
raise
GLOBAL_UNCON_CMD_TABLE.add_command("create", cmd_create)
GLOBAL_UNCON_CMD_TABLE.add_command("create", cmd_create, auto_help_override=False)
def cmd_quit(command):
"""
@ -124,4 +124,4 @@ def cmd_quit(command):
session = command.session
session.msg("Good bye! Disconnecting ...")
session.handle_close()
GLOBAL_UNCON_CMD_TABLE.add_command("quit", cmd_quit)
GLOBAL_UNCON_CMD_TABLE.add_command("quit", cmd_quit, auto_help_override=False)

View file

@ -88,6 +88,7 @@ PERM_CHANNELS = (
('page','May page other users.'),)
# help system access permissions
PERM_HELPSYS = (
("admin_help","May admin the help system"),
("staff_help", "May see staff help topics."),
("add_help", "May add or append to help entries"),
("del_help", "May delete help entries"),)
@ -135,31 +136,50 @@ PERM_ALL_CUSTOM = ()
# A dict defining the groups, on the form {group_name:(perm1,perm2,...),...}
PERM_GROUPS = \
{"Immortals":('irc.admin_irc_channels','imc2.admin_imc_channels','channels.emit_commchannel',
'channels.channel_admin','channels.page','helpsys.staff_help','helpsys.add_help',
'helpsys.del_help','objects.teleport','objects.wipe','objects.modify_attributes',
'objects.info','objects.create','objects.dig','objects.see_dbref','objects.admin_ownership',
'genperms.announce','genperms.admin_perm','genperms.admin_group','genperms.process_control',
'genperms.manage_players','genperms.game_info'),
"Wizards": ('irc.admin_irc_channels','imc2.admin_imc_channels','channels.emit_commchannel',
'channels.channel_admin','channels.page','helpsys.staff_help','helpsys.add_help',
'helpsys.del_help','objects.teleport','objects.wipe','objects.see_dbref',
'objects.modify_attributes',
'objects.info','objects.create','objects.dig','objects.admin_ownership','genperms.announce',
'genperms.game_info'),
"Builders":('channels.emit_commchannel','channels.page','helpsys.staff_help','helpsys.add_help',
'helpsys.del_help','objects.teleport','objects.wipe','objects.see_dbref',
'objects.modify_attributes', 'objects.info','objects.create','objects.dig',
'genperms.game_info'),
"Player Helpers":('channels.emit_commchannel', 'channels.page', 'helpsys.staff_help',
'helpsys.add_help','helpsys.del_help'),
"Players":('channels.emit_commchannel','channels.page')
{"Immortals":('irc.admin_irc_channels', 'imc2.admin_imc_channels', 'channels.emit_commchannel',
'channels.channel_admin', 'channels.page', 'helpsys.admin_help',
'helpsys.staff_help', 'helpsys.add_help',
'helpsys.del_help', 'objects.teleport', 'objects.wipe', 'objects.modify_attributes',
'objects.info','objects.create','objects.dig','objects.see_dbref',
'objects.admin_ownership', 'genperms.announce', 'genperms.admin_perm',
'genperms.admin_group', 'genperms.process_control', 'genperms.manage_players',
'genperms.game_info'),
"Wizards": ('irc.admin_irc_channels', 'imc2.admin_imc_channels', 'channels.emit_commchannel',
'channels.channel_admin', 'channels.page', 'helpsys.admin_help',
'helpsys.staff_help', 'helpsys.add_help',
'helpsys.del_help', 'objects.teleport', 'objects.wipe', 'objects.see_dbref',
'objects.modify_attributes', 'objects.info', 'objects.create', 'objects.dig',
'objects.admin_ownership', 'genperms.announce', 'genperms.game_info'),
"Builders":('channels.emit_commchannel', 'channels.page', 'helpsys.staff_help',
'helpsys.add_help', 'helpsys.del_help',
'objects.teleport', 'objects.wipe', 'objects.see_dbref',
'objects.modify_attributes', 'objects.info',
'objects.create', 'objects.dig', 'genperms.game_info'),
"Player Helpers":('channels.emit_commchannel', 'channels.page', 'helpsys.staff_help',
'helpsys.add_help', 'helpsys.del_help'),
"Players":('channels.emit_commchannel', 'channels.page')
}
# By defining a default player group, all players may start with some permissions pre-set.
PERM_DEFAULT_PLAYER_GROUP = "Players"
## Help system
## Evennia allows automatic help-updating of commands by use of the auto-help system
## which use the command's docstrings for documentation, automatically updating it
## as commands are reloaded. Auto-help is a powerful way to keep your help database
## up-to-date, but it will also overwrite manual changes made
## to the help database using other means (@set_help, admin interface etc), so
## for a production environment you might want to turn auto-help off. You can
## later activate auto-help on a per-command basis (e.g. when developing a new command)
## using the auto_help_override argument to add_command().
# activate the auto-help system
HELP_AUTO_ENABLED = True
# Add a dynamically calculated 'See also' footer to help entries
HELP_SHOW_RELATED = True
## Channels
## Your names of various default comm channels for emitting debug- or informative messages.
# Your names of various default comm channels for emitting debug- or informative messages.
COMMCHAN_MUD_INFO = 'MUDInfo'
COMMCHAN_MUD_CONNECTIONS = 'MUDConnections'
COMMCHAN_IMC2_INFO = 'MUDInfo'

411
src/helpsys/helpsystem.py Normal file
View file

@ -0,0 +1,411 @@
"""
Support functions for the help system.
Allows adding help to the data base from inside the mud as
well as creating auto-docs of commands based on their doc strings.
The system supports help-markup for multiple help entries as well
as a dynamically updating help index.
"""
import textwrap
from django.conf import settings
from src.helpsys.models import HelpEntry
from src import logger
from src import defines_global
class EditHelp(object):
"""
This sets up an object able to perform normal editing
operations on the help database.
"""
def __init__(self, indent=4, width=70):
"""
We check if auto-help is active or not and
set some formatting options.
"""
self.indent = indent # indentation of help text
self.width = width # width of help text
def format_help_text(self, help_text):
"""
This formats the help entry text for proper left-side indentation.
The first line is adjusted to the proper indentation and the
subsequent lines are then adjusted proportionally to the first;
so indentation relative this first line remains intact.
"""
lines = help_text.expandtabs().splitlines()
# strip empty lines above and below the text
while True:
if lines and not lines[0].strip():
lines.pop(0)
else:
break
while True:
if lines and not lines[-1].strip():
lines.pop()
else:
break
if not lines:
return ""
# produce a list of the indentations of each line initially
indentlist = [len(line) - len(line.lstrip()) for line in lines]
# use the first line to set the shift
lineshift = indentlist[0] - self.indent
# shift everything to the left
indentlist = [max(self.indent, indent-lineshift) for indent in indentlist]
trimmed = []
for il, line in enumerate(lines):
indentstr = " " * indentlist[il]
trimmed.append("%s%s" % (indentstr, line.strip()))
return "\n".join(trimmed)
def parse_markup_header(self, subtopic_header):
"""
The possible markup headers for splitting the help into sections are:
[[TopicTitle]]
[[TopicTitle,category]]
[[TopicTitle(perm1,perm2)]]
[[TopicTitle,category(perm1,perm2)]]
"""
subtitle = ""
subcategory = ""
subpermissions = ()
#identifying the header parts. The header can max have three parts:
# topicname, category (perm1,perm2,...)
try:
# find the permission tuple
lindex = subtopic_header.index('(')
rindex = subtopic_header.index(')')
if lindex < rindex:
permtuple = subtopic_header[lindex+1:rindex]
subpermissions = tuple([p.strip()
for p in permtuple.split(',')])
subtopic_header = subtopic_header[:lindex]
except ValueError:
# no permission tuple found
pass
# see if we have a name, category pair.
try:
subtitle, subcategory = subtopic_header.split(',')
subtitle, subcategory = subtitle.strip(), subcategory.strip()
except ValueError:
subtitle = subtopic_header.strip()
# we are done, return a tuple with the results
return ( subtitle, subcategory, subpermissions )
def format_help_entry(self, helptopic, category, helptext, permissions=None):
"""
helptopic (string) - name of the full help entry
helptext (string) - the help entry (may contain sections)
permissions (tuple) - tuple with permission/group names
defined for the entire help entry.
(markup permissions override those)
Handles help markup in order to split help into subsections.
These markup markers will be assumed to start a new line, regardless
of where they are located in the help entry. If no permission string
tuple and/or category is given, the overall permission/category of
the entire help entry is used.
"""
# sanitize input
topics = []
if '[[' not in helptext:
formatted_text = self.format_help_text(helptext)
topics.append((helptopic, category,
formatted_text, permissions))
return topics
subtopics = helptext.split('[[')
if subtopics[0]:
# the very first entry (before any markup) is the normal
# help entry for the helptopic at hand.
formatted_text = self.format_help_text(subtopics[0])
topics.append((helptopic, category, formatted_text, permissions))
for subtopic in subtopics[1:]:
# handle all extra topics designated with markup
try:
subtopic_header, subtopic_text = subtopic.split(']]', 1)
except ValueError:
# if we have no ending, the entry is malformed and
# we ignore this entry (better than overwriting
# something in the database).
logger.log_errmsg("Malformed help markup in %s: '%s'\n (missing end ']]' )" % \
(helptopic, subtopic))
continue
# parse and format the help entry parts
subtopic_header = self.parse_markup_header(subtopic_header)
if not subtopic_header[0]:
# we require a topic title.
logger.log_errmsg("Malformed help markup in '%s': Missing title." % subtopic_header)
return
# parse the header and use defaults
subtopic_name = subtopic_header[0]
subtopic_category = subtopic_header[1]
subtopic_text = self.format_help_text(subtopic_text)
subtopic_permissions = subtopic_header[2]
if not subtopic_category:
# no category set; inherit from main topic
subtopic_category = category
if not subtopic_permissions:
# no permissions set; inherit from main topic
subtopic_permissions = permissions
# We have a finished topic, add it to the list as a topic tuple.
topics.append((subtopic_name, subtopic_category,
subtopic_text, subtopic_permissions))
return topics
def create_help(self, newtopic):
"""
Add a help entry to the database, replace an old one if it exists.
topic (tuple) - this is a formatted tuple of data as prepared
by format_help_entry, on the form (title, category, text, (perm_tuple))
"""
#sanity checks;
topicname = newtopic[0]
category = newtopic[1]
entrytext = newtopic[2]
permissions = newtopic[3]
if not (topicname or entrytext):
# don't create anything if there we
# are missing vital parts
return
if not category:
# this will force the default
category = "General"
if permissions:
# the permissions tuple might be mangled;
# make sure we build a string properly.
if type(permissions) != type(tuple()):
permissions = "%s" % permissions
else:
permissions = ", ".join(permissions)
else:
permissions = ""
# check if the help topic already exist.
oldtopic = HelpEntry.objects.filter(topicname__iexact=newtopic[0])
if oldtopic:
#replace an old help file
topic = oldtopic[0]
topic.category = category
topic.entrytext = entrytext
topic.canview = permissions
topic.save()
else:
#we have a new topic - create a new help object
new_entry = HelpEntry(topicname=topicname,
category=category,
entrytext=entrytext,
canview=permissions)
new_entry.save()
def add_help_auto(self, topicstr, category, entrytext, permissions=()):
"""
This is used by the auto_help system to add help one or more
help entries to the system.
"""
# sanity checks
if permissions and type(permissions) != type(tuple()):
string = "Auto-Help: malformed perm_tuple %s: %s -> %s (fixed)" % \
(topicstr,permissions, (permissions,))
logger.log_errmsg(string)
permissions = (permissions,)
# identify markup and do nice formatting as well as eventual
# related entries to the help entries.
logger.log_infomsg("auto-help in: %s %s %s %s" % (topicstr, category, entrytext, permissions))
topics = self.format_help_entry(topicstr, category,
entrytext, permissions)
logger.log_infomsg("auto-help: %s -> %s" % (topicstr,topics))
# create the help entries:
if topics:
for topic in topics:
self.create_help(topic)
def add_help_manual(self, pobject, topicstr, category,
entrytext, permissions=(), force=False):
"""
This is used when a player wants to add a help entry to the database
manually (most often from inside the game)
force - this is given by the player and forces an overwrite also if the
entry already exists or there are multiple similar matches to
the entry.
"""
# permission check:
if not (pobject.is_superuser() or pobject.has_perm("helpsys.add_help")):
pobject.emit_to(defines_global.NOPERMS_MSG)
return None
# do a more fuzzy search to warn in case in case we are misspelling.
topic = HelpEntry.objects.find_topicmatch(pobject, topicstr)
if topic and not force:
return topic
self.add_help_auto(topicstr, category, entrytext, permissions)
pobject.emit_to("Added/appended help topic '%s'." % topicstr)
def del_help_auto(self, topicstr):
"""
Delete a help entry from the data base. Automatic version.
"""
topic = HelpEntry.objects.filter(topicname__iexact=topicstr)
if topic:
topic[0].delete()
def del_help_manual(self, pobject, topicstr):
"""
Deletes an entry from the database. Interactive version.
Note that it makes no sense to delete auto-added help entries this way since
they will be re-added on the next @reload. This is mostly useful for cleaning
the database from doublet or orphaned entries, or when auto-help is turned off.
"""
# find topic with permission checks
if not (pobject.is_superuser() or pobject.has_perm("helpsys.del_help")):
pobject.emit_to(defines_global.NOPERMS_MSG)
return None
topic = HelpEntry.objects.find_topicmatch(pobject, topicstr)
if not topic or len(topic) > 1:
return topic
# we have an exact match. Delete topic.
topic[0].delete()
pobject.emit_to("Help entry '%s' deleted." % topicstr)
def homogenize_database(self, category):
"""
This sets the entire help database to one category.
It can be used to mark an initially loaded help database
in a particular category, for later filtering.
In evennia dev version, this is done with MUX help database.
"""
entries = HelpEntry.objects.all()
for entry in entries:
entry.category = category
entry.save()
logger.log_infomsg("Help database homogenized to category %s" % category)
def autoclean_database(self, topiclist):
"""
This syncs the entire help database against a reference topic
list, deleting non-used or duplicate help entries that can be
the result of auto-help misspellings etc.
"""
pass
class ViewHelp(object):
"""
This class contains ways to view the
help database in a dynamical fashion.
"""
def __init__(self, indent=4, width=78, category_cols=4, entry_cols=6):
"""
indent (int) - number of spaces to indent tables with
width (int) - width of index tables
category_cols (int) - number of collumns per row for
category tables
entry_cols (int) - number of collumns per row for help entries
"""
self.width = width
self.indent = indent
self.category_cols = category_cols
self.entry_cols = entry_cols
self.show_related = settings.HELP_SHOW_RELATED
def make_table(self, items, cols):
"""
This takes a list of string items and displays them in collumn order,
(sorted horizontally-first), ie
A A A A
A B B B
B B C C
C C
cols is the number of collumns to format.
"""
items.sort()
if not items or not cols:
return []
length = len(items)
# split the list into sublists of length cols
rows = [items[i:i+cols] for i in xrange(0, length, cols)]
# build the table
string = ""
for row in rows:
string += self.indent * " " + ", ".join(row) + "\n"
return string
def index_full(self, pobject):
"""
This lists all available topics in the help index,
ordered after category.
The MUX category is not shown, it is for development
reference only.
"""
entries = HelpEntry.objects.all()
categories = [e.category for e in entries if e.category != 'MUX']
categories = list(set(categories)) # make list unique
categories.sort()
table = ""
for category in categories:
topics = [e.topicname.lower() for e in entries.filter(category__iexact=category)
if e.can_view(pobject)]
# pretty-printing the list
header = "--- Topics in category %s:" % category
nl = self.width - len(header)
if not topics:
text = self.indent*" " + "[There are no topics relevant to you in this category.]\n\r"
else:
text = self.make_table(topics, self.entry_cols)
table += "\r\n%s%s\n\r\n\r%s" % (header, "-"*nl, text)
return table
def index_categories(self):
"""
This lists all categories defined in the help index.
"""
entries = HelpEntry.objects.all()
categories = [e.category for e in entries]
categories = list(set(categories)) # make list unique
return self.make_table(categories, self.category_cols)
def index_category(self, pobject, category):
"""
List the help entries within a certain category
"""
entries = HelpEntry.objects.find_topics_with_category(pobject, category)
if not entries:
return []
# filter out those we can actually view
helptopics = [e.topicname.lower() for e in entries if e.can_view(pobject)]
if not helptopics:
# we don't have permission to view anything in this category
return " [There are no topics relevant to you in this category.]\n\r"
return self.make_table(helptopics, self.entry_cols)
def suggest_help(self, pobject, topic):
"""
This goes through the help database, searching for relatively
close matches to this topic. If those are found, they are
added as a nice footer to the end of the topic entry.
"""
if not self.show_related:
return None
topicname = topic.topicname
return HelpEntry.objects.find_topicsuggestions(pobject, topicname)
# Object instances
edithelp = EditHelp(indent=3,
width=80)
viewhelp = ViewHelp(indent=3,
width=80,
category_cols=4,
entry_cols=4)

View file

@ -1,230 +0,0 @@
"""
Support commands for a more advanced help system.
Allows adding help to the data base from inside the mud as
well as creating auto-docs of commands based on their doc strings.
The system supports help-markup for multiple help entries as well
as a dynamically updating help index.
"""
from django.contrib.auth.models import User
from src.helpsys.models import HelpEntry
from src.ansi import ANSITable
#
# Helper functions
#
def _privileged_help_search(topicstr):
"""
searches the topic data base without needing to know who calls it. Needed
for autohelp functionality. Will show all help entries, also those set to staff
only.
"""
if topicstr.isdigit():
t_query = HelpEntry.objects.filter(id=topicstr)
else:
exact_match = HelpEntry.objects.filter(topicname__iexact=topicstr)
if exact_match:
t_query = exact_match
else:
t_query = HelpEntry.objects.filter(topicname__istartswith=topicstr)
return t_query
def _create_help(topicstr, entrytext, staff_only=False, force_create=False,
pobject=None, noauth=False):
"""
Add a help entry to the database, replace an old one if it exists.
Note - noauth=True will bypass permission checks, so do not use this from
inside mud, it is needed by the autohelp system only.
"""
if noauth:
#do not check permissions (for autohelp)
topic = _privileged_help_search(topicstr)
elif pobject:
#checks access rights before searching (this should have been
#done already at the command level)
if not pobject.has_perm("helpsys.add_help"): return []
topic = HelpEntry.objects.find_topicmatch(pobject, topicstr)
else:
return []
if len(topic) == 1:
#replace an old help file
topic = topic[0]
topic.entrytext = entrytext
topic.staff_only = staff_only
topic.save()
return [topic]
elif len(topic) > 1 and not force_create:
#a partial match, return it for inspection.
return topic
else:
#we have a new topic - create a new help object
new_entry = HelpEntry(topicname=topicstr,
entrytext=entrytext,
staff_only=staff_only)
new_entry.save()
return [new_entry]
def handle_help_markup(topicstr, entrytext, staff_only, identifier="<<TOPIC:"):
"""
Handle help markup in order to split help into subsections.
Handles markup of the form <<TOPIC:STAFF:TopicTitle>> and
<<TOPIC:ALL:TopicTitle>> to override the staff_only flag on a per-subtopic
basis.
"""
topic_list = entrytext.split(identifier)
topic_dict = {}
staff_dict = {}
for txt in topic_list:
txt = txt.rstrip()
if txt.count('>>'):
topic, text = txt.split('>>',1)
text = text.rstrip()
topic = topic.lower()
if topic in topic_dict.keys():
#do not allow multiple topics of the same name
return {}, []
if 'all:' in topic:
topic = topic[4:]
staff_dict[topic] = False
elif 'staff:' in topic:
topic = topic[6:]
staff_dict[topic] = True
else:
staff_dict[topic] = staff_only
topic_dict[topic] = text
else:
#no markup, just add the entry as-is
topic = topicstr.lower()
topic_dict[topic] = txt
staff_dict[topic] = staff_only
return topic_dict, staff_dict
def format_footer(top, text, topic_dict, staff_dict):
"""
Formats the subtopic with a 'Related Topics:' footer. If mixed
staff-only flags are set, those help entries without the staff-only flag
will not see staff-only help files recommended in the footer. This allows
to separate out the staff-only help switches etc into a separate
help file so as not to confuse normal players.
"""
if text:
#only include non-staff related footers to non-staff commands
if staff_dict[top]:
other_topics = other_topics = filter(lambda o: o != top, topic_dict.keys())
else:
other_topics = other_topics = filter(lambda o: o != top and not staff_dict[o],
topic_dict.keys())
if other_topics:
footer = ANSITable.ansi['normal'] + "\n\r\n\r Related Topics: "
for t in other_topics:
footer += t + ', '
footer = footer[:-2] + '.'
return text + footer
else:
return text
else:
return False
#
# Access functions
#
def add_help(topicstr, entrytext, staff_only=False, force_create=False,
pobject=None, auto_help=False):
"""
Add a help topic to the database. This is also usable by autohelp, with auto=True.
Allows <<TOPIC:TopicTitle>> markup in the help text, to automatically spawn
subtopics. For creating mixed staff/ordinary subtopics, the <<TOPIC:STAFF:TopicTitle>> and
<<TOPIC:ALL:TopicTitle>> commands can override the overall staff_only setting for
that entry only.
"""
identifier = '<<TOPIC:'
if identifier in entrytext:
#There is markup in the entry, so we should split the doc into separate subtopics
topic_dict, staff_dict = handle_help_markup(topicstr, entrytext,
staff_only, identifier)
topics = []
for topic, text in topic_dict.items():
#format with nice footer
entry = format_footer(topic, text, topic_dict, staff_dict)
if entry:
#create the subtopic
newtopic = _create_help(topic, entry,staff_only=staff_dict[topic],
force_create=force_create,pobject=pobject,noauth=auto_help)
topics.extend(newtopic)
return topics
elif entrytext:
#if there were no topic sections, just create the help entry as normal
return _create_help(topicstr.lower(),entrytext,staff_only=staff_only,
force_create=force_create,pobject=pobject,noauth=auto_help)
def del_help(pobject,topicstr):
"""
Delete a help entry from the data base.
Note that it makes no sense to delete auto-added help entries this way since
they will just be re-added on the next @reload. Delete such entries by turning
off their auto-help functionality first.
"""
#find topic with permission checks
if not pobject.is_staff(): return []
topic = HelpEntry.objects.find_topicmatch(pobject, topicstr)
if topic:
if len(topic) == 1:
#delete topic
topic.delete()
return True
else:
return topic
else:
return []
def get_help_index(pobject,filter=None):
"""
Dynamically builds a help index depending on who asks for it, so
normal players won't see staff-only help files, for example.
The filter parameter allows staff to limit their view of the help index
no filter (default) - view all help files, staff and non-staff
filter='staff' - view only staff-specific help files
filter='player' - view only those files visible to all
"""
if pobject.has_perm("helpsys.staff_help"):
if filter == 'staff':
helpentries = HelpEntry.objects.filter(staff_only=True).order_by('topicname')
elif filter == 'player':
helpentries = HelpEntry.objects.filter(staff_only=False).order_by('topicname')
else:
helpentries = HelpEntry.objects.all().order_by('topicname')
else:
helpentries = HelpEntry.objects.filter(staff_only=False).order_by('topicname')
if not helpentries:
pobject.emit_to("No help entries found.")
return
topics = [entry.topicname for entry in helpentries]
#format help entries into suitable alphabetized collumns.
percollumn = 8
s = ""
i = 0
while True:
i += 1
try:
top = topics.pop(0)
s+= " %s " % top
if i%percollumn == 0: s += '\n\r'
except IndexError:
break
s += " (%i entries)" % (i-1)
pobject.emit_to(s)

View file

@ -4,35 +4,56 @@ Custom manager for HelpEntry objects.
from django.db import models
class HelpEntryManager(models.Manager):
def find_topicmatch(self, pobject, topicstr):
"""
This implements different ways to search for help entries.
"""
def find_topicmatch(self, pobject, topicstr, exact=False):
"""
Searches for matching topics based on player's input.
"""
is_staff = pobject.is_staff()
"""
if topicstr.isdigit():
t_query = self.filter(id=topicstr)
else:
exact_match = self.filter(topicname__iexact=topicstr)
if exact_match:
t_query = exact_match
else:
t_query = self.filter(topicname__istartswith=topicstr)
if not is_staff:
return t_query.exclude(staff_only=1)
return self.filter(id=topicstr)
t_query = self.filter(topicname__iexact=topicstr)
if not t_query and not exact:
t_query = self.filter(topicname__istartswith=topicstr)
# check permissions
t_query = [topic for topic in t_query if topic.can_view(pobject)]
return t_query
def find_topicsuggestions(self, pobject, topicstr):
"""
Do a fuzzier "contains" match.
"""
is_staff = pobject.is_staff()
t_query = self.filter(topicname__icontains=topicstr)
if not is_staff:
return t_query.exclude(staff_only=1)
return t_query
Do a fuzzy match, preferably within the category of the
current topic.
"""
basetopic = self.filter(topicname__iexact=topicstr)
if not basetopic:
# this topic does not exist; just reply partial
# matches to the string
topics = self.filter(topicname__icontains=topicstr)
return [topic for topic in topics if topic.can_view(pobject)]
# we know that the topic exists, try to find similar ones within
# its category.
basetopic = basetopic[0]
category = basetopic.category
topics = []
#remove the @
crop = topicstr.lstrip('@')
# first we filter for matches with the full name
topics = self.filter(category__iexact=category).filter(topicname__icontains=crop)
if len(crop) > 4:
# next search with a cropped version of the command.
ttemp = self.filter(category__iexact=category).filter(topicname__icontains=crop[:4])
ttemp = [topic for topic in ttemp if topic not in topics]
topics = list(topics) + list(ttemp)
# we need to clean away the given help entry.
return [topic for topic in topics if topic.topicname.lower() != topicstr.lower()]
def find_topics_with_category(self, pobject, category):
"""
Search topics having a particular category
"""
t_query = self.filter(category__iexact=category)
return [topic for topic in t_query if topic.can_view(pobject)]

View file

@ -11,12 +11,21 @@ class HelpEntry(models.Model):
A generic help entry.
"""
topicname = models.CharField(max_length=255, unique=True)
entrytext = models.TextField(blank=True, null=True)
staff_only = models.BooleanField(default=False)
category = models.CharField(max_length=255, default="General")
canview = models.CharField(max_length=255, blank=True)
entrytext = models.TextField(blank=True)
#deprecated, only here to allow MUX helpfile load.
staff_only = models.BooleanField(default=False)
objects = HelpEntryManager()
class Meta:
"""
Permissions here defines access to modifying help
entries etc, not which entries can be viewed (that
is controlled by the canview field).
"""
verbose_name_plural = "Help entries"
ordering = ['topicname']
permissions = settings.PERM_HELPSYS
@ -29,7 +38,24 @@ class HelpEntry(models.Model):
Returns the topic's name.
"""
return self.topicname
def get_category(self):
"""
Returns the category of this help entry.
"""
return self.category
def can_view(self, pobject):
"""
Check if the pobject has the necessary permission/group
to view this help entry.
"""
perm = self.canview.split(',')
if not perm or (len(perm) == 1 and not perm[0]) or \
pobject.has_perm("helpsys.admin_help"):
return True
return pobject.has_perm_list(perm)
def get_entrytext_ingame(self):
"""
Gets the entry text for in-game viewing.

View file

@ -11,6 +11,8 @@ from django.conf import settings
from src.objects.models import Object
from src.config.models import ConfigValue, CommandAlias, ConnectScreen
from src import comsys, defines_global, logger
from src.helpsys import helpsystem
def get_god_user():
"""
Returns the initially created 'god' User object.
@ -131,6 +133,16 @@ def import_help_files():
"""
management.call_command('loaddata', 'docs/help_files.json', verbosity=0)
def categorize_initial_helpdb():
"""
This makes sure that the initially loaded
database is separated into its own
help category.
"""
default_category = "MUX"
print " Moving initial imported help db to help category '%s'." % default_category
helpsystem.edithelp.homogenize_database(default_category)
def handle_setup():
"""
Main logic for the module.
@ -142,3 +154,4 @@ def handle_setup():
create_groups()
create_channels()
import_help_files()
categorize_initial_helpdb()

View file

@ -4,11 +4,13 @@ This is where all of the crucial, core object models reside.
import re
import traceback
try: import cPickle as pickle
except ImportError: import pickle
try:
import cPickle as pickle
except ImportError:
import pickle
from django.db import models
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import User
from django.conf import settings
from src.objects.util import object as util_object
from src.objects.managers.object import ObjectManager
@ -24,8 +26,6 @@ from src import logger
import src.flags
from src.util import functions_general
from src.logger import log_infomsg
class Attribute(models.Model):
"""
Attributes are things that are specific to different types of objects. For
@ -46,9 +46,9 @@ class Attribute(models.Model):
def __str__(self):
return "%s(%s)" % (self.attr_name, self.id)
"""
BEGIN COMMON METHODS
"""
#
# BEGIN COMMON METHODS
#
def get_name(self):
"""
Returns an attribute's name.
@ -74,7 +74,8 @@ class Attribute(models.Model):
"""
Returns True if the attribute is hidden.
"""
if self.attr_hidden or self.get_name().upper() in defines_global.HIDDEN_ATTRIBS:
if self.attr_hidden or self.get_name().upper() \
in defines_global.HIDDEN_ATTRIBS:
return True
else:
return False
@ -83,7 +84,8 @@ class Attribute(models.Model):
"""
Returns True if the attribute is unsettable.
"""
if self.get_name().upper() in defines_global.NOSET_ATTRIBS: return True
if self.get_name().upper() in defines_global.NOSET_ATTRIBS:
return True
else:
return False
@ -103,21 +105,28 @@ class Object(models.Model):
ROOM, or other entities within the database. Pretty much anything in the
game is an object. Objects may be one of several different types, and
may be parented to allow for differing behaviors.
We eventually want to find some way to implement object parents via loadable
modules or sub-classing.
"""
name = models.CharField(max_length=255)
ansi_name = models.CharField(max_length=255)
owner = models.ForeignKey('self', related_name="obj_owner", blank=True, null=True)
zone = models.ForeignKey('self', related_name="obj_zone", blank=True, null=True)
script_parent = models.CharField(max_length=255, blank=True, null=True)
home = models.ForeignKey('self', related_name="obj_home", blank=True, null=True)
owner = models.ForeignKey('self',
related_name="obj_owner",
blank=True, null=True)
zone = models.ForeignKey('self',
related_name="obj_zone",
blank=True, null=True)
script_parent = models.CharField(max_length=255,
blank=True, null=True)
home = models.ForeignKey('self',
related_name="obj_home",
blank=True, null=True)
type = models.SmallIntegerField(choices=defines_global.OBJECT_TYPES)
location = models.ForeignKey('self', related_name="obj_location", blank=True, null=True)
location = models.ForeignKey('self',
related_name="obj_location",
blank=True, null=True)
flags = models.TextField(blank=True, null=True)
nosave_flags = models.TextField(blank=True, null=True)
date_created = models.DateField(editable=False, auto_now_add=True)
date_created = models.DateField(editable=False,
auto_now_add=True)
# 'scriptlink' is a 'get' property for retrieving a reference to the correct
# script object. Defined down by get_scriptlink()
@ -125,10 +134,15 @@ class Object(models.Model):
objects = ObjectManager()
#state system can set a particular command table to be used (not persistent).
# state system can set a particular command
# table to be used (not persistent).
state = None
class Meta:
"""
Define permission types on the object class and
how it is ordered in the database.
"""
ordering = ['-date_created', 'id']
permissions = settings.PERM_OBJECTS
@ -147,13 +161,17 @@ class Object(models.Model):
"""
return "#%s" % str(self.id)
"""
BEGIN COMMON METHODS
"""
def search_for_object(self, ostring, emit_to_obj=None, search_contents=True,
search_location=True, dbref_only=False,
limit_types=False, search_aliases=False,
attribute_name=None):
#
# BEGIN COMMON METHODS
#
def search_for_object(self, ostring,
emit_to_obj=None,
search_contents=True,
search_location=True,
dbref_only=False,
limit_types=False,
search_aliases=False,
attribute_name=None):
"""
Perform a standard object search, handling multiple
results and lack thereof gracefully.
@ -187,13 +205,15 @@ class Object(models.Model):
attribute_name=attribute_name)
if len(results) > 1:
s = "More than one match for '%s' (please narrow target):" % ostring
string = "More than one match for '%s' (please narrow target):" % ostring
for num, result in enumerate(results):
invtext = ""
if result.get_location() == self:
invtext = " (carried)"
s += "\n %i-%s%s" % (num+1, result.get_name(show_dbref=False),invtext)
emit_to_obj.emit_to(s)
string += "\n %i-%s%s" % (num+1,
result.get_name(show_dbref=False),
invtext)
emit_to_obj.emit_to(string)
return False
elif len(results) == 0:
emit_to_obj.emit_to("I don't see that here.")
@ -224,13 +244,18 @@ class Object(models.Model):
for session in sessions:
session.msg(parse_ansi(message))
def execute_cmd(self, command_str, session=None):
def execute_cmd(self, command_str, session=None, ignore_state=False):
"""
Do something as this object.
bypass_state - ignore the fact that a player is in a state
(means the normal command table will be used
no matter what)
"""
# The Command object has all of the methods for parsing and preparing
# for searching and execution. Send it to the handler once populated.
cmdhandler.handle(cmdhandler.Command(self, command_str, session=session))
cmdhandler.handle(cmdhandler.Command(self, command_str, session=session),
ignore_state=ignore_state)
def emit_to_contents(self, message, exclude=None):
"""
@ -384,7 +409,8 @@ class Object(models.Model):
# When builder_override is enabled, a builder permission means
# the object controls the other.
if builder_override and not other_obj.is_player() and self.has_group('Builders'):
if builder_override and not other_obj.is_player() \
and self.has_group('Builders'):
return True
# They've failed to meet any of the above conditions.
@ -466,7 +492,8 @@ class Object(models.Model):
uobj.is_active = False
uobj.save()
except:
functions_general.log_errmsg('Destroying object %s but no matching player.' % (self,))
string = 'Destroying object %s but no matching player.' % (self,)
functions_general.log_errmsg(string)
# Set the object type to GOING
self.type = defines_global.OTYPE_GOING
@ -511,7 +538,7 @@ class Object(models.Model):
"""
# Gather up everything, other than exits and going/garbage, that is under
# the belief this is its location.
objs = self.obj_location.filter(type__in=[1,2,3])
objs = self.obj_location.filter(type__in=[1, 2, 3])
default_home_id = ConfigValue.objects.get_configvalue('default_home')
try:
default_home = Object.objects.get(id=default_home_id)
@ -644,7 +671,8 @@ class Object(models.Model):
"""
Returns a QuerySet of an object's attributes.
"""
return [attr for attr in self.attribute_set.all() if not attr.is_hidden()]
return [attr for attr in self.attribute_set.all()
if not attr.is_hidden()]
def clear_all_attributes(self):
@ -684,9 +712,11 @@ class Object(models.Model):
re.IGNORECASE)
# If the regular expression search returns a match object, add to results.
if exclude_noset:
return [attr for attr in attrs if match_exp.search(attr.get_name()) and not attr.is_hidden() and not attr.is_noset()]
return [attr for attr in attrs if match_exp.search(attr.get_name())
and not attr.is_hidden() and not attr.is_noset()]
else:
return [attr for attr in attrs if match_exp.search(attr.get_name()) and not attr.is_hidden()]
return [attr for attr in attrs if match_exp.search(attr.get_name())
and not attr.is_hidden()]
def has_flag(self, flag):
@ -751,7 +781,10 @@ class Object(models.Model):
self.save()
def unset_flag(self, flag):
self.set_flag(flag,value=False)
"""
Clear the flag.
"""
self.set_flag(flag, value=False)
def get_flags(self):
"""
@ -815,7 +848,8 @@ class Object(models.Model):
try:
return self.location
except:
functions_general.log_errmsg("Object '%s(#%d)' has invalid location: #%s" % (self.name,self.id,self.location_id))
functions_general.log_errmsg("Object '%s(#%d)' has invalid location: #%s" % \
(self.name,self.id,self.location_id))
return False
def get_scriptlink(self):
@ -827,7 +861,7 @@ class Object(models.Model):
# Load the script reference into the object's attribute.
self.scriptlink_cached = scripthandler.scriptlink(self,
script_to_load)
script_to_load)
if self.scriptlink_cached:
# If the scriptlink variable can't be populated, this will fail
# silently and let the exception hit in the scripthandler.
@ -857,7 +891,8 @@ class Object(models.Model):
script_parent: (string) String pythonic import path of the script parent
assuming the python path is game/gamesrc/parents.
"""
if script_parent != None and scripthandler.scriptlink(self, str(script_parent).strip()):
if script_parent != None and scripthandler.scriptlink(self,
str(script_parent).strip()):
#assigning a custom parent
self.script_parent = str(script_parent).strip()
self.save()
@ -1046,7 +1081,10 @@ class Object(models.Model):
#state access functions
def get_state(self):
def get_state(self):
"""
Returns the player's current state.
"""
return self.state
def set_state(self, state_name=None):

View file

@ -5,7 +5,6 @@ import time
from django.contrib.auth.models import User
from src.config.models import ConfigValue
from src import logger
from src.util import functions_general
# Our list of connected sessions.
session_list = []

View file

@ -1,32 +1,43 @@
"""
The state system allows the player to enter states/modes where only a special set of commands
are available. This can be used for all sorts of useful things:
- in-game menus (picking an option from a list, much more powerful than using 'rooms')
- inline text editors (entering text into a buffer)
- npc conversation (select replies)
- only allowing certain commands in special situations (e.g. special attack commands
when in 'combat' mode)
- adding special commands to the normal set (e.g. a 'howl' command when in 'werewolf' state)
- deactivating certain commands (e.g. removing the 'say' command in said 'werewolf' state ...)
State table
Basically the GLOBAL_STATE_TABLE contains a dict with command tables keyed after the
name of the state. To use, a function must set the 'state' variable on a player object
using the obj.set_state() function. The GLOBAL_STATE_TABLE will then be searched by the
command handler and if the state is defined its command table is used instead
The state system allows the player to enter states/modes where only a
special set of commands are available. This can be used for all sorts
of useful things: - in-game menus (picking an option from a list, much
more powerful than using 'rooms') - inline text editors (entering text
into a buffer) - npc conversation (select replies) - only allowing
certain commands in special situations (e.g. special attack commands
when in 'combat' mode) - adding special commands to the normal set
(e.g. a 'howl' command when in 'werewolf' state) - deactivating
certain commands (e.g. removing the 'say' command in said 'werewolf'
state ...)
Basically the GLOBAL_STATE_TABLE contains a dict with command tables
keyed after the name of the state. To use, a function must set the
'state' variable on a player object using the obj.set_state()
function. The GLOBAL_STATE_TABLE will then be searched by the command
handler and if the state is defined its command table is used instead
of the normal global command table.
The state system is pluggable, in the same way that commands are added to the global command
table, commands are added to the GLOBAL_STATE_TABLE using add_command supplying in
addition the name of the state. The main difference is that new states must first be created
using the GLOBAL_STATE_TABLE.add_state() command. See examples in game/gamesrc.
The state system is pluggable, in the same way that commands are added
to the global command table, commands are added to the
GLOBAL_STATE_TABLE using add_command supplying in addition the name of
the state. The main difference is that new states must first be
created using the GLOBAL_STATE_TABLE.add_state() command. See examples
in game/gamesrc.
"""
from django.conf import settings
from cmdtable import CommandTable, GLOBAL_CMD_TABLE
from logger import log_errmsg
import src.helpsys.management.commands.edit_helpfiles as edit_help
from src import defines_global
from src.helpsys import helpsystem
from src.helpsys.models import HelpEntry
class StateTable(object):
"""
This implements a variant of the command table handling states.
"""
state_table = None
def __init__(self):
@ -35,9 +46,9 @@ class StateTable(object):
self.state_flags = {}
def add_state(self, state_name,
global_cmds=None, global_filter=[],
global_cmds=None, global_filter=None,
allow_exits=False, allow_obj_cmds=False,
exit_command=False):
help_command=True, exit_command=False):
"""
Creates a new game state. Each state has its own unique set of commands and
can change how the game works.
@ -76,51 +87,58 @@ class StateTable(object):
special conditions or clean-up operations before allowing a player
to exit (e.g. combat states and text editors), in which case this
feature should be turned off and handled by custom exit commands.
help_command
"""
state_name = state_name.strip()
#create state
self.state_table[state_name] = CommandTable()
if global_cmds != None:
if global_cmds == 'all':
f = lambda c: True
elif global_cmds == 'include':
f = lambda c: c in global_filter
if not 'help' in global_filter:
global_filter.append('help')
elif global_cmds == 'exclude':
f = lambda c: c not in global_filter
else:
log_errmsg("ERROR: in statetable, state %s: Unknown global_cmds flag '%s'." %
(state_name, global_cmds))
return
for cmd in filter(f,GLOBAL_CMD_TABLE.ctable.keys()):
self.state_table[state_name].ctable[cmd] = \
GLOBAL_CMD_TABLE.get_command_tuple(cmd)
if exit_command:
#if we import global commands, we use the normal help index; thus add
#help for @exit to the global index.
self.state_table[state_name].add_command("@exit",
cmd_state_exit,
auto_help=True)
else:
#when no global cmds are imported, we create a small custom
#state-based help index instead
self.help_index.add_state(state_name)
self.add_command(state_name,'help',cmd_state_help)
if exit_command:
#add the @exit command
self.state_table[state_name].add_command("@exit",
cmd_state_exit)
self.help_index.add_state_help(state_name, "@exit",
cmd_state_exit.__doc__)
#store special state flags
self.state_flags[state_name] = {}
self.state_flags[state_name]['globals'] = global_cmds
self.state_flags[state_name]['exits'] = allow_exits
self.state_flags[state_name]['obj_cmds'] = allow_obj_cmds
if global_cmds == 'all':
# we include all global commands
func = lambda c: True
elif global_cmds == 'include':
# selective global inclusion
func = lambda c: c in global_filter
if not 'help' in global_filter:
global_filter.append('help')
elif global_cmds == 'exclude':
# selective global exclusion
func = lambda c: c not in global_filter
else:
# no global commands
func = lambda c: False
# add copies of the global command defs to the state's command table.
for cmd in filter(func, GLOBAL_CMD_TABLE.ctable.keys()):
self.state_table[state_name].ctable[cmd] = \
GLOBAL_CMD_TABLE.get_command_tuple(cmd)
# create a stand-alone state-based help index
self.help_index.add_state(state_name)
# if the auto-help command is not wanted, just make a custom command
# overwriting this default 'help' command. Keeps 'info' as a way to have
# both a custom help command and state auto-help; replace this too
# to completely hide auto-help functionality in the state.
self.add_command(state_name, 'help', cmd_state_help)
self.add_command(state_name, 'info', cmd_state_help)
if exit_command:
#add the @exit command
self.state_table[state_name].add_command("@exit",
cmd_state_exit)
self.help_index.add_state_help(state_name,
"@exit",
"General",
cmd_state_exit.__doc__)
def del_state(self, state_name):
"""
Permanently deletes a state from the state table. Make sure no users are in
@ -147,12 +165,12 @@ class StateTable(object):
return
try:
del self.state_table[state_name].ctable[command_string]
err = self.help_index.del_state_help(state_name,command_string)
self.help_index.del_state_help(state_name, command_string)
except KeyError:
pass
def add_command(self, state_name, command_string, function, priv_tuple=None,
extra_vals=None, auto_help=False, staff_help=False):
extra_vals=None, help_category="", priv_help_tuple=None):
"""
Transparently add commands to a specific state.
This command is similar to the normal
@ -163,12 +181,12 @@ class StateTable(object):
function: (reference) command function object
priv_tuple: (tuple) String tuple of permissions required for command.
extra_vals: (dict) Dictionary to add to the Command object.
auto_help: (bool) Activate the auto_help system. By default this stores the
help inside the statetable only (not in the main help database), and so
the help entries are only available when the player is actually inside
the state. Note that the auto_help system of state-commands do not
support <<TOPIC:mytitle>> markup.
staff_help: (bool) Help entry is only available for staff players.
Auto-help functionality: (only used if settings.HELP_AUTO_ENABLED=True)
help_category (str): An overall help category where auto-help will place
the help entry. If not given, 'General' is assumed.
priv_help_tuple (tuple) String tuple of permissions required to view this
help entry. If nothing is given, priv_tuple is used.
"""
if state_name not in self.state_table.keys():
@ -177,21 +195,30 @@ class StateTable(object):
return
state_name = state_name.strip()
#handle auto-help for the state commands
if auto_help:
if self.help_index.has_state(state_name):
#add the help text to state help index only, don't add
#it to the global help index
self.help_index.add_state_help(state_name,command_string,
function.__doc__,
staff_help=staff_help)
auto_help = False
#finally add the new command to the state's command table
# handle the creation of an auto-help entry in the
# stand-alone help index
topicstr = command_string
if not help_category:
help_category = state_name
help_category = help_category.capitalize()
self.help_index.add_state_help(state_name,
topicstr,
help_category,
function.__doc__,
priv_help_tuple)
# finally add the new command to the state's command table
self.state_table[state_name].add_command(command_string,
function, priv_tuple,
extra_vals,auto_help=auto_help,
staff_help=staff_help)
function,
priv_tuple,
extra_vals,
help_category,
priv_help_tuple,
auto_help_override=False)
def get_cmd_table(self, state_name):
"""
@ -202,150 +229,157 @@ class StateTable(object):
else:
return None
def get_state_flags(self, state_name):
def get_exec_rights(self, state_name):
"""
Return the state flags for a particular state.
Used by the cmdhandler. Accesses the relevant state flags
concerned with execution access for a particular state.
"""
if self.state_flags.has_key(state_name):
return self.state_flags[state_name]['exits'],\
self.state_flags[state_name]['obj_cmds']
return self.state_flags[state_name]['exits'], \
self.state_flags[state_name]['obj_cmds']
else:
return False, False
return False, False, False
class StateHelpIndex(object):
"""
Handles the dynamic state help system.
Handles the dynamic state help system. This is
a non-db based help system intended for the special
commands associated with a state.
The system gives preference to help matches within
the state, but defers to the normal, global help
system when it fails to find a help entry match.
"""
help_index = None
def __init__(self):
self.help_index = {}
self.identifier = '<<TOPIC:'
def add_state(self,state_name):
def add_state(self, state_name):
"Create a new state"
self.help_index[state_name] = {}
def has_state(self,state_name):
def has_state(self, state_name):
"Checks if we have this state"
return self.help_index.has_key(state_name)
def add_state_help(self, state,command,text,staff_help=False):
"""Store help for a command under a certain state.
Supports <<TOPIC:MyTopic>> and <<TOPIC:STAFF:MyTopic>> markup."""
if self.help_index.has_key(state):
text = text.rstrip()
if self.identifier in text:
topic_dict, staff_dict = edit_help.handle_help_markup(command, text, staff_help,
identifier=self.identifier)
for topic, text in topic_dict.items():
entry = edit_help.format_footer(topic,text,topic_dict,staff_dict)
if entry:
self.help_index[state][topic] = (staff_help, entry)
else:
self.help_index[state][command] = (staff_help, text)
def del_state_help(self, state, topic):
"""Manually delete a help topic from state help system. Note that this is
only going to last until the next @reload unless you also turn off auto_help
for the relevant topic."""
if self.help_index.has_key(state) and self.help_index[state].has_key(topic):
del self.help_index[state][topic]
return True
else:
return False
def add_state_help(self, state, topicstr, help_category,
help_text, priv_help_tuple=()):
"""
Store help for a command under a certain state.
Supports [[Title, category, (priv_tuple)]] markup
"""
def get_state_help(self,caller, state, command):
"get help for a particular command within a state"
if self.help_index.has_key(state) and self.help_index[state].has_key(command):
if not self.help_index.has_key(state):
return
# produce nicely formatted help entries, taking markup
# into account.
topics = helpsystem.edithelp.format_help_entry(topicstr,
help_category,
help_text,
priv_help_tuple)
# store in state's dict-based database
for topic in topics:
self.help_index[state][topic[0]] = topic
def get_state_help(self, caller, state, command):
"""
Get help for a particular command within a state.
"""
if self.help_index.has_key(state) and \
self.help_index[state].has_key(command):
# this is a state help entry.
help_tuple = self.help_index[state][command]
if caller.is_staff() or not help_tuple[0]:
return help_tuple[1]
return None
else:
return None
# check permissions
if help_tuple and help_tuple[2]:
if help_tuple[3] and not caller.has_perm_list(help_tuple[3]):
return None
return help_tuple[2]
else:
return None
def get_state_index(self,caller, state):
def get_state_index(self, state):
"list all help topics for a state"
if self.help_index.has_key(state):
if caller.is_staff():
index = self.help_index[state].keys()
else:
index = []
for key, tup in self.help_index[state].items():
if not tup[0]:
index.append(key)
return sorted(index)
else:
return None
tuples = self.help_index[state].items()
items = [tup[0] for tup in tuples]
table = helpsystem.viewhelp.make_table(items, 6)
return table
#default commands available for all special states
# default commands available for all special states. These
# are added to states during the state initialization if
# the proper flags are set.
def cmd_state_exit(command):
"""@exit (when in a state)
"""
@exit - exit from a state
This command only works when inside certain special game 'states' (like a menu or
editor or similar situations).
Usage:
@exit
It aborts what you were doing and force-exits back to the normal mode of
gameplay. Some states might deactivate the @exit command for various reasons."""
This command only works when inside certain special game 'states'
(like a menu or editor or similar situations).
It aborts what you were doing and force-exits back to the normal
mode of gameplay. Some states might deactivate the @exit command
for various reasons.
"""
source_object = command.source_object
source_object.clear_state()
source_object.emit_to("... Exited.")
source_object.execute_cmd('look')
## In-state help system. This is NOT tied to the normal help
## system and is not stored in the database. It is intended as a quick
## reference for users when in the state; if you want a detailed description
## of the state itself, you should probably add it to the main help system
## so the user can read it at any time.
## If you don't want to use the auto-system, turn off auto_help
## for all commands in the state. You could then for example make a custom help command
## that displays just a short help summary page instead.
## Note that at this time we are not displaying categories in the state help system;
## (although they are stored). Instead the state itself is treated as a
## category in itself.
def cmd_state_help(command):
"""
help <topic> (while in a special state)
help - view help database
In-state help system. This is NOT tied to the normal help
system and is not stored in the database. It is intended as a quick
reference for users when in the state; if you want a detailed description
of the state itself, you should probably add it to the main help system
so the user can read it at any time.
If you don't want to use the auto-system, turn off auto_help
for all commands in the state. You could then for example make a custom help command
that displays just a short help summary page instead.
Usage:
help <topic>
Shows the available help on <topic>. Use without a topic
to get the index.
"""
source_object = command.source_object
state = source_object.get_state()
args = command.command_argument
switches = command.command_switches
help_index = GLOBAL_STATE_TABLE.help_index
state = source_object.get_state()
if not args:
index = help_index.get_state_index(source_object, state)
if not index:
source_object.emit_to("There is no help available here.")
return
s = "Help topics for %s:\n\r" % state
for i in index:
s += " %s\n\r" % i
s = s[:-2]
source_object.emit_to(s)
# first show the normal help index
source_object.execute_cmd("help", ignore_state=True)
text = help_index.get_state_index(state)
if text:
# Try to list the state-specific help entries after the main list
string = "\n%s%s%s\n\r\n\r%s" % ("---", " Help topics for %s: " % \
state.capitalize(), "-"*(30-len(state)), text)
source_object.emit_to(string)
return
# try to first find a matching state help topic, then defer to global help
topicstr = args.strip()
helptext = help_index.get_state_help(source_object, state, topicstr)
if helptext:
source_object.emit_to("\n%s" % helptext)
else:
args = args.strip()
source_object.execute_cmd("help %s" % topicstr, ignore_state=True)
if 'del' in switches:
if not source_object.is_staff():
source_object.emit_to("Only staff can delete help topics.")
return
if help_index.del_state_help(state,args):
source_object.emit_to("Topic %s deleted." % args)
else:
source_object.emit_to("Topic %s not found." % args)
return
help = help_index.get_state_help(source_object, state, args)
if help:
source_object.emit_to("%s" % help)
else:
source_object.emit_to("No help available on %s." % args)
#import this into modules
#import this instance into your modules
GLOBAL_STATE_TABLE = StateTable()