Revision: 210
Author: richard.m.tew
Date: Fri Jan 31 01:58:28 2014 UTC
Log: - Lots of old changes I do not recall.
- Rewrite the general RL shell to extract and process escape sequences.
- Rewrite the client detection to only fall back on ENQ if no term type.
- Test and fix support for PuTTY and term.js.
http://code.google.com/p/sorrows-mudlib/source/detail?r=210
Added:
/trunk/bootstrapmud.py
/trunk/bootstraprl.py
/trunk/doc/design/00005 - Inventory and encumbrance.rst
/trunk/games/roguelike/shell.py
/trunk/games/room - simple/commands/consider.py
/trunk/mudlib/container.py
Modified:
/trunk/games/roguelike/shells/login.py
/trunk/games/roguelike/shells/roguelike.py
/trunk/games/roguelike/world/container.py
/trunk/games/roguelike/world/object.py
/trunk/games/room - simple/services/world.py
/trunk/games/room - simple/world/body.py
/trunk/games/room - simple/world/container.py
/trunk/games/room - simple/world/object.py
/trunk/games/room - simple/world/room.py
/trunk/games/room - simple/world/util.py
/trunk/mudlib/body.py
/trunk/mudlib/input.py
/trunk/mudlib/object.py
/trunk/mudlib/services/net/connectionTelnet.py
/trunk/mudlib/shell.py
=======================================
--- /dev/null
+++ /trunk/bootstrapmud.py Fri Jan 31 01:58:28 2014 UTC
@@ -0,0 +1,181 @@
+# Bootstrapping code.
+import sys, os, stackless, gc, logging, traceback, types
+
+dirPath = sys.path[0]
+if not len(dirPath):
+ raise RuntimeError("Expected to find the directory the script was
executed in in sys.path[0], but did not.")
+
+# Add the "contrib" directory to the path.
+contribDirPath = os.path.join(dirPath, "contrib")
+if os.path.exists(contribDirPath) and contribDirPath not in sys.path:
+ sys.path.append(contribDirPath)
+
+import pysupport
+
+STATE_STARTUP = 0
+STATE_RUNNING = 1
+STATE_SHUTDOWN = 2
+
+bootstrapState = STATE_STARTUP
+
+def OnClassCreation(class_):
+ class_.__instances__ = classmethod(pysupport.FindClassInstances)
+ if not hasattr(class_, "__subclasses__"):
+ class_.__subclasses__ = classmethod(pysupport.FindClassSubclasses)
+
+ class_.__events__ = set()
+
+ if bootstrapState != STATE_STARTUP:
+ print "CREATE", class_
+
+ events.ProcessClass(class_)
+
+ if bootstrapState != STATE_STARTUP:
+ events.ClassCreation(class_)
+
+def OnClassUpdate(class_):
+ if bootstrapState != STATE_STARTUP:
+ gc.collect()
+ instances = pysupport.FindInstances(class_)
+ instanceCount = sum(len(l) for l in instances.itervalues())
+ print "UPDATE", class_, instanceCount, "instances"
+
+ events.ProcessClass(class_)
+
+ if bootstrapState != STATE_STARTUP:
+ events.ClassUpdate(class_)
+
+def OnScriptValidation(scriptFile):
+ try:
+ from mudlib import GameCommand
+ except ImportError:
+ return
+
+ # TODO: Make this more generic.
+ for k, v in scriptFile.scriptGlobals.iteritems():
+ if type(v) in (types.ClassType, types.TypeType) and v is not
GameCommand:
+ if issubclass(v, GameCommand) and "Run" in v.__dict__:
+ raise Exception("Subclasses of GameCommand cannot override
Run")
+
+def Run():
+ global bootstrapState
+
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format='%(asctime)s;%(name)s;%(levelname)s;%(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S')
+
+ logging.getLogger().name = "default"
+ logging.getLogger("namespace").setLevel(logging.INFO)
+ logging.getLogger("reloader").setLevel(logging.INFO)
+
+ stackless.getcurrent().block_trap = True
+
+ iniFilename = os.path.join(dirPath, "config.ini")
+ if not os.path.exists(iniFilename):
+ print "Please copy 'config.ini.base' to 'config.ini' and modify it
appropriately."
+ sys.exit(0)
+
+ # Monkey-patch in the stackless-compatible sockets.
+ import stacklesssocket
+ import uthread2
+ import stacklesslib.main as stacklesslibmain
+ stacklesssocket._schedule_func = lambda delay=0:
stacklesslibmain.sleep(delay)
+ stacklesssocket._sleep_func = stacklesslibmain.sleep
+ stacklesssocket.install()
+
+ # Install the global event handler.
+ import __builtin__
+ from events import EventHandler
+ __builtin__.events = EventHandler()
+
+ # Add the "livecoding" contrib directory to the path.
+ livecodingDirPath = os.path.join(dirPath, "contrib", "livecoding")
+ if os.path.exists(livecodingDirPath):
+ sys.path.append(livecodingDirPath)
+
+ # Register the mudlib and game script directories with the livecoding
+ # module. This will compile and execute them all.
+ import reloader
+ gamePath = os.path.join("games", "room - simple")
+ #gamePath = os.path.join("games", "roguelike")
+ gameScriptPath = os.path.join(dirPath, gamePath)
+ mudlibScriptPath = os.path.join(dirPath, "mudlib")
+
+ cr = reloader.CodeReloader()
+ # Register for code reloading updates of managed classes.
+ # Broadcast an event when we receive an update.
+ cr.SetClassCreationCallback(OnClassCreation)
+ cr.SetClassUpdateCallback(OnClassUpdate)
+ cr.SetValidateScriptCallback(OnScriptValidation)
+ cr.AddDirectory("mudlib", mudlibScriptPath)
+ cr.AddDirectory("game", gameScriptPath)
+
+ import imp
+ __builtin__.sorrows = imp.new_module('sorrows')
+
+ from mudlib.services import ServiceService
+ svcSvc = ServiceService()
+ svcSvc.gameScriptPath = gamePath
+ svcSvc.gameDataPath = os.path.join(gamePath, "data")
+ svcSvc.Register()
+ svcSvc.Start()
+ del svcSvc
+
+ stackless.getcurrent().block_trap = False
+ bootstrapState = STATE_RUNNING
+
+ manualShutdown = False
+ try:
+ stacklesslibmain.mainloop.run()
+ except KeyboardInterrupt:
+ print
+ print '** EXITING: Server manually stopping.'
+ print
+
+ if stackless.runcount > 1:
+ print "Scheduled tasklets:", stackless.runcount
+ uthread2.PrintTaskletChain(stackless.current)
+ print
+
+ if False:
+ for entry in stacklesslibmain.event_queue.queue_a:
+ print "Sleeping tasklets:"
+ uthread2.PrintTaskletChain(uthread.yieldChannel.queue)
+ print
+
+ for timestamp, channel in uthread.sleepingTasklets:
+ if channel.queue:
+ print "Sleep channel (%d) tasklets:" % id(channel)
+ uthread2.PrintTaskletChain(channel.queue)
+ print
+
+ manualShutdown = True
+ finally:
+ cr.EndMonitoring()
+
+ bootstrapState = STATE_SHUTDOWN
+
+ if manualShutdown:
+ class HelperClass:
+ def ShutdownComplete(self):
+ stacklesslibmain.mainloop.stop()
+ managerTasklet.kill()
+
+ helper = HelperClass()
+ events.ShutdownComplete.Register(helper.ShutdownComplete)
+
+ stackless.tasklet(sorrows.services.Stop)()
+ # We have most likely killed the stacklesssocket tasklet.
+ managerTasklet = stacklesssocket.StartManager()
+ stacklesslibmain.mainloop.run()
+
+
logging.info("Shutdown complete")
+
+
+if __name__ == '__main__':
+ try:
+ Run()
+ finally:
+ logging.shutdown()
+
=======================================
--- /dev/null
+++ /trunk/bootstraprl.py Fri Jan 31 01:58:28 2014 UTC
@@ -0,0 +1,181 @@
+# Bootstrapping code.
+import sys, os, stackless, gc, logging, traceback, types
+
+dirPath = sys.path[0]
+if not len(dirPath):
+ raise RuntimeError("Expected to find the directory the script was
executed in in sys.path[0], but did not.")
+
+# Add the "contrib" directory to the path.
+contribDirPath = os.path.join(dirPath, "contrib")
+if os.path.exists(contribDirPath) and contribDirPath not in sys.path:
+ sys.path.append(contribDirPath)
+
+import pysupport
+
+STATE_STARTUP = 0
+STATE_RUNNING = 1
+STATE_SHUTDOWN = 2
+
+bootstrapState = STATE_STARTUP
+
+def OnClassCreation(class_):
+ class_.__instances__ = classmethod(pysupport.FindClassInstances)
+ if not hasattr(class_, "__subclasses__"):
+ class_.__subclasses__ = classmethod(pysupport.FindClassSubclasses)
+
+ class_.__events__ = set()
+
+ if bootstrapState != STATE_STARTUP:
+ print "CREATE", class_
+
+ events.ProcessClass(class_)
+
+ if bootstrapState != STATE_STARTUP:
+ events.ClassCreation(class_)
+
+def OnClassUpdate(class_):
+ if bootstrapState != STATE_STARTUP:
+ gc.collect()
+ instances = pysupport.FindInstances(class_)
+ instanceCount = sum(len(l) for l in instances.itervalues())
+ print "UPDATE", class_, instanceCount, "instances"
+
+ events.ProcessClass(class_)
+
+ if bootstrapState != STATE_STARTUP:
+ events.ClassUpdate(class_)
+
+def OnScriptValidation(scriptFile):
+ try:
+ from mudlib import GameCommand
+ except ImportError:
+ return
+
+ # TODO: Make this more generic.
+ for k, v in scriptFile.scriptGlobals.iteritems():
+ if type(v) in (types.ClassType, types.TypeType) and v is not
GameCommand:
+ if issubclass(v, GameCommand) and "Run" in v.__dict__:
+ raise Exception("Subclasses of GameCommand cannot override
Run")
+
+def Run():
+ global bootstrapState
+
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format='%(asctime)s;%(name)s;%(levelname)s;%(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S')
+
+ logging.getLogger().name = "default"
+ logging.getLogger("namespace").setLevel(logging.INFO)
+ logging.getLogger("reloader").setLevel(logging.INFO)
+
+ stackless.getcurrent().block_trap = True
+
+ iniFilename = os.path.join(dirPath, "config.ini")
+ if not os.path.exists(iniFilename):
+ print "Please copy 'config.ini.base' to 'config.ini' and modify it
appropriately."
+ sys.exit(0)
+
+ # Monkey-patch in the stackless-compatible sockets.
+ import stacklesssocket
+ import uthread2
+ import stacklesslib.main as stacklesslibmain
+ stacklesssocket._schedule_func = lambda delay=0:
stacklesslibmain.sleep(delay)
+ stacklesssocket._sleep_func = stacklesslibmain.sleep
+ stacklesssocket.install()
+
+ # Install the global event handler.
+ import __builtin__
+ from events import EventHandler
+ __builtin__.events = EventHandler()
+
+ # Add the "livecoding" contrib directory to the path.
+ livecodingDirPath = os.path.join(dirPath, "contrib", "livecoding")
+ if os.path.exists(livecodingDirPath):
+ sys.path.append(livecodingDirPath)
+
+ # Register the mudlib and game script directories with the livecoding
+ # module. This will compile and execute them all.
+ import reloader
+ #gamePath = os.path.join("games", "room - simple")
+ gamePath = os.path.join("games", "roguelike")
+ gameScriptPath = os.path.join(dirPath, gamePath)
+ mudlibScriptPath = os.path.join(dirPath, "mudlib")
+
+ cr = reloader.CodeReloader()
+ # Register for code reloading updates of managed classes.
+ # Broadcast an event when we receive an update.
+ cr.SetClassCreationCallback(OnClassCreation)
+ cr.SetClassUpdateCallback(OnClassUpdate)
+ cr.SetValidateScriptCallback(OnScriptValidation)
+ cr.AddDirectory("mudlib", mudlibScriptPath)
+ cr.AddDirectory("game", gameScriptPath)
+
+ import imp
+ __builtin__.sorrows = imp.new_module('sorrows')
+
+ from mudlib.services import ServiceService
+ svcSvc = ServiceService()
+ svcSvc.gameScriptPath = gamePath
+ svcSvc.gameDataPath = os.path.join(gamePath, "data")
+ svcSvc.Register()
+ svcSvc.Start()
+ del svcSvc
+
+ stackless.getcurrent().block_trap = False
+ bootstrapState = STATE_RUNNING
+
+ manualShutdown = False
+ try:
+ stacklesslibmain.mainloop.run()
+ except KeyboardInterrupt:
+ print
+ print '** EXITING: Server manually stopping.'
+ print
+
+ if stackless.runcount > 1:
+ print "Scheduled tasklets:", stackless.runcount
+ uthread2.PrintTaskletChain(stackless.current)
+ print
+
+ if False:
+ for entry in stacklesslibmain.event_queue.queue_a:
+ print "Sleeping tasklets:"
+ uthread2.PrintTaskletChain(uthread.yieldChannel.queue)
+ print
+
+ for timestamp, channel in uthread.sleepingTasklets:
+ if channel.queue:
+ print "Sleep channel (%d) tasklets:" % id(channel)
+ uthread2.PrintTaskletChain(channel.queue)
+ print
+
+ manualShutdown = True
+ finally:
+ cr.EndMonitoring()
+
+ bootstrapState = STATE_SHUTDOWN
+
+ if manualShutdown:
+ class HelperClass:
+ def ShutdownComplete(self):
+ stacklesslibmain.mainloop.stop()
+ managerTasklet.kill()
+
+ helper = HelperClass()
+ events.ShutdownComplete.Register(helper.ShutdownComplete)
+
+ stackless.tasklet(sorrows.services.Stop)()
+ # We have most likely killed the stacklesssocket tasklet.
+ managerTasklet = stacklesssocket.StartManager()
+ stacklesslibmain.mainloop.run()
+
+
logging.info("Shutdown complete")
+
+
+if __name__ == '__main__':
+ try:
+ Run()
+ finally:
+ logging.shutdown()
+
=======================================
--- /dev/null
+++ /trunk/doc/design/00005 - Inventory and encumbrance.rst Fri Jan 31
01:58:28 2014 UTC
@@ -0,0 +1,232 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ Inventory and Encumbrance
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+..
======================================================================== ..
+
+Milestone: Encumbrance
+----------------------
+
+This doesn't really add that much at this point, so probably best not to
spend
+too much time on it.
+
+Points to address
+^^^^^^^^^^^^^^^^^
+
+1. How to tell how much an item weighs.
+2. How to tell how much an character can carry.
+3. What happens when carries weight approaches or passes the limit.
+
+Use Case
+^^^^^^^^
+
+How the above points are to be handled, would determine the use case.
+
+Working Notes
+^^^^^^^^^^^^^
+
+* Implemented object weight.
+* Implemented body max carried weight.
+* Incomplete WRT 3.
+
+..
======================================================================== ..
+
+Milestone: Inventory
+--------------------
+
+At the simplest level, this adds slots where objects can be placed.
+
+Points to address
+^^^^^^^^^^^^^^^^^
+
+1. What are the default slots?
+2. Do they differ per-race?
+3. How would a backpack add carrying capacity?
+
+Brainstorm
+^^^^^^^^^^
+
+Default slots might be:
+* Head. Wear one item (headwear).
+* Torso. Wear one item (torso).
+* Left hand. Wear one item (glove). Hold one item (hand).
+* Right hand. Wear one item (glove). Hold one item (hand).
+* Feet. Hold one item (footwear).
+* Legs. Hold one item (legs).
+
+Special cases which came up:
+* Back should be an additional wear slot.
+ * Shield can be worn on the back when not in use.
+ * Pack can be worn on the back.
+ * Bow can be worn on the back when not in use.
+* Accessories / jewelery.
+ * Slots:
+ * neck (jewellery / accessories).
+ * eyes (glasses / blindfold).
+ * waist (belt / scabbard).
+ * ankle (manacle).
+ * wrist (manacle).
+
+Items / Item properties
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Start with just brainstorming and then move onto formalising it as a system
+I can put in game.
+
+Brainstorming
+`````````````
+
+Just a brain dump of ideas and slots.
+
+::
+
+ Fitting slot types:
+ * Headwear.
+ * Torso.
+ * Hand.
+ * Legs.
+ * Feet.
+ * Back.
+ * Waist.
+
+ Shield:
+ * Wear: back.
+ * Use: hand.
+
+ Scabbard:
+ * Wear: waist / back.
+
+ Sword:
+ * Wear: scabbard.
+ * Use: hand.
+
+ Quiver:
+ * Wear: back.
+
+ Arrow:
+ * Wear: quiver.
+
+ Spear:
+ * Wear: back.
+ * Use: hand.
+
+ Pack:
+ * Wear: back.
+
+ Sack
+
+ Longbow:
+ * Wear: back.
+ * Use: hand.
+
+Formalising
+```````````
+
+The above is a good breakdown of interested information. But it would be
best
+to have it specified in a data driven way. And the last thing I want to
do is
+write my own custom parsing format, or for that matter use something heavy-
+weight like YAML or XML. Even JSON looks cumbersome. Perhaps INI..
+
+::
+
+ [inventory]
+ slot-types=wear, use
+ wear-slots=head, torso, left-hand, right-hand, waist, legs, feet, back
+ use-slots=left-hand, right-hand
+ hold-slots=left-hand, right-hand
+
+ [inventory-property-types]
+ slot-types=strings
+ wear-slots=strings
+ use-slots=strings
+
+ [item-property-types]
+ name=string
+ weight=float
+ use-slots=strings
+ wear-slots=strings
+
+ [container-property-types]
+ capacity=float
+
+ [class-hierarchy]
+ container=item
+
+ [item-sword]
+ name=sword
+ weight=1.0
+ wear-slots=scabbard
+
+ [container-scabbard]
+ name=scabbard
+ weight=1.0
+ capacity=0.0
+
+ wear-slots=back, waist
+
+ [container-quiver]
+ name=quiver
+ weight=0.2
+ wear-slots=back
+
+ [item-arrow]
+ name=arrow
+ weight=0.05
+ use-slots=quiver
+
+ [item-shield]
+ name=shield
+ weight=3.0
+ use-slots=left-hand, right-hand
+ wear-slots=back
+
+ [container-pack]
+ name=pack
+ weight=0.1
+ wear-slots=back
+
+ [container-sack]
+ name=sack
+ weight=0.1
+
+ [item-spear]
+ name=spear
+ weight=0.5
+ wear-slots=back
+ use-slots=left-hand, right-hand
+
+ [item-longbow]
+ name=longbow
+ weight=2.0
+ wear-slots=back
+ use-slots=left-hand, right-hand
+
+That's a good initial set of data. At a later point, I'd like to have the
+data source be the D20 open data. But regardless of where the data comes
from
+I need administration commands to query.
+
+::
+
+ > data list items
+ A B C D E F
+ G H I J K L
+ > data list containers
+ A B C
+ > data make A
+
+That takes me to the point I have a range of items, and can instantiate
them
+on demand. Then I should be able to use them appropriately, whether
holding
+or wearing.
+
+::
+
+ > wear shield
+ ...
+ > hold shield
+ ...
+ > wear quiver on back
+ ...
+ > wear scabbard on belt
+ ...
+
+XXX
=======================================
--- /dev/null
+++ /trunk/games/roguelike/shell.py Fri Jan 31 01:58:28 2014 UTC
@@ -0,0 +1,90 @@
+# Akin to terminal support. Extracts and reacts to escape sequences,
before processing text.
+
+import stackless
+from stacklesslib.main import sleep as tasklet_sleep
+import mudlib
+
+ESC = chr(27)
+
+# Escape termination characters.
+#
+# The rule for detecting a full escape sequence is to keep reading
characters
+# after escape until any character from 'a'-'z', '{', '|', '@', 'A'-'Z' is
+# encountered.
+
+ESC_TERMINATORS = set()
+for v in range(97, 124+1):
+ ESC_TERMINATORS.add(chr(v))
+for v in range(64, 90+1):
+ ESC_TERMINATORS.add(chr(v))
+
+
+class Shell(mudlib.Shell):
+ def Setup(self, stack):
+ mudlib.Shell.Setup(self, stack)
+
+ self.inputBuffer = ""
+ self.escapeTasklet = None
+
+ def ReceiveInput(self, s, flush=False):
+ if False:
+ cnt = getattr(self, "xxx", 1)
+ self.xxx = cnt + 1
+ print cnt, "** ReceiveInput", [ ord(c) for c in s ], flush
+
+ if len(self.inputBuffer):
+ s = self.inputBuffer + s
+ self.inputBuffer = ""
+
+ escapeTasklet = self.escapeTasklet
+ self.escapeTasklet = None
+
+ if not flush:
+ if escapeTasklet:
+ escapeTasklet.kill()
+
+ if s[0] == ESC:
+ if len(s) == 1:
+ self.inputBuffer = s
+
+ # Might be a keypress or the start of an escape
sequence.
+ # The way to differentiate is to use a timeout to wait
for
+ # the rest of the escape sequence, and if nothing
arrives
+ # to assume it is a keypress.
+ self.escapeTasklet =
stackless.tasklet(self.ReceiveInput_EscapeTimeout)()
+ return
+
+ if s[1] != '[':
+ idx = s.find(ESC, 1)
+ if idx == -1:
+ self.DispatchInputSequence(s)
+ return
+ self.DispatchInputSequence(s[:idx])
+ self.ReceiveInput(s[idx:])
+ return
+
+ for i, c in enumerate(s):
+ if c in ESC_TERMINATORS:
+ self.DispatchInputSequence(s[:i+1])
+ remainingInput = s[i+1:]
+ if remainingInput:
+ self.ReceiveInput(remainingInput)
+ return
+
+ self.inputBuffer = s
+ return
+
+ idx = s.find(ESC)
+ if idx == -1 or idx == 0:
+ self.DispatchInputSequence(s)
+ else:
+ self.DispatchInputSequence(s[:idx])
+ self.ReceiveInput(s[idx:])
+ # print "** ReceiveInput - DONE"
+
+ def ReceiveInput_EscapeTimeout(self):
+ tasklet_sleep(0.1)
+ self.ReceiveInput("", flush=True)
+
+ def DispatchInputSequence(self, text):
+ pass
=======================================
--- /dev/null
+++ /trunk/games/room - simple/commands/consider.py Fri Jan 31 01:58:28
2014 UTC
@@ -0,0 +1,10 @@
+from mudlib import GameCommand
+
+class Consider(GameCommand):
+ __verbs__ = [ 'consider' ]
+
+ @staticmethod
+ def syntax_SUBJECT(context, matches):
+ for ob in matches:
+ context.user.Tell("Target: %s." % ob.shortDescription)
+ context.user.Tell(" Weight: %0.1f kg" % ob.GetTotalWeight())
=======================================
--- /dev/null
+++ /trunk/mudlib/container.py Fri Jan 31 01:58:28 2014 UTC
@@ -0,0 +1,19 @@
+class Container(object):
+ def __init__(self):
+ super(Container, self).__init__()
+ self.contents = []
+
+ def AddObject(self, ob):
+ self.contents.append(ob)
+
+ def RemoveObject(self, ob):
+ self.contents.remove(ob)
+
+ def GetContainedWeight(self):
+ weight = 0.0
+ for ob in self.contents:
+ weight += ob.GetTotalWeight()
+ return weight
+
+ def GetTotalWeight(self):
+ return self.weight + self.GetContainedWeight()
=======================================
--- /trunk/games/roguelike/shells/login.py Fri Jan 7 13:23:18 2011 UTC
+++ /trunk/games/roguelike/shells/login.py Fri Jan 31 01:58:28 2014 UTC
@@ -1,7 +1,10 @@
import logging, stackless
from stacklesslib.main import sleep as tasklet_sleep
-from mudlib import InputHandler, Shell
+from mudlib import InputHandler
import mudlib.shells
+from game import Shell
+
+logger = logging.getLogger("net")
# TODO: Rewrite this to detect graphical capability and to handle it
appropriately.
@@ -20,12 +23,22 @@
# - Select age.
# - ...
+STATE_CLIENT_EXAM = "CLIENT_EXAM"
+STATE_GET_LOGIN_NAME = "GET_LOGIN_NAME"
+STATE_GET_PASSWORD = "GET_PASSWORD"
+STATE_CREATE_PASSWORD1 = "CREATE_PASSWORD1"
+STATE_CREATE_PASSWORD2 = "CREATE_PASSWORD2"
+STATE_GET_CHARACTER = "GET_CHARACTER"
+
+PASSWORD_STATES = set([ STATE_GET_PASSWORD, STATE_CREATE_PASSWORD1,
STATE_CREATE_PASSWORD2 ])
+
loginPrompts = {
- "EnterLoginName": "Login name: ",
- "EnterPassword": "Password: ",
- "CreatePassword1": "Password: ",
- "CreatePassword2": "Password (again): ",
- "SelectCharacter": "Not yet implemented...",
+ STATE_CLIENT_EXAM: "",
+ STATE_GET_LOGIN_NAME: "Login name: ",
+ STATE_GET_PASSWORD: "Password: ",
+ STATE_CREATE_PASSWORD1: "Password: ",
+ STATE_CREATE_PASSWORD2: "Password (again): ",
+ STATE_GET_CHARACTER: "ERROR - NOT YET IMPLEMENTED",
}
MIN_USERNAME_SIZE = 3
@@ -34,102 +47,151 @@
MIN_PASSWORD_SIZE = 6
MAX_PASSWORD_SIZE = 20
+CTRL_E = chr(5) # ENQ
+
+
class LoginShell(Shell):
- state = "EnterLoginName"
+ state = STATE_CLIENT_EXAM
def Setup(self, stack):
Shell.Setup(self, stack)
self.userName = None
self.password = None
-
- # Print a login screen here if so desired.
- self.user.Tell(
sorrows.data.config.identity.name)
- self.user.Tell("Enter 'quit' or 'q' at any time during the login
process to disconnect.\n")
+
+ self.oldOptionLineMode = None
handler = InputHandler()
- handler.Setup(self, self.ReceiveInput, self.WritePrompt, 0)
+ handler.Setup(self, self.ReceiveInput, None, 0)
self.stack.SetShell(handler)
- stackless.tasklet(self.StartTelnetNegotiation)()
+ # Send telnet negotiation, but don't block this.
+
+ stackless.tasklet(self.StartClientExam)()
- def StartTelnetNegotiation(self):
+ def StartClientExam(self):
+ def _wait_func(channel, seconds):
+ tasklet_sleep(seconds)
+ channel.send(None)
+ channel1 = stackless.channel()
+ def _terminaltype_cb(terminalTypeName):
+ channel1.send(terminalTypeName)
+ self.user.connection.telneg_terminaltype_cb = _terminaltype_cb
+
telneg = self.user.connection.telneg
- # telneg.do_linemode()
- telneg.will_sga()
+ telneg.will_sga()
telneg.will_echo()
telneg.do_naws()
telneg.do_new_environ()
telneg.do_ttype()
- def ExecuteCommand(self):
- s = self.raw.strip()
+ # Wait for the soonest of a terminal type, give up after a short
delay.
+ stackless.tasklet(_wait_func)(channel1, 1.0)
+ terminalTypeName = channel1.receive()
+
+ if terminalTypeName:
+ self.user.connection.SetClientName(terminalTypeName)
+ if self.EndClientExam():
+ self._WritePrompt()
+ return
+
+ # Ask the client terminal what their name is.
+ self.oldOptionLineMode = self.user.connection.SetLineMode(False)
+ self.user.connection.SetPasswordMode(True)
+ self.user.Write(CTRL_E)
+
+ tasklet_sleep(1.0)
+ if self.EndClientExam():
+ self._WritePrompt()
+
+ def EndClientExam(self):
+ if self.state == STATE_CLIENT_EXAM:
+ self.state = STATE_GET_LOGIN_NAME
+
+ mudName =
sorrows.data.config.identity.name
+ if False and self.user.connection.clientName:
+ # Set client window title.
+ # self.user.Write("\x1b]2;%s\x07" % mudName)
+ # Enable mouse events.
+ self.user.Write("\x1b[?1003h")
+ if self.oldOptionLineMode is not None:
+ self.user.connection.SetLineMode(self.oldOptionLineMode)
+ self.oldOptionLineMode = None
+ self.user.connection.SetPasswordMode(False)
+ # Print a login screen here if so desired.
+ self.user.Tell(mudName)
+ self.user.Tell("Enter 'quit' or 'q' at any time during the
login process to disconnect.\n")
+ return True
+ return False
+
+ def DispatchInputSequence(self, s):
+ s = s.strip()
if s == 'q' or s == 'quit':
return self.user.ManualDisconnection()
- if self.state == "EnterLoginName":
+ #print "DispatchInputSequence", s
+ if self.state == STATE_CLIENT_EXAM:
+ self.user.connection.SetClientName(s)
+ self.EndClientExam()
+ elif self.state == STATE_GET_LOGIN_NAME:
userName = s
if sorrows.users.UserExists(userName):
self.user.Tell("Hello again.")
self.userName = userName
self.guessAttempts = 3
- self.state = "EnterPassword"
- return
- if len(userName) < MIN_USERNAME_SIZE:
+ self.state = STATE_GET_PASSWORD
+ elif len(userName) < MIN_USERNAME_SIZE:
self.user.Tell("That account name is too short. Minimum
length is %d characters." % MIN_USERNAME_SIZE)
- return
- if len(userName) > MAX_USERNAME_SIZE:
+ elif len(userName) > MAX_USERNAME_SIZE:
self.user.Tell("That account name is too long. Maximum
length is %d characters." % MAX_USERNAME_SIZE)
- return
- self.user.Tell("Creating a new account with the name '"+
userName +"'.")
- self.userName = userName
- self.state = "CreatePassword1"
- elif self.state == "CreatePassword1":
+ else:
+ self.user.Tell("Creating a new account with the name '"+
userName +"'.")
+ self.userName = userName
+ self.state = STATE_CREATE_PASSWORD1
+ elif self.state == STATE_CREATE_PASSWORD1:
password = s
self.user.Tell("")
if len(password) < MIN_PASSWORD_SIZE:
self.user.Tell("That password is too short. Minimum
length is %d characters." % MIN_PASSWORD_SIZE)
- return
- if len(password) > MAX_PASSWORD_SIZE:
+ elif len(password) > MAX_PASSWORD_SIZE:
self.user.Tell("That password is too long. Maximum length
is %d characters." % MAX_PASSWORD_SIZE)
- return
- self.password = password
- self.state = "CreatePassword2"
- elif self.state == "CreatePassword2":
+ else:
+ self.password = password
+ self.state = STATE_CREATE_PASSWORD2
+ elif self.state == STATE_CREATE_PASSWORD2:
password = s
self.user.Tell("")
if password != self.password:
self.user.Tell("That password does not match. Try again.")
- self.state = "CreatePassword1"
- return
-
- try:
- sorrows.users.Add(self.userName, self.password)
- except Exception:
- self.user.Tell("Someone created an account with that name
while you were in the process of creating yours.")
- self.state = "EnterLoginName"
- logging.exception("CreatePassword2")
- return
-
- self.user.connection.SetPasswordMode(False)
- #self.state = "SelectCharacter"
+ self.state = STATE_CREATE_PASSWORD1
+ else:
+ try:
+ sorrows.users.Add(self.userName, self.password)
+ self.user.connection.SetPasswordMode(False)
+ #self.state = "SelectCharacter"
- self.EnterGame()
- elif self.state == "EnterPassword":
+ return self.EnterGame()
+ except Exception:
+ self.user.Tell("Someone created an account with that
name while you were in the process of creating yours.")
+ self.state = STATE_GET_LOGIN_NAME
+ logging.exception(STATE_CREATE_PASSWORD2)
+ elif self.state == STATE_GET_PASSWORD:
self.user.Tell("")
if sorrows.users.CheckPassword(self.userName, s):
self.user.connection.SetPasswordMode(False)
- self.EnterGame()
+ return self.EnterGame()
elif self.guessAttempts > 1:
self.guessAttempts -= 1
else:
- self.user.ManualDisconnection()
+ return self.user.ManualDisconnection()
+
+ self._WritePrompt()
- def WritePrompt(self):
- if "Password" in self.state:
+ def _WritePrompt(self):
+ if self.state in PASSWORD_STATES:
self.user.connection.SetPasswordMode(True)
- return loginPrompts[self.state]
+ self.user.Write(loginPrompts[self.state])
def EnterGame(self):
self.user.name = self.userName
@@ -139,3 +201,9 @@
mudlib.shells.DeveloperGameShell().Setup(self.stack)
else:
mudlib.shells.GameShell().Setup(self.stack)
+
+ def OnRemovalFromStack(self):
+ if self.user.connection.connected:
+ if self.oldOptionLineMode is not None:
+ self.user.connection.SetLineMode(self.oldOptionLineMode)
+ Shell.OnRemovalFromStack(self)
=======================================
--- /trunk/games/roguelike/shells/roguelike.py Sat Jan 21 06:48:26 2012 UTC
+++ /trunk/games/roguelike/shells/roguelike.py Fri Jan 31 01:58:28 2014 UTC
@@ -38,10 +38,10 @@
# - Clean up use of ViewedTileRange() so it can be degeneratorised.
#
-import random, array, math, StringIO, time, stackless
-from stacklesslib.main import sleep as tasklet_sleep
+import random, array, math, StringIO, time, stackless, logging
import fov
-from mudlib import Shell, InputHandler
+from mudlib import InputHandler
+from game import Shell
# ASCII CODES
-----------------------------------------------------------------
@@ -66,19 +66,6 @@
ESC_RESET_ATTRS = "\x1b[0m"
ESC_NORMAL = "\x1b[22m"
-# Escape termination characters.
-#
-# The rule for detecting a full escape sequence is to keep reading
characters
-# after escape until any character from 'a'-'z', '{', '|', '@', 'A'-'Z' is
-# encountered.
-#
-
-ESC_TERMINATORS = set()
-for v in range(97, 124+1):
- ESC_TERMINATORS.add(chr(v))
-for v in range(64, 90+1):
- ESC_TERMINATORS.add(chr(v))
-
# Control codes.
CTRL_E = chr(5) # ENQ
@@ -105,7 +92,7 @@
WALL_TILE2: u"\u2592", # Medium shade.
WALL_TILE: u"\u2593", # Dark shade.
FLOOR_TILE: u"\xB7", # Middle dot.
- DOOR_TILE: u"\u25A0", # Black square.
+ DOOR_TILE: u"\u25FC", # Black square.
CUBE_TILE: u"\u2588", # Full block.
CHAR_TILE: u"@",
@@ -118,8 +105,30 @@
"light-up-and-left": u"\u2518",
"full-block": u"\u2588",
"medium-shade": u"\u2592",
- "black-square": u"\u25A0",
- "white-square": u"\u25A1",
+ "black-square": u"\u25FC",
+ "white-square": u"\u25FB",
+ },
+ "html" : {
+ # Map character mappings.
+ WALL_TILE1: "░",
+ WALL_TILE2: "▒",
+ WALL_TILE: "▓",
+ FLOOR_TILE: "·",
+ DOOR_TILE: "◼",
+ CUBE_TILE: "█",
+ CHAR_TILE: u"@",
+
+ # Line-drawing characters.
+ "light-horizontal": "─",
+ "light-vertical": "│",
+ "light-down-and-right": "┌",
+ "light-down-and-left": "┐",
+ "light-up-and-right": "└",
+ "light-up-and-left": "┘",
+ "full-block": "█",
+ "medium-shade": "▒",
+ "black-square": "◼",
+ "white-square": "◻",
}
}
@@ -175,15 +184,12 @@
handler.Setup(self, self.ReceiveInput, None, 0)
stack.Push(handler)
- self.oldOptionLineMode = self.user.connection.optionLineMode
- self.user.connection.optionLineMode = False
+ self.oldOptionLineMode = self.user.connection.SetLineMode(False)
# Defaults..
self.keyboard = None
self.menuOptions = []
self.menuSelection = 0
- self.inputBuffer = ""
- self.escapeTasklet = None
self.status = "-"
self.lastStatusBar = "-"
@@ -193,7 +199,6 @@
# self.user.connection.telneg.do_sga()
self.ShowCursor(False)
- self.QueryClientName()
self.mapArray = array.array('B', (0 for i in
xrange(sorrows.world.mapHeight * sorrows.world.mapWidth)))
self.drawRangesNew = {}
@@ -204,27 +209,21 @@
self.OnTerminalSizeChanged(cols, rows, redraw=False)
self.RecalculateWorldView()
- self.charsetResetSequence = None
- self.charsetEncoding = "cp437"
-
+ if self.user.connection.clientName.lower() in ("putty", "xterm"):
+ self.charsetEncoding = "utf8"
+ self.charsetResetSequence = ESC_UTF8_CHARSET # ESC_SCO_CHARSET
+ self.ResetCharset()
+ else:
+ self.charsetResetSequence = None
+ self.charsetEncoding = "cp437"
+
self.UpdateTitle()
self.UpdateStatusBar("Use the up and down cursor keys to choose an
option, and enter to select it.")
self.enteredGame = False
+
self.EnterKeyboardMenu()
- def QueryClientName(self):
- self.clientName = None
- self.user.Write(CTRL_E)
-
- def SetClientName(self, clientName):
- self.clientName = clientName.lower()
-
- if self.clientName == "putty":
- self.charsetEncoding = "utf8"
- self.charsetResetSequence = ESC_UTF8_CHARSET # ESC_SCO_CHARSET
- self.ResetCharset()
-
def SetMode(self, mode, status=None):
if status is None:
status = MODE_NAMES.get(mode, None)
@@ -236,7 +235,7 @@
def OnRemovalFromStack(self):
if self.user.connection.connected:
- self.user.connection.optionLineMode = self.oldOptionLineMode
+ self.user.connection.SetLineMode(self.oldOptionLineMode)
self.user.connection.telneg.will_echo()
self.user.Write(ESC_RESET_TERMINAL)
self.ScrollWindowVertically(-1)
@@ -266,68 +265,6 @@
if redraw:
self.DisplayScreen()
- def ReceiveInput(self, s, flush=False):
- if False:
- cnt = getattr(self, "xxx", 1)
- self.xxx = cnt + 1
- print cnt, "** ReceiveInput", [ ord(c) for c in s ], flush
-
- if len(self.inputBuffer):
- s = self.inputBuffer + s
- self.inputBuffer = ""
-
- escapeTasklet = self.escapeTasklet
- self.escapeTasklet = None
-
- if not flush:
- if escapeTasklet:
- escapeTasklet.kill()
-
- if s[0] == ESC:
- if len(s) == 1:
- self.inputBuffer = s
-
- # A lone escape can be one of two things:
- # - An actual press of the escape key.
- # - The start of an escape sequence.
- # The way to differentiate is to use a timeout to wait
for
- # the rest of the escape sequence, and if nothing
arrives
- # to assume it is a keypress.
- self.escapeTasklet =
stackless.tasklet(self.ReceiveInput_EscapeTimeout)()
- return
-
- if s[1] != '[':
- idx = s.find(ESC, 1)
- if idx == -1:
- self.DispatchInputSequence(s)
- return
- self.DispatchInputSequence(s[:idx])
- self.ReceiveInput(s[idx:])
- return
-
- for i, c in enumerate(s):
- if c in ESC_TERMINATORS:
- self.DispatchInputSequence(s[:i+1])
- remainingInput = s[i+1:]
- if remainingInput:
- self.ReceiveInput(remainingInput)
- return
-
- self.inputBuffer = s
- return
-
- idx = s.find(ESC)
- if idx == -1 or idx == 0:
- self.DispatchInputSequence(s)
- else:
- self.DispatchInputSequence(s[:idx])
- self.ReceiveInput(s[idx:])
- # print "** ReceiveInput - DONE"
-
- def ReceiveInput_EscapeTimeout(self):
- tasklet_sleep(0.1)
- self.ReceiveInput("", flush=True)
-
def DispatchInputSequence(self, s):
# print "** DispatchInputSequence", [ ord(c) for c in s ]
@@ -335,11 +272,7 @@
self.ReceiveInput_Gameplay(s)
elif MODE_FIRST_MENU <= self.mode <= MODE_LAST_MENU:
if self.mode == MODE_KEYBOARD_MENU:
- # We've sent <ENQ>, check for Putty's response.
- if s == "PuTTY":
- self.SetClientName(s)
- # Refresh the menu with proper characters.
- self.DisplayMenu(self.mode, self.menuOptions,
selected=self.menuSelection)
+ pass # self.DisplayMenu(self.mode, self.menuOptions,
selected=self.menuSelection)
self.ReceiveInput_Menu(s)
elif MODE_FIRST_DISPLAY <= self.mode <= MODE_LAST_DISPLAY:
@@ -377,7 +310,7 @@
self.menuSelection += shift
return self.DisplayMenu(self.mode, self.menuOptions,
selected=self.menuSelection, redraw=False)
- elif s == "\r\n":
+ elif s == "\r\n" or s == "\r":
option = self.menuOptions[self.menuSelection]
return MenuAction(option[1])
else:
=======================================
--- /trunk/games/roguelike/world/container.py Mon Mar 15 00:34:30 2010 UTC
+++ /trunk/games/roguelike/world/container.py Fri Jan 31 01:58:28 2014 UTC
@@ -1,17 +1,7 @@
-from game.world import Object
-
-class Container(Object):
- def __init__(self):
- Object.__init__(self)
-
- self.contents = []
-
- def AddObject(self, ob):
- self.contents.append(ob)
-
- def RemoveObject(self, ob):
- self.contents.remove(ob)
+import mudlib
+import game.world
+class Container(mudlib.Container, game.world.Object):
def LookString(self, viewer):
s = Object.LookString(self, viewer)
if len(self.contents):
=======================================
--- /trunk/games/roguelike/world/object.py Tue Aug 31 14:00:03 2010 UTC
+++ /trunk/games/roguelike/world/object.py Fri Jan 31 01:58:28 2014 UTC
@@ -9,6 +9,8 @@
position = None
def __init__(self, shortDescription=None):
+ super(Object, self).__init__()
+
if shortDescription is not None:
self.SetShortDescription(shortDescription)
=======================================
--- /trunk/games/room - simple/services/world.py Thu Dec 23 11:59:02 2010
UTC
+++ /trunk/games/room - simple/services/world.py Fri Jan 31 01:58:28 2014
UTC
@@ -17,16 +17,25 @@
ob = Object()
ob.SetShortDescription("brown pants")
ob.SetLongDescription("This is a pair of brown pants.")
+ ob.SetWeight(0.2)
ob.MoveTo(room)
ob = Object()
ob.SetShortDescription("green pants")
ob.SetLongDescription("This is a pair of green pants.")
+ ob.SetWeight(0.2)
ob.MoveTo(room)
+ ob = Object()
+ ob.SetShortDescription("big rock")
+ ob.SetLongDescription("This is a big rock.")
+ ob.SetWeight(10.0)
+ ob.MoveTo(room)
+
ob = Container()
ob.SetShortDescription("chest")
ob.SetLongDescription("This is a chest.")
+ ob.SetWeight(6.0)
ob.MoveTo(room)
room2 = self.secondRoom = Room()
=======================================
--- /trunk/games/room - simple/world/body.py Thu Dec 23 11:59:02 2010 UTC
+++ /trunk/games/room - simple/world/body.py Fri Jan 31 01:58:28 2014 UTC
@@ -1,14 +1,32 @@
from game.world import Container
+GENDER_SHE = 0
+GENDER_HE = 1
+GENDER_NEUTER = 2
+
class Body(Container):
+ gender = GENDER_HE
+ verbWord = "carry"
+ carriedWeightMax = 12.0
+
def __init__(self, service, user):
- Container.__init__(self)
+ super(Body, self).__init__()
self.service = service
self.user = user
- def Release(self):
- Container.Release(self)
+ def LookString(self, viewer):
+ s = super(Body, self).LookString(viewer)
+ carriedWeight = self.GetContainedWeight()
+ percentage = (self.GetContainedWeight() / self.carriedWeightMax) *
100
+ s += "\r\nEncumbrance: %0.1f/%0.1f kg (%d%%)" % (carriedWeight,
self.carriedWeightMax, percentage)
+ return s
+
+ def GetPronoun(self):
+ return [ "she", "he", "it" ][self.gender]
+
+ def GetMaxCarriedWeight(self):
+ return self.carriedWeightMax
def MoveDirection(self, verb):
destinationRoom = self.container.GetExitRoom(verb)
=======================================
--- /trunk/games/room - simple/world/container.py Sun Feb 6 11:07:58 2011
UTC
+++ /trunk/games/room - simple/world/container.py Fri Jan 31 01:58:28 2014
UTC
@@ -1,19 +1,11 @@
+import mudlib
import game.world
-class Container(game.world.Object):
- def __init__(self):
- game.world.Object.__init__(self)
-
- self.contents = []
-
- def AddObject(self, ob):
- self.contents.append(ob)
-
- def RemoveObject(self, ob):
- self.contents.remove(ob)
+class Container(mudlib.Container, game.world.Object):
+ verbWord = "contain"
def LookString(self, viewer):
- s = game.world.Object.LookString(self, viewer)
+ s = super(Container, self).LookString(viewer)
if len(self.contents):
contentsString = ""
for idx, ob in enumerate(self.contents):
@@ -24,5 +16,6 @@
contentsString
+= "{0.s}".format(game.world.ViewedObject(viewer=viewer, object=ob))
else:
contentsString = "Nothing"
- s += "\r\nIt contains: "+ contentsString +"."
+ vo = game.world.ViewedObject(viewer=viewer, object=self,
verb=self.verbWord)
+ s += "\r\n{0.Pn} {0.v}: {1}.".format(vo, contentsString)
return s
=======================================
--- /trunk/games/room - simple/world/object.py Thu Dec 23 11:59:02 2010 UTC
+++ /trunk/games/room - simple/world/object.py Fri Jan 31 01:58:28 2014 UTC
@@ -10,6 +10,8 @@
longDescription = "THIS OBJECT IS UNDESCRIBED"
def __init__(self, shortDescription=None):
+ super(Object, self).__init__()
+
self.nouns = set()
self.plurals = set()
self.adjectives = set()
@@ -21,9 +23,9 @@
if self.container:
self.container.RemoveObject(self)
self.container = None
- mudlib.Object.Release(self)
+ super(Object, self).Release()
- def IdentifiedBy(self, noun):
+ def IdentifiedBy(self, noun, actor=None):
return noun in self.nouns or noun in self.plurals
def DescribedBy(self, adjectives):
@@ -66,6 +68,9 @@
def GetLongDescription(self):
return self.longDescription
+ def GetPronoun(self):
+ return "it"
+
def LookString(self, viewer):
return self.longDescription
=======================================
--- /trunk/games/room - simple/world/room.py Sun Feb 6 11:07:58 2011 UTC
+++ /trunk/games/room - simple/world/room.py Fri Jan 31 01:58:28 2014 UTC
@@ -7,7 +7,7 @@
class Room(Container):
def __init__(self):
- Container.__init__(self)
+ super(Room, self).__init__()
self.exits = weakref.WeakValueDictionary()
@@ -20,7 +20,7 @@
def LookString(self, viewer):
s = self.shortDescription +"\r\n"
- s += Container.LookString(self, viewer)
+ s += super(Room, self).LookString(viewer)
exitNames = self.exits.keys()
exitNames.sort()
@@ -86,7 +86,7 @@
def testActorName(self):
self.assertEqual(self.bodyIndirectPerspective.s, "dwarf", "Actor
does not see viewed body by the short description")
- self.assertEqual(self.bodyIndirectPerspective.S, "dwarf", "Actor
does not see viewed body by the uncapitalised short description")
+ self.assertEqual(self.bodyIndirectPerspective.S, "Dwarf", "Actor
does not see viewed body by the uncapitalised short description")
self.assertEqual(self.bodyDirectPerspective.s, "you", "Actor does
not see themselves as the lowercase 'you'")
self.assertEqual(self.bodyDirectPerspective.S, "You", "Actor does
not see themselves as the capitalised 'you'")
=======================================
--- /trunk/games/room - simple/world/util.py Sun Feb 6 11:07:58 2011 UTC
+++ /trunk/games/room - simple/world/util.py Fri Jan 31 01:58:28 2014 UTC
@@ -1,3 +1,4 @@
+import textsupport
import game.world
class ViewedObject(object):
@@ -16,32 +17,37 @@
def __getattr__(self, attrName):
"""
- s - Short description.
- S - Short description (capitalised).
- v - Verb.
+ Capitalised versions of these give the capitalised result.
+ s - Short description.
+ pn - Pronoun.
+ v - Verb.
"""
+ attrNameLower = attrName.lower()
- if attrName in ("s", "S"):
- if self._object is self._viewer:
- if attrName == "S":
- return "You"
- return "you"
-
+ if self._object is self._viewer:
+ text = "you"
+ if attrNameLower == "v":
+ text = self._verb
+ elif attrNameLower == "s":
# If the object is a body, use their short description
directly.
text = self._object.shortDescription
if isinstance(self._object, game.world.Body):
- return text
+ pass
# If the viewer is the actor, use the description with "the"
article.
- if self._viewer is self._actor:
- return "the "+ text
+ elif self._viewer is self._actor:
+ text = "the "+ text
# If the viewer is not the actor, use the description
with "a"/"an" article.
- if text[0] in ("a", "e", "i", "o", "u"):
- return "an "+ text
- return "a "+ text
-
- if attrName == "v":
- if self._object is self._viewer:
- return self._verb
- return self._verb +"s"
+ elif text[0] in ("a", "e", "i", "o", "u"):
+ text = "an "+ text
+ else:
+ text = "a "+ text
+ elif attrNameLower == "pn":
+ text = self._object.GetPronoun()
+ elif attrNameLower == "v":
+ text = textsupport.pluralise(self._verb)
+ else:
+ raise AttributeError("%s instance has no attribute '%s'" %
(self.__class__.__name__, attrName))
- raise AttributeError("%s instance has no attribute '%s'" %
(self.__class__.__name__, attrName))
+ if attrName != attrNameLower:
+ return text.capitalize()
+ return text
=======================================
--- /trunk/mudlib/body.py Wed Feb 24 08:17:49 2010 UTC
+++ /trunk/mudlib/body.py Fri Jan 31 01:58:28 2014 UTC
@@ -2,13 +2,13 @@
class Body(Object):
def __init__(self, service, user):
- Object.__init__(self, service)
+ super(Body, self).__init__(service)
self.service = service
self.user = user
def Release(self):
- Object.Release(self)
+ super(Body, self).Release()
#
------------------------------------------------------------------------
# Methods body subclasses need to override.
=======================================
--- /trunk/mudlib/input.py Mon Sep 13 12:05:19 2010 UTC
+++ /trunk/mudlib/input.py Fri Jan 31 01:58:28 2014 UTC
@@ -72,12 +72,11 @@
def WritePrompt(self):
prompt = self.stack[-1].prompt
if type(prompt) is types.MethodType:
- prompt = apply(prompt,())
- self.user.Write(prompt)
+ self.user.Write(prompt())
def ReceiveInput(self, input, bottomlevel=False):
handler = self.GetHandler(bottomlevel)
- apply(handler.function, (input,))
+ handler.function(input)
if self.user:
self.WritePrompt()
=======================================
--- /trunk/mudlib/object.py Mon Mar 15 00:34:30 2010 UTC
+++ /trunk/mudlib/object.py Fri Jan 31 01:58:28 2014 UTC
@@ -1,3 +1,14 @@
-class Object:
+class Object(object):
+ weight = 0.0
+
+ def SetWeight(self, weight):
+ self.weight = weight
+
+ def GetContainedWeight(self):
+ return 0.0
+
+ def GetTotalWeight(self):
+ return self.weight
+
def Release(self):
pass
=======================================
--- /trunk/mudlib/services/net/connectionTelnet.py Sun Jan 22 01:48:04 2012
UTC
+++ /trunk/mudlib/services/net/connectionTelnet.py Fri Jan 31 01:58:28 2014
UTC
@@ -1,3 +1,4 @@
+import weakref
import stackless
from stacklesslib.main import sleep as tasklet_sleep
from
mudlib.services.net import Connection
@@ -15,11 +16,13 @@
self.terminalType = None
self.terminalTypes = []
+ self.clientName = None
self.optionEcho = False
self.optionLineMode = True
self.readlineBuffer = ""
self.telneg = TelnetNegotiation(self.TelnetNegotiationSend,
self.TelnetSubnegotiation, self.TelnetCommand)
+ self.telneg_terminaltype_cb = None
if False:
self.user = None
@@ -29,7 +32,12 @@
self.user = User(self, "login")
self.user.SetupInputStack()
- stackless.tasklet(self.ManageConnection)()
+ self.loopTasklet = None
+ self.loopIsReading = True
+ self.RestartLoop()
+
+ def SetClientName(self, clientName):
+ self.clientName = clientName
def SetPasswordMode(self, flag):
if flag:
@@ -38,6 +46,13 @@
else:
self.suppressEcho = False
# self.telneg.wont_echo()
+
+ def SetLineMode(self, flag):
+ oldValue = self.user.connection.optionLineMode
+ self.user.connection.optionLineMode = flag
+ if oldValue != flag:
+ self.RestartLoop(onlyIfReading=True)
+ return oldValue
def TelnetNegotiationSend(self, data):
self.service and self.service.LogDebug("SEND(%s)%s%s",
self.user.name, self.clientAddress, [ord(c) for c in data])
@@ -66,6 +81,8 @@
# Select the first one the client offered.
self.terminalType = result.parameters[0]
self.telneg.do_ttype(self.terminalType)
+ if self.telneg_terminaltype_cb:
+ self.telneg_terminaltype_cb(self.terminalType)
elif result.command == NEW_ENVIRON:
self.service.LogDebug("ENVIRONMENT VARIABLES %s (%s)%s",
result.parameters,
self.user.name, self.clientAddress)
for k, v in result.parameters:
@@ -75,19 +92,37 @@
else:
raise Exception("Unhandled subnegotiation", result)
+ def RestartLoop(self, onlyIfReading=False):
+ if onlyIfReading and not self.loopIsReading:
+ return
+ if self.loopTasklet is not None:
+ t = self.loopTasklet()
+ if t is not None:
+ t.kill()
+ t = stackless.tasklet(self.ManageConnection)()
+ self.loopTasklet = weakref.ref(t)
+
def ManageConnection(self):
while not self.released:
if not self._ManageConnection():
break
def _ManageConnection(self):
- if self.optionLineMode:
- input = self.readline()
- else:
- input = self.read(65536)
- # We may recieve an empty string if there was only negotiation.
- if input == "":
- return True
+ self.loopIsReading = True
+ try:
+ if self.optionLineMode:
+ input = self.readline()
+ else:
+ if self.readlineBuffer:
+ input = self.readlineBuffer
+ self.readlineBuffer = ""
+ else:
+ input = self.read(65536)
+ # We may recieve an empty string if there was only
negotiation.
+ if input == "":
+ return True
+ finally:
+ self.loopIsReading = False
if input is None:
return False
@@ -135,7 +170,7 @@
def read(self, bytes):
s = self.recv(65536)
- # print "INPUT-CHARS", [ ord(c) for c in s ], s
+ #print "INPUT-CHARS", [ ord(c) for c in s ], s
if s == "":
return None
@@ -182,7 +217,7 @@
s = self.recv(65536)
if s == "":
return None
- # print "INPUT-RECEIVED", [ ord(c) for c in s ]
+ #print "INPUT-RECEIVED", [ ord(c) for c in s ]
#if s[0] == "\x1b":
# print "ESCAPE-SEQUENCE-PENDING", s
=======================================
--- /trunk/mudlib/shell.py Mon Mar 22 08:50:29 2010 UTC
+++ /trunk/mudlib/shell.py Fri Jan 31 01:58:28 2014 UTC
@@ -23,4 +23,4 @@
result = self.ExecuteCommand()
def ExecuteCommand(self):
- raise NameError, 'ExecuteCommand not found in the inheriting shell
object'
+ raise NameError('ExecuteCommand not found in the inheriting shell
object')