File indexing completed on 2024-04-14 04:00:24

0001 #!/usr/bin/env python
0002 
0003 #---------------------------------------------------------------
0004 # KMuddy scripting support
0005 # 
0006 # Run this file directly (as a script from within KMuddy)
0007 # for a basic functionality test
0008 #---------------------------------------------------------------
0009 
0010 import os, sys, socket, select
0011 
0012 #---------------------------------------------------------------
0013 
0014 """This module provides full scripting support for KMuddy.
0015 Functions:
0016 
0017 initSocket([bufsize]) -- create a new KMuddy connection
0018 closeSocket() -- close the KMuddy connection
0019 getVariable(var) -- get a variable
0020 setVariable(var, value) -- set a variable
0021 unsetVariable(var) -- unset a variable
0022 incVariable(var, [amount]) -- increase var's value
0023 decVariable(var, [amount]) -- decrease var's value
0024 lockVariable(var) -- lock a variable
0025 unlockVariable(var) -- unlock a variable
0026 sendCommand(command) -- send command to MUD
0027 provideResource(resource) -- provide a resource
0028 requestResource(resource) -- request a resource
0029 registerEvent(portno, [func]) -- register an event
0030 unregisterEvent(portno) -- unregister an event
0031 wait4Event() -- wait for an event\n
0032 See method __doc__ properties for details\n"""
0033 
0034 evSocks = {}
0035 evPorts = {}
0036 evHandlers = {}
0037 socketName = ""
0038 sock = None
0039 bufferSize = 1024
0040 
0041 def initSocket(bufsize = 1024):
0042     global sock, socketName
0043     """initSocket([bufsize])\n
0044 Connect to KMuddy server in order to get/set variables, send commands, etc.\n\n"
0045 bufsize -- length of character buffer (optional, defaults to 32)\n"""
0046     bufferSize = bufsize
0047     socketName = os.environ.get("KMUDDY_SOCKET")
0048     if socketName is None:
0049         raise RuntimeError, "Environment variable KMUDDY_SOCKET is not set.\nDid you activate \"Communicate variables\" in the \"Input/Output\" section of the script properties?"
0050 
0051     sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
0052     if sock is None:
0053         raise RuntimeError, "Could not create socket"
0054     try:
0055         sock.connect(socketName)
0056     except:
0057         raise RuntimeError, "Could not connect to Kmuddy. Check KMUDDY_SOCKET environment variable."
0058 
0059 #---------------------------------------------------------------
0060 
0061 def closeSocket():
0062     global sock
0063     """closeSocket()\n\nClose connection to KMuddy server. Doesn't hurt to call this at the end of your script."""
0064 
0065     sock.close()
0066     sock = None
0067 
0068 #---------------------------------------------------------------
0069 
0070 def getVariable(var):
0071     global sock, bufferSize
0072     """getVariable(var) -> string\n\nReturn value of variable `var'"""
0073 
0074     sock.send("get %s\n" % var)
0075     sock.flush()
0076     return sock.recv(bufferSize)[:-2]
0077 
0078 #---------------------------------------------------------------
0079 
0080 def setVariable(var, value):
0081     global sock, bufferSize
0082     """setVariable(var, value) -> boolean\n\nSet variable `var' to `value'"""
0083 
0084     sock.send("set %s %s\n" % (var, value))
0085     sock.flush()
0086     return sock.recv(bufferSize)[:-2] == "OK"
0087 
0088 #---------------------------------------------------------------
0089 
0090 def unsetVariable(var):
0091     global sock, bufferSize
0092     """unsetVariable(var) -> boolean\n\nRemove variable `var'"""
0093 
0094     sock.send("unset %s\n" % var)
0095     sock.flush()
0096     return sock.recv(bufferSize)[:-2] == "OK"
0097 
0098 #---------------------------------------------------------------
0099 
0100 def incVariable(var, amount = 1):
0101     global sock, bufferSize
0102     """incVariable(var, [amount]) -> boolean\n\nIncrease variable `var' by `amount' (defaults to 1)"""
0103 
0104     sock.send("inc %s %d\n" % (var, amount))
0105     sock.flush()
0106     return sock.recv(bufferSize)[:-2] == "OK"
0107 
0108 #---------------------------------------------------------------
0109 
0110 def decVariable(var, amount = 1):
0111     global sock, bufferSize
0112     """decVariable(var, [amount]) -> boolean\n\nDecrease variable `var' by `amount' (defaults to 1)"""
0113 
0114     sock.send("dec %s %d\n" % (var, amount))
0115     sock.flush()
0116     return sock.recv(bufferSize)[:-2] == "OK"
0117 
0118 #---------------------------------------------------------------
0119 
0120 def lockVariable(var):
0121     global sock, bufferSize
0122     """lockVariable(var) -> boolean\n\nLock variable `var'"""
0123 
0124     sock.send("lock %s\n" % var)
0125     sock.flush()
0126     return sock.recv(bufferSize)[:-2] == "OK"
0127 
0128 #---------------------------------------------------------------
0129 
0130 def unlockVariable(var):
0131     global sock, bufferSize
0132     """unlockVariable(var) -> boolean\n\nUnlock variable `var'"""
0133 
0134     sock.send("unlock %s\n" % var)
0135     sock.flush()
0136     return sock.recv(bufferSize)[:-2] == "OK"
0137 
0138 #---------------------------------------------------------------
0139 
0140 def sendCommand(command):
0141     global sock, bufferSize
0142     """sendCommand(command) -> boolean\n\nSend a command string to the MUD"""
0143 
0144     sock.send("send %s\n" % command)
0145     sock.flush()
0146     return sock.recv(bufferSize)[:-2] == "OK"
0147 
0148 #---------------------------------------------------------------
0149 
0150 def provideResource(resource):
0151     global sock, bufferSize
0152     """provideResource(resource) -> boolean\n\nProvide a resource"""
0153 
0154     sock.send("provide %s\n" % resource)
0155     sock.flush()
0156     return sock.recv(bufferSize)[:-2] == "OK"
0157 
0158 #---------------------------------------------------------------
0159 
0160 def requestResource(resource):
0161     global sock, bufferSize
0162     """requestResource(resource) -> boolean\n\n Request a resource from the MUD"""
0163 
0164     sock.send("request %s\n" % resource)
0165     sock.flush()
0166     return sock.recv(bufferSize)[:-2] == "OK"
0167 
0168 #---------------------------------------------------------------
0169 
0170 def registerEvent(portno, func = None):
0171     global evSocks, evPorts, evHandlers
0172     """registerEvent(portno, [func]) -> boolean\n
0173 Register/update an event for notification by KMuddy\n
0174 This function binds a TCP socket to the local loopback address on the given port.
0175 The script can then be notified from KMuddy using /notify <portno> <data>.
0176 If the respective port number has been registered before, its handler is updated.
0177 
0178 portno -- TCP port number to bind to (1-65535, ports <= 1024 require root privileges)
0179 func -- event handler function (optional)
0180     
0181 If func is given, future calls to wait4Event() will transparently call it whenever
0182 the event occurs.
0183 If func is omitted, future calls to wait4Event() will return whenever the event occurs,
0184 yielding a list containing tuples of the form (portno, data_string).
0185     
0186 func is a function taking a single string argument containing the text that was
0187 sent to the script.
0188 
0189 registerEvent() returns True on success and False in case of an error."""
0190 
0191     if portno in evSocks:
0192         if portno in evHandlers and func is None:
0193             del evHandlers[portno]
0194             return True
0195         return False
0196     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
0197     s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
0198     s.setblocking(0)
0199     s.bind(("", portno))
0200     s.listen(1)
0201     evSocks[portno] = s
0202     evPorts[s] = portno
0203     if func is not None:
0204         evHandlers[portno] = func
0205     return True
0206     
0207 #---------------------------------------------------------------
0208 
0209 def unregisterEvent(portno):
0210     global evSocks, evPorts, evHandlers
0211     """unregisterEvent(portno) -> boolean\n
0212 Unregister an event associated to port `portno'\n
0213 Returns False if the event was not registered before, True on success"""
0214     if portno not in evSocks:
0215         return False
0216     del evPorts[evSocks[portno]]
0217     del evSocks[portno]
0218     if portno in evHandlers:
0219         del evHandlers[portno]
0220     return True
0221 
0222 #---------------------------------------------------------------
0223 
0224 def wait4Event(timeout = None):
0225     global evSocks, evPorts, evHandlers
0226     """wait4Event([timeout]) -> list of tuples
0227 Wait for an event to occur\n
0228 timeout -- seconds to wait before giving up and returning None (optional),
0229            0 causes immediate return if no data is present.\n
0230 wait4Event() efficiently suspends the script until one or more previously registered
0231 events occur.
0232 For events with an attached event handler the latter is called, getting the received
0233 line passed as the only argument.
0234 If at least one event without an attached event handler occured, a tuple containing the
0235 port number and the received data is added to a list which is returned when all pending
0236 events have been processed.
0237 If all registered events possess an attached event handler, wait4Event() resumes infinitely.
0238 If any event handler returns an actual value (i.e. not None), wait4Event() immediately
0239 returns this value. This should be used for clean script termination.
0240 However, there is no guarantee that all pending events are processed in that case
0241 (although it's highly unlikely that more than one event occurs at a time).
0242 Eventually, if the optional timeout expires, an empty list is returned."""
0243 
0244     while 1:
0245         if evSocks == {}:
0246             return None
0247         retval = []
0248         handler_retval = None
0249         for s in select.select(evSocks.values(), [], [], timeout)[0]:
0250             port = evPorts[s]
0251             try:
0252                 in_s = s.recv(bufferSize) # socket already connected
0253             except socket.error: # new connection
0254                 del evSocks[port]
0255                 del evPorts[s]
0256                 evSocks[port] = s.accept()[0]
0257                 evPorts[evSocks[port]] = port
0258                 in_s = evSocks[port].recv(bufferSize)
0259             if in_s == "": # conn reset by peer
0260                 del evPorts[evSocks[port]]
0261                 del evSocks[port]
0262                 continue
0263             if port in evHandlers:
0264                 for line in in_s[:-1].split("\n"):
0265                     handlerRetval = evHandlers[port](line)
0266                     if handlerRetval is not None:
0267                         return handlerRetval
0268             else:
0269                 retval.append((port, in_s))
0270         if retval != [] or timeout is not None:
0271             return retval
0272 
0273 #---------------------------------------------------------------
0274 
0275 def my_func(args):
0276     print "HANDLER CALLED: " + args
0277     return True
0278 
0279 #---------------------------------------------------------------
0280 
0281 if __name__ == "__main__":
0282 
0283     initSocket()
0284     print "KMUDDY_SOCKET: " + socketName
0285     setVariable("test", "this is some test")
0286     print "Value of variable 'test' has been set"
0287     test = getVariable("test")
0288     print "So, value of test = " + test
0289     unsetVariable("test")
0290     print "The variable test is no longer set"
0291     registerEvent(1234, my_func)
0292     wait4Event()
0293     closeSocket()
0294