From 674e6c23db39fd68ab2d781d20389a1a65bb8089 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Mon, 10 Jul 2006 03:30:46 +0000 Subject: [PATCH] Imported from archive. * LICENSE, mudpy, mudpy.py: Altered the copyright statements to correctly mention all years instead of only the most recent. * command (command:chat, command:say), mydpy.py (User.send) (command_chat, first_word, handler_active): Added a chat command which toggles the avatar in and out of a mode where all further lines of input are passed as parameters to the say command. * command (command:quit): Minor clarifications to several command help texts. * command (command:say), mudpy.conf (internal:language), mudpy.py (command_say): Moved the capitalize_words list facet to a more flexible typos dict, and condensed punctuation_* facets into an actions dict facet. * mudpy.py (User.deactivate_avatar): Fixed a bug where avatars without a location could trigger an exception. (command_help): Added a see_also list facet for menu elements, which allow help output to suggest other related commands. (create_pidfile, remove_pidfile): Minor adjustments to assure file path canonicalization. (get_loglines): Aesthetic tweaks to the show log output. (on_pulse): Create internal:counters if it doesn't exist, to avoid throwing an exception. --- LICENSE | 3 +- command | 11 ++++- mudpy | 5 ++- mudpy.conf | 10 ++--- mudpy.py | 140 ++++++++++++++++++++++++++++++++++++++++++------------------- 5 files changed, 115 insertions(+), 54 deletions(-) diff --git a/LICENSE b/LICENSE index 901a49d..c1ce24f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ -Copyright (c) 2006 mudpy, Jeremy Stanley , all rights reserved. +Copyright (c) 2005, 2006 Jeremy Stanley . All rights +reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/command b/command index 409cbcf..c5499c9 100644 --- a/command +++ b/command @@ -1,6 +1,12 @@ [__control__] read_only = yes +[command:chat] +action = command_chat(actor) +description = Enter and leave chat mode. +help = The chat command toggles chat mode. When in chat mode, all input is passed as a parameter to the say command, unless prepended by an exclamation mark (!). For example, to leave chat mode, use:$(eol)$(eol) !chat +see_also = say + [command:create] action = command_create(actor, parameters) administrative = yes @@ -43,7 +49,7 @@ help = You move in a direction by entering:$(eol)$(eol) move north [command:quit] action = command_quit(actor) description = Leave Example. -help = This will save your account and disconnect your client connection. +help = This will deactivate your avatar and return you to the main menu. [command:reload] action = command_reload(actor) @@ -54,7 +60,8 @@ help = This will reload all python modules and read-only data files. [command:say] action = command_say(actor, parameters) 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. +help = This allows you to speak to other characters within the same room. If you end your sentence with punctuation, the message displayed will incorporate an appropriate action (ask, exclaim, et cetera). It will also correct common typographical errors, add punctuation and capitalize your sentence as needed (assuming you speak one sentence per line). For example:$(eol)$(eol) > say youre sure i went teh wrong way?$(eol) You ask, "You're sure I went the wrong way?" +see_also = chat [command:set] action = command_set(actor, parameters) diff --git a/mudpy b/mudpy index 7a5d5b2..519f07b 100755 --- a/mudpy +++ b/mudpy @@ -1,8 +1,9 @@ #!/usr/bin/python """Skeletal executable for the mudpy engine.""" -# Copyright (c) 2006 mudpy, Jeremy Stanley , all rights reserved. -# Licensed per terms in the LICENSE file distributed with this software. +# Copyright (c) 2005, 2006 Jeremy Stanley . All rights +# reserved. Licensed per terms in the LICENSE file distributed with this +# software. # core objects for the mudpy engine import mudpy diff --git a/mudpy.conf b/mudpy.conf index 0ef7482..8fa0876 100644 --- a/mudpy.conf +++ b/mudpy.conf @@ -5,13 +5,9 @@ private_files = account read_only = yes [internal:language] -capitalize_words = [ "i", "i'd", "i'll", "i'm" ] +actions = { "?": "ask", ",": "begin", "-": "begin", ":": "begin", ";": "begin", "!": "exclaim", "...": "muse", ".": "say" } default_punctuation = . -punctuation_ask = ? -punctuation_begin = [ ",", "-", ":", ";" ] -punctuation_exclaim = ! -punctuation_muse = ... -punctuation_say = . +typos = { "i": "I", "i'd": "I'd", "i'll": "I'll", "i'm": "I'm", "teh": "the", "theyre": "they're", "youre": "you're" } [internal:limits] #default_admins = admin @@ -22,7 +18,7 @@ password_tries = 3 [internal:logging] #file = mudpy.log max_log_lines = 1000 -#stdout = yes +stdout = yes #syslog = mudpy [internal:network] diff --git a/mudpy.py b/mudpy.py index 9f5cd1d..06563f7 100644 --- a/mudpy.py +++ b/mudpy.py @@ -1,7 +1,8 @@ """Core objects for the mudpy engine.""" -# Copyright (c) 2006 mudpy, Jeremy Stanley , all rights reserved. -# Licensed per terms in the LICENSE file distributed with this software. +# Copyright (c) 2005, 2006 Jeremy Stanley . All rights +# reserved. Licensed per terms in the LICENSE file distributed with this +# software. # import some things we need from ConfigParser import RawConfigParser @@ -13,6 +14,7 @@ from re import match from signal import SIGHUP, SIGTERM, signal from socket import AF_INET, SO_REUSEADDR, SOCK_STREAM, SOL_SOCKET, socket from stat import S_IMODE, ST_MODE +from string import digits, letters, punctuation, uppercase from sys import argv, stderr from syslog import LOG_PID, LOG_INFO, LOG_DAEMON, closelog, openlog, syslog from telnetlib import DO, DONT, ECHO, EOR, GA, IAC, LINEMODE, SB, SE, SGA, WILL, WONT @@ -449,6 +451,7 @@ class Universe: element.clean_contents() def new(self): + """Create a new, empty Universe (the Big Bang).""" new_universe = Universe() for attribute in vars(self).keys(): exec("new_universe." + attribute + " = self." + attribute) @@ -646,7 +649,10 @@ class User: # tack on a prompt if active if self.state == "active": if not just_prompt: output += "$(eol)" - if add_prompt: output += "> " + if add_prompt: + output += "> " + mode = self.avatar.get("mode") + if mode: output += "(" + mode + ") " # find and replace macros in the output output = replace_macros(self, output) @@ -856,10 +862,11 @@ class User: """Have the active avatar leave the world.""" if self.avatar: current = self.avatar.get("location") - self.avatar.set("default_location", current) - self.avatar.echo_to_location("You suddenly wonder where " + self.avatar.get("name") + " went.") - del universe.contents[current].contents[self.avatar.key] - self.avatar.remove_facet("location") + if current: + self.avatar.set("default_location", current) + self.avatar.echo_to_location("You suddenly wonder where " + self.avatar.get("name") + " went.") + del universe.contents[current].contents[self.avatar.key] + self.avatar.remove_facet("location") self.avatar.owner = None self.avatar = None @@ -892,8 +899,6 @@ def log(message, level=0): # a couple references we need file_name = universe.categories["internal"]["logging"].get("file") - if not isabs(file_name): - file_name = path_join(universe.startdir, file_name) max_log_lines = universe.categories["internal"]["logging"].getint("max_log_lines") syslog_name = universe.categories["internal"]["logging"].get("syslog") timestamp = asctime()[4:19] @@ -903,6 +908,8 @@ def log(message, level=0): # send the timestamp and line to a file if file_name: + if not isabs(file_name): + file_name = path_join(universe.startdir, file_name) file_descriptor = file(file_name, "a") for line in lines: file_descriptor.write(timestamp + " " + line + "\n") file_descriptor.flush() @@ -957,8 +964,8 @@ def get_loglines(level, start, stop): message = "There are " + str(total_count) message += " log lines in memory and " + str(filtered_count) message += " at or above level " + str(level) + "." - message += " The lines from " + str(stop) + " to " + str(start) - message += " are:$(eol)$(eol)" + message += " The matching lines from " + str(stop) + " to " + message += str(start) + " are:$(eol)$(eol)" # add the text from the selected lines if stop > 1: range_lines = loglines[-start:-(stop-1)] @@ -1144,6 +1151,13 @@ def escape_macros(text): """Escapes replacement macros in text.""" return text.replace("$(", "$_(") +def first_word(text, separator=" "): + """Returns a tuple of the first word and the rest.""" + if text: + if text.find(separator) > 0: return text.split(separator, 1) + else: return text, "" + else: return "", "" + def on_pulse(): """The things which should happen on each pulse, aside from reloads.""" @@ -1158,6 +1172,10 @@ def on_pulse(): # iterate over the connected users for user in universe.userlist: user.pulse() + # add an element for counters if it doesn't exist + if not "counters" in universe.categories["internal"]: + universe.categories["internal"]["counters"] = Element("internal:counters", universe) + # update the log every now and then if not universe.categories["internal"]["counters"].getint("mark"): log(str(len(universe.userlist)) + " connection(s)") @@ -1523,12 +1541,16 @@ def handler_active(user): # is there input? if input_data: - # split out the command (first word) and parameters (everything else) - if input_data.find(" ") > 0: - command_name, parameters = input_data.split(" ", 1) + # split out the command and parameters + actor = user.avatar + mode = actor.get("mode") + if mode and input_data.startswith("!"): + command_name, parameters = first_word(input_data[1:]) + elif mode == "chat": + command_name = "say" + parameters = input_data else: - command_name = input_data - parameters = "" + command_name, parameters = first_word(input_data) # lowercase the command command_name = command_name.lower() @@ -1539,7 +1561,6 @@ def handler_active(user): else: command = None # if it's allowed, do it - actor = user.avatar if actor.can_run(command): exec(command.get("action")) # otherwise, give an error @@ -1608,6 +1629,24 @@ def command_help(actor, parameters): help_text = "No help is provided for this command." output += help_text + # list related commands + see_also = command.getlist("see_also") + if see_also: + really_see_also = "" + for item in see_also: + if item in universe.categories["command"]: + command = universe.categories["command"][item] + if actor.can_run(command): + if really_see_also: + really_see_also += ", " + if command.getboolean("administrative"): + really_see_also += "$(red)" + else: + really_see_also += "$(grn)" + really_see_also += item + "$(nrm)" + if really_see_also: + output += "$(eol)$(eol)See also: " + really_see_also + # no data for the requested command word else: output = "That is not an available command." @@ -1654,35 +1693,40 @@ def command_say(actor, parameters): # the user entered a message elif parameters: - # get rid of quote marks on the ends of the message and - # capitalize the first letter + # get rid of quote marks on the ends of the message message = parameters.strip("\"'`") - message = message[0].capitalize() + message[1:] - - # a dictionary of punctuation:action pairs - actions = {} - for facet in universe.categories["internal"]["language"].facets(): - if facet.startswith("punctuation_"): - action = facet.split("_")[1] - for mark in universe.categories["internal"]["language"].getlist(facet): - actions[mark] = action # match the punctuation used, if any, to an action + actions = universe.categories["internal"]["language"].getdict("actions") default_punctuation = universe.categories["internal"]["language"].get("default_punctuation") - action = actions[default_punctuation] + action = "" for mark in actions.keys(): - if message.endswith(mark) and mark != default_punctuation: + if message.endswith(mark): action = actions[mark] break - # 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 + # add punctuation if needed + if not action: + action = actions[default_punctuation] + if message and not message[-1] in punctuation: + message += default_punctuation + + # decapitalize the first letter to improve matching + if message and message[0] in uppercase: + message = message[0].lower() + message[1:] + + # iterate over all words in message, replacing typos + typos = universe.categories["internal"]["language"].getdict("typos") + words = message.split() + for index in range(len(words)): + word = words[index] + bare_word = word.strip(punctuation) + if bare_word in typos.keys(): + words[index] = word.replace(bare_word, typos[bare_word]) + message = " ".join(words) - # capitalize a list of words within the message - capitalize_words = universe.categories["internal"]["language"].getlist("capitalize_words") - for word in capitalize_words: - message = message.replace(" " + word + " ", " " + word.capitalize() + " ") + # capitalize the first letter + message = message[0].upper() + message[1:] # tell the room actor.echo_to_location(actor.get("name") + " " + action + "s, \"" + message + "\"") @@ -1692,6 +1736,17 @@ def command_say(actor, parameters): else: actor.send("What do you want to say?") +def command_chat(actor): + """Toggle chat mode.""" + mode = actor.get("mode") + if not mode: + actor.set("mode", "chat") + actor.send("Entering chat mode (use $(grn)!chat$(nrm) to exit).") + elif mode == "chat": + actor.remove_facet("mode") + actor.send("Exiting chat mode.") + else: actor.send("Sorry, but you're already busy with something else!") + def command_show(actor, parameters): """Show program data.""" message = "" @@ -1870,9 +1925,9 @@ def create_pidfile(universe): pid = str(getpid()) log("Process ID: " + pid) file_name = universe.contents["internal:process"].get("pidfile") - if not isabs(file_name): - file_name = path_join(universe.startdir, file_name) if file_name: + if not isabs(file_name): + file_name = path_join(universe.startdir, file_name) file_descriptor = file(file_name, 'w') file_descriptor.write(pid + "\n") file_descriptor.flush() @@ -1881,9 +1936,10 @@ def create_pidfile(universe): def remove_pidfile(universe): """Remove the file containing the current process ID.""" file_name = universe.contents["internal:process"].get("pidfile") - if not isabs(file_name): - file_name = path_join(universe.startdir, file_name) - if file_name and access(file_name, W_OK): remove(file_name) + if file_name: + if not isabs(file_name): + file_name = path_join(universe.startdir, file_name) + if access(file_name, W_OK): remove(file_name) def excepthook(excepttype, value, traceback): """Handle uncaught exceptions.""" -- 2.11.0