X-Git-Url: https://mudpy.org/gitweb?p=mudpy.git;a=blobdiff_plain;f=mudpy.py;h=06563f737175a986c746f9f4bced1dd8329fda7e;hp=9f5cd1d07a46e8df7d46601dc2a25643262537de;hb=674e6c23db39fd68ab2d781d20389a1a65bb8089;hpb=03997fe4497ea4ebaaf35d29e331615d438b56de 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."""