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.
+[time]
+description = Show the current world time in elapsed increments.
+help = This will show the current world time in elapsed increments.
+
--- /dev/null
+[index]
+files = active
+
--- /dev/null
+[index]
+files = account_creation active login miscellaneous
+
echo = off
error_incorrect = Incorrect password, please try again...
+[disconnecting duplicates]
+prompt = $(red)Closing your previous connection...$(nrm)$(eol)
+
[disconnecting]
-description = Disconnecting...
+description = $(red)Disconnecting...$(nrm)
# does the files:commands setting exist yet?
try:
- if muffconf.config_data.get("files", "commands"): pass
+ if muffconf.get("files", "commands"): pass
# if not, reload the muffconf module
except AttributeError:
reload(muffconf)
# now we can safely nab the command path setting and build a list of data files
-command_path = muffconf.config_data.get("files", "commands")
+command_path = muffconf.get("files", "commands")
+command_files_index = ConfigParser.SafeConfigParser()
+command_files_index.read(command_path + "/index")
command_files = []
-for each_file in os.listdir(command_path):
+for each_file in command_files_index.get("index", "files").split():
command_files.append(command_path + "/" + each_file)
# read the command data files
# this creates a list of commands mentioned in the data files
command_list = command_data.sections()
-def handle_user_input(user, input_data):
+def handle_user_input(user):
"""The main handler, branches to a state-specific handler."""
- # the pairings of user state and command to run
- handler_dictionary = {
- "active": handler_active,
- "entering account name": handler_entering_account_name,
- "checking password": handler_checking_password,
- "checking new account name": handler_checking_new_account_name,
- "entering new password": handler_entering_new_password,
- "verifying new password": handler_verifying_new_password
- }
# check to make sure the state is expected, then call that handler
- if user.state in handler_dictionary.keys():
- handler_dictionary[user.state](user, input_data)
-
- # if there's input with an unknown user state, something is wrong
- else: handler_fallthrough(user, input_data)
+ exec("handler_" + user.state.replace(" ", "_") + "(user)")
# since we got input, flag that the menu/prompt needs to be redisplayed
user.menu_seen = False
-def handler_entering_account_name(user, input_data):
+def handler_entering_account_name(user):
"""Handle the login account name."""
+ # get the next waiting line of input
+ input_data = user.input_queue.pop(0)
+
# did the user enter anything?
if input_data:
user.name = user.proposed_name
user.proposed_name = None
user.load()
+ muffmisc.log("New user: " + user.name)
user.state = "checking new account name"
# if the user entered nothing for a name, then buhbye
else:
user.state = "disconnecting"
-def handler_checking_password(user, input_data):
+def handler_checking_password(user):
"""Handle the login account password."""
+ # get the next waiting line of input
+ input_data = user.input_queue.pop(0)
+
# does the hashed input equal the stored hash?
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)
- user.load()
-
- # now go active
- # TODO: branch to character creation and selection menus
- user.state = "active"
+ if not user.replace_old_connections():
+ user.load()
+ user.authenticate()
+ user.state = "active"
# if at first your hashes don't match, try, try again
- elif user.password_tries < muffconf.config_data.getint("general", "password_tries"):
+ elif user.password_tries < muffconf.getint("general", "password_tries"):
user.password_tries += 1
user.error = "incorrect"
user.send("$(eol)$(red)Too many failed password attempts...$(nrm)$(eol)")
user.state = "disconnecting"
-def handler_checking_new_account_name(user, input_data):
+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."""
+ # get the next waiting line of input
+ input_data = user.input_queue.pop(0)
+
# if there's input, take the first character and lowercase it
if input_data:
choice = input_data.lower()[0]
# user selected to disconnect
if choice == "d":
- user.state == "disconnecting"
+ user.state = "disconnecting"
# go back to the login screen
elif choice == "g":
else:
user.error = "default"
-def handler_entering_new_password(user, input_data):
+def handler_entering_new_password(user):
"""Handle a new password entry."""
+ # get the next waiting line of input
+ input_data = user.input_queue.pop(0)
+
# make sure the password is strong--at least one upper, one lower and
# one digit, seven or more characters in length
if len(input_data) > 6 and len(filter(lambda x: x>="0" and x<="9", input_data)) and len(filter(lambda x: x>="A" and x<="Z", input_data)) and len(filter(lambda x: x>="a" and x<="z", input_data)):
user.state = "verifying new password"
# the password was weak, try again if you haven't tried too many times
- elif user.password_tries < muffconf.config_data.getint("general", "password_tries"):
+ elif user.password_tries < muffconf.getint("general", "password_tries"):
user.password_tries += 1
user.error = "weak"
user.send("$(eol)$(red)Too many failed password attempts...$(nrm)$(eol)")
user.state = "disconnecting"
-def handler_verifying_new_password(user, input_data):
+def handler_verifying_new_password(user):
"""Handle the re-entered new password for verification."""
+ # get the next waiting line of input
+ input_data = user.input_queue.pop(0)
+
# hash the input and match it to storage
if md5.new(user.name + input_data).hexdigest() == user.passhash:
+ user.authenticate()
+ user.save()
# the hashes matched, so go active
# TODO: branch to character creation and selection menus
- user.state = "active"
+ if not user.replace_old_connections(): user.state = "active"
# go back to entering the new password as long as you haven't tried
# too many times
- elif user.password_tries < muffconf.config_data.getint("general", "password_tries"):
+ elif user.password_tries < muffconf.getint("general", "password_tries"):
user.password_tries += 1
user.error = "differs"
user.state = "entering new password"
user.send("$(eol)$(red)Too many failed password attempts...$(nrm)$(eol)")
user.state = "disconnecting"
-def handler_active(user, input_data):
+def handler_active(user):
"""Handle input for active users."""
- # users reaching this stage should be considered authenticated
- # TODO: this should actually happen before or in load() instead
- if not user.authenticated: user.authenticated = True
+ # get the next waiting line of input
+ input_data = user.input_queue.pop(0)
# split out the command (first word) and parameters (everything else)
try:
# no data matching the entered command word
elif command: command_error(user, command, parameters)
-def handler_fallthrough(user, input_data):
- """Input received in an unknown user state."""
- if input_data:
- muffmisc.log("User \"" + user + "\" entered \"" + input_data + "\" while in unknown state \"" + user.state + "\".")
-
def command_halt(user, command="", parameters=""):
"""Halt the world."""
- # let everyone know
- # TODO: optionally take input for the message
- muffmisc.broadcast(user.name + " halts the world.")
+ # see if there's a message or use a generic one
+ if parameters: message = "Halting: " + parameters
+ else: message = "User " + user.name + " halted the world."
- # save everyone
- # TODO: probably want a misc function for this
- for each_user in muffvars.userlist:
- each_user.save()
+ # let everyone know
+ muffmisc.broadcast(message)
+ muffmisc.log(message)
# set a flag to terminate the world
muffvars.terminate_world = True
def command_reload(user, command="", parameters=""):
"""Reload all code modules, configs and data."""
- # let the user know
+ # let the user know and log
user.send("Reloading all code modules, configs and data.")
+ muffmisc.log("User " + user.name + " reloaded the world.")
# set a flag to reload
muffvars.reload_modules = True
"""Quit the world."""
user.state = "disconnecting"
+def command_time(user, command="", parameters=""):
+ """Show the current world time in elapsed increments."""
+ user.send(muffmisc.repr_long(muffmisc.getlong(muffvars.variable_data,
+ "time", "elapsed")) + " increments elapsed since the world was created.")
+
def command_help(user, command="", parameters=""):
"""List available commands and provide help for commands."""
def command_say(user, command="", parameters=""):
"""Speak to others in the same room."""
+ # check for replacement macros
+ if muffmisc.replace_macros(user, parameters, True) != parameters:
+ user.send("You cannot speak $_(replacement macros).")
+
# the user entered a message
- if parameters:
+ elif parameters:
# get rid of quote marks on the ends of the message and
# capitalize the first letter
message = parameters.strip("\"'`").capitalize()
- # exclaim because the message ended in an exclamation mark
- # TODO: use the ends() function instead of an index throughout
- if message[-1] == "!":
- action = "exclaim"
+ # a dictionary of punctuation:action pairs
+ actions = {}
+ for option in muffconf.config_data.options("language"):
+ if option.startswith("punctuation_"):
+ action = option.split("_")[1]
+ for mark in muffconf.config_data.get("language", option).split():
+ actions[mark] = action
- # begin because the message ended in miscellaneous punctuation
- elif message[-1] in [ ",", "-", ":", ";" ]:
- action = "begin"
+ # set the default action
+ action = actions[muffconf.config_data.get("language", "default_punctuation")]
- # muse because the message ended in an ellipsis
- elif message[-3:] == "...":
- action = "muse"
+ # match the punctuation used, if any, to an action
+ default_punctuation = muffconf.config_data.get("language", "default_punctuation")
+ for mark in actions.keys():
+ if message.endswith(mark) and mark != default_punctuation:
+ action = actions[mark]
+ break
- # ask because the message ended in a question mark
- elif message[-1] == "?":
- action = "ask"
-
- # say because the message ended in a singular period
- else:
- action = "say"
- if message.endswith("."):
- message += "."
+ # if the action is default and there is no mark, add one
+ if action == actions[default_punctuation] and not message.endswith(default_punctuation):
+ message += default_punctuation
# capitalize a list of words within the message
- # TODO: move this list to the config
- capitalization = [ "i", "i'd", "i'll" ]
- for word in capitalization:
+ capitalize = muffconf.get("language", "capitalize").split()
+ for word in capitalize:
message = message.replace(" " + word + " ", " " + word.capitalize() + " ")
# tell the room
# muff configuration files use the ini format supported by ConfigParser
import ConfigParser
+# need os for testing whether the config file exists and is readable
+import os
+
# hack to load all modules in teh muff package
import muff
for module in muff.__all__:
# name of the config file
config_name = "muff.conf"
-# build a list of possible config files
-config_files = []
+# find the config file
for each_dir in config_dirs:
- config_files.append(each_dir + "/" + config_name)
+ config_file = each_dir + "/" + config_name
+ if os.access(config_file, os.R_OK): break
# read the config
config_data = ConfigParser.SafeConfigParser()
-config_data.read(config_files)
+config_data.read(config_file)
+
+def get(section, option):
+ """Convenience function to get configuration data."""
+ return config_data.get(section, option)
+
+def getfloat(section, option):
+ "Convenience function to get floating-point configuration data."""
+ return config_data.getfloat(section, option)
+
+def getint(section, option):
+ """Convenience function to get integer configuration data."""
+ return config_data.getint(section, option)
+
+def set(section, option, value):
+ """Convenienve function to set miscellaneous configuration data."""
+ return muffmisc.setlong(config_data, section, option, value)
def main():
"""The main loop."""
- # loop indefinitely while the world is not flagged for termination
- while not muffvars.terminate_world:
-
- # open the listening socket if it hasn't been already
- if not muffvars.newsocket: muffsock.initialize_server_socket()
-
- # pause for a configurable amount of time (decimal seconds)
- time.sleep(muffconf.config_data.getfloat("general", "increment"))
+ # loop indefinitely while the world is not flagged for termination or
+ # there are connected users
+ while not muffvars.terminate_world or muffvars.userlist:
# the world was flagged for a reload of all code/data
if muffvars.reload_modules:
for module in muff.__all__:
exec("reload(muff." + module + ")")
+ # move data into new persistent objects
+ muffmisc.reload_data()
+
# reset the reload flag
muffvars.reload_modules = False
- # assign a user if a new connection is waiting
- user = muffsock.check_for_connection(muffvars.newsocket)
-
- # there was a new connection
- if user:
-
- # welcome to the user list
- muffvars.userlist.append(user)
-
- # make a note of it
- muffmisc.log(str(len(muffvars.userlist)) + " connection(s)")
-
- # iterate over the connected users
- for each_user in muffvars.userlist:
-
- # show the user a menu as needed
- each_user.show_menu()
-
- # disconnect users with the appropriate state
- if each_user.state == "disconnecting":
-
- # save to cold storage
- each_user.save()
-
- # close the connection
- each_user.connection.close()
-
- # remove from the list
- each_user.remove()
-
- else:
-
- # check for some input
- # TODO: make a separate function for this
- try:
- input_data = each_user.connection.recv(1024)
- except:
- input_data = ""
- # we got something
- if input_data:
-
- # tack this on to any previous partial
- each_user.partial_input += input_data
-
- # the held input ends in a newline
- if each_user.partial_input[-1] == "\n":
-
- # filter out non-printables
- each_user.partial_input = filter(lambda x: x>=' ' and x<='~', each_user.partial_input)
-
- # strip off extra whitespace
- each_user.partial_input = string.strip(each_user.partial_input)
-
- # put on the end of the queue
- each_user.input_queue.append(each_user.partial_input)
-
- # reset the held partial input
- each_user.partial_input = ""
-
- # pass first item in the input
- # queue to the main handler
- muffcmds.handle_user_input(each_user, each_user.input_queue[0])
-
- # then remove it from the queue
- each_user.input_queue.remove(each_user.input_queue[0])
+ # do what needs to be done on each pulse
+ muffmisc.on_pulse()
- # the loop has terminated, so tear down all sockets
- # TODO: move the save from command_halt() to here
- muffsock.destroy_all_sockets()
+ # the loop has terminated, so save persistent variables
+ muffvars.save()
# log a final message
muffmisc.log("Shutting down now.")
# see if the menupath can be retrieved from muffconf
try:
- if muffconf.config_data.get("files", "menus"): pass
+ if muffconf.get("files", "menus"): pass
# otherwise, reload muffconf
except AttributeError:
reload(muffconf)
-# build a list of files in the menus directory
+# now we can safely nab the menu path setting and build a list of data files
+menu_path = muffconf.get("files", "menus")
+menu_files_index = ConfigParser.SafeConfigParser()
+menu_files_index.read(menu_path + "/index")
menu_files = []
-menu_path = muffconf.config_data.get("files", "menus")
-for each_file in os.listdir(menu_path):
+for each_file in menu_files_index.get("index", "files").split():
menu_files.append(menu_path + "/" + each_file)
# read the menu files
# used by several functions for random calls
import random
+# used to match the 'L' at the end of a long int in repr_long
+import re
+
# random_name uses string.strip
import string
def broadcast(message):
"""Send a message to all connected users."""
- for each_user in muffvars.userlist: each_user.send(message)
+ for each_user in muffvars.userlist: each_user.send("$(eol)" + message)
def log(message):
"""Log a message."""
# strip any leading quotemark, capitalize and return the name
return string.strip(name, "'").capitalize()
+def repr_long(value):
+ string_value = repr(value)
+ if re.match('\d*L$', string_value): return string_value.strip("L")
+ else: return string_value
+
+def getlong(config, section, option):
+ return int(config.get(section, option).strip("L"))
+
+def setlong(config, section, option, value):
+ return config.set(section, option, repr_long(value))
+
+def replace_macros(user, text, is_input=False):
+ while True:
+ macro_start = string.find(text, "$(")
+ if macro_start == -1: break
+ macro_end = string.find(text, ")", macro_start) + 1
+ macro = text[macro_start:macro_end]
+ if macro in muffvars.macros.keys():
+ text = string.replace(text, macro, muffvars.macros[macro])
+
+ # the user's account name
+ elif macro == "$(account)":
+ text = string.replace(text, macro, user.name)
+
+ # if we get here, log and replace it with null
+ else:
+ text = string.replace(text, macro, "")
+ if not is_input:
+ log("Unexpected replacement macro " + macro + " encountered.")
+
+ # replace the look-like-a-macro sequence
+ text = string.replace(text, "$_(", "$(")
+
+ return text
+
+def check_time(frequency):
+ """Check for a factor of the current increment count."""
+ if type(frequency) is str:
+ frequency = muffconf.getint("time", frequency)
+ return not getlong(muffvars.variable_data, "time", "elapsed") % frequency
+
+def on_pulse():
+ """The things which should happen on each pulse, aside from reloads."""
+
+ # open the listening socket if it hasn't been already
+ if not muffvars.newsocket: muffsock.initialize_server_socket()
+
+ # assign a user if a new connection is waiting
+ user = muffsock.check_for_connection(muffvars.newsocket)
+ if user: muffvars.userlist.append(user)
+
+ # iterate over the connected users
+ for user in muffvars.userlist: user.pulse()
+
+ # update the log every now and then
+ if check_time("frequency_log"):
+ log(repr(len(muffvars.userlist)) + " connection(s)")
+
+ # periodically save everything
+ if check_time("frequency_save"):
+ muffvars.save()
+ for user in muffvars.userlist: user.save()
+
+ # pause for a configurable amount of time (decimal seconds)
+ time.sleep(muffconf.getfloat("time", "increment"))
+
+ # increment the elapsed increment counter
+ setlong(muffvars.variable_data, "time", "elapsed",
+ getlong(muffvars.variable_data, "time", "elapsed") + 1)
+
+def reload_data():
+ """Reload data into new persistent objects."""
+
+ # reload the users
+ temporary_userlist = []
+ for user in muffvars.userlist: temporary_userlist.append(user)
+ for user in temporary_userlist: user.reload()
+ del(temporary_userlist)
+
connection.setblocking(0)
# create a new user object
- user = muffuser.User();
+ user = muffuser.User()
# associate this connection with it
user.connection = connection
newsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# bind the socket to to our desired server ipa and port
- newsocket.bind((muffconf.config_data.get("network", "host"), muffconf.config_data.getint("network", "port")))
+ newsocket.bind((muffconf.get("network", "host"), muffconf.getint("network", "port")))
# disable blocking so we can proceed whether or not we can send/receive
newsocket.setblocking(0)
# store this in a globally-accessible place
muffvars.newsocket = newsocket
-def destroy_all_sockets():
- """Go through all connected users and close their sockets."""
-
- # note that we're closing all sockets
- muffmisc.log("Closing remaining connections...")
-
- # iterate over each connected user and close their associated sockets
- for user in muffvars.userlist:
- user.connection.close()
-
# an object containing persistent account data
self.record = ConfigParser.SafeConfigParser()
+ def quit(self):
+ """Log, save, close the connection and remove."""
+ if self.name: message = "User " + self.name
+ else: message = "An unnamed user"
+ message += " logged out."
+ muffmisc.log(message)
+ self.save()
+ self.connection.close()
+ self.remove()
+
+ def reload(self):
+ """Save, load a new user and relocate the connection."""
+
+ # unauthenticated connections get the boot
+ if not self.authenticated:
+ muffmisc.log("An unauthenticated user was disconnected during reload.")
+ self.state = "disconnecting"
+
+ # authenticated users
+ else:
+
+ # save and get out of the list
+ self.save()
+ self.remove()
+
+ # create a new user object
+ new_user = muffuser.User()
+
+ # give it the same name
+ new_user.name = self.name
+
+ # load from file
+ new_user.load()
+
+ # set everything else equivalent
+ new_user.address = self.address
+ new_user.last_address = self.last_address
+ new_user.connection = self.connection
+ new_user.authenticated = self.authenticated
+ new_user.password_tries = self.password_tries
+ new_user.state = self.state
+ new_user.menu_seen = self.menu_seen
+ new_user.error = self.error
+ new_user.input_queue = self.input_queue
+ new_user.output_queue = self.output_queue
+ new_user.partial_input = self.partial_input
+ new_user.echoing = self.echoing
+
+ # add it to the list
+ muffvars.userlist.append(new_user)
+
+ # get rid of the old user object
+ del(self)
+
+ def replace_old_connections(self):
+ """Disconnect active users with the same name."""
+
+ # the default return value
+ return_value = False
+
+ # iterate over each user in the list
+ for old_user in muffvars.userlist:
+
+ # the name is the same but it's not us
+ if old_user.name == self.name and old_user is not self:
+
+ # make a note of it
+ muffmisc.log("User " + self.name + " reconnected--closing old connection to " + old_user.address + ".")
+ old_user.send("$(eol)$(red)New connection from " + self.address + ". Terminating old connection...$(nrm)$(eol)")
+ self.send("$(eol)$(red)Taking over old connection from " + old_user.address + ".$(nrm)")
+
+ # close the old connection
+ old_user.connection.close()
+
+ # replace the old connection with this one
+ old_user.connection = self.connection
+ old_user.last_address = old_user.address
+ old_user.address = self.address
+ old_user.echoing = self.echoing
+
+ # take this one out of the list and delete
+ self.remove()
+ del(self)
+ return_value = True
+ break
+
+ # true if an old connection was replaced, false if not
+ return return_value
+
+ def authenticate(self):
+ """Flag the user as authenticated and disconnect duplicates."""
+ if not self.state is "authenticated":
+ muffmisc.log("User " + self.name + " logged in.")
+ self.authenticated = True
+
def load(self):
"""Retrieve account data from cold storage."""
# what the filename for the user account should be
- filename = muffconf.config_data.get("files", "accounts") + "/" + self.name
+ filename = muffconf.get("files", "accounts") + "/" + self.name
# try to load the password hash and last connection ipa
try:
"""Retrieve the user's account password hash from storage."""
# what the filename for the user account could be
- filename = muffconf.config_data.get("files", "accounts") + "/" + self.proposed_name
+ filename = muffconf.get("files", "accounts") + "/" + self.proposed_name
# create a temporary account record object
temporary_record = ConfigParser.SafeConfigParser()
self.record.set("account", "last_address", self.address)
# the account files live here
- account_path = muffconf.config_data.get("files", "accounts")
+ account_path = muffconf.get("files", "accounts")
# the filename to which we'll write
filename = account_path + "/" + self.name.lower()
# if the directory doesn't exist, create it
- # TODO: create account_path with 0700 perms
try:
if os.listdir(account_path): pass
except:
- os.mkdir(account_path, )
+ os.mkdir(account_path)
# open the user account file for writing
- # TODO: create filename with 0600 perms
record_file = file(filename, "w")
# dump the account data to it
self.record.write(record_file)
# close the user account file
+ record_file.flush()
record_file.close()
+ # set the permissions to 0600
+ os.chmod(filename, 0600)
+
def show_menu(self):
"""Send the user their current menu."""
self.send(muffmenu.get_menu(self))
# start with a newline, append the message, then end
# with the optional eol string passed to this function
- output = "$(eol)" + output + eol
-
- # replace eol markers with a crlf
- # TODO: search for markers and replace from a dict
- output = string.replace(output, "$(eol)", "\r\n")
+ # and the ansi escape to return to normal text
+ output = "\r\n" + output + eol + chr(27) + "[0m"
- # replace display markers with ansi escapse sequences
- output = string.replace(output, "$(bld)", chr(27)+"[1m")
- output = string.replace(output, "$(nrm)", chr(27)+"[0m")
- output = string.replace(output, "$(blk)", chr(27)+"[30m")
- output = string.replace(output, "$(grn)", chr(27)+"[32m")
- output = string.replace(output, "$(red)", chr(27)+"[31m")
-
- # the user's account name
- output = string.replace(output, "$(account)", self.name)
+ # 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
except:
pass
+ def pulse(self):
+ """All the things to do to the user per increment."""
+
+ # if the world is terminating, disconnect
+ if muffvars.terminate_world:
+ self.state = "disconnecting"
+ self.menu_seen = False
+
+ # show the user a menu as needed
+ self.show_menu()
+
+ # disconnect users with the appropriate state
+ if self.state == "disconnecting":
+ self.quit()
+
+ # the user is unique and not flagged to disconnect
+ else:
+
+ # check for input and add it to the queue
+ self.enqueue_input()
+
+ # there is input waiting in the queue
+ if self.input_queue: muffcmds.handle_user_input(self)
+
+ def enqueue_input(self):
+ """Process and enqueue any new input."""
+
+ # check for some input
+ try:
+ input_data = self.connection.recv(1024)
+ except:
+ input_data = ""
+
+ # we got something
+ if input_data:
+
+ # tack this on to any previous partial
+ self.partial_input += input_data
+
+ # separate multiple input lines
+ new_input_lines = self.partial_input.split("\n")
+
+ # if input doesn't end in a newline, replace the
+ # held partial input with the last line of it
+ if not self.partial_input.endswith("\n"):
+ self.partial_input = new_input_lines.pop()
+
+ # otherwise, chop off the extra null input and reset
+ # the held partial input
+ else:
+ new_input_lines.pop()
+ self.partial_input = ""
+
+ # iterate over the remaining lines
+ for line in new_input_lines:
+
+ # filter out non-printables
+ line = filter(lambda x: x>=' ' and x<='~', line)
+
+ # strip off extra whitespace
+ line = line.strip()
+
+ # put on the end of the queue
+ self.input_queue.append(line)
+
# 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
+
# hack to load all modules in the muff package
import muff
for module in muff.__all__:
exec("import " + module)
+# does the files:variable setting exist yet?
+try:
+ if muffconf.get("files", "variable"): pass
+
+# if not, reload the muffconf module
+except AttributeError:
+ reload(muffconf)
+
+# now we can safely load persistent variables from file
+variable_file = muffconf.get("files", "variable")
+variable_data = ConfigParser.SafeConfigParser()
+variable_data.read(variable_file)
+
# if there is no userlist, create an empty one
try:
if userlist: pass
# flag to raise when all code modules, config and data should be reloaded
reload_modules = False
+# a dict of replacement macros
+macros = {
+ "$(eol)": "\r\n",
+ "$(bld)": chr(27) + "[1m",
+ "$(nrm)": chr(27) + "[0m",
+ "$(blk)": chr(27) + "[30m",
+ "$(grn)": chr(27) + "[32m",
+ "$(red)": chr(27) + "[31m"
+ }
+
+# function to save persistent variables to file
+def save():
+ file_descriptor = open(variable_file, "w")
+ variable_data.write(file_descriptor)
+ file_descriptor.flush()
+ file_descriptor.close()
+
commands = ./lib/commands
menus = ./lib/menus
modules = ./lib
+variable = ./lib/save/variable
[general]
-increment = 0.1
password_tries = 3
+[language]
+capitalize = i i'd i'll i'm
+default_punctuation = .
+punctuation_ask = ?
+punctuation_begin = , - : ;
+punctuation_exclaim = !
+punctuation_muse = ...
+punctuation_say = .
+
[network]
-host =
+host =
port = 6669
+[time]
+definition_d = 24h
+definition_h = 60mi
+definition_mi = 10r
+definition_mo = 28d
+definition_r = 6
+definition_w = 7d
+definition_y = 12mo
+frequency_log = 6000
+frequency_save = 600
+increment = 0.1
+