Even more death and destruction. A lot more stuff moved to src/. A lot more bugs probably introduced.

This commit is contained in:
Greg Taylor 2008-06-15 19:38:39 +00:00
parent d3808c1ea2
commit 4bb00013ae
28 changed files with 45 additions and 38 deletions

94
src/ansi.py Executable file
View file

@ -0,0 +1,94 @@
import re
"""
ANSI related stuff.
"""
ansi = {}
ansi["beep"] = "\07"
ansi["escape"] = "\033"
ansi["normal"] = "\033[0m"
ansi["underline"] = "\033[4m"
ansi["hilite"] = "\033[1m"
ansi["blink"] = "\033[5m"
ansi["inverse"] = "\033[7m"
ansi["inv_hilite"] = "\033[1;7m"
ansi["inv_blink"] = "\033[7;5m"
ansi["blink_hilite"] = "\033[1;5m"
ansi["inv_blink_hilite"] = "\033[1;5;7m"
# Foreground colors
ansi["black"] = "\033[30m"
ansi["red"] = "\033[31m"
ansi["green"] = "\033[32m"
ansi["yellow"] = "\033[33m"
ansi["blue"] = "\033[34m"
ansi["magenta"] = "\033[35m"
ansi["cyan"] = "\033[36m"
ansi["white"] = "\033[37m"
# Background colors
ansi["back_black"] = "\033[40m"
ansi["back_red"] = "\033[41m"
ansi["back_green"] = "\033[42m"
ansi["back_yellow"] = "\033[43m"
ansi["back_blue"] = "\033[44m"
ansi["back_magenta"] = "\033[45m"
ansi["back_cyan"] = "\033[46m"
ansi["back_white"] = "\033[47m"
# Formatting Characters
ansi["return"] = "\r\n"
ansi["tab"] = "\t"
ansi["space"] = " "
def parse_ansi(string, strip_ansi=False, strip_formatting=False):
"""
Parses a string, subbing color codes as needed.
"""
if strip_formatting:
char_return = ""
char_tab = ""
char_space = ""
else:
char_return = ansi["return"]
char_tab = ansi["tab"]
char_space = ansi["space"]
ansi_subs = [
(r'%r', char_return),
(r'%t', char_tab),
(r'%b', char_space),
(r'%cf', ansi["blink"]),
(r'%ci', ansi["inverse"]),
(r'%ch', ansi["hilite"]),
(r'%cn', ansi["normal"]),
(r'%cx', ansi["black"]),
(r'%cX', ansi["back_black"]),
(r'%cr', ansi["red"]),
(r'%cR', ansi["back_red"]),
(r'%cg', ansi["green"]),
(r'%cG', ansi["back_green"]),
(r'%cy', ansi["yellow"]),
(r'%cY', ansi["back_yellow"]),
(r'%cb', ansi["blue"]),
(r'%cB', ansi["back_blue"]),
(r'%cm', ansi["magenta"]),
(r'%cM', ansi["back_magenta"]),
(r'%cc', ansi["cyan"]),
(r'%cC', ansi["back_cyan"]),
(r'%cw', ansi["white"]),
(r'%cW', ansi["back_white"]),
]
for sub in ansi_subs:
p = re.compile(sub[0], re.DOTALL)
if strip_ansi:
string = p.sub("", string)
else:
string = p.sub(sub[1], string)
if strip_ansi:
return '%s' % (string)
else:
return '%s%s' % (string, ansi["normal"])

258
src/cmdhandler.py Executable file
View file

@ -0,0 +1,258 @@
"""
This is the command processing module. It is instanced once in the main
server module and the handle() function is hit every time a player sends
something.
"""
from traceback import format_exc
import time
from apps.objects.models import Object
import defines_global
import cmdtable
import functions_general
import functions_log
import functions_comsys
class UnknownCommand(Exception):
"""
Throw this when a user enters an an invalid command.
"""
def match_exits(pobject, searchstr):
"""
See if we can find an input match to exits.
"""
exits = pobject.get_location().get_contents(filter_type=4)
return Object.objects.list_search_object_namestr(exits, searchstr, match_type="exact")
def parse_command(command_string):
"""
Tries to handle the most common command strings and returns a dictionary with various data.
Common command types:
- Complex:
@pemit[/option] <target>[/option]=<data>
- Simple:
look
look <target>
I'm not married to either of these terms, but I couldn't think of anything better. If you can, lets change it :)
The only cases that I haven't handled is if someone enters something like:
@pemit <target> <target>/<switch>=<data>
- Ends up considering both targets as one with a space between them, and the switch as a switch.
@pemit <target>/<switch> <target>=<data>
- Ends up considering the first target a target, and the second target as part of the switch.
"""
# Each of the bits of data starts off as None, except for the raw, original
# command
parsed_command = dict(
raw_command=command_string,
data=None,
original_command=None,
original_targets=None,
base_command=None,
command_switches=None,
targets=None,
target_switches=None
)
try:
# If we make it past this next statement, then this is what we
# consider a complex command
(command_parts, data) = command_string.split('=', 1)
parsed_command['data'] = data
# First we deal with the command part of the command and break it
# down into the base command, along with switches
# If we make it past the next statement, then they must have
# entered a command like:
# p =<data>
# So we should probably just let it get caught by the ValueError
# again and consider it a simple command
(total_command, total_targets) = command_parts.split(' ', 1)
parsed_command['original_command'] = total_command
parsed_command['original_targets'] = total_targets
split_command = total_command.split('/')
parsed_command['base_command'] = split_command[0]
parsed_command['command_switches'] = split_command[1:]
# Now we move onto the target data
try:
# Look for switches- if they give target switches, then we don't
# accept multiple targets
(target, switch_string) = total_targets.split('/', 1)
parsed_command['targets'] = [target]
parsed_command['target_switches'] = switch_string.split('/')
except ValueError:
# Alright, no switches, so lets consider multiple targets
parsed_command['targets'] = total_targets.split()
except ValueError:
# Ok, couldn't find an =, so not a complex command
try:
(command, data) = command_string.split(' ', 1)
parsed_command['base_command'] = command
parsed_command['data'] = data
except ValueError:
# No arguments
# ie:
# - look
parsed_command['base_command'] = command_string
return parsed_command
def handle(cdat):
"""
Use the spliced (list) uinput variable to retrieve the correct
command, or return an invalid command error.
We're basically grabbing the player's command by tacking
their input on to 'cmd_' and looking it up in the GenCommands
class.
"""
session = cdat['session']
server = cdat['server']
try:
# TODO: Protect against non-standard characters.
if cdat['uinput'] == '':
return
parsed_input = {}
parsed_input['parsed_command'] = parse_command(cdat['uinput'])
# First we split the input up by spaces.
parsed_input['splitted'] = cdat['uinput'].split()
# Now we find the root command chunk (with switches attached).
parsed_input['root_chunk'] = parsed_input['splitted'][0].split('/')
# And now for the actual root command. It's the first entry in root_chunk.
parsed_input['root_cmd'] = parsed_input['root_chunk'][0].lower()
# Keep around the full, raw input in case a command needs it
cdat['raw_input'] = cdat['uinput']
# Now we'll see if the user is using an alias. We do a dictionary lookup,
# if the key (the player's root command) doesn't exist on the dict, we
# don't replace the existing root_cmd. If the key exists, its value
# replaces the previously splitted root_cmd. For example, sa -> say.
alias_list = server.cmd_alias_list
parsed_input['root_cmd'] = alias_list.get(parsed_input['root_cmd'],parsed_input['root_cmd'])
# This will hold the reference to the command's function.
cmd = None
if session.logged_in:
# Store the timestamp of the user's last command.
session.cmd_last = time.time()
# Lets the users get around badly configured NAT timeouts.
if parsed_input['root_cmd'] == 'idle':
return
# Increment our user's command counter.
session.cmd_total += 1
# Player-visible idle time, not used in idle timeout calcs.
session.cmd_last_visible = time.time()
# Just in case. Prevents some really funky-case crashes.
if len(parsed_input['root_cmd']) == 0:
raise UnknownCommand
# Shortened say alias.
if parsed_input['root_cmd'][0] == '"':
parsed_input['splitted'].insert(0, "say")
parsed_input['splitted'][1] = parsed_input['splitted'][1][1:]
parsed_input['root_cmd'] = 'say'
# Shortened pose alias.
elif parsed_input['root_cmd'][0] == ':':
parsed_input['splitted'].insert(0, "pose")
parsed_input['splitted'][1] = parsed_input['splitted'][1][1:]
parsed_input['root_cmd'] = 'pose'
# Pose without space alias.
elif parsed_input['root_cmd'][0] == ';':
parsed_input['splitted'].insert(0, "pose/nospace")
parsed_input['root_chunk'] = ['pose', 'nospace']
parsed_input['splitted'][1] = parsed_input['splitted'][1][1:]
parsed_input['root_cmd'] = 'pose'
# Channel alias match.
elif functions_comsys.plr_has_channel(session,
parsed_input['root_cmd'],
alias_search=True,
return_muted=True):
calias = parsed_input['root_cmd']
cname = functions_comsys.plr_cname_from_alias(session, calias)
cmessage = ' '.join(parsed_input['splitted'][1:])
if cmessage == "who":
functions_comsys.msg_cwho(session, cname)
return
elif cmessage == "on":
functions_comsys.plr_chan_on(session, calias)
return
elif cmessage == "off":
functions_comsys.plr_chan_off(session, calias)
return
elif cmessage == "last":
functions_comsys.msg_chan_hist(session, cname)
return
second_arg = "%s=%s" % (cname, cmessage)
parsed_input['splitted'] = ["@cemit/sendername", second_arg]
parsed_input['root_chunk'] = ['@cemit', 'sendername', 'quiet']
parsed_input['root_cmd'] = '@cemit'
# Get the command's function reference (Or False)
cmdtuple = cmdtable.return_cmdtuple(parsed_input['root_cmd'])
if cmdtuple:
# If there is a permissions element to the entry, check perms.
if cmdtuple[1]:
if not session.get_pobject().user_has_perm_list(cmdtuple[1]):
session.msg(defines_global.NOPERMS_MSG)
return
# If flow reaches this point, user has perms and command is ready.
cmd = cmdtuple[0]
else:
# Not logged in, look through the unlogged-in command table.
cmdtuple = cmdtable.return_cmdtuple(parsed_input['root_cmd'], unlogged_cmd=True)
if cmdtuple:
cmd = cmdtuple[0]
# Debugging stuff.
#session.msg("ROOT : %s" % (parsed_input['root_cmd'],))
#session.msg("SPLIT: %s" % (parsed_input['splitted'],))
if callable(cmd):
cdat['uinput'] = parsed_input
try:
cmd(cdat)
except:
session.msg("Untrapped error, please file a bug report:\n%s" % (format_exc(),))
functions_log.log_errmsg("Untrapped error, evoker %s: %s" %
(session, format_exc()))
return
if session.logged_in:
# If we're not logged in, don't check exits.
pobject = session.get_pobject()
exit_matches = match_exits(pobject, ' '.join(parsed_input['splitted']))
if exit_matches:
targ_exit = exit_matches[0]
if targ_exit.get_home():
cdat['uinput'] = parsed_input
# SCRIPT: See if the player can traverse the exit
if not targ_exit.get_scriptlink().default_lock({
"pobject": pobject
}):
session.msg("You can't traverse that exit.")
else:
pobject.move_to(targ_exit.get_home())
session.execute_cmd("look")
else:
session.msg("That exit leads to nowhere.")
return
# If we reach this point, we haven't matched anything.
raise UnknownCommand
except UnknownCommand:
session.msg("Huh? (Type \"help\" for help.)")

89
src/cmdtable.py Normal file
View file

@ -0,0 +1,89 @@
"""
Command Table Entries
---------------------
Each command entry consists of a key and a tuple containing a reference to the
command's function, and a tuple of the permissions to match against. The user
only need have one of the permissions in the permissions tuple to gain
access to the command. Obviously, super users don't have to worry about this
stuff. If the command is open to all (or you want to implement your own
privilege checking in the command function), use None in place of the
permissions tuple.
"""
import commands.general
import commands.privileged
import commands.comsys
import commands.unloggedin
import commands.info
import commands.objmanip
# -- Unlogged-in Command Table --
# Command Name Command Function Privilege Tuple
uncon_ctable = {
"connect": (commands.unloggedin.cmd_connect, None),
"create": (commands.unloggedin.cmd_create, None),
"quit": (commands.unloggedin.cmd_quit, None),
}
# -- Command Table --
# Command Name Command Function Privilege Tuple
ctable = {
"addcom": (commands.comsys.cmd_addcom, None),
"comlist": (commands.comsys.cmd_comlist, None),
"delcom": (commands.comsys.cmd_delcom, None),
"drop": (commands.general.cmd_drop, None),
"examine": (commands.general.cmd_examine, None),
"get": (commands.general.cmd_get, None),
"help": (commands.general.cmd_help, None),
"idle": (commands.general.cmd_idle, None),
"inventory": (commands.general.cmd_inventory, None),
"look": (commands.general.cmd_look, None),
"page": (commands.general.cmd_page, None),
"pose": (commands.general.cmd_pose, None),
"quit": (commands.general.cmd_quit, None),
"say": (commands.general.cmd_say, None),
"time": (commands.info.cmd_time, None),
"uptime": (commands.info.cmd_uptime, None),
"version": (commands.info.cmd_version, None),
"who": (commands.general.cmd_who, None),
"@alias": (commands.objmanip.cmd_alias, None),
"@boot": (commands.privileged.cmd_boot, ("genperms.manage_players")),
"@ccreate": (commands.comsys.cmd_ccreate, ("objects.add_commchannel")),
"@cdestroy": (commands.comsys.cmd_cdestroy, ("objects.delete_commchannel")),
"@cemit": (commands.comsys.cmd_cemit, None),
"@clist": (commands.comsys.cmd_clist, None),
"@create": (commands.objmanip.cmd_create, ("genperms.builder")),
"@describe": (commands.objmanip.cmd_description, None),
"@destroy": (commands.objmanip.cmd_destroy, ("genperms.builder")),
"@dig": (commands.objmanip.cmd_dig, ("genperms.builder")),
"@emit": (commands.general.cmd_emit, ("genperms.announce")),
# "@pemit": (commands.general.cmd_pemit, None),
"@find": (commands.objmanip.cmd_find, ("genperms.builder")),
"@link": (commands.objmanip.cmd_link, ("genperms.builder")),
"@list": (commands.info.cmd_list, ("genperms.process_control")),
"@name": (commands.objmanip.cmd_name, None),
"@nextfree": (commands.objmanip.cmd_nextfree, ("genperms.builder")),
"@newpassword": (commands.privileged.cmd_newpassword, ("genperms.manage_players")),
"@open": (commands.objmanip.cmd_open, ("genperms.builder")),
"@password": (commands.general.cmd_password, None),
"@ps": (commands.info.cmd_ps, ("genperms.process_control")),
"@reload": (commands.privileged.cmd_reload, ("genperms.process_control")),
"@set": (commands.objmanip.cmd_set, None),
"@shutdown": (commands.privileged.cmd_shutdown, ("genperms.process_control")),
"@stats": (commands.info.cmd_stats, None),
"@teleport": (commands.objmanip.cmd_teleport, ("genperms.builder")),
"@unlink": (commands.objmanip.cmd_unlink, ("genperms.builder")),
"@wall": (commands.general.cmd_wall, ("genperms.announce")),
"@wipe": (commands.objmanip.cmd_wipe, None),
}
def return_cmdtuple(func_name, unlogged_cmd=False):
"""
Returns a reference to the command's tuple. If there are no matches,
returns false.
"""
if not unlogged_cmd:
cfunc = ctable.get(func_name, False)
else:
cfunc = uncon_ctable.get(func_name, False)
return cfunc

0
src/commands/__init__.py Normal file
View file

292
src/commands/comsys.py Normal file
View file

@ -0,0 +1,292 @@
"""
Comsys command module. Pretty much every comsys command should go here for
now.
"""
import time
from django.conf import settings
import functions_general
import functions_comsys
import defines_global
from src import ansi
def cmd_addcom(cdat):
"""
addcom
Adds an alias for a channel.
addcom foo=Bar
"""
session = cdat['session']
pobject = session.get_pobject()
server = cdat['server']
args = cdat['uinput']['splitted'][1:]
if len(args) == 0:
session.msg("You need to specify a channel alias and name.")
return
eq_args = args[0].split('=')
if len(eq_args) < 2:
session.msg("You need to specify a channel name.")
return
chan_alias = eq_args[0]
chan_name = eq_args[1]
if len(chan_name) == 0:
session.msg("You need to specify a channel name.")
return
if chan_alias in session.channels_subscribed:
session.msg("You are already on that channel.")
return
name_matches = functions_comsys.cname_search(chan_name, exact=True)
if name_matches:
chan_name_parsed = name_matches[0].get_name()
session.msg("You join %s, with an alias of %s." % \
(chan_name_parsed, chan_alias))
functions_comsys.plr_set_channel(session, chan_alias, chan_name_parsed, True)
# Announce the user's joining.
join_msg = "[%s] %s has joined the channel." % \
(chan_name_parsed, pobject.get_name(show_dbref=False))
functions_comsys.send_cmessage(chan_name_parsed, join_msg)
else:
session.msg("Could not find channel %s." % (chan_name,))
def cmd_delcom(cdat):
"""
delcom
Removes the specified alias to a channel. If this is the last alias,
the user is effectively removed from the channel.
"""
session = cdat['session']
pobject = session.get_pobject()
uinput= cdat['uinput']['splitted']
chan_alias = ' '.join(uinput[1:])
if len(chan_alias) == 0:
session.msg("You must specify a channel alias.")
return
if chan_alias not in session.channels_subscribed:
session.msg("You are not on that channel.")
return
chan_name = session.channels_subscribed[chan_alias][0]
session.msg("You have left %s." % (chan_name,))
functions_comsys.plr_del_channel(session, chan_alias)
# Announce the user's leaving.
leave_msg = "[%s] %s has left the channel." % \
(chan_name, pobject.get_name(show_dbref=False))
functions_comsys.send_cmessage(chan_name, leave_msg)
def cmd_comlist(cdat):
"""
Lists the channels a user is subscribed to.
"""
session = cdat['session']
session.msg("Alias Channel Status")
for chan in session.channels_subscribed:
if session.channels_subscribed[chan][1]:
chan_on = "On"
else:
chan_on = "Off"
session.msg("%-9.9s %-19.19s %s" %
(chan, session.channels_subscribed[chan][0], chan_on))
session.msg("-- End of comlist --")
def cmd_allcom(cdat):
"""
allcom
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.
"""
pass
def cmd_clearcom(cdat):
"""
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.
"""
pass
def cmd_clist(cdat):
"""
@clist
Lists all available channels on the game.
"""
session = cdat['session']
session.msg("** Channel Owner Description")
for chan in functions_comsys.get_all_channels():
session.msg("%s%s %-13.13s %-15.15s %-45.45s" %
('-', '-', chan.get_name(), chan.get_owner().get_name(), 'No Description'))
session.msg("-- End of Channel List --")
def cmd_cdestroy(cdat):
"""
@cdestroy
Destroys a channel.
"""
session = cdat['session']
uinput= cdat['uinput']['splitted']
cname = ' '.join(uinput[1:])
if cname == '':
session.msg("You must supply a name!")
return
name_matches = functions_comsys.cname_search(cname, exact=True)
if not name_matches:
session.msg("Could not find channel %s." % (cname,))
else:
session.msg("Channel %s destroyed." % (name_matches[0],))
name_matches.delete()
def cmd_cset(cdat):
"""
@cset
Sets various flags on a channel.
"""
pass
def cmd_ccharge(cdat):
"""
@ccharge
Sets the cost to transmit over a channel. Default is free.
"""
pass
def cmd_cboot(cdat):
"""
@cboot
Kicks a player or object from the channel.
"""
pass
def cmd_cemit(cdat):
"""
@cemit
@cemit/noheader <message>
@cemit/sendername <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
provide the /sendername switch.
"""
session = cdat['session']
pobject = session.get_pobject()
server = cdat['server']
args = cdat['uinput']['splitted'][1:]
switches = cdat['uinput']['root_chunk'][1:]
if len(args) == 0:
session.msg("Channel emit what?")
return
# Combine the arguments into one string, split it by equal signs into
# channel (entry 0 in the list), and message (entry 1 and above).
eq_args = ' '.join(args).split('=')
cname = eq_args[0]
cmessage = ' '.join(eq_args[1:])
if len(eq_args) != 2:
session.msg("You must provide a channel name and a message to emit.")
return
if len(cname) == 0:
session.msg("You must provide a channel name to emit to.")
return
if len(cmessage) == 0:
session.msg("You must provide a message to emit.")
return
name_matches = functions_comsys.cname_search(cname, exact=True)
try:
# Safety first, kids!
cname_parsed = name_matches[0].get_name()
except:
session.msg("Could not find channel %s." % (cname,))
return
if "noheader" in switches:
if not pobject.user_has_perm("objects.emit_commchannel"):
session.msg(defines_global.NOPERMS_MSG)
return
final_cmessage = cmessage
else:
if "sendername" in switches:
if not functions_comsys.plr_has_channel(session, cname_parsed, return_muted=False):
session.msg("You must be on %s to do that." % (cname_parsed,))
return
final_cmessage = "[%s] %s: %s" % (cname_parsed, pobject.get_name(show_dbref=False), cmessage)
else:
if not pobject.user_has_perm("objects.emit_commchannel"):
session.msg(defines_global.NOPERMS_MSG)
return
final_cmessage = "[%s] %s" % (cname_parsed, cmessage)
if not "quiet" in switches:
session.msg("Sent - %s" % (name_matches[0],))
functions_comsys.send_cmessage(cname_parsed, final_cmessage)
def cmd_cwho(cdat):
"""
@cwho
Displays the name, status and object type for a given channel.
Adding /all after the channel name will list disconnected players
as well.
"""
pass
def cmd_ccreate(cdat):
"""
@ccreate
Creates a new channel with the invoker being the default owner.
"""
session = cdat['session']
pobject = session.get_pobject()
uinput= cdat['uinput']['splitted']
cname = ' '.join(uinput[1:])
if cname == '':
session.msg("You must supply a name!")
return
name_matches = functions_comsys.cname_search(cname, exact=True)
if name_matches:
session.msg("A channel with that name already exists.")
else:
# Create and set the object up.
cdat = {"name": cname, "owner": pobject}
new_chan = functions_comsys.create_channel(cdat)
session.msg("Channel %s created." % (new_chan.get_name(),))
def cmd_cchown(cdat):
"""
@cchown
Changes the owner of a channel.
"""
pass

537
src/commands/general.py Normal file
View file

@ -0,0 +1,537 @@
"""
Generic command module. Pretty much every command should go here for
now.
"""
import time
from django.conf import settings
from apps.config.models import ConfigValue
from apps.helpsys.models import HelpEntry
from apps.objects.models import Object
import functions_general
import defines_global
from src import session_mgr
from src import ansi
def cmd_password(cdat):
"""
Changes your own password.
@newpass <Oldpass>=<Newpass>
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
eq_args = ' '.join(args).split('=')
oldpass = ''.join(eq_args[0])
newpass = ''.join(eq_args[1:])
if len(oldpass) == 0:
session.msg("You must provide your old password.")
elif len(newpass) == 0:
session.msg("You must provide your new password.")
else:
uaccount = pobject.get_user_account()
if not uaccount.check_password(oldpass):
session.msg("The specified old password isn't correct.")
elif len(newpass) < 3:
session.msg("Passwords must be at least three characters long.")
return
else:
uaccount.set_password(newpass)
uaccount.save()
session.msg("Password changed.")
def cmd_emit(cdat):
"""
Emits something to your location.
"""
session = cdat['session']
pobject = session.get_pobject()
uinput= cdat['uinput']['splitted']
message = ' '.join(uinput[1:])
if message == '':
session.msg("Emit what?")
else:
pobject.get_location().emit_to_contents(message)
def cmd_wall(cdat):
"""
Announces a message to all connected players.
"""
session = cdat['session']
wallstring = ' '.join(cdat['uinput']['splitted'][1:])
pobject = session.get_pobject()
if wallstring == '':
session.msg("Announce what?")
return
message = "%s shouts \"%s\"" % (session.get_pobject().get_name(show_dbref=False), wallstring)
functions_general.announce_all(message)
def cmd_idle(cdat):
"""
Returns nothing, this lets the player set an idle timer without spamming
his screen.
"""
pass
def cmd_inventory(cdat):
"""
Shows a player's inventory.
"""
session = cdat['session']
pobject = session.get_pobject()
session.msg("You are carrying:")
for item in pobject.get_contents():
session.msg(" %s" % (item.get_name(),))
money = int(pobject.get_attribute_value("MONEY", default=0))
if money == 1:
money_name = ConfigValue.objects.get_configvalue("MONEY_NAME_SINGULAR")
else:
money_name = ConfigValue.objects.get_configvalue("MONEY_NAME_PLURAL")
session.msg("You have %d %s." % (money,money_name))
def cmd_look(cdat):
"""
Handle looking at objects.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
if len(args) == 0:
target_obj = pobject.get_location()
else:
target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args))
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
# SCRIPT: Get the item's appearance from the scriptlink.
session.msg(target_obj.get_scriptlink().return_appearance({
"target_obj": target_obj,
"pobject": pobject
}))
# SCRIPT: Call the object's script's a_desc() method.
target_obj.get_scriptlink().a_desc({
"target_obj": pobject
})
def cmd_get(cdat):
"""
Get an object and put it in a player's inventory.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
plr_is_staff = pobject.is_staff()
if len(args) == 0:
session.msg("Get what?")
return
else:
target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args), search_contents=False)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if pobject == target_obj:
session.msg("You can't get yourself.")
return
if not plr_is_staff and (target_obj.is_player() or target_obj.is_exit()):
session.msg("You can't get that.")
return
if target_obj.is_room() or target_obj.is_garbage() or target_obj.is_going():
session.msg("You can't get that.")
return
target_obj.move_to(pobject, quiet=True)
session.msg("You pick up %s." % (target_obj.get_name(),))
pobject.get_location().emit_to_contents("%s picks up %s." % (pobject.get_name(), target_obj.get_name()), exclude=pobject)
# SCRIPT: Call the object's script's a_get() method.
target_obj.get_scriptlink().a_get({
"pobject": pobject
})
def cmd_drop(cdat):
"""
Drop an object from a player's inventory into their current location.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
plr_is_staff = pobject.is_staff()
if len(args) == 0:
session.msg("Drop what?")
return
else:
target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args), search_location=False)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if not pobject == target_obj.get_location():
session.msg("You don't appear to be carrying that.")
return
target_obj.move_to(pobject.get_location(), quiet=True)
session.msg("You drop %s." % (target_obj.get_name(),))
pobject.get_location().emit_to_contents("%s drops %s." % (pobject.get_name(), target_obj.get_name()), exclude=pobject)
# SCRIPT: Call the object's script's a_drop() method.
target_obj.get_scriptlink().a_drop({
"pobject": pobject
})
def cmd_examine(cdat):
"""
Detailed object examine command
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
attr_search = False
if len(args) == 0:
# If no arguments are provided, examine the invoker's location.
target_obj = pobject.get_location()
else:
# Look for a slash in the input, indicating an attribute search.
attr_split = args[0].split("/")
# 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.
searchstr = attr_split[0]
# Just in case there's a slash in an attribute name.
attr_searchstr = '/'.join(attr_split[1:])
else:
searchstr = ' '.join(args)
target_obj = Object.objects.standard_plr_objsearch(session, searchstr)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if attr_search:
attr_matches = target_obj.attribute_namesearch(attr_searchstr)
if attr_matches:
for attribute in attr_matches:
session.msg(attribute.get_attrline())
else:
session.msg("No matching attributes found.")
# End attr_search if()
else:
session.msg("%s\r\n%s" % (
target_obj.get_name(fullname=True),
target_obj.get_description(no_parsing=True),
))
session.msg("Type: %s Flags: %s" % (target_obj.get_type(), target_obj.get_flags()))
session.msg("Owner: %s " % (target_obj.get_owner(),))
session.msg("Zone: %s" % (target_obj.get_zone(),))
for attribute in target_obj.get_all_attributes():
session.msg(attribute.get_attrline())
con_players = []
con_things = []
con_exits = []
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)
if con_players or con_things:
session.msg("%sContents:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],))
for player in con_players:
session.msg('%s' % (player.get_name(fullname=True),))
for thing in con_things:
session.msg('%s' % (thing.get_name(fullname=True),))
if con_exits:
session.msg("%sExits:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],))
for exit in con_exits:
session.msg('%s' %(exit.get_name(fullname=True),))
if not target_obj.is_room():
if target_obj.is_exit():
session.msg("Destination: %s" % (target_obj.get_home(),))
else:
session.msg("Home: %s" % (target_obj.get_home(),))
session.msg("Location: %s" % (target_obj.get_location(),))
def cmd_page(cdat):
"""
Send a message to target user (if online).
"""
session = cdat['session']
pobject = session.get_pobject()
server = cdat['server']
args = cdat['uinput']['splitted'][1:]
parsed_command = cdat['uinput']['parsed_command']
# We use a dict to ensure that the list of targets is unique
targets = dict()
# Get the last paged person
last_paged_dbrefs = pobject.get_attribute_value("LASTPAGED")
# If they have paged someone before, go ahead and grab the object of
# that person.
if last_paged_dbrefs is not False:
last_paged_objects = list()
try:
last_paged_dbref_list = [
x.strip() for x in last_paged_dbrefs.split(',')]
for dbref in last_paged_dbref_list:
if not Object.objects.is_dbref(dbref):
raise ValueError
last_paged_object = Object.objects.dbref_search(dbref)
if last_paged_object is not None:
last_paged_objects.append(last_paged_object)
except ValueError:
# LASTPAGED Attribute is not a list of dbrefs
last_paged_dbrefs = False
# Remove the invalid LASTPAGED attribute
pobject.clear_attribute("LASTPAGED")
# If they don't give a target, or any data to send to the target
# then tell them who they last paged if they paged someone, if not
# tell them they haven't paged anyone.
if parsed_command['targets'] is None and parsed_command['data'] is None:
if last_paged_dbrefs is not False and not last_paged_objects == list():
session.msg("You last paged: %s." % (
', '.join([x.name for x in last_paged_objects])))
return
session.msg("You have not paged anyone.")
return
# Build a list of targets
# If there are no targets, then set the targets to the last person they
# paged.
if parsed_command['targets'] is None:
if not last_paged_objects == list():
targets = dict([(target, 1) for target in last_paged_objects])
else:
# First try to match the entire target string against a single player
full_target_match = Object.objects.player_name_search(
parsed_command['original_targets'])
if full_target_match is not None:
targets[full_target_match] = 1
else:
# For each of the targets listed, grab their objects and append
# it to the targets list
for target in parsed_command['targets']:
# If the target is a dbref, behave appropriately
if Object.objects.is_dbref(target):
session.msg("Is dbref.")
matched_object = Object.objects.dbref_search(target,
limit_types=[defines_global.OTYPE_PLAYER])
if matched_object is not None:
targets[matched_object] = 1
else:
# search returned None
session.msg("Player '%s' does not exist." % (
target))
else:
# Not a dbref, so must be a username, treat it as such
matched_object = Object.objects.player_name_search(
target)
if matched_object is not None:
targets[matched_object] = 1
else:
# search returned None
session.msg("Player '%s' does not exist." % (
target))
data = parsed_command['data']
sender_name = pobject.get_name(show_dbref=False)
# Build our messages
target_message = "%s pages: %s"
sender_message = "You paged %s with '%s'."
# Handle paged emotes
if data.startswith(':'):
data = data[1:]
target_message = "From afar, %s %s"
sender_message = "Long distance to %s: %s %s"
# Handle paged emotes without spaces
if data.startswith(';'):
data = data[1:]
target_message = "From afar, %s%s"
sender_message = "Long distance to %s: %s%s"
# We build a list of target_names for the sender_message later
target_names = []
for target in targets.keys():
# Check to make sure they're connected, or a player
if target.is_connected_plr():
target.emit_to(target_message % (sender_name, data))
target_names.append(target.get_name(show_dbref=False))
else:
session.msg("Player %s does not exist or is not online." % (
target.get_name(show_dbref=False)))
if len(target_names) > 0:
target_names_string = ', '.join(target_names)
try:
session.msg(sender_message % (target_names_string, sender_name, data))
except TypeError:
session.msg(sender_message % (target_names_string, data))
# Now set the LASTPAGED attribute
pobject.set_attribute("LASTPAGED", ','.join(
["#%d" % (x.id) for x in targets.keys()]))
def cmd_quit(cdat):
"""
Gracefully disconnect the user as per his own request.
"""
session = cdat['session']
session.msg("Quitting!")
session.handle_close()
def cmd_who(cdat):
"""
Generic WHO command.
"""
session_list = session_mgr.get_session_list()
session = cdat['session']
pobject = session.get_pobject()
show_session_data = pobject.user_has_perm("genperms.see_session_data")
# Only those with the see_session_data or superuser status can see
# session details.
if show_session_data:
retval = "Player Name On For Idle Room Cmds Host\n\r"
else:
retval = "Player Name On For Idle\n\r"
for player in session_list:
if not player.logged_in:
continue
delta_cmd = time.time() - player.cmd_last_visible
delta_conn = time.time() - player.conn_time
plr_pobject = player.get_pobject()
if show_session_data:
retval += '%-16s%9s %4s%-3s#%-6d%5d%3s%-25s\r\n' % \
(plr_pobject.get_name(show_dbref=False)[:25].ljust(27), \
# On-time
functions_general.time_format(delta_conn,0), \
# Idle time
functions_general.time_format(delta_cmd,1), \
# Flags
'', \
# Location
plr_pobject.get_location().id, \
player.cmd_total, \
# More flags?
'', \
player.address[0])
else:
retval += '%-16s%9s %4s%-3s\r\n' % \
(plr_pobject.get_name(show_dbref=False)[:25].ljust(27), \
# On-time
functions_general.time_format(delta_conn,0), \
# Idle time
functions_general.time_format(delta_cmd,1), \
# Flags
'')
retval += '%d Players logged in.' % (len(session_list),)
session.msg(retval)
def cmd_say(cdat):
"""
Room-based speech command.
"""
session = cdat['session']
if not functions_general.cmd_check_num_args(session, cdat['uinput']['splitted'], 1, errortext="Say what?"):
return
session_list = session_mgr.get_session_list()
pobject = session.get_pobject()
speech = ' '.join(cdat['uinput']['splitted'][1:])
players_present = [player for player in session_list if player.get_pobject().get_location() == session.get_pobject().get_location() and player != session]
retval = "You say, '%s'" % (speech,)
for player in players_present:
player.msg("%s says, '%s'" % (pobject.get_name(show_dbref=False), speech,))
session.msg(retval)
def cmd_pose(cdat):
"""
Pose/emote command.
"""
session = cdat['session']
pobject = session.get_pobject()
switches = cdat['uinput']['root_chunk'][1:]
if not functions_general.cmd_check_num_args(session, cdat['uinput']['splitted'], 1, errortext="Do what?"):
return
session_list = session_mgr.get_session_list()
speech = ' '.join(cdat['uinput']['splitted'][1:])
if "nospace" in switches:
sent_msg = "%s%s" % (pobject.get_name(show_dbref=False), speech)
else:
sent_msg = "%s %s" % (pobject.get_name(show_dbref=False), speech)
players_present = [player for player in session_list if player.get_pobject().get_location() == session.get_pobject().get_location()]
for player in players_present:
player.msg(sent_msg)
def cmd_help(cdat):
"""
Help system commands.
"""
session = cdat['session']
pobject = session.get_pobject()
topicstr = ' '.join(cdat['uinput']['splitted'][1:])
if len(topicstr) == 0:
topicstr = "Help Index"
elif len(topicstr) < 2 and not topicstr.isdigit():
session.msg("Your search query is too short. It must be at least three letters long.")
return
topics = HelpEntry.objects.find_topicmatch(pobject, topicstr)
if len(topics) == 0:
session.msg("No matching topics found, please refine your search.")
suggestions = HelpEntry.objects.find_topicsuggestions(pobject, topicstr)
if len(suggestions) > 0:
session.msg("Matching similarly named topics:")
for result in suggestions:
session.msg(" %s" % (result,))
session.msg("You may type 'help <#>' to see any of these topics.")
elif len(topics) > 1:
session.msg("More than one match found:")
for result in topics:
session.msg("%3d. %s" % (result.id, result.get_topicname()))
session.msg("You may type 'help <#>' to see any of these topics.")
else:
topic = topics[0]
session.msg("\r\n%s%s%s" % (ansi.ansi["hilite"], topic.get_topicname(), ansi.ansi["normal"]))
session.msg(topic.get_entrytext_ingame())

111
src/commands/info.py Normal file
View file

@ -0,0 +1,111 @@
"""
Commands that are generally staff-oriented that show information regarding
the server instance.
"""
import os
import time
import functions_general
if not functions_general.host_os_is('nt'):
# Don't import the resource module if the host OS is Windows.
import resource
import django
from apps.objects.models import Object
from src import scheduler
import defines_global
def cmd_version(cdat):
"""
Version info command.
"""
session = cdat['session']
retval = "-"*50 +"\n\r"
retval += " Evennia %s\n\r" % (defines_global.EVENNIA_VERSION,)
retval += " Django %s\n\r" % (django.get_version())
retval += "-"*50
session.msg(retval)
def cmd_time(cdat):
"""
Server local time.
"""
session = cdat['session']
session.msg('Current server time : %s' % (time.strftime('%a %b %d %H:%M %Y (%Z)', time.localtime(),)))
def cmd_uptime(cdat):
"""
Server uptime and stats.
"""
session = cdat['session']
server = cdat['server']
start_delta = time.time() - server.start_time
loadavg = os.getloadavg()
session.msg('Current server time : %s' % (time.strftime('%a %b %d %H:%M %Y (%Z)', time.localtime(),)))
session.msg('Server start time : %s' % (time.strftime('%a %b %d %H:%M %Y', time.localtime(server.start_time),)))
session.msg('Server uptime : %s' % functions_general.time_format(start_delta, style=2))
session.msg('Server load (1 min) : %.2f' % loadavg[0])
def cmd_list(cdat):
"""
Shows some game related information.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
argstr = ''.join(args)
msg_invalid = "Unknown option. Use one of: commands, flags, process"
if len(argstr) == 0:
session.msg(msg_invalid)
elif argstr == "commands":
session.msg('Commands: '+ ' '.join(session.server.command_list()))
elif argstr == "process":
if not functions_general.host_os_is('nt'):
loadvg = os.getloadavg()
psize = resource.getpagesize()
rusage = resource.getrusage(resource.RUSAGE_SELF)
session.msg("Process ID: %10d %10d bytes per page" % (os.getpid(), psize))
session.msg("Time used: %10d user %10d sys" % (rusage[0],rusage[1]))
session.msg("Integral mem:%10d shared %10d private%10d stack" % (rusage[3], rusage[4], rusage[5]))
session.msg("Max res mem: %10d pages %10d bytes" % (rusage[2],rusage[2] * psize))
session.msg("Page faults: %10d hard %10d soft %10d swapouts" % (rusage[7], rusage[6], rusage[8]))
session.msg("Disk I/O: %10d reads %10d writes" % (rusage[9], rusage[10]))
session.msg("Network I/O: %10d in %10d out" % (rusage[12], rusage[11]))
session.msg("Context swi: %10d vol %10d forced %10d sigs" % (rusage[14], rusage[15], rusage[13]))
else:
session.msg("Feature not available on Windows.")
return
elif argstr == "flags":
session.msg("Flags: "+" ".join(defines_global.SERVER_FLAGS))
else:
session.msg(msg_invalid)
def cmd_ps(cdat):
"""
Shows the process/event table.
"""
session = cdat['session']
session.msg("-- Interval Events --")
for event in scheduler.schedule:
session.msg(" [%d/%d] %s" % (scheduler.get_event_nextfire(event),
scheduler.get_event_interval(event),
scheduler.get_event_description(event)))
session.msg("Totals: %d interval events" % (len(scheduler.schedule),))
def cmd_stats(cdat):
"""
Shows stats about the database.
4012 objects = 144 rooms, 212 exits, 613 things, 1878 players. (1165 garbage)
"""
session = cdat['session']
stats_dict = Object.objects.object_totals()
session.msg("%d objects = %d rooms, %d exits, %d things, %d players. (%d garbage)" % (stats_dict["objects"],
stats_dict["rooms"],
stats_dict["exits"],
stats_dict["things"],
stats_dict["players"],
stats_dict["garbage"]))

547
src/commands/objmanip.py Normal file
View file

@ -0,0 +1,547 @@
"""
These commands typically are to do with building or modifying Objects.
"""
from apps.objects.models import Object
# We'll import this as the full path to avoid local variable clashes.
import src.flags
from src import ansi
from src import session_mgr
def cmd_teleport(cdat):
"""
Teleports an object somewhere.
"""
session = cdat['session']
pobject = session.get_pobject()
server = cdat['server']
args = cdat['uinput']['splitted'][1:]
if len(args) == 0:
session.msg("Teleport where/what?")
return
eq_args = args[0].split('=')
search_str = ''.join(args)
# If we have more than one entry in our '=' delimited argument list,
# then we're doing a @tel <victim>=<location>. If not, we're doing
# a direct teleport, @tel <destination>.
if len(eq_args) > 1:
# Equal sign teleport.
victim = Object.objects.standard_plr_objsearch(session, eq_args[0])
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not victim:
return
destination = Object.objects.standard_plr_objsearch(session, eq_args[1])
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not destination:
return
if victim.is_room():
session.msg("You can't teleport a room.")
return
if victim == destination:
session.msg("You can't teleport an object inside of itself!")
return
session.msg("Teleported.")
victim.move_to(destination)
# This is somewhat kludgy right now, we'll have to find a better way
# to do it sometime else. If we can find a session in the server's
# session list matching the object we're teleporting, force it to
# look. This is going to typically be a player.
victim_session = session_mgr.session_from_object(victim)
if victim_session:
victim_session.execute_cmd("look")
else:
# Direct teleport (no equal sign)
target_obj = Object.objects.standard_plr_objsearch(session, search_str)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if target_obj == pobject:
session.msg("You can't teleport inside yourself!")
return
session.msg("Teleported.")
pobject.move_to(target_obj)
session.execute_cmd("look")
def cmd_stats(cdat):
"""
Shows stats about the database.
4012 objects = 144 rooms, 212 exits, 613 things, 1878 players. (1165 garbage)
"""
session = cdat['session']
stats_dict = Object.objects.object_totals()
session.msg("%d objects = %d rooms, %d exits, %d things, %d players. (%d garbage)" % (stats_dict["objects"],
stats_dict["rooms"],
stats_dict["exits"],
stats_dict["things"],
stats_dict["players"],
stats_dict["garbage"]))
def cmd_alias(cdat):
"""
Assigns an alias to a player object for ease of paging, etc.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
if len(args) == 0:
session.msg("Alias whom?")
return
# Resplit the args on = to check for an almost-required =
eq_args = ' '.join(args).split('=')
if len(eq_args) < 2:
session.msg("Alias missing.")
return
target = Object.objects.standard_plr_objsearch(session, eq_args[0])
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target:
session.msg("Alias whom?")
return
duplicates = Object.objects.player_alias_search(pobject, eq_args[1])
if duplicates:
session.msg("Alias '%s' already exists." % (eq_args[1],))
return
else:
if pobject.controls_other(target):
target.set_attribute('ALIAS', eq_args[1])
session.msg("Alias '%s' set for %s." % (eq_args[1], target.get_name()))
else:
session.msg("You do not have access to set an alias for %s." % (target.get_name(),))
def cmd_wipe(cdat):
"""
Wipes an object's attributes, or optionally only those matching a search
string.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
attr_search = False
if len(args) == 0:
session.msg("Wipe what?")
return
# Look for a slash in the input, indicating an attribute wipe.
attr_split = args[0].split("/")
# 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.
searchstr = attr_split[0]
# Just in case there's a slash in an attribute name.
attr_searchstr = '/'.join(attr_split[1:])
else:
searchstr = ' '.join(args)
target_obj = Object.objects.standard_plr_objsearch(session, searchstr)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if attr_search:
# User has passed an attribute wild-card string. Search for name matches
# and wipe.
attr_matches = target_obj.attribute_namesearch(attr_searchstr, exclude_noset=True)
if attr_matches:
for attr in attr_matches:
target_obj.clear_attribute(attr.get_name())
session.msg("%s - %d attributes wiped." % (target_obj.get_name(), len(attr_matches)))
else:
session.msg("No matching attributes found.")
else:
# User didn't specify a wild-card string, wipe entire object.
attr_matches = target_obj.attribute_namesearch("*", exclude_noset=True)
for attr in attr_matches:
target_obj.clear_attribute(attr.get_name())
session.msg("%s - %d attributes wiped." % (target_obj.get_name(), len(attr_matches)))
def cmd_set(cdat):
"""
Sets flags or attributes on objects.
"""
session = cdat['session']
pobject = session.get_pobject()
server = cdat['server']
args = cdat['uinput']['splitted'][1:]
if len(args) == 0:
session.msg("Set what?")
return
# There's probably a better way to do this. Break the arguments (minus
# the root command) up so we have two items in the list, 0 being the victim,
# 1 being the list of flags or the attribute/value pair.
eq_args = ' '.join(args).split('=')
if len(eq_args) < 2:
session.msg("Set what?")
return
victim = Object.objects.standard_plr_objsearch(session, eq_args[0])
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not victim:
return
if not pobject.controls_other(victim):
session.msg(defines_global.NOCONTROL_MSG)
return
attrib_args = eq_args[1].split(':')
if len(attrib_args) > 1:
# We're dealing with an attribute/value pair.
attrib_name = attrib_args[0].upper()
splicenum = eq_args[1].find(':') + 1
attrib_value = eq_args[1][splicenum:]
# In global_defines.py, see NOSET_ATTRIBS for protected attribute names.
if not src.flags.is_modifiable_attrib(attrib_name) and not pobject.is_superuser():
session.msg("You can't modify that attribute.")
return
if attrib_value:
# An attribute value was specified, create or set the attribute.
verb = 'set'
victim.set_attribute(attrib_name, attrib_value)
else:
# No value was given, this means we delete the attribute.
verb = 'cleared'
victim.clear_attribute(attrib_name)
session.msg("%s - %s %s." % (victim.get_name(), attrib_name, verb))
else:
# Flag manipulation form.
flag_list = eq_args[1].split()
for flag in flag_list:
flag = flag.upper()
if flag[0] == '!':
# We're un-setting the flag.
flag = flag[1:]
if not src.flags.is_modifiable_flag(flag):
session.msg("You can't set/unset the flag - %s." % (flag,))
else:
session.msg('%s - %s cleared.' % (victim.get_name(), flag.upper(),))
victim.set_flag(flag, False)
else:
# We're setting the flag.
if not src.flags.is_modifiable_flag(flag):
session.msg("You can't set/unset the flag - %s." % (flag,))
else:
session.msg('%s - %s set.' % (victim.get_name(), flag.upper(),))
victim.set_flag(flag, True)
def cmd_find(cdat):
"""
Searches for an object of a particular name.
"""
session = cdat['session']
server = cdat['server']
searchstring = ' '.join(cdat['uinput']['splitted'][1:])
pobject = session.get_pobject()
can_find = pobject.user_has_perm("genperms.builder")
if searchstring == '':
session.msg("No search pattern given.")
return
results = Object.objects.global_object_name_search(searchstring)
if len(results) > 0:
session.msg("Name matches for: %s" % (searchstring,))
for result in results:
session.msg(" %s" % (result.get_name(fullname=True),))
session.msg("%d matches returned." % (len(results),))
else:
session.msg("No name matches found for: %s" % (searchstring,))
def cmd_create(cdat):
"""
Creates a new object of type 'THING'.
"""
session = cdat['session']
server = session.server
pobject = session.get_pobject()
uinput= cdat['uinput']['splitted']
thingname = ' '.join(uinput[1:])
if thingname == '':
session.msg("You must supply a name!")
else:
# Create and set the object up.
odat = {"name": thingname, "type": 3, "location": pobject, "owner": pobject}
new_object = Object.objects.create_object(odat)
session.msg("You create a new thing: %s" % (new_object,))
def cmd_nextfree(cdat):
"""
Returns the next free object number.
"""
session = cdat['session']
nextfree = Object.objects.get_nextfree_dbnum()
session.msg("Next free object number: #%s" % (nextfree,))
def cmd_open(cdat):
"""
Handle the opening of exits.
Forms:
@open <Name>
@open <Name>=<Dbref>
@open <Name>=<Dbref>,<Name>
"""
session = cdat['session']
pobject = session.get_pobject()
server = cdat['server']
args = cdat['uinput']['splitted'][1:]
if len(args) == 0:
session.msg("Open an exit to where?")
return
eq_args = ' '.join(args).split('=')
exit_name = eq_args[0]
if len(exit_name) == 0:
session.msg("You must supply an exit name.")
return
# If we have more than one entry in our '=' delimited argument list,
# then we're doing a @open <Name>=<Dbref>[,<Name>]. If not, we're doing
# an un-linked exit, @open <Name>.
if len(eq_args) > 1:
# Opening an exit to another location via @open <Name>=<Dbref>[,<Name>].
comma_split = eq_args[1].split(',')
destination = Object.objects.standard_plr_objsearch(session, comma_split[0])
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not destination:
return
if destination.is_exit():
session.msg("You can't open an exit to an exit!")
return
odat = {"name": exit_name, "type": 4, "location": pobject.get_location(), "owner": pobject, "home":destination}
new_object = Object.objects.create_object(odat)
session.msg("You open the an exit - %s to %s" % (new_object.get_name(),destination.get_name()))
if len(comma_split) > 1:
second_exit_name = ','.join(comma_split[1:])
odat = {"name": second_exit_name, "type": 4, "location": destination, "owner": pobject, "home": pobject.get_location()}
new_object = Object.objects.create_object(odat)
session.msg("You open the an exit - %s to %s" % (new_object.get_name(),pobject.get_location().get_name()))
else:
# Create an un-linked exit.
odat = {"name": exit_name, "type": 4, "location": pobject.get_location(), "owner": pobject, "home":None}
new_object = Object.objects.create_object(odat)
session.msg("You open an unlinked exit - %s" % (new_object,))
def cmd_link(cdat):
"""
Sets an object's home or an exit's destination.
Forms:
@link <Object>=<Target>
"""
session = cdat['session']
pobject = session.get_pobject()
server = cdat['server']
args = cdat['uinput']['splitted'][1:]
if len(args) == 0:
session.msg("Link what?")
return
eq_args = args[0].split('=')
target_name = eq_args[0]
dest_name = '='.join(eq_args[1:])
if len(target_name) == 0:
session.msg("What do you want to link?")
return
if len(eq_args) > 1:
target_obj = Object.objects.standard_plr_objsearch(session, target_name)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if not pobject.controls_other(target_obj):
session.msg(defines_global.NOCONTROL_MSG)
return
# If we do something like "@link blah=", we unlink the object.
if len(dest_name) == 0:
target_obj.set_home(None)
session.msg("You have unlinked %s." % (target_obj,))
return
destination = Object.objects.standard_plr_objsearch(session, dest_name)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not destination:
return
target_obj.set_home(destination)
session.msg("You link %s to %s." % (target_obj,destination))
else:
# We haven't provided a target.
session.msg("You must provide a destination to link to.")
return
def cmd_unlink(cdat):
"""
Unlinks an object.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
if len(args) == 0:
session.msg("Unlink what?")
return
else:
target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args))
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if not pobject.controls_other(target_obj):
session.msg(defines_global.NOCONTROL_MSG)
return
target_obj.set_home(None)
session.msg("You have unlinked %s." % (target_obj.get_name(),))
def cmd_dig(cdat):
"""
Creates a new object of type 'ROOM'.
"""
session = cdat['session']
pobject = session.get_pobject()
uinput= cdat['uinput']['splitted']
roomname = ' '.join(uinput[1:])
if roomname == '':
session.msg("You must supply a name!")
else:
# Create and set the object up.
odat = {"name": roomname, "type": 2, "location": None, "owner": pobject}
new_object = Object.objects.create_object(odat)
session.msg("You create a new room: %s" % (new_object,))
def cmd_name(cdat):
"""
Handle naming an object.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
eq_args = ' '.join(args).split('=')
searchstring = ''.join(eq_args[0])
if len(args) == 0:
session.msg("What do you want to name?")
elif len(eq_args) < 2:
session.msg("What would you like to name that object?")
else:
target_obj = Object.objects.standard_plr_objsearch(session, searchstring)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if len(eq_args[1]) == 0:
session.msg("What would you like to name that object?")
else:
newname = '='.join(eq_args[1:])
session.msg("You have renamed %s to %s." % (target_obj, ansi.parse_ansi(newname, strip_formatting=True)))
target_obj.set_name(newname)
def cmd_description(cdat):
"""
Set an object's description.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
eq_args = ' '.join(args).split('=')
searchstring = ''.join(eq_args[0])
if len(args) == 0:
session.msg("What do you want to describe?")
elif len(eq_args) < 2:
session.msg("How would you like to describe that object?")
else:
target_obj = Object.objects.standard_plr_objsearch(session, searchstring)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if not pobject.controls_other(target_obj):
session.msg(defines_global.NOCONTROL_MSG)
return
new_desc = '='.join(eq_args[1:])
session.msg("%s - DESCRIPTION set." % (target_obj,))
target_obj.set_description(new_desc)
def cmd_destroy(cdat):
"""
Destroy an object.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
switches = cdat['uinput']['root_chunk'][1:]
switch_override = False
if "override" in switches:
switch_override = True
if len(args) == 0:
session.msg("Destroy what?")
return
else:
target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args))
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if target_obj.is_player():
if pobject.id == target_obj.id:
session.msg("You can't destroy yourself.")
return
if not switch_override:
session.msg("You must use @destroy/override on players.")
return
if target_obj.is_superuser():
session.msg("You can't destroy a superuser.")
return
elif target_obj.is_going() or target_obj.is_garbage():
session.msg("That object is already destroyed.")
return
session.msg("You destroy %s." % (target_obj.get_name(),))
target_obj.destroy()

130
src/commands/privileged.py Normal file
View file

@ -0,0 +1,130 @@
from apps.objects.models import Object
import defines_global
import functions_general
from src import ansi
"""
This file contains commands that require special permissions to use. These
are generally @-prefixed commands, but there are exceptions.
"""
def cmd_reload(cdat):
"""
Reloads all modules.
"""
session = cdat['session']
server = session.server.reload(session)
def cmd_boot(cdat):
"""
Boot a player object from the server.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
eq_args = ' '.join(args).split('=')
searchstring = ''.join(eq_args[0])
switches = cdat['uinput']['root_chunk'][1:]
switch_quiet = False
switch_port = False
if not pobject.is_staff():
session.msg("You do not have permission to do that.")
return
if "quiet" in switches:
switch_quiet = True
if "port" in switches:
switch_port = True
if len(args) == 0:
session.msg("Who would you like to boot?")
return
else:
boot_list = []
if switch_port:
sessions = session_mgr.get_session_list(True)
for sess in sessions:
if sess.getClientAddress()[1] == int(searchstring):
boot_list.append(sess)
# We're done here
break
else:
# Grab the objects that match
objs = Objects.object.global_object_name_search(searchstring)
if len(objs) < 1:
session.msg("Who would you like to boot?")
return
if not objs[0].is_player():
session.msg("You can only boot players.")
return
if not pobject.controls_other(objs[0]):
if objs[0].is_superuser():
session.msg("You cannot boot a Wizard.")
return
else:
session.msg("You do not have permission to boot that player.")
return
if objs[0].is_connected_plr():
boot_list.append(session_mgr.session_from_object(objs[0]))
for boot in boot_list:
if not switch_quiet:
boot.msg("You have been disconnected by %s." % (pobject.name))
boot.disconnectClient()
session_mgr.remove_session(boot)
return
def cmd_newpassword(cdat):
"""
Set a player's password.
"""
session = cdat['session']
pobject = session.get_pobject()
args = cdat['uinput']['splitted'][1:]
eq_args = ' '.join(args).split('=')
searchstring = ''.join(eq_args[0])
newpass = ''.join(eq_args[1:])
if len(args) == 0:
session.msg("What player's password do you want to change")
return
if len(newpass) == 0:
session.msg("You must supply a new password.")
return
target_obj = Objects.object.standard_plr_objsearch(session, searchstring)
# Use standard_plr_objsearch to handle duplicate/nonexistant results.
if not target_obj:
return
if not target_obj.is_player():
session.msg("You can only change passwords on players.")
elif not pobject.controls_other(target_obj):
session.msg("You do not control %s." % (target_obj.get_name(),))
else:
uaccount = target_obj.get_user_account()
if len(newpass) == 0:
uaccount.set_password()
else:
uaccount.set_password(newpass)
uaccount.save()
session.msg("%s - PASSWORD set." % (target_obj.get_name(),))
target_obj.emit_to("%s has changed your password." % (pobject.get_name(show_dbref=False),))
def cmd_shutdown(cdat):
"""
Shut the server down gracefully.
"""
session = cdat['session']
server = cdat['server']
pobject = session.get_pobject()
session.msg('Shutting down...')
print 'Server shutdown by %s' % (pobject.get_name(show_dbref=False),)
server.shutdown()

View file

@ -0,0 +1,97 @@
"""
Commands that are available from the connect screen.
"""
from django.contrib.auth.models import User
from apps.objects.models import Attribute, Object
import functions_general
import defines_global
def cmd_connect(cdat):
"""
This is the connect command at the connection screen. Fairly simple,
uses the Django database API and User model to make it extremely simple.
"""
session = cdat['session']
# Argument check.
if not functions_general.cmd_check_num_args(session, cdat['uinput']['splitted'], 2):
return
uemail = cdat['uinput']['splitted'][1]
password = cdat['uinput']['splitted'][2]
# Match an email address to an account.
email_matches = Object.objects.get_user_from_email(uemail)
autherror = "Specified email does not match any accounts!"
# No username match
if email_matches.count() == 0:
session.msg(autherror)
return
# We have at least one result, so we can check the password.
user = email_matches[0]
if not user.check_password(password):
session.msg(autherror)
else:
uname = user.username
session.login(user)
def cmd_create(cdat):
"""
Handle the creation of new accounts.
"""
session = cdat['session']
# Argument check.
if not functions_general.cmd_check_num_args(session, cdat['uinput']['splitted'], 2):
return
server = session.server
quote_split = ' '.join(cdat['uinput']['splitted']).split("\"")
if len(quote_split) < 2:
session.msg("You must enclose your username in quotation marks.")
return
uname = quote_split[1]
lastarg_split = quote_split[2].split()
if len(lastarg_split) != 2:
session.msg("You must specify an email address, followed by a password!")
return
email = lastarg_split[0]
password = lastarg_split[1]
# Search for a user object with the specified username.
account = User.objects.filter(username=uname)
# Match an email address to an account.
email_matches = Object.objects.get_user_from_email(email)
# Look for any objects with an 'Alias' attribute that matches
# the requested username
alias_matches = Object.objects.filter(attribute__attr_name__exact="ALIAS",
attribute__attr_value__iexact=uname).filter(
type=defines_global.OTYPE_PLAYER)
if not account.count() == 0 or not alias_matches.count() == 0:
session.msg("There is already a player with that name!")
elif not email_matches.count() == 0:
session.msg("There is already a player with that email address!")
elif len(password) < 3:
session.msg("Your password must be 3 characters or longer.")
else:
Object.objects.create_user(cdat, uname, email, password)
def cmd_quit(cdat):
"""
We're going to maintain a different version of the quit command
here for unconnected users for the sake of simplicity. The logged in
version will be a bit more complicated.
"""
session = cdat['session']
session.msg("Disconnecting...")
session.handle_close()

11
src/events.py Normal file
View file

@ -0,0 +1,11 @@
import session_mgr
"""
Holds the events scheduled in scheduler.py.
"""
def evt_check_sessions():
"""
Event: Check all of the connected sessions.
"""
session_mgr.check_all_sessions()

18
src/initial_setup.py Normal file
View file

@ -0,0 +1,18 @@
from django.contrib.auth.models import User, Group
from apps.objects.models import Object
from apps.config.models import ConfigValue
def handle_setup():
# Set the initial user's username on the #1 object.
god_user = User.objects.filter(id=1)[0]
god_user_obj = Object.objects.filter(id=1)[0]
god_user_obj.set_name(god_user.username)
groups = ("Immortals", "Wizards", "Builders", "Player Helpers")
for group in groups:
newgroup = Group()
newgroup.name = group
newgroup.save()
# We don't want to do initial setup tasks every startup, only the first.
ConfigValue.objects.set_configvalue('game_firstrun', '0')

108
src/scheduler.py Normal file
View file

@ -0,0 +1,108 @@
import time
from twisted.internet import task
from src import events
"""
This file contains the event scheduler system.
ADDING AN EVENT:
* Create an event function to call.
* Add an entry to the 'schedule' dictionary here.
* Profit.
"""
# Dictionary of events with a list in the form of:
# [<function>, <interval>, <lastrantime>, <taskobject>, <description>]
schedule = {
'evt_check_sessions': [events.evt_check_sessions, 60, time.time(), None, "Session consistency checks."]
}
def start_events():
"""
Start the event system, which is built on Twisted's framework.
"""
for event in schedule:
event_func = get_event_function(event)
if callable(event_func):
# Set the call-back function for the task to trigger_event, but pass
# a reference to the event function.
event_task = task.LoopingCall(trigger_event, event_func, event)
# Start the task up with the specified interval.
event_task.start(get_event_interval(event), now=False)
# Set a reference to the event's task object in the dictionary so we
# can re-schedule, start, and stop events from elsewhere.
set_event_taskobj(event, event_task)
def get_event(event_name):
"""
Return the relevant entry in the schedule dictionary for the named event.
event_name: (string) The key of the event in the schedule dictionary.
"""
return schedule.get(event_name, None)
def get_event_function(event_name):
"""
Return a reference to the event's function.
event_name: (string) The key of the event in the schedule dictionary.
"""
return get_event(event_name)[0]
def get_event_interval(event_name):
"""
Return the event's execution interval.
event_name: (string) The key of the event in the schedule dictionary.
"""
return get_event(event_name)[1]
def get_event_nextfire(event_name):
"""
Returns a value in seconds when the event is going to fire off next.
event_name: (string) The key of the event in the schedule dictionary.
"""
return (get_event(event_name)[2]+get_event_interval(event_name))-time.time()
def get_event_taskobj(event_name):
"""
Returns an event's task object.
event_name: (string) The key of the event in the schedule dictionary.
"""
return get_event(event_name)[3]
def get_event_description(event_name):
"""
Returns an event's description.
event_name: (string) The key of the event in the schedule dictionary.
"""
return get_event(event_name)[4]
def set_event_taskobj(event_name, taskobj):
"""
Sets an event's task object.
event_name: (string) The key of the event in the schedule dictionary.
"""
get_event(event_name)[3] = taskobj
def set_event_lastfired(event_name):
"""
Sets an event's last fired time.
event_name: (string) The key of the event in the schedule dictionary.
"""
get_event(event_name)[2] = time.time()
def trigger_event(event_func, event_name):
"""
Update the last ran time and fire off the event.
event_func: (func_reference) Reference to the event function to fire.
eventname: (string) The name of the event (as per schedule dict).
"""
event_func()
set_event_lastfired(event_name)

68
src/scripthandler.py Normal file
View file

@ -0,0 +1,68 @@
"""
This module is responsible for managing scripts and their connection to the
Object class model. It is important to keep this as independent from the
codebase as possible in order to allow for drop-in replacements. All
interaction with actual script methods should happen via calls to Objects.
"""
import os
from traceback import format_exc
import settings
import functions_log
# A dictionary with keys equivalent to the script's name and values that
# contain references to the associated module for each key.
cached_scripts = {}
def scriptlink(source_obj, scriptname):
"""
Each Object will refer to this function when trying to execute a function
contained within a scripted module. For the sake of ease of management,
modules are cached and compiled as they are requested and stored in
the cached_scripts dictionary.
Returns a reference to an instance of the script's class as per it's
class_factory() method.
source_obj: (Object) A reference to the object being scripted.
scriptname: (str) Name of the module to load (minus 'scripts').
"""
# The module is already cached, just return it rather than re-load.
retval = cached_scripts.get(scriptname, False)
if retval:
return retval.class_factory(source_obj)
##
## NOTE: Only go past here when the script isn't already cached.
##
# Split the script name up by periods to give us the directory we need
# to change to. I really wish we didn't have to do this, but there's some
# strange issue with __import__ and more than two directories worth of
# nesting.
path_split = scriptname.split('.')
newpath_str = '/'.join(path_split[:-1])
# Lop the module name off the end.
modname = path_split[-1]
try:
# Change the working directory to the location of the script and import.
os.chdir('%s/%s/' % (settings.SCRIPT_ROOT, newpath_str))
functions_log.log_infomsg("SCRIPT: Caching and importing %s." % (modname))
modreference = __import__(modname)
# Store the module reference for later fast retrieval.
cached_scripts[scriptname] = modreference
except ImportError:
functions_log.log_infomsg('Error importing %s: %s' % (modname, format_exc()))
os.chdir(settings.BASE_PATH)
return
except OSError:
functions_log.log_infomsg('Invalid module path: %s' % (format_exc()))
os.chdir(settings.BASE_PATH)
return
# Change back to the original working directory.
os.chdir(settings.BASE_PATH)
# The new script module has been cached, return the reference.
return modreference.class_factory(source_obj)

0
src/scripts/__init__.py Normal file
View file

142
src/scripts/basicobject.py Normal file
View file

@ -0,0 +1,142 @@
"""
This will be the base object type/interface that all scripts are derived from by
default. It will have the necessary outline for developers to sub-class and override.
"""
from src import ansi
class BasicObject:
def __init__(self, source_obj):
"""
Get our ducks in a row.
source_obj: (Object) A reference to the object being scripted (the child).
"""
self.source_obj = source_obj
def a_desc(self, values):
"""
Perform this action when someone uses the LOOK command on the object.
values: (Dict) Script arguments with keys:
* pobject: The object requesting the action.
"""
# Un-comment the line below for an example
#print "SCRIPT TEST: %s looked at %s." % (values["pobject"], self.source_obj)
pass
def return_appearance(self, values):
"""
Returns a string representation of an object's appearance when LOOKed at.
values: (Dict) Script arguments with keys:
* pobject: The object requesting the action.
"""
target_obj = self.source_obj
pobject = values["pobject"]
description = target_obj.get_description()
if description is not None:
retval = "%s\r\n%s" % (
target_obj.get_name(),
target_obj.get_description(),
)
else:
retval = "%s" % (
target_obj.get_name(),
)
con_players = []
con_things = []
con_exits = []
for obj in target_obj.get_contents():
if obj.is_player():
if obj != pobject and obj.is_connected_plr():
con_players.append(obj)
elif obj.is_exit():
con_exits.append(obj)
else:
con_things.append(obj)
if not con_players == []:
retval += "\n\r%sPlayers:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],)
for player in con_players:
retval +='\n\r%s' %(player.get_name(),)
if not con_things == []:
retval += "\n\r%sContents:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],)
for thing in con_things:
retval += '\n\r%s' %(thing.get_name(),)
if not con_exits == []:
retval += "\n\r%sExits:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],)
for exit in con_exits:
retval += '\n\r%s' %(exit.get_name(),)
return retval
def a_get(self, values):
"""
Perform this action when someone uses the GET command on the object.
values: (Dict) Script arguments with keys:
* pobject: The object requesting the action.
"""
# Un-comment the line below for an example
#print "SCRIPT TEST: %s got %s." % (values["pobject"], self.source_obj)
pass
def a_drop(self, values):
"""
Perform this action when someone uses the GET command on the object.
values: (Dict) Script arguments with keys:
* pobject: The object requesting the action.
"""
# Un-comment the line below for an example
#print "SCRIPT TEST: %s dropped %s." % (values["pobject"], self.source_obj)
pass
def default_lock(self, values):
"""
This method returns a True or False boolean value to determine whether
the actor passes the lock. This is generally used for picking up
objects or traversing exits.
values: (Dict) Script arguments with keys:
* pobject: The object requesting the action.
"""
# Assume everyone passes the default lock by default.
return True
def use_lock(self, values):
"""
This method returns a True or False boolean value to determine whether
the actor passes the lock. This is generally used for seeing whether
a player can use an object or any of its commands.
values: (Dict) Script arguments with keys:
* pobject: The object requesting the action.
"""
# Assume everyone passes the use lock by default.
return True
def enter_lock(self, values):
"""
This method returns a True or False boolean value to determine whether
the actor passes the lock. This is generally used for seeing whether
a player can enter another object.
values: (Dict) Script arguments with keys:
* pobject: The object requesting the action.
"""
# Assume everyone passes the enter lock by default.
return True
def class_factory(source_obj):
"""
This method is called any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently. I'm not
sure how well this will scale, but we'll find out. We may need to
re-factor this eventually.
source_obj: (Object) A reference to the object being scripted (the child).
"""
return BasicObject(source_obj)

View file

View file

View file

121
src/server.py Executable file
View file

@ -0,0 +1,121 @@
from traceback import format_exc
import time, sys
from twisted.application import internet, service
from twisted.internet import protocol, reactor, defer
from twisted.python import log
from django.db import models
from django.db import connection
from django.conf import settings
from apps.config.models import CommandAlias, ConfigValue
from src.session import SessionProtocol
from src import scheduler
import functions_general
from src import session_mgr
from src import cmdtable
from src import initial_setup
class EvenniaService(service.Service):
def __init__(self, filename="blah"):
self.cmd_alias_list = {}
self.game_running = True
sys.path.append('.')
# Database-specific startup optimizations.
if settings.DATABASE_ENGINE == "sqlite3":
self.sqlite3_prep()
# Wipe our temporary flags on all of the objects.
cursor = connection.cursor()
cursor.execute("UPDATE objects_object SET nosave_flags=''")
print '-'*50
# Load command aliases into memory for easy/quick access.
self.load_cmd_aliases()
if ConfigValue.objects.get_configvalue('game_firstrun') == '1':
print ' Game started for the first time, setting defaults.'
initial_setup.handle_setup()
self.start_time = time.time()
print ' %s started on port(s):' % (ConfigValue.objects.get_configvalue('site_name'),)
for port in settings.GAMEPORTS:
print ' * %s' % (port)
print '-'*50
scheduler.start_events()
"""
BEGIN SERVER STARTUP METHODS
"""
def load_cmd_aliases(self):
"""
Load up our command aliases.
"""
alias_list = CommandAlias.objects.all()
for alias in alias_list:
self.cmd_alias_list[alias.user_input] = alias.equiv_command
print ' Command Aliases Loaded: %i' % (len(self.cmd_alias_list),)
pass
def sqlite3_prep(self):
"""
Optimize some SQLite stuff at startup since we can't save it to the
database.
"""
cursor = connection.cursor()
cursor.execute("PRAGMA cache_size=10000")
cursor.execute("PRAGMA synchronous=OFF")
cursor.execute("PRAGMA count_changes=OFF")
cursor.execute("PRAGMA temp_store=2")
"""
BEGIN GENERAL METHODS
"""
def shutdown(self, message='The server has been shutdown. Please check back soon.'):
functions_general.announce_all(message)
session_mgr.disconnect_all_sessions()
reactor.callLater(0, reactor.stop)
def command_list(self):
"""
Return a string representing the server's command list.
"""
clist = cmdtable.ctable.keys()
clist.sort()
return clist
def reload(self, session):
"""
Reload modules that don't have any variables that can be reset.
For changes to the scheduler, server, or session_mgr modules, a cold
restart is needed.
"""
reload_list = []
for mod in reload_list:
reload(sys.modules[mod])
session.msg("Modules reloaded.")
functions_general.log_infomsg("Modules reloaded by %s." % (session,))
def getEvenniaServiceFactory(self):
f = protocol.ServerFactory()
f.protocol = SessionProtocol
f.server = self
return f
"""
END Server CLASS
"""
application = service.Application('Evennia')
mud_service = EvenniaService('Evennia Server')
# Sheet sheet, fire ze missiles!
serviceCollection = service.IServiceCollection(application)
for port in settings.GAMEPORTS:
internet.TCPServer(port, mud_service.getEvenniaServiceFactory()).setServiceParent(serviceCollection)

186
src/session.py Executable file
View file

@ -0,0 +1,186 @@
"""
This module contains classes related to Sessions. session_mgr has the things
needed to manage them.
"""
import time
import sys
from datetime import datetime
from twisted.conch.telnet import StatefulTelnetProtocol
from django.utils import simplejson
from django.contrib.auth.models import User
from apps.objects.models import Object
from apps.config.models import ConnectScreen, ConfigValue
import cmdhandler
import functions_general
import functions_log
import session_mgr
import ansi
class SessionProtocol(StatefulTelnetProtocol):
"""
This class represents a player's sesssion. From here we branch down into
other various classes, please try to keep this one tidy!
"""
def connectionMade(self):
"""
What to do when we get a connection.
"""
self.prep_session()
functions_log.log_infomsg('Connection: %s' % (self,))
session_mgr.add_session(self)
self.game_connect_screen()
def getClientAddress(self):
"""
Returns the client's address and port in a tuple. For example
('127.0.0.1', 41917)
"""
return self.transport.client
def prep_session(self):
self.server = self.factory.server
self.address = self.getClientAddress()
self.name = None
self.uid = None
self.logged_in = False
# The time the user last issued a command.
self.cmd_last = time.time()
# Player-visible idle time, excluding the IDLE command.
self.cmd_last_visible = time.time()
# Total number of commands issued.
self.cmd_total = 0
# The time when the user connected.
self.conn_time = time.time()
self.channels_subscribed = {}
def disconnectClient(self):
"""
Manually disconnect the client.
"""
self.transport.loseConnection()
def connectionLost(self, reason):
"""
Execute this when a client abruplty loses their connection.
"""
functions_log.log_infomsg('Disconnect: %s' % (self,))
self.handle_close()
def load_user_channels(self):
"""
Parse JSON dict of a user's channel list from their CHANLIST attribute.
"""
chan_list = self.get_pobject().get_attribute_value("__CHANLIST")
if chan_list:
self.channels_subscribed = simplejson.loads(chan_list)
def lineReceived(self, data):
"""
Any line return indicates a command for the purpose of a MUD. So we take
the user input and pass it to our command handler.
"""
line = (''.join(data))
line = line.strip('\r')
uinput = line
# Stuff anything we need to pass in this dictionary.
cdat = {"server": self.factory.server, "uinput": uinput, "session": self}
cmdhandler.handle(cdat)
def execute_cmd(self, cmdstr):
"""
Executes a command as this session.
"""
self.lineReceived(data=cmdstr)
def handle_close(self):
"""
Break the connection and do some accounting.
"""
pobject = self.get_pobject()
if pobject:
pobject.set_flag("CONNECTED", False)
pobject.get_location().emit_to_contents("%s has disconnected." % (pobject.get_name(show_dbref=False),), exclude=pobject)
uaccount = pobject.get_user_account()
uaccount.last_login = datetime.now()
uaccount.save()
self.disconnectClient()
self.logged_in = False
session_mgr.remove_session(self)
def get_pobject(self):
"""
Returns the object associated with a session.
"""
try:
result = Object.objects.get(id=self.uid)
return result
except:
return False
def game_connect_screen(self):
"""
Show the banner screen. Grab from the 'connect_screen' config directive.
"""
screen = ConnectScreen.objects.get_random_connect_screen()
buffer = ansi.parse_ansi(screen.connect_screen_text)
self.msg(buffer)
def is_loggedin(self):
"""
Returns a boolean True if the session is logged in.
"""
try:
return self.logged_in
except:
return False
def login(self, user):
"""
After the user has authenticated, handle logging him in.
"""
self.uid = user.id
self.name = user.username
self.logged_in = True
self.conn_time = time.time()
pobject = self.get_pobject()
session_mgr.disconnect_duplicate_session(self)
pobject.set_flag("CONNECTED", True)
self.msg("You are now logged in as %s." % (self.name,))
pobject.get_location().emit_to_contents("%s has connected." % (pobject.get_name(show_dbref=False),), exclude=pobject)
self.execute_cmd("look")
functions_log.log_infomsg("Login: %s" % (self,))
# Update their account's last login time.
user.last_login = datetime.now()
user.save()
pobject.set_attribute("Last", "%s" % (time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()),))
pobject.set_attribute("Lastsite", "%s" % (self.address[0],))
# Load their channel selection from a JSON-encoded string attribute.
self.load_user_channels()
def msg(self, message):
"""
Sends a message to the session.
"""
if isinstance(message, unicode):
message = message.encode("utf-8")
self.sendLine("%s" % (message,))
def __str__(self):
"""
String representation of the user session class. We use
this a lot in the server logs and stuff.
"""
if self.is_loggedin():
symbol = '#'
else:
symbol = '?'
return "<%s> %s@%s" % (symbol, self.name, self.address,)

104
src/session_mgr.py Normal file
View file

@ -0,0 +1,104 @@
"""
Session manager, handles connected players.
"""
import time
from apps.config.models import ConfigValue
import functions_general
import functions_log
# Our list of connected sessions.
session_list = []
def add_session(session):
"""
Adds a session to the session list.
"""
session_list.insert(0, session)
functions_log.log_infomsg('Sessions active: %d' % (len(get_session_list(return_unlogged=True),)))
def get_session_list(return_unlogged=False):
"""
Lists the connected session objects.
"""
if return_unlogged:
return session_list
else:
return [sess for sess in session_list if sess.is_loggedin()]
def disconnect_all_sessions():
"""
Cleanly disconnect all of the connected sessions.
"""
for sess in get_session_list():
sess.handle_close()
def disconnect_duplicate_session(session):
"""
Disconnects any existing session under the same object. This is used in
connection recovery to help with record-keeping.
"""
sess_list = get_session_list()
new_pobj = session.get_pobject()
for sess in sess_list:
if new_pobj == sess.get_pobject() and sess != session:
sess.msg("Your account has been logged in from elsewhere, disconnecting.")
sess.disconnectClient()
return True
return False
def check_all_sessions():
"""
Check all currently connected sessions and see if any are dead.
"""
idle_timeout = int(ConfigValue.objects.get_configvalue('idle_timeout'))
if len(session_list) <= 0:
return
if idle_timeout <= 0:
return
for sess in get_session_list(return_unlogged=True):
if (time.time() - sess.cmd_last) > idle_timeout:
sess.msg("Idle timeout exceeded, disconnecting.")
sess.handle_close()
def remove_session(session):
"""
Removes a session from the session list.
"""
try:
session_list.remove(session)
functions_log.log_infomsg('Sessions active: %d' % (len(get_session_list()),))
except ValueError:
#functions_log.log_errmsg("Unable to remove session: %s" % (session,))
pass
def session_from_object(targobject):
"""
Return the session object given a object (if there is one open).
session_list: (list) The server's session_list attribute.
targobject: (Object) The object to match.
"""
results = [prospect for prospect in session_list if prospect.get_pobject() == targobject]
if results:
return results[0]
else:
return False
def session_from_dbref(dbstring):
"""
Return the session object given a dbref (if there is one open).
dbstring: (int) The dbref number to match against.
"""
if is_dbref(dbstring):
results = [prospect for prospect in session_list if prospect.get_pobject().dbref_match(dbstring)]
if results:
return results[0]
else:
return False