X-Git-Url: https://mudpy.org/gitweb?p=mudpy.git;a=blobdiff_plain;f=mudpy.py;h=699eae147751906aa8b76c8c96e3dd3509e5e634;hp=01b914293971b7b412a08d391eb77a3444e794f2;hb=a89c4b2060bf3c93207a659d88f51a4bf0f891fa;hpb=7e176424017ddeeb66e2d6828fd0fd8f5479b5cc diff --git a/mudpy.py b/mudpy.py index 01b9142..699eae1 100644 --- a/mudpy.py +++ b/mudpy.py @@ -1,8 +1,8 @@ """Core objects for the mudpy engine.""" -# Copyright (c) 2005, 2006 Jeremy Stanley . All rights -# reserved. Licensed per terms in the LICENSE file distributed with this -# software. +# Copyright (c) 2004-2008 Jeremy Stanley . Permission +# to use, copy, modify, and distribute this software is granted under +# terms provided in the LICENSE file distributed with this software. # import some things we need from ConfigParser import RawConfigParser @@ -508,6 +508,7 @@ class User: self.error = "" self.input_queue = [] self.last_address = "" + self.last_input = universe.get_time() self.menu_choices = {} self.menu_seen = False self.negotiation_pause = 0 @@ -530,6 +531,27 @@ class User: self.connection.close() self.remove() + def check_idle(self): + """Warn or disconnect idle users as appropriate.""" + idletime = universe.get_time() - self.last_input + linkdead_dict = universe.categories["internal"]["time"].getdict("linkdead") + if self.state in linkdead_dict: linkdead_state = self.state + else: linkdead_state = "default" + if idletime > linkdead_dict[linkdead_state]: + self.send("$(eol)$(red)You've done nothing for far too long... goodbye!$(nrm)$(eol)", flush=True, add_prompt=False) + logline = "Disconnecting " + if self.account and self.account.get("name"): logline += self.account.get("name") + else: logline += "an unknown user" + logline += " after idling too long in a " + self.state + " state." + log(logline, 2) + self.state = "disconnecting" + self.menu_seen = False + idle_dict = universe.categories["internal"]["time"].getdict("idle") + if self.state in idle_dict: idle_state = self.state + else: idle_state = "default" + if idletime == idle_dict[idle_state]: + self.send("$(eol)$(red)If you continue to be unproductive, you'll be shown the door...$(nrm)$(eol)") + def reload(self): """Save, load a new user and relocate the connection.""" @@ -677,6 +699,9 @@ class User: self.state = "disconnecting" self.menu_seen = False + # check for an idle connection and act appropriately + else: self.check_idle() + # if output is paused, decrement the counter if self.state == "initial": if self.negotiation_pause: self.negotiation_pause -= 1 @@ -992,21 +1017,40 @@ def wrap_ansi_text(text, width): # ignoring color escape sequences relative_position = 0 + # whether the current character is part of a telnet IAC sequence + iac_counter = 0 + # whether the current character is part of a color escape sequence escape = False # iterate over each character from the begining of the text for each_character in text: + # the current character is the telnet IAC character + if each_character == IAC and not iac_counter: + iac_counter = 2 + + # the current character is within an IAC sequence + elif iac_counter: + + # the current character is another IAC, + # terminating the sequence + if each_character == IAC: + iac_counter = 0 + + # otherwise, decrement the IAC counter + else: + iac_counter -= 1 + # the current character is the escape character - if each_character == chr(27): + elif each_character == chr(27) and not escape: escape = True # the current character is within an escape sequence elif escape: # the current character is m, which terminates the - # current escape sequence + # escape sequence if each_character == "m": escape = False @@ -1017,7 +1061,7 @@ def wrap_ansi_text(text, width): # the current character meets the requested maximum line width, # so we need to backtrack and find a space at which to wrap - elif relative_position == width: + elif relative_position == width and not each_character == "\r": # distance of the current character examined from the # relative position @@ -1092,56 +1136,75 @@ def random_name(): def replace_macros(user, text, is_input=False): """Replaces macros in text output.""" + # third person pronouns + pronouns = { + "female": { "obj": "her", "pos": "hers", "sub": "she" }, + "male": { "obj": "him", "pos": "his", "sub": "he" }, + "neuter": { "obj": "it", "pos": "its", "sub": "it" } + } + + # a dict of replacement macros + macros = { + "eol": "\r\n", + "bld": chr(27) + "[1m", + "nrm": chr(27) + "[0m", + "blk": chr(27) + "[30m", + "blu": chr(27) + "[34m", + "cyn": chr(27) + "[36m", + "grn": chr(27) + "[32m", + "mgt": chr(27) + "[35m", + "red": chr(27) + "[31m", + "yel": chr(27) + "[33m", + } + + # add dynamic macros where possible + if user.account: + account_name = user.account.get("name") + if account_name: + macros["account"] = account_name + if user.avatar: + avatar_gender = user.avatar.get("gender") + if avatar_gender: + macros["tpop"] = pronouns[avatar_gender]["obj"] + macros["tppp"] = pronouns[avatar_gender]["pos"] + macros["tpsp"] = pronouns[avatar_gender]["sub"] + # loop until broken while True: - # third person pronouns - pronouns = { - "female": { "obj": "her", "pos": "hers", "sub": "she" }, - "male": { "obj": "him", "pos": "his", "sub": "he" }, - "neuter": { "obj": "it", "pos": "its", "sub": "it" } - } - - # a dict of replacement macros - macros = { - "$(eol)": "\r\n", - "$(bld)": chr(27) + "[1m", - "$(nrm)": chr(27) + "[0m", - "$(blk)": chr(27) + "[30m", - "$(blu)": chr(27) + "[34m", - "$(cyn)": chr(27) + "[36m", - "$(grn)": chr(27) + "[32m", - "$(mgt)": chr(27) + "[35m", - "$(red)": chr(27) + "[31m", - "$(yel)": chr(27) + "[33m", - } - - # add dynamic macros where possible - if user.account: - account_name = user.account.get("name") - if account_name: - macros["$(account)"] = account_name - if user.avatar: - avatar_gender = user.avatar.get("gender") - if avatar_gender: - macros["$(tpop)"] = pronouns[avatar_gender]["obj"] - macros["$(tppp)"] = pronouns[avatar_gender]["pos"] - macros["$(tpsp)"] = pronouns[avatar_gender]["sub"] - # find and replace per the macros dict macro_start = text.find("$(") if macro_start == -1: break macro_end = text.find(")", macro_start) + 1 - macro = text[macro_start:macro_end] + macro = text[macro_start+2:macro_end-1] if macro in macros.keys(): - text = text.replace(macro, macros[macro]) + replacement = macros[macro] + + # this is how we handle local file inclusion (dangerous!) + elif macro.startswith("inc:"): + incfile = path_join(universe.startdir, macro[4:]) + if exists(incfile): + incfd = file(incfile) + replacement = "" + for line in incfd: + if line.endswith("\n") and not line.endswith("\r\n"): + line = line.replace("\n", "\r\n") + replacement += line + # lose the trailing eol + replacement = replacement[:-2] + else: + replacement = "" + log("Couldn't read included " + incfile + " file.", 6) # if we get here, log and replace it with null else: - text = text.replace(macro, "") + replacement = "" if not is_input: log("Unexpected replacement macro " + macro + " encountered.", 6) + # and now we act on the replacement + text = text.replace("$(" + macro + ")", replacement) + # replace the look-like-a-macro sequence text = text.replace("$_(", "$(") @@ -1410,6 +1473,9 @@ def handle_user_input(user): # since we got input, flag that the menu/prompt needs to be redisplayed user.menu_seen = False + # update the last_input timestamp while we're at it + user.last_input = universe.get_time() + def generic_menu_handler(user): """A generic menu choice handler."""