* lib/menus/active: Merged into lib/menus/miscellaneous and removed.
* lib/menus/miscellaneous (choose_gender, choose_name)
(main_utility): Added menus to enter/exit the game, select gender
and pick a randomly-generated avatar name from a list.
* lib/muff/muffcmds.py: Changed user state names to employ
underscores instead of spaces.
(command_show): New admin command to ease debugging of data
relationships.
(generic_menu_handler): New function abstract out common routines
from typical menu state handlers.
(handle_user_input): For aesthetics, user input is now followed by a
blank line of output if we expect the client isn't echoing.
* lib/muff/muffmain.py (main): The main loop is now followed by a
muffiniv.universe.save during shutdown, in case any changes have
occurred since the last save.
* lib/muff/muffmenu.py
(get_menu): Refactored to be more modular, and broke out a lot of
its pieces into additional functions.
(get_choice_action, get_choice_branch, get_default_action)
(get_default_branch, get_default_menu_choice)
(get_formatted_default_menu_choice, get_formatted_menu_choices)
(get_menu_actions, get_menu_branches, get_menu_choices)
(get_menu_description, get_menu_prompt): New functions abstracted
out of the previously spaghetti-like get_menu function.
(get_echo_message, get_echo_sequence, menu_echo_on): New functions
to take care of enabling and disabling echo for password prompts.
* lib/muff/muffmisc.py (replace_macros): Implemented gender-specific
pronoun replacement macros, to make English-language messages less
awkward.
* lib/muff/muffuniv.py: New module implementing DataFile, Element
and Universe classes, for a more object-oriented approach.
* muff: Renamed to mud.py.
* muff.conf: Renamed to mudpy.conf.
description = State something out loud.
help = This allows you to speak to other characters within the same room. If you end your sentence with specific punctuation, the aparent speech action (ask, exclaim, et cetera) will be adapted accordingly. It will also add punctuation and capitalize your message where needed.
+[show]
+description = Show program data.
+help = For now, this is used to show things like "universe" and "avatars".
+
[time]
description = Show the current world time in elapsed increments.
help = This will show the current world time in elapsed increments.
-[checking new account name]
+[checking_new_account_name]
description = There is no existing account for "$(account)" (note that an account name is not the same as a character name). Would you like to create a new account by this name, go back and enter a different name or disconnect now?
choice_d = disconnect now
choice_g = go back
prompt = Enter your choice:
default = d
-[entering new password]
+[entering_new_password]
prompt = Enter a new password for "$(account)":
echo = off
error_weak = That is a weak password... Try something at least 7 characters long with a combination of mixed-case letters, numbers and punctuation/spaces.
error_differs = The two passwords did not match. Try again...
-[verifying new password]
+[verifying_new_password]
prompt = Enter the same new password again:
echo = off
+++ /dev/null
-[active]
-prompt = >
-
[index]
-files = account_creation active login miscellaneous
+files = account_creation login miscellaneous
-[entering account name]
+[entering_account_name]
description = Welcome to the mudpy example...
prompt = Identify yourself:
-[checking password]
+[checking_password]
prompt = Password:
echo = off
error_incorrect = Incorrect password, please try again...
-[disconnecting duplicates]
+[disconnecting_duplicates]
prompt = $(red)Closing your previous connection...$(nrm)$(eol)
+[active]
+prompt = >
+
[disconnecting]
description = $(red)Disconnecting...$(nrm)
+[main_utility]
+description = From here you can activate, create and delete avatars. An avatar is your persona in the world of Example.
+choice_a = activate an existing avatar
+#branch_a = activate_avatar
+branch_a = active
+choice_c = create a new avatar
+action_c = user.new_avatar()
+branch_c = choose_gender
+choice_d = delete an unwanted avatar
+branch_d = delete_avatar
+choice_l = leave example for now
+branch_l = disconnecting
+choice_p = permanently remove your account
+branch_p = delete_account
+prompt = What would you like to do?
+error_no_avatars = You don't have any avatars yet. An avatar is your persona in the world of Example. It is recommended that you create one now.
+
+[choose_gender]
+description = First, your new avatar needs a gender. In the world of Example, all avatars are either male or female.
+choice_f = female
+choice_m = male
+action = user.avatar.rec("gender", user.menu_choices[choice])
+branch = choose_name
+prompt = Pick a gender for your new avatar:
+
+[choose_name]
+description = Your new avatar needs a name. This will be the name with which $(tpsp) grew up, and will initially be the name by which $(tpsp) is known in the world of Example. There are ways for your new avatar to make a name for $(tpop)self over time, so $(tpsp) won't be stuck going by such an unremarkable name forever.
+create_1 = muffmisc.random_name()
+create_2 = muffmisc.random_name()
+create_3 = muffmisc.random_name()
+create_4 = muffmisc.random_name()
+create_5 = muffmisc.random_name()
+create_6 = muffmisc.random_name()
+create_7 = muffmisc.random_name()
+action = user.avatar.rec("name", user.menu_choices[choice])
+branch = active
+prompt = Choose a name for $(tpop):
+
# these are all the modules included in the muff package; if you create
# another, be sure to add it to this list
-__all__ = [ "muffcmds", "muffconf", "muffmain", "muffmenu", "muffmisc", "muffsock", "muffuser", "muffvars" ]
+__all__ = [ "muffcmds", "muffconf", "muffmain", "muffmenu", "muffmisc", "muffsock", "muffuniv", "muffuser", "muffvars" ]
"""The main handler, branches to a state-specific handler."""
# check to make sure the state is expected, then call that handler
- exec("handler_" + user.state.replace(" ", "_") + "(user)")
+ try:
+ exec("handler_" + user.state + "(user)")
+ except NameError:
+ generic_menu_handler(user)
# since we got input, flag that the menu/prompt needs to be redisplayed
user.menu_seen = False
+ # if the user's client echo is off, send a blank line for aesthetics
+ if not user.echoing: user.send("", "")
+
+def generic_menu_handler(user):
+ """A generic menu choice handler."""
+
+ # get a lower-case representation of the next line of input
+ choice = user.input_queue.pop(0)
+ if choice: choice = choice.lower()
+
+ # run any script related to this choice
+ exec(muffmenu.get_choice_action(user, choice))
+
+ # move on to the next state or return an error
+ new_state = muffmenu.get_choice_branch(user, choice)
+ if new_state: user.state = new_state
+ else: user.error = "default"
+
def handler_entering_account_name(user):
"""Handle the login account name."""
# if we have a password hash, time to request a password
if user.get_passhash():
- user.state = "checking password"
+ user.state = "checking_password"
# otherwise, this could be a brand new user
else:
user.proposed_name = None
user.load()
muffmisc.log("New user: " + user.name)
- user.state = "checking new account name"
+ user.state = "checking_new_account_name"
# if the user entered nothing for a name, then buhbye
else:
if md5.new(user.proposed_name + input_data).hexdigest() == user.passhash:
# if so, set the username and load from cold storage
- # TODO: branch to character creation and selection menus
user.name = user.proposed_name
del(user.proposed_name)
if not user.replace_old_connections():
user.load()
user.authenticate()
- user.state = "active"
+ user.state = "main_utility"
# if at first your hashes don't match, try, try again
elif user.password_tries < muffconf.getint("general", "password_tries"):
user.send("$(eol)$(red)Too many failed password attempts...$(nrm)$(eol)")
user.state = "disconnecting"
-def handler_disconnecting(user):
- """Waiting for the user's connection to close."""
- pass
-
-def handler_disconnecting_duplicates(user):
- """Waiting for duplicate connections to close."""
- pass
-
def handler_checking_new_account_name(user):
"""Handle input for the new user menu."""
# if there's no input, use the default
else:
- choice = muffmenu.get_default(user)
+ choice = muffmenu.get_default_menu_choice(user.state)
# user selected to disconnect
if choice == "d":
# go back to the login screen
elif choice == "g":
- user.state = "entering account name"
+ user.state = "entering_account_name"
# new user, so ask for a password
elif choice == "n":
- user.state = "entering new password"
+ user.state = "entering_new_password"
# user entered a non-existent option
else:
# hash and store it, then move on to verification
user.passhash = md5.new(user.name + input_data).hexdigest()
- user.state = "verifying new password"
+ user.state = "verifying_new_password"
# the password was weak, try again if you haven't tried too many times
elif user.password_tries < muffconf.getint("general", "password_tries"):
user.save()
# the hashes matched, so go active
- # TODO: branch to character creation and selection menus
- if not user.replace_old_connections(): user.state = "active"
+ if not user.replace_old_connections(): user.state = "main_utility"
# go back to entering the new password as long as you haven't tried
# too many times
elif user.password_tries < muffconf.getint("general", "password_tries"):
user.password_tries += 1
user.error = "differs"
- user.state = "entering new password"
+ user.state = "entering_new_password"
# otherwise, sayonara
else:
else:
user.send("What do you want to say?")
+def command_show(user, command="", parameters=""):
+ """Show program data."""
+ if parameters == "universe":
+ message = "These are the current elements in the universe:$(eol)"
+ keys = muffuniv.universe.contents.keys()
+ keys.sort()
+ for key in keys: message += "$(eol) $(grn)" + key + "$(nrm)"
+ elif parameters == "avatars":
+ message = "These are the avatars managed by your account:$(eol)"
+ avatars = user.list_avatar_names()
+ avatars.sort()
+ for avatar in avatars: message += "$(eol) $(grn)" + avatar + "$(nrm)"
+ elif parameters: message = "I don't know what \"" + parameters + "\" is."
+ else: message = "What do you want to show?"
+ user.send(message)
+
def command_error(user, command="", parameters=""):
"""Generic error for an unrecognized command word."""
config_dirs = [".", "./etc", "/usr/local/muff", "/usr/local/muff/etc", "/etc/muff", "/etc" ]
# name of the config file
-config_name = "muff.conf"
+config_name = "mudpy.conf"
# find the config file
for each_dir in config_dirs:
# do what needs to be done on each pulse
muffmisc.on_pulse()
- # the loop has terminated, so save persistent variables
+ # the loop has terminated, so save persistent data
muffvars.save()
+ muffuniv.universe.save()
# log a final message
muffmisc.log("Shutting down now.")
# muff uses menu data stored in ini-style files supported by ConfigParser
import ConfigParser
-# os.listdir is needed to get a file listing from a config directory
-import os
-
-# re.match is used to find menu options based on the choice_ prefix
-import re
-
# hack to load all modules in the muff package
import muff
for module in muff.__all__:
menu_data = ConfigParser.SafeConfigParser()
menu_data.read(menu_files)
-def get_default(user):
- """Return the default choice for a menu."""
- return menu_data.get(user.state, "default")
-
-def get_menu(user):
+def get_menu(state, error=None, echoing=True, choices={}):
"""Show the correct menu text to a user."""
- # the menu hasn't been shown yet since the user's last input
- if not user.menu_seen:
-
- # echo is currently on for the user, so don't add an extra eol
- if user.echoing:
- spacer = ""
+ # begin with a telnet echo command sequence if needed
+ message = get_echo_sequence(state, echoing)
- # echo is currently off for the user, so add an extra eol
- else:
- spacer = "$(eol)"
+ # get the description or error text
+ message += get_menu_description(state, error)
- # if the user has echo on and the menu specifies it should be
- # turned off, send: iac + will + echo + null
- try:
- if menu_data.get(user.state, "echo") == "off" and user.echoing:
- echo = chr(255) + chr(251) + chr(1) + chr(0)
- user.echoing = False
- else:
- echo = ""
-
- # if echo is not set to off in the menu and the user curently
- # has echo off, send: iac + wont + echo + null
- except:
- if not user.echoing:
- echo = chr(255) + chr(252) + chr(1) + chr(0)
- user.echoing = True
- else:
- echo = ""
-
- # and error condition was raised by the handler
- if user.error:
+ # get menu choices for the current state
+ message += get_formatted_menu_choices(state, choices)
- # try to get an error message matching the condition
- # and current state
- try:
- description = "$(red)" + menu_data.get(user.state, "error_" + user.error) + "$(nrm)$(eol)$(eol)"
+ # try to get a prompt, if it was defined
+ message += get_menu_prompt(state)
- # otherwise, use a generic one
- except:
- description = "$(red)That is not a valid choice...$(nrm)$(eol)$(eol)"
+ # throw in the default choice, if it exists
+ message += get_formatted_default_menu_choice(state)
- # now clear the error condition
- user.error = ""
+ # display a message indicating if echo is off
+ message += get_echo_message(state)
- # there was no error condition raised by the handler
- else:
+ # return the assembly of various strings defined above
+ return message
- # try to get a menu description for the current state
- try:
- description = menu_data.get(user.state, "description") + "$(eol)$(eol)"
+def menu_echo_on(state):
+ """True if echo is on, false if it is off."""
+ try:
+ return menu_data.getboolean(state, "echo")
+ except:
+ return True
- # otherwise, leave it blank
- except:
- description = ""
-
- # try to get menu choices for the current state
- try:
+def get_echo_sequence(state, echoing):
+ """Build the appropriate IAC ECHO sequence as needed."""
- # build a dict of choice:meaning
- choices = {}
- for option in menu_data.options(user.state):
- if re.match("choice_", option):
- choices[option.split("_")[1]] = menu_data.get(user.state, option)
+ # if the user has echo on and the menu specifies it should be turned
+ # off, send: iac + will + echo + null
+ if echoing and not menu_echo_on(state): return chr(255) + chr(251) + chr(1) + chr(0)
- # make a sorted list of choices
- choice_keys = choices.keys()
- choice_keys.sort()
+ # if echo is not set to off in the menu and the user curently has echo
+ # off, send: iac + wont + echo + null
+ elif not echoing and menu_echo_on(state): return chr(255) + chr(252) + chr(1) + chr(0)
- # concatenate them all into a list for display
- choicestring = ""
- for choice in choice_keys:
- choicestring += " [$(red)" + choice + "$(nrm)] " + choices[choice] + "$(eol)"
+ # default is not to send an echo control sequence at all
+ else: return ""
- # throw in an additional blank line after the choices,
- # if there were any
- if choicestring:
- choicestring += "$(eol)"
+def get_echo_message(state):
+ """Return a message indicating that echo is off."""
+ if menu_echo_on(state): return ""
+ else: return "(won't echo) "
- # there were no choices, so leave them blank
- except:
- choicestring = ""
-
- # try to get a prompt, if it was defined
+def get_default_menu_choice(state):
+ """Return the default choice for a menu."""
+ try:
+ return menu_data.get(state, "default")
+ except:
+ return ""
+def get_formatted_default_menu_choice(state):
+ """Default menu choice foratted for inclusion in a prompt string."""
+ default = get_default_menu_choice(state)
+ if default: return "[$(red)" + default + "$(nrm)] "
+ else: return ""
+
+def get_menu_description(state, error):
+ """Get the description or error text."""
+
+ # an error condition was raised by the handler
+ if error:
+
+ # try to get an error message matching the condition
+ # and current state
try:
- prompt = menu_data.get(user.state, "prompt") + " "
+ return "$(red)" + menu_data.get(state, "error_" + error) + "$(nrm)$(eol)$(eol)"
- # otherwise, leave it blank
+ # otherwise, use a generic one
except:
- prompt = ""
+ return "$(red)That is not a valid choice...$(nrm)$(eol)$(eol)"
+
+ # there was no error condition
+ else:
- # throw in the default choice, if it exists
+ # try to get a menu description for the current state
try:
- default = "[$(red)" + menu_data.get(user.state, "default") + "$(nrm)] "
+ return menu_data.get(state, "description") + "$(eol)$(eol)"
# otherwise, leave it blank
except:
- default = ""
-
- # echo is on, so don't display a message about it
- if user.echoing:
- echoing = ""
-
- # echo is off, so let the user know
- else:
- echoing = "(won't echo) "
-
- # assemble and send the various strings defined above
- user.send(echo + spacer + description + choicestring + prompt + default + echoing, "")
-
- # flag that the menu has now been displayed
- user.menu_seen = True
+ return ""
+
+def get_menu_prompt(state):
+ """Try to get a prompt, if it was defined."""
+ try:
+ return menu_data.get(state, "prompt") + " "
+ except:
+ return ""
+
+def get_menu_choices(user):
+ """Return a dict of choice:meaning."""
+ choices = {}
+ for option in menu_data.options(user.state):
+ if option.startswith("choice_"):
+ choices[option.split("_", 2)[1]] = menu_data.get(user.state, option)
+ elif option.startswith("create_"):
+ choices[option.split("_", 2)[1]] = eval(menu_data.get(user.state, option))
+ return choices
+
+def get_formatted_menu_choices(state, choices):
+ """Returns a formatted string of menu choices."""
+ choice_output = ""
+ choice_keys = choices.keys()
+ choice_keys.sort()
+ for choice in choice_keys:
+ choice_output += " [$(red)" + choice + "$(nrm)] " + choices[choice] + "$(eol)"
+ if choice_output: choice_output += "$(eol)"
+ return choice_output
+
+def get_menu_branches(state):
+ """Return a dict of choice:branch."""
+ branches = {}
+ try:
+ for option in menu_data.options(state):
+ if option.startswith("branch_"):
+ branches[option.split("_", 2)[1]] = menu_data.get(state, option)
+ except:
+ pass
+ return branches
+
+def get_default_branch(state):
+ """Return the default branch."""
+ try:
+ return menu_data.get(state, "branch")
+ except:
+ return ""
+
+def get_choice_branch(user, choice):
+ """Returns the new state matching the given choice."""
+ branches = get_menu_branches(user.state)
+ if not choice: choice = get_default_menu_choice(user.state)
+ if choice in branches.keys(): return branches[choice]
+ elif choice in user.menu_choices.keys(): return get_default_branch(user.state)
+ else: return ""
+
+def get_menu_actions(state):
+ """Return a dict of choice:branch."""
+ actions = {}
+ try:
+ for option in menu_data.options(state):
+ if option.startswith("action_"):
+ actions[option.split("_", 2)[1]] = menu_data.get(state, option)
+ except:
+ pass
+ return actions
+
+def get_default_action(state):
+ """Return the default action."""
+ try:
+ return menu_data.get(state, "action")
+ except:
+ return ""
+
+def get_choice_action(user, choice):
+ """Run any indicated script for the given choice."""
+ actions = get_menu_actions(user.state)
+ if not choice: choice = get_default_menu_choice(user.state)
+ if choice in actions.keys(): return actions[choice]
+ elif choice in user.menu_choices.keys(): return get_default_action(user.state)
+ else: return ""
# Copyright (c) 2005 mudpy, Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
# Licensed per terms in the LICENSE file distributed with this software.
+import ConfigParser
+
# used by several functions for random calls
import random
else: return string_value
def getlong(config, section, option):
- return int(config.get(section, option).strip("L"))
+ try:
+ return int(config.get(section, option).strip("L"))
+ except ConfigParser.NoSectionError:
+ config.add_section(section)
+ return getlong(config, section, option)
+ except ConfigParser.NoOptionError:
+ setlong(config, section, option, 0)
+ return getlong(config, section, option)
def setlong(config, section, option, value):
return config.set(section, option, repr_long(value))
elif macro == "$(account)":
text = string.replace(text, macro, user.name)
+ # third person subjective pronoun
+ elif macro == "$(tpsp)":
+ if user.avatar.get("gender") == "male":
+ text = string.replace(text, macro, "he")
+ elif user.avatar.get("gender") == "female":
+ text = string.replace(text, macro, "she")
+ else:
+ text = string.replace(text, macro, "it")
+
+ # third person objective pronoun
+ elif macro == "$(tpop)":
+ if user.avatar.get("gender") == "male":
+ text = string.replace(text, macro, "him")
+ elif user.avatar.get("gender") == "female":
+ text = string.replace(text, macro, "her")
+ else:
+ text = string.replace(text, macro, "it")
+
+ # third person possessive pronoun
+ elif macro == "$(tppp)":
+ if user.avatar.get("gender") == "male":
+ text = string.replace(text, macro, "his")
+ elif user.avatar.get("gender") == "female":
+ text = string.replace(text, macro, "hers")
+ else:
+ text = string.replace(text, macro, "its")
+
# if we get here, log and replace it with null
else:
text = string.replace(text, macro, "")
if check_time("frequency_save"):
muffvars.save()
for user in muffvars.userlist: user.save()
+ muffuniv.universe.save()
# pause for a configurable amount of time (decimal seconds)
time.sleep(muffconf.getfloat("time", "increment"))
--- /dev/null
+# Copyright (c) 2005 mudpy, Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
+# Licensed per terms in the LICENSE file distributed with this software.
+
+# persistent variables are stored in ini-style files supported by ConfigParser
+import ConfigParser
+
+# need to know the directory separator
+import os
+
+# hack to load all modules in the muff package
+import muff
+for module in muff.__all__:
+ exec("import " + module)
+
+class Element:
+ """An element of the universe."""
+ def __init__(self, key, origin, universe):
+ """Default values for the in-memory element variables."""
+ self.key = key
+ self.origin = origin
+ universe.contents[key] = self
+ if not universe.files[origin].data.has_section(key):
+ universe.files[origin].data.add_section(key)
+ def facets(self):
+ """Return a list of facets for this element."""
+ return universe.files[self.origin].data.options(self.key)
+ def get(self, facet):
+ """Retrieve values."""
+ if facet in dir(self): return self.facet
+ else:
+ try:
+ return universe.files[self.origin].data.get(self.key, facet)
+ except:
+ return None
+ def set(self, facet, value):
+ """Set values."""
+ if facet in universe.files[self.origin].data.options(self.key):
+ universe.files[self.origin].data.set(self.key, facet, value)
+ else: self.facet = value
+ def rec(self, facet, value):
+ """Add initial values to the record."""
+ universe.files[self.origin].data.set(self.key, facet, value)
+
+class DataFile:
+ """A file containing universe elements."""
+ def __init__(self, filename, universe):
+ filedir = os.sep.join(filename.split(os.sep)[:-1])
+ data = ConfigParser.SafeConfigParser()
+ data.read(filename)
+ self.filename = filename
+ self.data = data
+ universe.files[filename] = self
+ for section in data.sections():
+ if section == "include":
+ for option in data.options(section):
+ includefile = data.get(section, option)
+ if not includefile.startswith(os.sep):
+ includefile = filedir + os.sep + includefile
+ DataFile(includefile, universe)
+ else:
+ Element(section, filename, universe)
+ def save(self):
+ basedir = os.sep.join(self.filename.split(os.sep)[:-1])
+ if not os.access(basedir, os.F_OK): os.makedirs(basedir)
+ file_descriptor = file(self.filename, "w")
+ self.data.write(file_descriptor)
+ file_descriptor.flush()
+ file_descriptor.close()
+
+class Universe:
+ """The universe."""
+ def __init__(self):
+ filename = muffconf.get("files", "data")
+ if not filename.startswith(os.sep): filename = os.getcwd() + os.sep + filename
+ self.contents = {}
+ self.files = {}
+ DataFile(filename, self)
+ def save(self):
+ for key in self.files.keys(): self.files[key].save()
+
+def element_exists(key): return key in universe.contents.keys()
+
+# reload the muffconf module if the files:data setting does not exist
+try:
+ if muffconf.get("files", "data"): pass
+except AttributeError:
+ reload(muffconf)
+
+# if there is no universe, create an empty one
+if not "universe" in dir(): universe = Universe()
+
# user accounts are stored in ini-style files supported by ConfigParser
import ConfigParser
-# test for existence of the account dir with os.listdir and os.mkdir to make it
+# os is used to test for existence of the account dir and, if necessary, make it
import os
# string.replace is used to perform substitutions for color codes and the like
self.password_tries = 1
# the current state of the user
- self.state = "entering account name"
+ self.state = "entering_account_name"
# flag to indicate whether a menu has been displayed
self.menu_seen = False
# flag to indicate the current echo status of the client
self.echoing = True
+ # the active avatar
+ self.avatar = None
+
# an object containing persistent account data
self.record = ConfigParser.SafeConfigParser()
# the filename to which we'll write
filename = account_path + "/" + self.name.lower()
- # if the directory doesn't exist, create it
+ # open the user account file for writing
try:
- if os.listdir(account_path): pass
- except:
- os.mkdir(account_path)
+ record_file = file(filename, "w")
- # open the user account file for writing
- record_file = file(filename, "w")
+ # if the directory doesn't exist, create it first
+ except IOError:
+ os.makedirs(account_path)
+ record_file = file(filename, "w")
# dump the account data to it
self.record.write(record_file)
def show_menu(self):
"""Send the user their current menu."""
- self.send(muffmenu.get_menu(self))
+ if not self.menu_seen:
+ self.menu_choices = muffmenu.get_menu_choices(self)
+ self.send(muffmenu.get_menu(self.state, self.error, self.echoing, self.menu_choices), "")
+ self.menu_seen = True
+ self.error = False
+ self.adjust_echoing()
+
+ def adjust_echoing(self):
+ """Adjust echoing to match state menu requirements."""
+ if self.echoing and not muffmenu.menu_echo_on(self.state): self.echoing = False
+ elif not self.echoing and muffmenu.menu_echo_on(self.state): self.echoing = True
def remove(self):
"""Remove a user from the list of connected users."""
"""Send arbitrary text to a connected user."""
# only when there is actual output
- if output:
+ #if output:
- # start with a newline, append the message, then end
- # with the optional eol string passed to this function
- # and the ansi escape to return to normal text
- output = "\r\n" + output + eol + chr(27) + "[0m"
+ # start with a newline, append the message, then end
+ # with the optional eol string passed to this function
+ # and the ansi escape to return to normal text
+ output = "\r\n" + output + eol + chr(27) + "[0m"
- # find and replace macros in the output
- output = muffmisc.replace_macros(self, output)
+ # find and replace macros in the output
+ output = muffmisc.replace_macros(self, output)
- # wrap the text at 80 characters
- # TODO: prompt user for preferred wrap width
- output = muffmisc.wrap_ansi_text(output, 80)
+ # wrap the text at 80 characters
+ # TODO: prompt user for preferred wrap width
+ output = muffmisc.wrap_ansi_text(output, 80)
- # drop the formatted output into the output queue
- self.output_queue.append(output)
+ # drop the formatted output into the output queue
+ self.output_queue.append(output)
- # try to send the last item in the queue, remove it and
- # flag that menu display is not needed
- try:
- self.connection.send(self.output_queue[0])
- self.output_queue.remove(self.output_queue[0])
- self.menu_seen = False
+ # try to send the last item in the queue, remove it and
+ # flag that menu display is not needed
+ try:
+ self.connection.send(self.output_queue[0])
+ self.output_queue.remove(self.output_queue[0])
+ self.menu_seen = False
- # but if we can't, that's okay too
- except:
- pass
+ # but if we can't, that's okay too
+ except:
+ pass
def pulse(self):
"""All the things to do to the user per increment."""
# put on the end of the queue
self.input_queue.append(line)
+ def new_avatar(self):
+ """Instantiate a new, unconfigured avatar for this user."""
+ try:
+ counter = muffvars.variable_data.getint("counters", "next_actor")
+ except:
+ muffmisc.log("get next_actor failed")
+ counter = 1
+ while muffuniv.element_exists("actor:" + repr(counter)): counter += 1
+ muffvars.variable_data.set("counters", "next_actor", counter + 1)
+ self.avatar = muffuniv.Element("actor:" + repr(counter))
+ try:
+ avatars = self.record.get("account", "avatars").split()
+ except:
+ avatars = []
+ avatars.append(self.avatar.key)
+ self.record.set("account", "avatars", " ".join(avatars))
+
+ def list_avatar_names(self):
+ """A test function to list names of assigned avatars."""
+ try:
+ avatars = self.record.get("account", "avatars").split()
+ except:
+ avatars = []
+ avatar_names = []
+ for avatar in avatars:
+ avatar_names.append(muffuniv.universe.contents[avatar].get("name"))
+ return avatar_names
+
# persistent variables are stored in ini-style files supported by ConfigParser
import ConfigParser
+# need to be able to create the variable save file's directory
+import os
+
# hack to load all modules in the muff package
import muff
for module in muff.__all__:
"$(red)": chr(27) + "[31m"
}
-# function to save persistent variables to file
def save():
- file_descriptor = open(variable_file, "w")
+ """Function to save persistent variables to a file."""
+
+ # try to open the variable file
+ try:
+ file_descriptor = file(variable_file, "w")
+
+ # failing that, make the directory in which it resides first
+ except IOError:
+ os.makedirs(os.sep.join(variable_file.split(os.sep)[:-1]))
+ file_descriptor = file(variable_file, "w")
+
+ # now write the data and close out the file
variable_data.write(file_descriptor)
file_descriptor.flush()
file_descriptor.close()
--- /dev/null
+[include]
+actors = actors
+rooms = rooms
+
--- /dev/null
+[room:0,0,0,0]
+
+[room:1,0,0,0]
+
+[room:-1,0,0,0]
+
# figure out where to find our main configuration file
config_data = ConfigParser.SafeConfigParser()
config_dirs = [".", "./etc", "/usr/local/muff", "/usr/local/muff/etc", "/etc/muff", "/etc" ]
- config_name = "muff.conf"
+ config_name = "mudpy.conf"
config_files = []
for each_dir in config_dirs:
config_files.append(each_dir + "/" + config_name)
menus = ./lib/menus
modules = ./lib
variable = ./lib/save/variable
+data = lib/index
[general]
password_tries = 3