From 5d61e939aa78271adbc1cabbed4245e07ba7e38c Mon Sep 17 00:00:00 2001 From: Jeremy Penner Date: Fri, 18 Mar 2011 17:10:02 -0700 Subject: [PATCH] initial commit --- Makefile | 2 + ansi.py | 13 + ansi_cython.pyx | 540 +++++++++++++++++++++++++++++++ ansi_python.py | 294 +++++++++++++++++ auth.py | 27 ++ basetoken.py | 826 +++++++++++++++++++++++++++++++++++++++++++++++ build_entity.py | 16 + config.py | 23 ++ engine.py | 517 +++++++++++++++++++++++++++++ entity.py | 1 + haxor.py | 71 ++++ login.py | 35 ++ marmots_todo.txt | 12 + runserver.bat | 3 + save.sh | 4 + savecycle.sh | 33 ++ scripting.py | 711 ++++++++++++++++++++++++++++++++++++++++ setup.py | 12 + telnet.py | 233 +++++++++++++ testsig.py | 8 + tpers.py | 268 +++++++++++++++ tpers_old.py | 103 ++++++ util.py | 127 ++++++++ whiteboard.py | 678 ++++++++++++++++++++++++++++++++++++++ 24 files changed, 4557 insertions(+) create mode 100644 Makefile create mode 100644 ansi.py create mode 100644 ansi_cython.pyx create mode 100644 ansi_python.py create mode 100644 auth.py create mode 100644 basetoken.py create mode 100644 build_entity.py create mode 100644 config.py create mode 100644 engine.py create mode 100644 entity.py create mode 100644 haxor.py create mode 100644 login.py create mode 100644 marmots_todo.txt create mode 100644 runserver.bat create mode 100644 save.sh create mode 100644 savecycle.sh create mode 100644 scripting.py create mode 100644 setup.py create mode 100644 telnet.py create mode 100644 testsig.py create mode 100644 tpers.py create mode 100644 tpers_old.py create mode 100644 util.py create mode 100644 whiteboard.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4a1f97a --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +all: + python setup.py build_ext --inplace diff --git a/ansi.py b/ansi.py new file mode 100644 index 0000000..b7fdfaf --- /dev/null +++ b/ansi.py @@ -0,0 +1,13 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20091215224421.2:@thin ansi.py +import config +if config.USE_CYTHON: + from ansi_cython import * +else: + from ansi_python import * + +#@+others +#@-others +#@nonl +#@-node:jpenner.20091215224421.2:@thin ansi.py +#@-leo diff --git a/ansi_cython.pyx b/ansi_cython.pyx new file mode 100644 index 0000000..11091f7 --- /dev/null +++ b/ansi_cython.pyx @@ -0,0 +1,540 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20091212133112.2:@thin ansi_cython.pyx +#@@language python +import config + +cdef extern from "stdio.h": + int sprintf(char *, char *,...) + +cdef extern from "stdlib.h": + ctypedef unsigned long size_t + void free(void *ptr) + void *malloc(size_t size) + void *calloc(size_t nelem, size_t elsize) + void *realloc(void *ptr, size_t size) + size_t strlen(char *s) + char *strcpy(char *dest, char *src) + +#@+others +#@+node:jpenner.20091212133112.3:Codes +esc = '%s['%chr(27) +reset = '%s0m'%esc +cls = '%s2j'%esc + +BLACK = 0 +RED = 1 +GREEN = 2 +YELLOW = 3 +BLUE = 4 +MAGENTA = 5 +CYAN = 6 +WHITE = 7 + +FBRIGHT = 8 +FBLINK = 16 + +rgcolorBg = range(8) +rgcolorFg = range(8) +rgcolorFg.extend([col | FBRIGHT for col in range(8)]) + +K_BACKSPACE = chr(8) +K_TAB = chr(9) +K_RETURN = chr(10) +K_DEL = chr(127) +K_LEFT = 256 +K_RIGHT = 257 +K_UP = 258 +K_DOWN = 259 +K_HOME = 260 +K_END = 261 +K_PGUP = 262 +K_PGDN = 263 + +def StrKey(ch): + if type(ch) == str: + return ord(ch) + return str(ch) + +def Ctrl(ch): + ch = ch.upper() + assert ch != 'M' and ch != 'J' and ch != 'H' + return chr(ord(ch - 'A' + 1)) + +def FEnter(key): + return key == "\n" or key == "\r" +#@nonl +#@-node:jpenner.20091212133112.3:Codes +#@+node:jpenner.20091212133112.4:Ach +# ANSI Character - contains prop and character + +cdef struct SAch: + unsigned char ch + char fgCol + char bgCol + +cdef union UAch: + int ach + SAch sach + +cpdef MkAch(char *ch, char fgcol=WHITE, char bgcol=BLACK): + cdef UAch uach + uach.sach.ch = ch[0] + uach.sach.fgCol = fgcol + uach.sach.bgCol = bgcol + return uach.ach + +# the class must be named Ach for backwards compatibility, but the hungarian name is OAch +class Ach(object): + def __init__(self, int ach): + cdef UAch uach + uach.ach = ach + self.ch = chr(uach.sach.ch) + self.fgCol = uach.sach.fgCol + self.bgCol = uach.sach.bgCol + + def ToAch(self): + return MkAch(self.ch, self.fgCol, self.bgCol) + +cdef AchFromOach(oach): + if oach == None: + return achBlankI + return MkAch(oach.ch, oach.fgCol, oach.bgCol) +cdef OachFromAch(int ach): + if ach == achBlankI: + return None + assert ach != achInvdI + return Ach(ach) + +def ChFromAch(int ach): + cdef UAch uach + uach.ach = ach + return chr(uach.sach.ch) + +achBlank = 0 +achInvd = -1 + +cdef enum: + achBlankI = 0 + achInvdI = -1 + chesc = 27 + FBRIGHTI = 8 + FBLINKI = 16 + +cdef inline AstFromAch(int ach, int achPrev = achInvdI): + cdef UAch uach, uachPrev + uach.ach = ach + uachPrev.ach = achPrev + cdef char ast[60] + cdef char sgr[4] + cdef int csgr = 0 + + if (achPrev == achInvdI) or (uachPrev.sach.fgCol != uach.sach.fgCol): + sgr[csgr] = (uach.sach.fgCol & 7) + 30 + csgr = csgr + 1 + fBright = uach.sach.fgCol & FBRIGHTI + if (achPrev == achInvdI) or (fBright != uachPrev.sach.fgCol & FBRIGHTI): + if fBright: + sgr[csgr] = 1 + else: + sgr[csgr] = 2 + csgr = csgr + 1 + fBlink = uach.sach.fgCol & FBLINKI + if (achPrev == achInvdI) or (fBlink != uachPrev.sach.fgCol & FBLINKI): + if fBlink: + sgr[csgr] = 5 + else: + sgr[csgr] = 25 + csgr = csgr + 1 + if (achPrev == achInvdI) or (uachPrev.sach.bgCol != uach.sach.bgCol): + sgr[csgr] = uach.sach.bgCol + 40 + csgr = csgr + 1 + if csgr == 0: + sprintf(ast, "%c", uach.sach.ch) + elif csgr == 1: + sprintf(ast, "%c[%dm%c", chesc, sgr[0], uach.sach.ch) + elif csgr == 2: + sprintf(ast, "%c[%d;%dm%c", chesc, sgr[0], sgr[1], uach.sach.ch) + elif csgr == 3: + sprintf(ast, "%c[%d;%d;%dm%c", chesc, sgr[0], sgr[1], sgr[2], uach.sach.ch) + elif csgr == 4: + sprintf(ast, "%c[%d;%d;%d;%dm%c", chesc, sgr[0], sgr[1], sgr[2], sgr[3], uach.sach.ch) + else: + assert csgr >= 0 and csgr <= 4, "more sgr possibilities, please handle" + return ast + +#@-node:jpenner.20091212133112.4:Ach +#@+node:jpenner.20091212133112.5:HTML +mpch_entity = [0, 9786, 9787, 9829, 9830, 9827, 9824, 8226, 9688, 9675, 9689, 9794, 9792, 9834, 9835, 9788, 9658, 9668, 8597, 8252, + 182, 167, 9644, 8616, 8593, 8595, 8594, 8592, 8735, 8596, 9650, 9660, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 8962, 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, + 249, 255, 214, 220, 162, 163, 165, 8359, 402, 225, 237, 243, 250, 241, 209, 170, 186, 191, 8976, 172, 189, 188, 161, 171, + 187, 9617, 9618, 9619, 9474, 9508, 9569, 9570, 9558, 9557, 9571, 9553, 9559, 9565, 9564, 9563, 9488, 9492, 9524, 9516, + 9500, 9472, 9532, 9566, 9567, 9562, 9556, 9577, 9574, 9568, 9552, 9580, 9575, 9576, 9572, 9573, 9561, 9560, 9554, 9555, + 9579, 9578, 9496, 9484, 9608, 9604, 9612, 9616, 9600, 945, 223, 915, 960, 931, 963, 181, 964, 934, 920, 937, 948, 8734, + 966, 949, 8745, 8801, 177, 8805, 8804, 8992, 8993, 247, 8776, 176, 8729, 183, 8730, 8319, 178, 9632, 160] + +setch_unsafe = set([ord('&'), ord('<'), ord('>'), ord('"'), ord("'")]) +mpcol_hcol = ["#000000", "#AA0000", "#00AA00", "#AA5500", "#0000AA", "#AA00AA", "#00AAAA", "#AAAAAA", + "#555555", "#FF5555", "#55FF55", "#FFFF55", "#5555FF", "#FF55FF", "#55FFFF", "#FFFFFF"] + +cdef HstFromAch(int ach, int achPrev = achInvdI): + cdef UAch uach, uachPrev + uach.ach = ach + uachPrev.ach = achPrev + stStyle = None + if achPrev == achInvdI or uachPrev.sach.fgCol != uach.sach.fgCol or uachPrev.sach.bgCol != uach.sach.bgCol: + stStyle = "color:" + mpcol_hcol[uach.sach.fgCol & 15] + ";background-color:" + mpcol_hcol[uach.sach.bgCol & 15] + rgst = [] + if stStyle != None and achPrev != achInvdI: + rgst.append("") + if stStyle != None: + rgst.append("") + if ach == achBlank: + rgst.append(' ') + else: + entity = mpch_entity[uach.sach.ch] + if entity == uach.sach.ch and not uach.sach.ch in setch_unsafe: + rgst.append(chr(uach.sach.ch)) + else: + rgst.append("&#" + str(entity) + ";") + return ''.join(rgst) + +#@-node:jpenner.20091212133112.5:HTML +#@+node:jpenner.20091212133112.6:Pos +class Pos(object): + def __init__(self, int w=config.W, int h=config.H, int x=1, int y=1): + self.i = PosI(w, h, x, y) + def X(self): + cdef PosI posI = self.i + return posI.X() + def Y(self): + cdef PosI posI = self.i + return posI.Y() + def Inc(self): + cdef PosI posI = self.i + posI.Inc() + def Up(self): + cdef PosI posI = self.i + return posI.Up() + def Down(self): + cdef PosI posI = self.i + return posI.Down() + def Left(self): + cdef PosI posI = self.i + return posI.Left() + def Right(self): + cdef PosI posI = self.i + return posI.Right() + def __setstate__(self, state): + self.i = PosI(state["w"], state["h"], state["xz"] + 1, state["yz"] + 1) + def __getstate__(self): + cdef PosI posI = self.i + return {"w": posI.w, "h": posI.h, "xz": posI.xz, "yz": posI.yz} + +cdef class PosI(object): + cdef int w, h, xz, yz + def __init__(self, int w=config.W, int h=config.H, int x=1, int y=1): + self.w = w + self.h = h + self.xz = x - 1 + self.yz = y - 1 + + cdef inline X(self): return self.xz + 1 + cdef inline Y(self): return self.yz + 1 + + cdef inline Inc(self): + if self.xz == self.w - 1: + if self.yz < self.h: + self.xz = 0 + self.yz = self.yz + 1 + else: + self.xz = self.xz + 1 + + cdef inline Up(self): + if self.yz > 0: + self.yz = self.yz - 1 + return True + cdef inline Down(self): + if self.yz < self.h - 1: + self.yz = self.yz + 1 + return True + cdef inline Left(self): + if self.xz > 0: + self.xz = self.xz - 1 + return True + cdef inline Right(self): + if self.xz < self.w - 1: + self.xz = self.xz + 1 + return True + cdef ToPos(self): + return Pos(self.w, self.h, self.X(), self.Y()) +#@nonl +#@-node:jpenner.20091212133112.6:Pos +#@+node:jpenner.20100105170222.1:Ascr + +class Ascr(object): + def __init__(self, int w=config.W, int h=config.H, int achFill = achBlankI): + cdef AscrI ascrI = AscrI(w,h) + self.i = ascrI + if achFill != achBlankI: + ascrI.Fill(achFill, w, h) + + def __setstate__(self, state): + cdef AscrI ascrI = AscrI(state["w"], state["h"]) + self.i = ascrI + cdef int iach = 0 + for mpxz_oach in state["mpyzxz_ach"]: + for oach in mpxz_oach: + ascrI.rgach[iach] = AchFromOach(oach) + iach = iach + 1 + + def __getstate__(self): + cdef AscrI ascrI = self.i + cdef int iach = 0 + mpyzxz_oach = [] + for yz in range(ascrI.h): + mpxz_oach = [] + for xz in range(ascrI.w): + mpxz_oach.append(OachFromAch(ascrI.rgach[iach])) + iach = iach + 1 + mpyzxz_oach.append(mpxz_oach) + return {"w":ascrI.w, "h":ascrI.h, "mpyzxz_ach": mpyzxz_oach} + + def W(self): + cdef AscrI ascrI = self.i + return ascrI.w + def H(self): + cdef AscrI ascrI = self.i + return ascrI.h + def ZPos(self, int xz, int yz): + cdef AscrI ascrI = self.i + return ascrI.ZPos(xz, yz) + + #@ @+others + #@+node:jpenner.20100105170222.2:Put + def PutAch(self, int ach, int x, int y): + cdef AscrI ascrI = self.i + ascrI.PutAch(ach, x, y) + + def GetAch(self, int x, int y): + cdef AscrI ascrI = self.i + return ascrI.rgach[ascrI.IAch(x - 1, y - 1)] + + def PutRgach(self, rgach, int x, int y): + cdef AscrI ascrI = self.i + ascrI.PutRgach(rgach, x, y) + + def PutSt(self, char *st, int x, int y, int colFg = WHITE, int colBg=BLACK): + cdef AscrI ascrI = self.i + ascrI.PutSt(st, x, y, colFg, colBg) + + def Fill(self, int ach, int wIn, int hIn, int x = 1, int y = 1): + cdef AscrI ascrI = self.i + ascrI.Fill(ach, wIn, hIn, x, y) + + def PutAscr(self, ascr, int x=1, int y=1): + cdef AscrI ascrI = self.i + ascrI.PutAscr(ascr.i, x, y) + #@nonl + #@-node:jpenner.20100105170222.2:Put + #@+node:jpenner.20100105170222.3:Diff + def AstDiff(self, ascr): + cdef AscrI ascrI = self.i + return ascrI.AstDiff(ascr.i) + + def Hst(self): + cdef AscrI ascrI = self.i + return ascrI.Hst() + #@-node:jpenner.20100105170222.3:Diff + #@+node:jpenner.20091212133112.10:Ast + def Ast(self): + return Ascr(self.W(), self.H(), achInvdI).AstDiff(self) #stupid implementation + #@nonl + #@-node:jpenner.20091212133112.10:Ast + #@-others +#@-node:jpenner.20100105170222.1:Ascr +#@+node:jpenner.20091212133112.7:AscrI + +# ANSI Screen +cdef class AscrI(object): + cdef int w, h + cdef int *rgach + def __cinit__(self, int w=config.W, int h=config.H, int achFill = achBlankI): + self.rgach = calloc(w * h, sizeof(int)) + if achFill != achBlankI: + for iach in range(w * h): + self.rgach[iach] = achFill + self.w = w + self.h = h + + def __dealloc__(self): + free(self.rgach) + + cdef Zpos(self, int xz, int yz): + return Pos(self.w, self.h, xz + 1, yz + 1) + + #@ @+others + #@+node:jpenner.20091212133112.8:Put + cdef inline IAch(self, int xz, int yz): + return xz + (self.w * yz) + + cdef PutAch(self, int ach, int x, int y): + if not (x < 1 or y < 1 or x > self.w or y > self.h): #clip + self.rgach[self.IAch(x - 1, y - 1)] = ach + #self.mpyzxz_ach[y-1][x-1] = ach + + cdef inline GetAch(self, int x, int y): + return self.rgach[self.IAch(x - 1, y - 1)] + + cdef PutRgach(self, rgach, int x, int y): + cdef int xz = x - 1 + cdef int iach + if xz < self.w: + if xz < 0: + rgach = rgach[min(-xz, len(rgach)):] + xz = 0 + if xz + len(rgach) >= self.w: + rgach = rgach[:self.w - xz] + iach = self.IAch(xz, y - 1) + for ach in rgach: + if ach != achBlankI: + self.rgach[iach] = ach + iach = iach + 1 + #self.mpyzxz_ach[y-1][xz:xz + len(rgach)] = rgach + + cdef PutSt(self, char *st, int x, int y, int colFg = WHITE, int colBg=BLACK): + if y < 1 or y > self.h: + return + cdef int cch = len(st) + cdef int ichStart = 0 + cdef int xz = x - 1 + if xz < 0: + ichStart = (-xz) + cch = cch - ichStart + xz = 0 + cch = min(cch, self.w - xz) + cdef UAch uach + uach.sach.fgCol = colFg + uach.sach.bgCol = colBg + cdef int iach = self.IAch(xz, y - 1) + for ich in range(ichStart, ichStart + cch): + uach.sach.ch = st[ich] + self.rgach[iach] = uach.ach + iach = iach + 1 + + #self.PutRgach([MkAch(ch, colFg, colBg) for ch in st], x, y) + + cdef Fill(self, int ach, int wIn, int hIn, int x = 1, int y = 1): + if x > self.w or y > self.h or ach == achBlankI: + return + cdef int xz = max(x - 1, 0) + cdef int yz = max(y - 1, 0) + cdef int w = min(self.w - xz, wIn) + cdef int h = min(self.h - yz, hIn) + cdef int iach + for yzT in range(yz, yz + h): + iach = self.IAch(xz, yzT) + for xzT in range(w): + self.rgach[iach] = ach + iach = iach + 1 + #self.mpyzxz_ach[yzT][xz:xz + w] = ach + + cdef PutAscr(self, AscrI ascr, int x=1, int y=1): + if x > self.w or y > self.h: + return + cdef int xz = max(x - 1, 0) + cdef int yz = max(y - 1, 0) + cdef int w = min(self.w - xz, ascr.w) + cdef int h = min(self.h - yz, ascr.h) + cdef int yzOther = 0 + cdef int iach, iachOther, ach + for yzT in range(yz, yz + h): + iach = self.IAch(xz, yzT) + iachOther = ascr.IAch(0, yzOther) + for xzT in range(w): + ach = ascr.rgach[iachOther] + if ach != achBlankI: + self.rgach[iach] = ach + iach = iach + 1 + iachOther = iachOther + 1 + yzOther = yzOther + 1 + #@-node:jpenner.20091212133112.8:Put + #@+node:jpenner.20091212133112.9:AstDiff + cdef AstDiff(self, AscrI ascr): + assert self.w == ascr.w and self.h == ascr.h + cdef int xz = 0 + cdef int yz = 0 + cdef int xzPred = -1 + cdef int achPrev = achInvdI + cdef int achBeforeEOL = achInvdI + cdef int achOld, achNew, achNewRaw + cdef int achSpace = MkAch(' ') + + rgast = [] + for iach in range(self.w * self.h): + achOld = self.rgach[iach] + achNewRaw = ascr.rgach[iach] + if achNewRaw == achBlankI: + achNew = achSpace + else: + achNew = achNewRaw + if xz == self.w - 2: + achBeforeEOL = achNew + if achOld != achNewRaw: + if xz == self.w - 1: + # Linewrap avoidance algorithm: + # when drawing the last character on a line, draw it in the space occupied by the character before. + rgast.append("%s%d;%dH" % (esc, yz + 1, xz)) + rgast.append(AstFromAch(achNew, achInvdI)) + # then, move the cursor back onto the character we just drew, and perform an insert. + rgast.append("%sD%s@" % (esc, esc)) + # finally, draw the character before the last character in the line. + rgast.append(AstFromAch(achBeforeEOL, achNew)) + else: + if xz != xzPred: + xzPred = xz + rgast.append("%s%d;%dH" % (esc, yz + 1, xz + 1)) + achPrev = achInvdI + rgast.append(AstFromAch(achNew, achPrev)) + + achPrev = achNew + xzPred = xzPred + 1 + xz = xz + 1 + if xz == self.w: + xz = 0 + xzPred = -1 + yz = yz + 1 + return "".join(rgast) + #@-node:jpenner.20091212133112.9:AstDiff + #@+node:jpenner.20091212133112.11:Hst + cdef Hst(self): + rgst = ["
"]
+        cdef int achPrev = achInvdI
+        cdef int xz = 0
+        cdef int ach
+        for iach in range(self.w * self.h):
+            ach = self.rgach[iach]
+            rgst.append(HstFromAch(ach, achPrev))
+            achPrev = ach
+            xz = xz + 1
+            if xz == self.w:
+                xz = 0
+                rgst.append("
") + + rgst.append("
") + return ''.join(rgst) + #@-node:jpenner.20091212133112.11:Hst + #@-others +#@-node:jpenner.20091212133112.7:AscrI +#@+node:jpenner.20091212133112.12:FKeyPrintable +def FKeyPrintable(key): + return type(key) == str and (ord(key) >= 32 and ord(key) <= 126) + +#@-node:jpenner.20091212133112.12:FKeyPrintable +#@-others +#@nonl +#@-node:jpenner.20091212133112.2:@thin ansi_cython.pyx +#@-leo diff --git a/ansi_python.py b/ansi_python.py new file mode 100644 index 0000000..e4b38a0 --- /dev/null +++ b/ansi_python.py @@ -0,0 +1,294 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20090510085557.4:@thin ansi_python.py +#@@language python +import config +import numpy + +#@+others +#@+node:jpenner.20091212133112.3:Codes +esc = '%s['%chr(27) +reset = '%s0m'%esc +cls = '%s2j'%esc + +BLACK = 0 +RED = 1 +GREEN = 2 +YELLOW = 3 +BLUE = 4 +MAGENTA = 5 +CYAN = 6 +WHITE = 7 + +FBRIGHT = 8 +FBLINK = 16 + +rgcolorBg = range(8) +rgcolorFg = range(8) +rgcolorFg.extend([col | FBRIGHT for col in range(8)]) + +K_BACKSPACE = chr(8) +K_TAB = chr(9) +K_RETURN = chr(10) +K_DEL = chr(127) +K_LEFT = 256 +K_RIGHT = 257 +K_UP = 258 +K_DOWN = 259 +K_HOME = 260 +K_END = 261 +K_PGUP = 262 +K_PGDN = 263 + +def StrKey(ch): + if type(ch) == str: + return ord(ch) + return str(ch) + +def Ctrl(ch): + ch = ch.upper() + assert ch != 'M' and ch != 'J' and ch != 'H' + return chr(ord(ch - 'A' + 1)) + +def FEnter(key): + return key == "\n" or key == "\r" +#@nonl +#@-node:jpenner.20091212133112.3:Codes +#@+node:jpenner.20090510085557.6:Ach +# ANSI Character - contains prop and character +class Ach(object): + def __init__(self, ch, fgcol=WHITE, bgcol=BLACK): + self.ch = ch + self.fgCol = fgcol + self.bgCol = bgcol + def __eq__(self, ach): + return type(ach) is Ach and self.ch == ach.ch and self.fgCol == ach.fgCol and self.bgCol == ach.bgCol + def __ne__(self, ach): + return not (self == ach) + +def MkAch(ch, fgcol=WHITE, bgcol=BLACK): + return Ach(ch, fgcol, bgcol) + +def ChFromAch(ach): + return ach.ch + +achBlank = Ach(' ') +achInvd = Ach(chr(255), -1, -1) + +def AstFromAch(ach, achPrev = None): + ast = ach.ch + rgsgr = [] + if (achPrev == None) or (achPrev.fgCol != ach.fgCol): + rgsgr.append(str((ach.fgCol & 7) + 30)) + fBright = ach.fgCol & FBRIGHT + if (achPrev == None) or (fBright != achPrev.fgCol & FBRIGHT): + if fBright: + rgsgr.append("1") + else: + rgsgr.append("2") + fBlink = ach.fgCol & FBLINK + if (achPrev == None) or (fBlink != achPrev.fgCol & FBLINK): + if fBlink: + rgsgr.append("5") + else: + rgsgr.append("25") + if (achPrev == None) or (achPrev.bgCol != ach.bgCol): + rgsgr.append(str(ach.bgCol + 40)) + if len(rgsgr) > 0: + ast = esc + ";".join(rgsgr) + "m" + ast + return ast + +dtAch = numpy.dtype(object) + +#@-node:jpenner.20090510085557.6:Ach +#@+node:jpenner.20091016050502.2:HTML +mpch_entity = [0, 9786, 9787, 9829, 9830, 9827, 9824, 8226, 9688, 9675, 9689, 9794, 9792, 9834, 9835, 9788, 9658, 9668, 8597, 8252, + 182, 167, 9644, 8616, 8593, 8595, 8594, 8592, 8735, 8596, 9650, 9660, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 8962, 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, + 249, 255, 214, 220, 162, 163, 165, 8359, 402, 225, 237, 243, 250, 241, 209, 170, 186, 191, 8976, 172, 189, 188, 161, 171, + 187, 9617, 9618, 9619, 9474, 9508, 9569, 9570, 9558, 9557, 9571, 9553, 9559, 9565, 9564, 9563, 9488, 9492, 9524, 9516, + 9500, 9472, 9532, 9566, 9567, 9562, 9556, 9577, 9574, 9568, 9552, 9580, 9575, 9576, 9572, 9573, 9561, 9560, 9554, 9555, + 9579, 9578, 9496, 9484, 9608, 9604, 9612, 9616, 9600, 945, 223, 915, 960, 931, 963, 181, 964, 934, 920, 937, 948, 8734, + 966, 949, 8745, 8801, 177, 8805, 8804, 8992, 8993, 247, 8776, 176, 8729, 183, 8730, 8319, 178, 9632, 160] + +setch_unsafe = set(['&', '<', '>', '"', "'"]) +mpcol_hcol = ["#000000", "#AA0000", "#00AA00", "#AA5500", "#0000AA", "#AA00AA", "#00AAAA", "#AAAAAA", + "#555555", "#FF5555", "#55FF55", "#FFFF55", "#5555FF", "#FF55FF", "#55FFFF", "#FFFFFF"] + +def HstFromAch(ach, achPrev = None): + stStyle = None + if achPrev == None or achPrev.fgCol != ach.fgCol or achPrev.bgCol != ach.bgCol: + stStyle = "color:" + mpcol_hcol[ach.fgCol & 15] + ";background-color:" + mpcol_hcol[ach.bgCol & 15] + rgst = [] + if stStyle != None and achPrev != None: + rgst.append("
") + if stStyle != None: + rgst.append("") + ch = ord(ach.ch) + entity = mpch_entity[ch] + if entity == ch and not ach.ch in setch_unsafe: + rgst.append(ach.ch) + else: + rgst.append("&#" + str(mpch_entity[ord(ach.ch)]) + ";") + return ''.join(rgst) +#@nonl +#@-node:jpenner.20091016050502.2:HTML +#@+node:jpenner.20090510085557.7:Pos +class Pos(object): + def __init__(self, w=config.W, h=config.H, x=1, y=1): + self.w = w + self.h = h + self.xz = x - 1 + self.yz = y - 1 + + def X(self): return self.xz + 1 + def Y(self): return self.yz + 1 + + def Inc(self): + if self.xz == self.w - 1: + if self.yz < self.h: + self.xz = 0 + self.yz = self.yz + 1 + else: + self.xz = self.xz + 1 + + def Up(self): + if self.yz > 0: + self.yz = self.yz - 1 + def Down(self): + if self.yz < self.h - 1: + self.yz = self.yz + 1 + def Left(self): + if self.xz > 0: + self.xz = self.xz - 1 + def Right(self): + if self.xz < self.w - 1: + self.xz = self.xz + 1 +#@-node:jpenner.20090510085557.7:Pos +#@+node:jpenner.20090510085557.8:Ascr +# ANSI Screen +class Ascr(object): + def __init__(self, w=config.W, h=config.H, achFill = None): + self.mpyzxz_ach = numpy.empty((h,w), dtAch) + self.mpyzxz_ach[:] = achFill + self.w = w + self.h = h + + def Zpos(self, xz, yz): + return Pos(self.w, self.h, xz + 1, yz + 1) + + def W(self): return self.w + def H(self): return self.h + + #@ @+others + #@+node:jpenner.20090526105844.8:Put + def PutAch(self, ach, x, y): + if not (x < 1 or y < 1 or x > self.w or y > self.h): #clip + self.mpyzxz_ach[y-1][x-1] = ach + + def GetAch(self, x, y): + return self.mpyzxz_ach[y-1][x-1] + + def PutRgach(self, rgach, x, y): + xz = x - 1 + if xz < self.w: + if xz < 0: + rgach = rgach[min(-xz, len(rgach)):] + xz = 0 + if xz + len(rgach) >= self.w: + rgach = rgach[:self.w - xz] + self.mpyzxz_ach[y-1][xz:xz + len(rgach)] = rgach + + def PutSt(self, st, x, y, colFg = WHITE, colBg=BLACK): + self.PutRgach([MkAch(ch, colFg, colBg) for ch in st], x, y) + + def Fill(self, ach, w, h, x = 1, y = 1): + if x > self.w or y > self.h: + return + xz = max(x - 1, 0) + yz = max(y - 1, 0) + w = min(self.w - xz, w) + h = min(self.h - yz, h) + for yzT in range(yz, yz + h): + self.mpyzxz_ach[yzT][xz:xz + w] = ach + + def PutAscr(self, ascr, x=1, y=1): + for irow, rgachRow in enumerate(ascr.mpyzxz_ach): + iachPut = -1 + for iach, ach in enumerate(rgachRow): + if iachPut < 0 and ach != None: + iachPut = iach + elif iachPut >= 0 and ach == None: + self.PutRgach(rgachRow[iachPut:iach], x + iachPut, y + irow) + iachPut = -1 + if iachPut >= 0: + self.PutRgach(rgachRow[iachPut:], x + iachPut, y + irow) + iachPut = -1 + #@-node:jpenner.20090526105844.8:Put + #@+node:jpenner.20090527144214.3:Diff + def AstDiff(self, ascr): + assert self.w == ascr.w and self.h == ascr.h + rgast = [] + achPrev = None + yz = 0 + for rgachRowOld, rgachRowNew in zip(self.mpyzxz_ach, ascr.mpyzxz_ach): + xz = 0 + xzPred = -1 + for achOld, achNew in zip(rgachRowOld, rgachRowNew): + if xz == self.w - 2: + achBeforeEOL = achNew + if achOld != achNew: + achNew = achNew or achBlank + if xz == self.w - 1: + # Linewrap avoidance algorithm: + # when drawing the last character on a line, draw it in the space occupied by the character before. + rgast.append("%s%d;%dH" % (esc, yz + 1, xz)) + rgast.append(AstFromAch(achNew, None)) + # then, move the cursor back onto the character we just drew, and perform an insert. + rgast.append("%sD%s@" % (esc, esc)) + # finally, draw the character before the last character in the line. + rgast.append(AstFromAch(achBeforeEOL or achBlank, achNew)) + else: + if xz != xzPred: + xzPred = xz + rgast.append("%s%d;%dH" % (esc, yz + 1, xz + 1)) + achPrev = None + rgast.append(AstFromAch(achNew, achPrev)) + achPrev = achNew + xzPred = xzPred + 1 + xz = xz + 1 + yz = yz + 1 + return "".join(rgast) + #@nonl + #@-node:jpenner.20090527144214.3:Diff + #@+node:jpenner.20090529173104.26:Ast + def Ast(self): + return Ascr(self.w, self.h, achInvd).AstDiff(self) #stupid implementation + #@nonl + #@-node:jpenner.20090529173104.26:Ast + #@+node:jpenner.20091016050502.3:Hst + def Hst(self): + rgst = ["
"]
+        achPrev = None
+        for rgachRow in self.mpyzxz_ach:
+            for ach in rgachRow:
+                rgst.append(HstFromAch(ach or achBlank, achPrev))
+                achPrev = ach or achBlank
+            rgst.append("
") + rgst.append("
") + return ''.join(rgst) + #@nonl + #@-node:jpenner.20091016050502.3:Hst + #@-others +#@-node:jpenner.20090510085557.8:Ascr +#@+node:jpenner.20090521203229.341:FKeyPrintable +def FKeyPrintable(key): + return type(key) == str and (ord(key) >= 32 and ord(key) <= 126) + +#@-node:jpenner.20090521203229.341:FKeyPrintable +#@-others +#@nonl +#@-node:jpenner.20090510085557.4:@thin ansi_python.py +#@-leo diff --git a/auth.py b/auth.py new file mode 100644 index 0000000..bf3556b --- /dev/null +++ b/auth.py @@ -0,0 +1,27 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20090607135901.1:@thin auth.py +import MySQLdb +import hashlib +import config + +class NoopAuth(object): + def FAuthorized(self, user, passwd): + return True + +class DrupalAuth(object): + def FAuthorized(self, user, passwd): + conn = MySQLdb.connect(host = config.drupaldb_host, user = config.drupaldb_user, passwd = config.drupaldb_password, db = config.drupaldb) + try: + cursor = conn.cursor() + try: + cursor.execute("SELECT name, pass FROM users WHERE name = %s AND pass = %s", (user, hashlib.md5(passwd).hexdigest())) + return cursor.fetchone() != None + finally: + cursor.close() + except Exception: + return False + finally: + conn.close() + +#@-node:jpenner.20090607135901.1:@thin auth.py +#@-leo diff --git a/basetoken.py b/basetoken.py new file mode 100644 index 0000000..bb503aa --- /dev/null +++ b/basetoken.py @@ -0,0 +1,826 @@ +from engine import * +import config +from zope.interface import implements, Interface +import weakref +from util import * + +class Blinker(Token): + def InitPersistent(self, owner, setter, rgVal = [True, False], interval = 500): + Token.InitPersistent(self, owner) + self.setter = setter + self.rgVal = rgVal + self.interval = interval + def run(self): + while True: + for val in self.rgVal: + self.setter(val) + self.wait(self.interval) + +class StaticGraphic(Token): + def InitPersistent(self, owner, ascr, pos, *rgtag): + Token.InitPersistent(self, owner) + self.pos = pos + self.tag("drawable", *rgtag) + self.ascr = ascr + def draw(self, ascr, client): + ascr.PutAscr(self.ascr, self.pos[0], self.pos[1]) + + +class AnimatedGraphic(StaticGraphic): + def InitPersistent(self, owner, rgascr, pos, interval, *rgtag): + StaticGraphic.InitPersistent(self, owner, rgascr[0], pos, *rgtag) + Blinker(self, self.setAscr, rgascr, interval) + def setAscr(self, ascr): + self.ascr = ascr + +class Terminal(Token): + def InitPersistent(self, owner, client, w = config.W, h = config.H): + Token.InitPersistent(self, owner, "drawable", "background") + self.client = client + self.x = 0 + self.y = 0 + self.w = w + self.h = h + self.fWrapHoriz = False + self.fWrapVert = True + self.stLine = None + self.xLine = None + self.yLine = None + self.screen = [] + self.fBlink = False + self.colBg = ansi.BLUE + self.chEcho = None + for y in range(h): + self.screen.append("") + Blinker(self, self.setBlink) + + def setBlink(self, fBlink): + self.fBlink = fBlink + + def getKey(self): + return self.client.evKey.receive(self) + + def getPrintableKey(self): + while True: + key = self.getKey() + if ansi.FKeyPrintable(key): + return key + + def deleteCh(self): + stOld = self.screen[self.y] + self.screen[self.y] = stOld[0:self.x] + stOld[self.x + 1:] + + def printSt(self, st): + stOld = self.screen[self.y] + stNew = stOld[0:self.x] + st + stOld[self.x+len(st):] + self.screen[self.y] = stNew + self.moveTo(self.x + len(st), self.y) + + def moveTo(self, x, y): + self.x = x + self.y = y + while (self.x >= self.w): + if (self.fWrapHoriz): + self.y = self.y + 1 + self.x = self.x - self.w + else: + self.x = self.w - 1 + while (self.x < 0): + self.y = self.y - 1 + self.x = self.x + self.w + if (self.y < 0): + self.y = 0 + while (self.fWrapVert and self.y >= self.h): + self.screen = self.screen[1:] + self.screen.append("") + self.y = self.y - 1 + self.screen[self.y] = self.screen[self.y].ljust(self.x) + + def type(self, st): + self.client.beep() + for iSt in range(len(st)): + self.printSt(st[iSt:iSt + 1]) + self.wait(35) + + def typeLn(self, st): + self.type(st) + self.newLine() + + def centerLn(self, st): + self.typeLn(st.rjust((self.w / 2) + (len(st) / 2))) + + def getLine(self): + self.xLine = self.x + self.yLine = self.y + self.stLine = "" + + fWrapHorizStart = self.fWrapHoriz + self.fWrapHoriz = False + while True: + key = self.getKey() + fBeep = True + if (key == ansi.K_BACKSPACE and self.x > self.xLine): + self.stLine = self.stLine[:self.x - self.xLine - 1] + self.stLine[self.x - self.xLine:] + self.moveTo(self.x - 1, self.y) + elif (key == ansi.K_LEFT and self.x > self.xLine): + self.moveTo(self.x - 1, self.y) + elif (key == ansi.K_RIGHT): + self.moveTo(self.x + 1, self.y) + if (self.x - self.xLine) > len(self.stLine): + self.stLine = self.stLine + " " + elif (ansi.FKeyPrintable(key)): + self.stLine = self.stLine[:self.x - self.xLine] + key + self.stLine[(self.x + 1) - self.xLine:] + self.moveTo(self.x + 1, self.y) + elif key == ansi.K_RETURN: + break + else: + print "weird key", ansi.StrKey(key) + fBeep = False + if fBeep: + self.client.beep() + self.fWrapHoriz = fWrapHorizStart + + x,y = self.x, self.y + self.moveTo(self.xLine, self.yLine) + self.printSt(self.StLineDisp()) + self.moveTo(x,y) + + stLine = self.stLine.strip() + self.stLine = None + self.xLine = None + self.yLine = None + return stLine + + def StLineDisp(self): + return self.stLine if not self.chEcho else self.chEcho * len(self.stLine) + def draw(self, ascr, client): + if client == self.client: + y = 1 + for line in self.screen: + ascr.PutSt("{0:{1}}".format(line, self.w), 1, y, ansi.WHITE | ansi.FBRIGHT, self.colBg) + y = y + 1 + if self.stLine: + ascr.PutSt(self.StLineDisp(), self.xLine + 1, self.yLine + 1, ansi.WHITE | ansi.FBRIGHT, self.colBg) + if (self.fBlink): + ascr.PutAch(ansi.MkAch(" ", ansi.WHITE | ansi.FBRIGHT, ansi.WHITE), self.x + 1, self.y + 1) + def spin(self, stSpin): + self.moveTo(self.x - 1, self.y) + self.printSt(stSpin) + + def newSpinner(self): + return Blinker(self.game, self.spin, ["/","-","\\", "|"], 100) + + def newLine(self, count = 1, wait = 0): + for i in range(count): + self.moveTo(0, self.y + 1) + self.beep() + self.wait(wait) + + def beep(self): + self.client.beep() + + def setColBg(self, colBg): + self.colBg = colBg +class AutoJoiner(Token): + def InitPersistent(self, owner, *rgclsTok): + Token.InitPersistent(self, owner) + self.rgclsTok = rgclsTok + + def InitTransient(self): + Token.InitTransient(self) + self.mpclient_rgtok = {} + def run(self): + def dgoobLeave(client): + for tok in self.mpclient_rgtok[client]: + print "killing ", tok + tok.die() + while True: + with self.game.evLeave.oob(self, dgoobLeave): + client = self.game.evJoin.receive(self) + rgtok = [] + for clsTok in self.rgclsTok: + if FIter(clsTok): + tok = clsTok[0](self.game, client, *clsTok[1:]) + else: + tok = clsTok(self.game, client) + rgtok.append(tok) + self.mpclient_rgtok[client] = rgtok + +class AliveWithPlayers(Token): + def InitPersistent(self, owner, *rgclsTok): + Token.InitPersistent(self, owner) + self.rgclsTok = rgclsTok + + def InitTransient(self): + Token.InitTransient(self) + self.rgclient = [] + + def OnLeave(self, client): + self.rgclient.remove(client) + if len(self.rgclient) == 0: + for tok in [x for x in self.rgtokOwn]: + tok.die() + + def run(self): + with self.game.evLeave.oob(self, self.OnLeave): + while True: + self.rgclient.append(self.game.evJoin.receive(self)) + if len(self.rgclient) == 1: + for clsTok in self.rgclsTok: + if FIter(clsTok): + tok = clsTok[0](self, clsTok[1:]) + else: + tok = clsTok(self) + +class AutoKiller(Token): + def InitTransient(self): + Token.InitTransient(self) + self.rgclient = [] + def OnLeave(self, client): + self.rgclient.remove(client) + if len(self.rgclient) == 0: + self.game.finish() + def run(self): + with self.game.evLeave.oob(self, self.OnLeave): + while True: + self.rgclient.append(self.game.evJoin.receive(self)) + +#selection oracle +class Selor(TPrs): + NONE = 0 + PRI = 1 + SEC = 2 + + def __init__(self, mi, selorParent, rgmiSec, fPri): + self.selorParent = selorParent + self.mi = mi + self.rgmiSec = rgmiSec + self.fPri = fPri + + def MiParent(self): + if self.selorParent != None: + return self.selorParent.mi + return None + + def KSel(self): + if self.fPri: + return self.PRI + elif self.mi in self.rgmiSec: + return self.SEC + else: + return self.NONE + + def SelorChild(self, miChild, fPri): + return Selor(miChild, self, self.rgmiSec, self.fPri and fPri) + + def SelorDeselect(self): + return Selor(self.mi, self.selorParent, self.rgmiSec, False) +class Mi(TPrs): + def InitPersistent(self): + self.miParent = None + self.fNotice = False + self.rgmiNotice = [self] + def Height(self, w): + return 1 + + def StName(self): + return "Unnamed" + + def StKey(self): + return self.StName() + def HandleKey(self, key): + return False + def FIgnoreParent(self): + return False + + def DrawWithParent(self, selorParent, ascr, x, y, w, fPri): + self.Draw(ascr, x, y, w, selorParent.SelorChild(self, fPri)) + + def ColBgDefault(self): + return ansi.BLUE + def ColBgSelected(self, fPri): + return ansi.YELLOW if fPri else ansi.CYAN + def ColBgNotice(self): + return ansi.CYAN + + def ColBg(self, selor): + if selor != None and selor.KSel() != Selor.NONE: + if selor.KSel() == Selor.PRI: + for mi in self.rgmiNotice: + if mi != self and mi.fNotice: + print "Notice cleared for ", mi.StName(), "by ", self.StName() + mi.fNotice = False + return self.ColBgSelected(True) + else: + return self.ColBgSelected(False) + elif self.FNoticeFlagged(): + return self.ColBgNotice() + elif not self.FIgnoreParent() and selor != None and selor.MiParent() != None: + return selor.MiParent().ColBg(selor.selorParent.SelorDeselect()) + else: + return self.ColBgDefault() + + def ColFg(self): + return ansi.WHITE | ansi.FBRIGHT + def FlagNotice(self): + self.fNotice = True + + def ListenForNotice(self, mi): + self.rgmiNotice.append(mi) + + def FNoticeFlagged(self): + for mi in self.rgmiNotice: + if mi.fNotice: + return True + return False + def FSelectable(self): + return True +class MiInvisible(Mi): + def Height(self, w): + return 0 + def Draw(self, *rgarg): + pass + def FSelectable(self): + return False + +miInvisible = MiInvisible() +class MiStatic(Mi): + def InitPersistent(self, stText): + Mi.InitPersistent(self) + self.stText = stText + def StName(self): + return self.stText + def FSelectable(self): + return False + def Height(self, w): + return len(RgstWrapped(self.StName(), w, 0)) + def Draw(self, ascr, x, y, w, selor): + for st in RgstWrapped(self.StName(), w, 0): + ascr.PutSt("{0:^{1}}".format(st, w), x, y, self.ColFg(), self.ColBg(selor)) + y = y + 1 + +class MiDgText(MiStatic): + def InitPersistent(self, dgText): + MiStatic.InitPersistent(self, None) + self.dgText = dgText + def StName(self): + return self.dgText() +class MiButton(MiStatic): + def InitPersistent(self, stName, dgCmd, value = None): + MiStatic.InitPersistent(self, stName) + self.dgCmd = dgCmd + self.value = value + + def StName(self): + if self.stText == None and self.value != None: + return self.value.StName() + return self.stText + + def Exec(self): + return self.dgCmd() + + def FSelectable(self): + return True + def HandleKey(self, key): + if ansi.FEnter(key) or key == ' ': + return self.Exec() + return False + + def Value(self): + return self.value + +class MiToggle(MiButton): + def InitPersistent(self, stName, fInitial = False): + MiButton.InitPersistent(self, stName, None, fInitial) + + def Exec(self): + self.value = not self.value + return True + + def StName(self): + stPre = "[X] " if self.value else "[ ] " + return stPre + self.stText + + def StKey(self): + return self.stText +class MiList(Mi): + def InitPersistent(self, rgval, ival = 0): + Mi.InitPersistent(self) + self.rgval = rgval + self.ival = ival + + def Value(self): + return self.rgval[self.ival] + + def Inc(self): + if self.ival < len(self.rgval) - 1: + self.ival = self.ival + 1 + + def Dec(self): + if self.ival > 0: + self.ival = self.ival - 1 + + def HandleKey(self, key): + if key == ansi.K_LEFT: + self.Dec() + elif key == ansi.K_RIGHT: + self.Inc() + else: + return False + return True + + def Draw(self, ascr, x, y, w, selor): + xText = w / 4 + xVal = w / 2 + ascr.PutSt(self.StName() + ":", xText, y, self.ColFg(), self.ColBg(selor)) + self.DrawValue(ascr, xVal, y, selor.SelorDeselect()) +class MiTypein(Mi): + def InitPersistent(self, dgEnter, stName = "Typein", pctW = 100): + Mi.InitPersistent(self) + self.stedit = Stedit(dgEnter) + self.stName = stName + self.pctW = pctW + assert pctW > 0 and pctW <= 100 + + def StName(self): + return self.stName + + def Value(self): + return self.stedit.GetValue() + + def SetValue(self, st): + self.stedit.SetValue(st) + def HandleKey(self, key): + return self.stedit.HandleKey(key) + + def ColBgDefault(self): + return ansi.BLACK + def ColBgSelected(self, fPri): + return ansi.WHITE if fPri else ansi.BLUE + def ColFg(self): + return ansi.WHITE + + def FIgnoreParent(self): + return True + def Height(self, wT): + return len(RgstWrapped(self.GetStToWrap(), self.GetW(wT))) + def GetW(self, wT): + return int(wT * (self.pctW / 100.0)) + def GetStToWrap(self): + return self.stedit.StForSize() + def Draw(self, ascr, xT, y, wT, selor): + w = self.GetW(wT) + x = xT + ((wT - w) / 2) + if selor.KSel() != Selor.NONE: + stToWrap = self.GetStToWrap() + else: + stToWrap = self.stedit.GetValue() + for snippet in RgSnippetWrapped(stToWrap, w): + ascr.PutSt("{0:{1}}".format(snippet.st, w), x, y, self.ColFg(), self.ColBg(selor.SelorDeselect())) + if selor.KSel() != Selor.NONE: + self.stedit.DrawCursor(ascr, x, y, self.ColFg(), self.ColBg(selor), snippet) + y = y + 1 +class MiTab(MiStatic): + def InitPersistent(self, stText, mi): + MiStatic.InitPersistent(self, stText) + self.mi = mi + self.ListenForNotice(mi) + def Value(self): + return self.mi + def FSelectable(self): + return True + + @staticmethod + def MiTabbed(*rgstTab_mi): + miTabRow = MiMenuHoriz([MiTab(*stTab_mi) for stTab_mi in rgstTab_mi]) + return MiMenu([miTabRow, MiTabBody(miTabRow)]) +class MiTabBody(Mi): + def InitPersistent(self, miMenu): + Mi.InitPersistent(self) + self.miMenu = miMenu + def Mi(self): + return self.miMenu.Value() + def HandleKey(self, key): + return self.Mi().HandleKey(key) + def Draw(self, ascr, x, y, w, selor): + self.Mi().Draw(ascr, x, y, w, selor) + def Height(self,w): + return self.Mi().Height(w) +class MiValue(Mi): + def InitPersistent(self, mi): + Mi.InitPersistent(self) + self.mi = mi + def StName(self): + return self.mi.StName() + def Value(self): + return self.mi.Value() + def Height(self,w): + return self.mi.Height(w) + def Draw(self, ascr, x, y, w, selor): + self.mi.DrawValue(ascr, x, y, selor.SelorChild(self.mi, False)) + +class Selection(TPrs): + def InitPersistent(self, rgo, o = None, io = 0): + self.rgo = rgo + self.o = o + self.io = -1 if o == None else io + self.Validate() + + def Invalidate(self): + self.o = None + self.io = -1 + + def Validate(self): + # aw hell, selection only handles inserts, not deletes :( + if self.o == None or self.io < 0: + self.Invalidate() + elif len(self.rgo) <= self.io or self.rgo[self.io] != self.o: + try: + self.io = self.rgo.index(self.o) + except Exception: + self.Invalidate() + if self.o == None and not self.FEmpty(): + self.io = IoInc(self.rgo, self.io, self.FSelectableI) + self.o = self.rgo[self.io] + + def GetSelected(self): + self.Validate() + return self.o + + def SetSelected(self, o): + self.o = o + self.Validate() + def ITopScroll(self, h, dgHeight): + # requirements: + # selection must be visible + # if possible, selection should be centered + if self.io < 0: + return 0 + iotop = self.io + iobot = self.io + hCurr = dgHeight(self.o) + yobot = hCurr + fGrowUp = False + while hCurr < h: + fDoneUp = iotop == 0 + fDoneDown = iobot == len(self.rgo) - 1 + if (fGrowUp or fDoneDown) and not fDoneUp: + ho = dgHeight(self.rgo[iotop - 1]) + if (yobot + ho >= h): # handle case where thing above selected thing is huge and pushes the selection off the screen + break + iotop = iotop - 1 + yobot = yobot + ho + elif not fDoneDown: + ho = dgHeight(self.rgo[iobot + 1]) + iobot = iobot + 1 + else: + break + hCurr = hCurr + ho + fGrowUp = not fGrowUp + return iotop + + def Inc(self): + return self.ChangeI(IoInc) + + def Dec(self): + return self.ChangeI(IoDec) + + def FEmpty(self): + for o in self.rgo: + if self.FSelectableI(o): + return False + return True + + def ChangeI(self, fnChange): + self.Validate() + if self.o != None: + ioT = self.io + self.io = fnChange(self.rgo, self.io, self.FSelectableI) + self.o = self.rgo[self.io] + return ioT != self.io + + def FSelectableI(self, o): + return o != None and o.FSelectable() + +class MiMenuI(Mi): + def InitPersistent(self, rgmi): + Mi.InitPersistent(self) + self.selMi = Selection(rgmi) + self.mpstName_mi = {} + for mi in rgmi: + if mi != None: + self.mpstName_mi[mi.StKey().lower()] = mi + def Value(self, stKey = None): + if stKey == None: + return self.selMi.GetSelected().Value() + return self.mpstName_mi[stKey.lower()].Value() + def MiByName(self, stKey): + return self.mpstName_mi[stKey.lower()] + def HeightMi(self, mi, w): + if mi == None: + return 1 + return mi.Height(w) + def ColBgSelected(self, fPri): + return self.ColBgDefault() + +class MiMenu(MiMenuI): + def InitPersistent(self, rgmi, hMax = -1): + MiMenuI.InitPersistent(self, rgmi) + self.hMax = hMax + def HandleKey(self, key): + mi = self.selMi.GetSelected() + if mi == None or not mi.HandleKey(key): + if key == ansi.K_DOWN: + return self.selMi.Inc() + elif key == ansi.K_UP: + return self.selMi.Dec() + return False + return True + + def HMax(self, ascr, y): + if self.hMax < 0: + return (ascr.H() - y) + 1 + return self.hMax + + def Height(self, w): + return sum([self.HeightMi(mi,w) for mi in self.selMi.rgo]) + + def Draw(self, ascr, x, y, w, selor): + ascr.Fill(ansi.MkAch(' ', self.ColFg(), self.ColBg(selor)), w, self.Height(w), x, y) + miSelected = self.selMi.GetSelected() + h = 0 + hMax = self.HMax(ascr, y) + itop = self.selMi.ITopScroll(hMax, lambda mi: self.HeightMi(mi, w)) + for imi, mi in enumerate(self.selMi.rgo): + if imi < itop: + continue + if mi != None: + mi.DrawWithParent(selor, ascr, x, y + h, w, miSelected == mi) + h = h + self.HeightMi(mi, w) + if h >= hMax: + break +class MiMenuHoriz(MiMenuI): + def InitPersistent(self, rgmi, rgsize = None): + MiMenuI.InitPersistent(self, rgmi) + if rgsize == None: + rgsize = [1 for mi in rgmi] + self.rgsize = rgsize + self.sizeMax = float(sum(rgsize)) + + def Width(self, imi, w): + return int((self.rgsize[imi] / self.sizeMax) * w) + + def HandleKey(self, key): + mi = self.selMi.GetSelected() + if mi == None or not mi.HandleKey(key): + if key == ansi.K_RIGHT: + return self.selMi.Inc() + elif key == ansi.K_LEFT: + return self.selMi.Dec() + return False + return True + + def Height(self, w): + return max([self.HeightMi(mi, w) for mi in self.selMi.rgo]) + def Draw(self, ascr, x, y, w, selor): + ascr.Fill(ansi.MkAch(' ', self.ColFg(), self.ColBg(selor)), w, self.Height(w), x, y) + miSelected = self.selMi.GetSelected() + for imi, mi in enumerate(self.selMi.rgo): + wT = self.Width(imi, w) + if mi != None: + mi.DrawWithParent(selor, ascr, x, y, wT, miSelected == mi) + x = x + wT + +def DgProjectMiButton(dgExec): + def DgProject(value): + return MiButton(None, lambda: dgExec(value), value) + return DgProject + +class RgmiProjected(TPrs): + def InitPersistent(self, rgvalue, dgProject): + self.rgvalue = rgvalue + self.dgProject = dgProject + + def InitTransient(self): + self.mpvalue_wrmi = weakref.WeakKeyDictionary() + + def __getitem__(self, i): + return self.MiFromValue(self.rgvalue[i]) + + def MiFromValue(self, value): + if value in self.mpvalue_wrmi: + mi = self.mpvalue_wrmi[value]() + if mi != None: + return mi + mi = self.dgProject(value) + mi._valueProjected = value + self.mpvalue_wrmi[value] = weakref.ref(mi) # this probably gc's much more than is necessary ... :/ + return mi + + def __len__(self): + return len(self.rgvalue) + + def index(self, mi): + return self.rgvalue.index(mi._valueProjected) + + +class OvStatic(TokenClient): + TOP = 1 + MIDDLE = 2 + BOTTOM = 3 + FILL = 4 + + def InitPersistent(self, owner, client, miMenu, kplace, *rgtag): + TokenClient.InitPersistent(self, owner, client, "drawable", "menu", *rgtag) + self.miMenu = miMenu + self.kplace = kplace + self.mpst_miSel = {} + def SetSelection(self, stSel, mi): + self.mpst_miSel[stSel] = mi + + def GetSelection(self, stSel): + try: + return self.mpst_miSel[stSel] + except KeyError: + return None + def draw(self, ascr, client): + self.drawI(ascr, client, False) + + def drawI(self, ascr, client, fSelected): + if client == self.client: + selor = Selor(self.miMenu, None, self.mpst_miSel.values(), fSelected) + if self.kplace & self.FILL: + h = ascr.H() + y = 1 + dh = h - self.miMenu.Height(ascr.W()) + if dh > 0: + ascr.Fill(ansi.MkAch(' ', self.miMenu.ColFg(), self.miMenu.ColBg(selor)), ascr.W(), dh, 1, ascr.H() - dh + 1) + else: + h = self.miMenu.Height(ascr.W()) + if self.kplace == self.TOP: + y = 1 + elif self.kplace == self.MIDDLE: + y = (ascr.H() / 2) - (h / 2) + else: # BOTTOM + y = ascr.H() - h + 1 + self.miMenu.Draw(ascr, 1, y, ascr.W(), selor) + + def Value(self, stName = None): + return self.miMenu.Value(stName) +class OvMenu(OvStatic): + def run(self): + while True: + self.miMenu.HandleKey(self.client.evKey.receive(self)) + + def draw(self, ascr, client): + self.drawI(ascr, client, True) +class OvPopup(OvStatic): + def InitPersistent(self, owner, client, miMenu): + OvStatic.InitPersistent(self, owner, client, miMenu, self.MIDDLE, "drawable", "menu") + self.evShow = Event(self.game) + self.evDone = Event(self.game) + self.fAwake = False + + def run(self): + while True: + self.evShow.receive(self) + self.fAwake = True + while self.fAwake: + key = self.client.evKey.receive(self) + if not self.miMenu.HandleKey(key): + if key == ansi.K_TAB or key == ansi.K_DEL or key == ' ' or ansi.FEnter(key): + self.fAwake = False + self.evDone.fire() + + def draw(self, ascr, client): + if self.fAwake: + self.drawI(ascr, client, True) +class MsgScroller(Token): + def InitPersistent(self, owner, colFg=ansi.BLUE, colBg=ansi.BLACK, y=config.H): + Token.InitPersistent(self, owner, "drawable", "msgscroller", "overlay") + self.colFg = colFg + self.colBg = colBg + self.rgmsg = [] + self.msg = None + self.evPost = Event(self.game) + self.y = y + + def postI(self, msg): + self.rgmsg.append(msg) + + def run(self): + while True: + self.postI(self.evPost.receive(self)) + with self.evPost.oob(self, self.postI): + while len(self.rgmsg) > 0: + self.msg = self.rgmsg[0] + self.rgmsg = self.rgmsg[1:] + self.x = config.W + while self.x > -len(self.msg): + self.x = self.x - 1 + self.wait(30) + self.msg = None + + def draw(self, ascr, client): + if self.msg: + ascr.PutSt(self.msg, self.x, self.y, self.colFg, self.colBg) diff --git a/build_entity.py b/build_entity.py new file mode 100644 index 0000000..2abbacf --- /dev/null +++ b/build_entity.py @@ -0,0 +1,16 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20091016050502.1:@thin build_entity.py +import re + +reEnt = re.compile(r"^.*&#(\d+);.*$"); +mpch_entity = [0] + +with open("cp437_html.txt") as fp: + for line in fp.readlines(): + ent = reEnt.match(line).group(1) + mpch_entity.append(int(ent)) + +print "mpch_entity = ", mpch_entity +#@nonl +#@-node:jpenner.20091016050502.1:@thin build_entity.py +#@-leo diff --git a/config.py b/config.py new file mode 100644 index 0000000..0931d5e --- /dev/null +++ b/config.py @@ -0,0 +1,23 @@ +# Port to run the telnet server on. +PORT_TELNET = 19999 +# Port to run the flash policy server on. Set to None to turn off. +PORT_FLASHPOLICY = 20001 +# Hostname to bind servers on. Leave as "" for all. +BIND_HOSTNAME = "" +# width and height; default 80x25 +W = 80 +H = 25 +# directory to save ANSI pictures to. leave as "." for current directory. no trailing slash required. "" == / +DIR_ANSI = "." +# authorization. Options are "Noop" (make up a username, no password), "Drupal" (fetches from MySQL Drupal database) +AUTH = "Noop" +# if Drupal, configure these options: +#drupaldb = "database" +#drupaldb_user = "user" +#drupaldb_password = "password" +#drupaldb_host = "localhost" + +# Port to run the command server on. Set to None to turn off. Only set this port for testing: it will allow anyone to take down the server! +PORT_CMDSERVER = 20002 +# Flag whether we should build and use Cython modules to improve performance. +USE_CYTHON = True diff --git a/engine.py b/engine.py new file mode 100644 index 0000000..09ceae0 --- /dev/null +++ b/engine.py @@ -0,0 +1,517 @@ +import stackless +import random +import logging +import pickle +import time +import ansi +import auth +import config +import telnet +import traceback +from twisted.internet import reactor +from tpers import TPrs, TPLS, Version +import tpers + +class Event(TPrs): + def InitPersistent(self, game): + self.game = game + + def InitTransient(self): + self.rgtk = TPLS() + self.stkValue = [] + + def oob(self, tok, dgoob): + return Oob(self, tok, dgoob) + + def receive(self, tok): + chNotify = tok.chEv + self.rgtk.append(tok) + while True: + ev, value = chNotify.receive() + if ev == self: + return value + ev.rgtk.append(tok) + if ev in tok.mpevDgoob and tok.mpevDgoob[ev](value): + raise Event.OobBreakException() + + def fire(self, value = None): + self.stkValue.append(value) + self.game.queueEv.append(self) + self.game.ensureRun() + + def fireI(self): + rgtkOld = self.rgtk + value = self.stkValue[0] + self.stkValue = self.stkValue[1:] + self.rgtk = TPLS() + for tk in rgtkOld: + if tk.fRunning: + tk.chEv.send((self,value)) + stackless.run() + +class Oob(object): + def __init__(self, ev, tok, dgoob): + self.ev = ev + self.tok = tok + self.dgoob = dgoob + def __enter__(self): + self.ev.rgtk.append(self.tok) + self.tok.mpevDgoob[self.ev] = self.dgoob + def __exit__(self, etype, eval, tb): + if etype != TaskletExit: + try: + self.tok.mpevDgoob.pop(self.ev) + self.ev.rgtk.remove(self.tok) + if etype == OobBreakException: + return True + except Exception: + print "exception exiting oob:" + traceback.print_exc() + print "oob:", etype, eval + traceback.print_tb(tb) + +class OobBreakException(Exception): + pass + +class Runner(TPrs): + def InitPersistent(self): + self.mpuser_cld = {} + print "New Runner created" + + def InitTransient(self): + self.rgclient = [] + + def GetCld(self, user): + cld = self.mpuser_cld.get(user) + if cld == None: + cld = Cld(user) + self.mpuser_cld[user] = cld + return cld + + def RunGameI(self, client): + client.runner = self + self.rgclient.append(client) + def OnDisconnect(): + self.rgclient.remove(client) + client.addDgQuit(OnDisconnect) + return self.RunGame(client) + + def RunServer(self): + for client in self.rgclient: + client.quit(True) + telnet.RunServer(self.RunGameI) +def Run(clsRunner, fn): + runner = tpers.Odb.Load(fn) or clsRunner() + + def ExecCmd(stCmd): + if stCmd == "save": + tpers.Odb.Save(runner, fn) + return "Saved to " + fn + return "Unrecognized command: '" + stCmd + "'" + telnet.RunCmdServer(ExecCmd) + + runner.RunServer() + +class Ownable(TPrs): + def InitPersistent(self, owner = None): + self.owner = owner + self.rgtokOwn = TPLS() + if owner != None: + owner.rgtokOwn.append(self) + def FPersist(self): + if self.owner != None and not self.owner.FPersist(): + return False + return TPrs.FPersist(self) + def die(self): + if self.owner != None: + self.owner.rgtokOwn.remove(self) + for tok in [x for x in self.rgtokOwn]: + tok.die() + self.DontPersist() + +@Version(2) +class Game(Ownable): + def InitPersistent(self): + Ownable.InitPersistent(self) + self.fQuit = False + self.running = False + self.mptagTokenset = {} + self.rgdgdie = [] + self.evJoin = Event(self) + self.evLeave = Event(self) + self.mpuser_cldg = {} + self.CreateRgtok(self.GetRgclsTokPers(), True) + + def InitTransient(self): + self.MakeTransient("fScheduled", False) + self.rgclient = [] + self.rgproc = [PTimer(), PEvent(), PDraw(*self.GetRgtagDraw())] + for proc in self.rgproc: + proc.setGame(self) + # don't recreate tokens when game has died (shouldn't even be persisting) + if not self.FDead(): + self.CreateRgtok(self.GetRgclsTokTrans(), False) + else: + print "Dead", self.__class__, "has persisted" + + def FDead(self): + try: + rc = self.rc + return True + except: + return False + def GetCldg(self, user): + cldg = self.mpuser_cldg.get(user) + if cldg == None: + cldg = Cldg() + self.mpuser_cldg[user] = cldg + return cldg + + def GetRgclsTokPers(self): + return [] + + def GetRgclsTokTrans(self): + return [] + + def GetRgtagDraw(self): + return ["background", "solid", "overlay"] + + def CreateRgtok(self, rgclsTok, fPersist): + for clsTok in rgclsTok: + if FIter(clsTok): + tok = clsTok[0](self, *clsTok[1:]) + else: + tok = clsTok(self) + if not fPersist: + tok.DontPersist() + + def tagToken(self, token, tag): + if not (tag in self.mptagTokenset): + self.mptagTokenset[tag] = set([token]) + else: + self.mptagTokenset[tag].add(token) + + def untagToken(self, token, tag): + if tag in self.mptagTokenset: + self.mptagTokenset[tag].discard(token) + + def rgtoken(self, tag, rgpriority = []): + if not (tag in self.mptagTokenset): + return [] + + rgtoken = [] + tokenSet = self.mptagTokenset[tag].copy() + for priority in rgpriority: + if (priority in self.mptagTokenset): + ptokenSet = self.mptagTokenset[priority] & tokenSet + for token in ptokenSet: + rgtoken.append(token) + tokenSet -= ptokenSet + for token in tokenSet: + rgtoken.append(token) + + return rgtoken + + def quit(self): + self.running = False + self.fQuit = True + + def finish(self, rc = None): + self.running = False + self.rc = rc + class QuitException(Exception): + pass + + class LoadException(Exception): + pass + + def die(self): + for dgdie in self.rgdgdie: + dgdie() + Ownable.die(self) + def runStep(self): + try: + if self.running: + for proc in self.rgproc: + proc.process() + if not self.running: + self.die() + for client in self.rgclient: + client.leaveGame(self.rc) + for client in self.rgclient: + client.postRunStep() + finally: + self.fScheduled = False + return self.running + def joinGame(self,client): + self.rgclient.append(client) + self.evJoin.fire(client) + + def leaveGame(self, client): + self.evLeave.fire(client) + + def leaveGameI(self, client): + self.rgclient.remove(client) + def ensureRun(self): + self.running = True + if not self.fScheduled: + self.fScheduled = True + reactor.callLater(0, self.runStep) + def UpgradeFrom(self, versionOld): + if versionOld < 2: + self.mpuser_cldg = {} + +# Client data +@Version(2) +class Cld(TPrs): + def InitPersistent(self, user): + self.user = user + def UpgradeFrom(self, versionOld): + if versionOld < 2: + del self.mpgame_cldg + +#per-game client data +class Cldg(TPrs): + def FPersist(self): + if len(self._persistent) == 0: + return False + return TPrs.FPersist(self) + +class Client(TPrs): + def InitTransient(self): + self.chRun = stackless.channel() + self.gameCurr = None + self.chCont = None + self.fRunning = False + self.dgJoinGame = None + self.rgdgQuit = [] + self.fQuit = False + self.cld = None + self.runner = None + + def FPersist(self): + return False + def go(self, fnRun, protocol): + self.protocol = protocol + self.tskRun = stackless.tasklet(fnRun) + self.tskRun(self) + if self._runTskRun(): + self.gameCurr.ensureRun() + def joinGame(self, game): + assert not self.fRunning, "already running" + chCont = stackless.channel() + self.chRun.send((game, chCont)) + return chCont.receive() + + def leaveGame(self, rc = None): + self.fRunning = False + self.gameCurr.leaveGame(self) + self.rc = rc + + def quit(self): + self.leaveGame(None) + self.fQuit = True + def _runTskRun(self): + "run self.tskRun after it has been scheduled, and await results" + stackless.run() + if not self.tskRun.scheduled: + return False + self.gameCurr, self.chCont = self.chRun.receive() + self.gameCurr.joinGame(self) + self.fRunning = True + if self.dgJoinGame: + self.dgJoinGame(self, self.gameCurr) + return True + + def postRunStep(self): + if not self.fRunning: + self.gameCurr.leaveGameI(self) + if not self.fQuit: + self.chCont.send(self.rc) + self.fQuit = not self._runTskRun() + if self.fQuit: + for dgQuit in self.rgdgQuit: + dgQuit() + return self.fQuit + def beep(self): + pass + def login(self, user, passwd): + authr = getattr(auth, config.AUTH + "Auth")() + if (authr.FAuthorized(user, passwd)): + self.cld = self.runner.GetCld(user) + return True + return False + def addDgQuit(self, dgquit): + self.rgdgQuit.append(dgquit) + + def removeDgQuit(self, dgquit): + self.rgdgQuit.remove(dgquit) + def Cldg(self, game = None): + if game == None: + game = self.gameCurr + return game.GetCldg(self.cld.user) + +class Processor(TPrs): + def setGame(self, game): + self.game = game + + def process(self): + pass + +class PTimer(Processor): + def msNow(self): + return int(time.time() * 1000) + def setGame(self, game): + self.game = game + self.fReset = False + game.evTick = Event(game) + game.ms = 0 + game.msDelta = 0 + self.dms = -self.msNow() + + def __setstate__(self, dict): + Processor.__setstate__(self, dict) + self.fReset = True + def process(self): + if self.fReset: + # keep game.ms constant + self.dms = self.game.ms - self.msNow() + self.fReset = False + msNew = self.msNow() + self.dms + self.game.msDelta = msNew - self.game.ms + self.game.ms = msNew + self.game.evTick.fire(self.game.msDelta) + +class PEvent(Processor): + def setGame(self, game): + self.game = game + game.queueEv = [] + game.rgdgdie.append(self.die) + + def die(self): + for ev in self.game.queueEv: + for tk in ev.rgtk: + tk.die() + ev.stkValue = [] + self.game.queueEv = [] + + def process(self): + while self.game.queueEv: + self.game.queueEv[0].fireI() + self.game.queueEv.pop(0) + +class PDraw(Processor): + def InitPersistent(self, *rgprio): + self.rgprio = rgprio + self.msDrawAgain = -1 + self.fTimerSet = False + + def process(self): + if self.game.ms >= self.msDrawAgain: + for client in self.game.rgclient: + ascr = ansi.Ascr() + for tok in self.game.rgtoken("drawable", self.rgprio): + try: + tok.draw(ascr, client) + except Exception: + print "error drawing" + traceback.print_exc() + + client.protocol.Draw(ascr) + self.msDrawAgain = self.game.ms + 120 + self.fTimerSet = False + elif not self.fTimerSet: + reactor.callLater((self.msDrawAgain - self.game.ms) / 1000.0, self.game.ensureRun) + self.fTimerSet = True +class Taggable(Ownable): + def InitPersistent(self, owner, *rgtag): + Ownable.InitPersistent(self, owner) + game = owner + while not isinstance(game, Game): + game = game.owner + self.game = game + self.tags = set() + self.tag(*rgtag) + + def tag(self, *rgtag): + self.tags |= set(rgtag) + for tag in rgtag: + self.game.tagToken(self, tag) + + def untag(self, *rgtag): + self.tags -= set(rgtag) + for tag in rgtag: + self.game.untagToken(self, tag) + + def die(self): + for tag in self.tags.copy(): + self.untag(tag) + Ownable.die(self) + +class Token(Taggable): + def InitPersistent(self, owner, *rgtag): + Taggable.InitPersistent(self, owner, *rgtag) + self.msGamePreWait = 0 + self.ms = 0 + self.fRunning = True + self.rgtokOwn = TPLS() + + def InitTransient(self): + Taggable.InitTransient(self) + self.chEv = stackless.channel() + self.tasklet = stackless.tasklet(self.runI) + self.mpevDgoob = {} + + self._fWriteToPersistent = True + self.tasklet() + def __enter__(self): + return self + def __exit__(self, etype, eval, traceback): + self.die() + def wait(self, msDelta): + # have we waited once already this tick? If not, reset + if (self.msGamePreWait != self.game.ms): + self.ms = self.game.ms + + msEnd = self.ms + msDelta + + delay = (msEnd - self.game.ms) / 1000.0 + if delay > 0: + reactor.callLater(delay, self.game.ensureRun) + + while (self.game.ms < msEnd): + self.game.evTick.receive(self) + self.ms = msEnd + + self.msGamePreWait = self.game.ms + + + def die(self): + self.tasklet.kill() + self.fRunning = False + Taggable.die(self) + def run(self): + pass + + def runI(self): + try: + self.run() + #except TaskletExit: + # raise + except Exception: + print "token script crashed:" + traceback.print_exc() +def FIter(l): + try: + iter(l) + return True + except Exception: + return False + +class TokenClient(Token): + def InitPersistent(self, owner, client, *rgtag): + Token.InitPersistent(self, owner, *rgtag) + self.client = client + + def FPersist(self): + return False diff --git a/entity.py b/entity.py new file mode 100644 index 0000000..d7c2766 --- /dev/null +++ b/entity.py @@ -0,0 +1 @@ +mpch_entity = [0, 9786, 9787, 9829, 9830, 9827, 9824, 8226, 9688, 9675, 9689, 9794, 9792, 9834, 9835, 9788, 9658, 9668, 8597, 8252, 182, 167, 9644, 8616, 8593, 8595, 8594, 8592, 8735, 8596, 9650, 9660, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 8962, 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165, 8359, 402, 225, 237, 243, 250, 241, 209, 170, 186, 191, 8976, 172, 189, 188, 161, 171, 187, 9617, 9618, 9619, 9474, 9508, 9569, 9570, 9558, 9557, 9571, 9553, 9559, 9565, 9564, 9563, 9488, 9492, 9524, 9516, 9500, 9472, 9532, 9566, 9567, 9562, 9556, 9577, 9574, 9568, 9552, 9580, 9575, 9576, 9572, 9573, 9561, 9560, 9554, 9555, 9579, 9578, 9496, 9484, 9608, 9604, 9612, 9616, 9600, 945, 223, 915, 960, 931, 963, 181, 964, 934, 920, 937, 948, 8734, 966, 949, 8745, 8801, 177, 8805, 8804, 8992, 8993, 247, 8776, 176, 8729, 183, 8730, 8319, 178, 9632, 160] diff --git a/haxor.py b/haxor.py new file mode 100644 index 0000000..3474b26 --- /dev/null +++ b/haxor.py @@ -0,0 +1,71 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20090526212048.3:@thin haxor.py +from engine import * +from basetoken import * +import telnet + +#@<> +#@+node:jpenner.20090526212048.6:<> +class IntroTerm(Terminal): + #@ @+others + #@+node:jpenner.20090526212048.5:run + def run(self): + self.newLine(3, 300) + self.type("Logon Please: ") + name = self.getLine() + self.newLine(2, 300) + self.typeLn("Welcome, " + name) + self.type("You have ") + self.wait(200) + self.typeLn("top security clearance.") + self.type("You are looking ") + self.wait(200) + self.typeLn("good to-day.") + while True: + self.typeLn("Would you like to download") + self.type("all secret files? [Y/N] ") + if (self.getPrintableKey().upper() == "Y"): + break + self.printSt("No") + self.newLine() + self.typeLn("But thou must!") + self.wait(500) + self.printSt("Yessir!") + self.newLine(2, 50) + self.type("Downloading... ") + spinner = self.newSpinner() + self.wait(3000) + spinner.die() + blnkScrn = Blinker(self.game, self.setColBg, [ansi.RED, ansi.BLUE]) + self.newLine(2, 50) + self.typeLn("EXPERT HAX0R DETECTED") + self.typeLn("CODE RED CODE RED") + self.newLine(2, 400) + self.wait(500) + self.typeLn("OH SHIIIIIIIIIIIIIIIIT!") + self.wait(800) + blnkScrn.die() + self.setColBg(ansi.BLACK) + self.newLine(2, 50) + self.printSt("NO CARRIER") + self.newLine() + self.client.quit() + #@-node:jpenner.20090526212048.5:run + #@-others +#@nonl +#@-node:jpenner.20090526212048.6:<> +#@nl + +class Haxor(object): + def __init__(self): + self.game = GmSimple([AutoJoiner, IntroTerm]) + def RunGame(self, client): + client.joinGame(self.game) + def RunServer(self): + telnet.RunServer(self.RunGame) + +if __name__ == "__main__": + Haxor().RunServer() +#@nonl +#@-node:jpenner.20090526212048.3:@thin haxor.py +#@-leo diff --git a/login.py b/login.py new file mode 100644 index 0000000..3b20b8c --- /dev/null +++ b/login.py @@ -0,0 +1,35 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20090607135901.3:@thin login.py +from basetoken import * +import ansi + +class LoginTerm(Terminal): + def run(self): + self.setColBg(ansi.BLUE) + self.newLine(3, 300) + self.type("Logon Please: ") + user = self.getLine() + self.newLine() + self.type("Password: ") + self.chEcho = "*" + passwd = self.getLine() + self.chEcho = None + self.newLine(2, 300) + if self.client.login(user, passwd): + self.typeLn("Welcome to MarMOTS, " + user) + self.typeLn("I see you have driven here in your Ferrari.") + self.typeLn("Logging you in...") + self.wait(250) + self.game.finish(True) + else: + self.setColBg(ansi.RED) + self.typeLn("I am sorry, " + user + ", but you appear to be insufficiently rhinoceros.") + self.typeLn("Please report back after joining the Klik of the Month Klub.") + self.newLine(2,300) + self.setColBg(ansi.BLACK) + self.printSt("NO CARRIER") + self.wait(200) + self.game.finish(False) +#@nonl +#@-node:jpenner.20090607135901.3:@thin login.py +#@-leo diff --git a/marmots_todo.txt b/marmots_todo.txt new file mode 100644 index 0000000..149114f --- /dev/null +++ b/marmots_todo.txt @@ -0,0 +1,12 @@ +debugging output: +- decorate classes which we expect not to be in the output, warn if we persist them +- + +TRef + Load + Unload (save and close reference) + + -- we do not want to hold on to references + -- actually in our case we don't want to save GameWBs at all, we want to persist boards lazily (except for metadata?) +Cldg + -- well, this sucks :P guid? attach to game? \ No newline at end of file diff --git a/runserver.bat b/runserver.bat new file mode 100644 index 0000000..8c2af20 --- /dev/null +++ b/runserver.bat @@ -0,0 +1,3 @@ +setup.py build_ext --inplace -c mingw32 +whiteboard.py +pause diff --git a/save.sh b/save.sh new file mode 100644 index 0000000..f7a060d --- /dev/null +++ b/save.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo save | telnet localhost 20002 + diff --git a/savecycle.sh b/savecycle.sh new file mode 100644 index 0000000..cabe96b --- /dev/null +++ b/savecycle.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +fnnew="whiteboard.marm.`date "+%F_%H-%M"`" + +cd /home/jeremy/marmots + +save () +{ + echo save | telnet localhost 20002 &> /dev/null + sleep 5 # wait for save to complete +} + +backup () +{ + cp whiteboard.marm $fnnew + gzip $fnnew +} + +if [ -e whiteboard.marm ] +then + cp whiteboard.marm whiteboard.marm.old + save + cmp whiteboard.marm whiteboard.marm.old &> /dev/null + if [ $? != 0 ] + then + backup + fi + rm whiteboard.marm.old +else + save + backup +fi + diff --git a/scripting.py b/scripting.py new file mode 100644 index 0000000..268dbef --- /dev/null +++ b/scripting.py @@ -0,0 +1,711 @@ +from engine import * +from basetoken import * +from tpers import * +from util import * + +# scope -- a block? a scril? (scril lets you specifically avoid vars that are defined after a certain point) + game? +# method: rgvar(stype) -- returns a list of variables in scope that match the current stype +# var -- a variable +# contains: stype, sargInitializer +# a var IS A SCRIL +# the scmd creates a new vari in the current ectx, and adds the scril to a map of things that refer to it +# sproj -- an object that can project a sarg into an pw +# stypes should be sprojectors and express a default projection as a class method (why?) +# MiSarg -- an editor for sargs +# MiSargNum, MiSargDrop +# MiScriptLine -- projects a scriptline +# sobj -- generic hungarian for any object that is usable in scripts +# fail -- a FAILURE OBJECT; if it is used as a sarg when evaluating an scmd, a failure is logged and the scmd returns fail. + +# execution: +# ectx -- execution context +# contains: mpvar_sobj +# block +# iscril (selection?) +# ectxReturn +# methods: call(block) + +class Block(TPrs): + def InitPersistent(self, scrilParent): + self.rgscril = [] + self.scrilParent = scrilParent + def InsertScrilAfter(self, scrilAfter, scmd = None): + scrilNew = Scril(self, scmd) + if scrilAfter == None: + self.rgscril.insert(0, scrilNew) + else: + self.rgscril.insert(self.rgscril.index(scrilAfter) + 1, scrilNew) + return scrilNew + def Project(self, pwBlock, dxIndent = 0): + if pwBlock == None: + pwBlock = PwBlock(None, self, 0) + PwButtonHidden(pwBlock, "[insert new line]", None, self.InsertScrilAfter, dxIndent) + for scril in self.rgscril: + scril.Project(pwBlock, dxIndent) + return pwBlock + +class Typeable(TPrs): + def InitTransient(self): + self.steditTypein = Stedit() + def GetStTypein(self): + st = self.steditTypein.GetValue() + return None if st == "" else st + def SetStTypein(self, stTypein): + self.steditTypein.SetValue(stTypein or "") + def HandleTypeinKey(self, key): + return self.steditTypein.HandleKey(key) + +# Sarg -- pointer to scripting object / argument to a command +# this object type appears to exist solely so that MiSarg can edit something without digging through a list +class Sarg(Typeable): + def InitPersistent(self, stype, scril): + self.stype = stype + self.sobj = stype.SobjDefault(scril) + + def FExpr(self): + "Returns true if the sobj must be evaluated to get a result." + return self.sobj.__class__ == Scril + + def Eval(self, ectx): + if self.FExpr(): + return self.sobj.Exec(ectx) + else: + return self.sobj + +class Fail(TPrs): + def InitPersistent(self, stFailure): + self.stFailure == stFailure + +# stype -- a description of a sobj (scripting object) +# method: FMember(sobj) -- returns true if an object is in the set of things that this stype represents +# method: FOverlaps(stype) -- returns true if there is some overlap between the two stypes +# -- there is no subtype/supertype relation because stypes are used to help the user, +# and I suspect being specific in this manner is too much of a burden to place on them. +# methods: rgsarg(scope) -- returns a list of all legal objects that exist in the current scope +# this includes functions which return things of this stype, and variables +# literals are not included +# project(cls, game, sarg) -- returns an MiSarg which displays and edits (default sproj for a sarg?) +class Stype(object): + """A definition of a scripting object type. All methods are class methods; stypes are never instantiated.""" + def __init__(self): + raise "do not instantiate stypes" + @classmethod + def FMember(cls, sobj): + return isinstance(sobj, cls.pythontype) + @classmethod + def FOverlaps(cls, stype): + return stype == self + @classmethod + def Rgsobj(cls, scope): + return [] + @classmethod + def Rgsobj_st(cls, scope): + return [(sobj, str(sobj)) for sobj in cls.Rgsobj(scope)] + @classmethod + def OnSelect(cls, sarg, sobj): + sarg.sobj = sobj + @classmethod + def Sproj(cls, sarg, pwBlock, pwScril): + ProjectTypein(pwScril, sarg, str(sarg.sobj), cls.Rgsobj_st, cls.OnSelect) + return False + +def ProjectTypein(pwParent, typeable, st, dgRgsobj_st, dgOnSelect): + def OnSelect(sobj): + dgOnSelect(typeable, sobj) + typeable.SetStTypein(None) + if typeable.GetStTypein() != None: + pwParent = PwDropdown(pwParent) + PwTypein(pwParent, st, typeable) + if typeable.GetStTypein() != None: + for sobj, st in dgRgsobj_st(typeable): + PwButton(pwParent, st, sobj, OnSelect) + +class StypeBool(Stype): + pythontype = bool + @classmethod + def SobjDefault(cls, scril): + return False + @classmethod + def Rgsobj(cls, scope): + return [True, False] + +class StypeBlock(Stype): + pythontype = Block + @classmethod + def SobjDefault(cls, scril): + return Block(scril) + @classmethod + def Sproj(cls, sarg, pwBlock, pwScril): + sarg.sobj.Project(pwBlock, pwScril.DxIndent() + 2) + return True + +# scmd -- a definition of a scripting command +class Scmd(object): + """A definition of a scripting command. All methods are class methods; scmds are never instantiated.""" + def __init__(self): + raise "do not instantiate scmds" + + # used by RegScmd to build a list of all valid scmds for editing + rgscmd = [] + + @classmethod + def Rgscmd_st(cls, scril): + return [(scmd, scmd.StName()) for scmd in cls.rgscmd] + @classmethod + def Desc(cls, *rgsarg): + """ + Returns a list of desces. A desce can be a constant string, representing nonselectable helper text, + an sproj, a list containing an sproj and a reference to a passed-in sarg, which explicitly binds that + sproj to that sarg, or a list containing a string, which stands for the selectable text which represents + the scril itself. + ie, [["Move"], " ", [SprojMovable, rgsarg[1]], " to ", [SprojPosition, rgsarg[0]]] + If a bare sproj is given, bind it to the first unbound sarg (after processing all explicitly bound sargs). + + This method must accept invalid sargs; it is the sproj's job to relay the invalidness to the user. + """ + return cls.desc + + stName = None + + @classmethod + def StName(cls): + "Returns the name that the user types in to select this scmd." + if cls.stName != None: + return cls.stName + for desce in cls.Desc(Scril(None, cls).rgsarg): + if type(desce) == list and type(desce[0]) == str: + return desce[0] + assert False, "no selectable self in desc" + + @classmethod + def Csarg(cls): + "Returns the number of arguments that this command takes." + return len(cls.rgstypeArg) + @classmethod + def StypeResult(cls): + return cls.stypeResult + @classmethod + def RgstypeArg(cls): + return cls.rgstypeArg + +def RegScmd(stypeResult, *rgstypeArg): + def RegI(cls): + cls.stypeResult = stypeResult + cls.rgstypeArg = rgstypeArg + Scmd.rgscmd.append(cls) + return cls + return RegI + +@RegScmd(None) +class ScmdBlank(Scmd): + stName = "" + desc = [[" "]] + @classmethod + def Exec(cls, ectx): + pass + +@RegScmd(None) +class ScmdHelloWorld(Scmd): + "Hello, world!" + desc = [["Hello World"], "!"] + @classmethod + def Exec(cls, ectx): + print "Hello, world!" + +@RegScmd(None, StypeBool, StypeBlock, StypeBlock) +class ScmdIf(Scmd): + desc = [["If"], " ", StypeBool.Sproj, ", then:", StypeBlock.Sproj, "or else:", StypeBlock.Sproj] + @classmethod + def Exec(cls, ectx, sargExpr, sargIfTrue, sargIfFalse): + if sargExpr.Eval(ectx): + print "true!" + +# needs work + #=========================================================================== + # @RegScmd(None, StypeSt, StypeObj) + # class ScmdVar(Scmd): + # desc = ["Define a new ", ["variable"], " called \"", StypeSt, "\", set to ", StypeObj, "."] + # @classmethod + # def Exec(cls, ectx, sargStName, sargInit): + # ectx.mpvar_sobj[ectx.ScrilCurr()] = sargInit.Eval() + #=========================================================================== + +class Scril(Typeable): + "A line in a script." + def InitPersistent(self, blockParent, scmd = None): + self.scmd = scmd or ScmdBlank + self.rgsarg = [Sarg(stypeArg, self) for stypeArg in self.scmd.RgstypeArg()] + self.blockParent = blockParent + + def SetScmd(self, scmd): + if (scmd.Csarg() > self.scmd.Csarg()): + self.rgsarg.extend([Sarg(stypeArg, self) for stypeArg in scmd.RgstypeArg()[self.scmd.Csarg():]]) + elif (scmd.Csarg() < self.scmd.Csarg()): + self.rgsarg = self.rgsarg[:scmd.Csarg()] + self.scmd = scmd + + def Exec(self, ectx): + return self.scmd.Exec(ectx, *self.rgsarg) + + def HandleKey(self, pwKey, pov, psel, key): + if ansi.FEnter(key): + self.blockParent.InsertScrilAfter(self, ScmdBlank) + return True + return False + def Project(self, pwBlock, dxindent): + "adds horizontal pws to a vertical pw" + pwScril = None + + desc = self.scmd.Desc(*self.rgsarg) + rgsargUnbound = list(self.rgsarg) + #filter bound sargs + for desce in desc: + if isinstance(desce, list) and len(desce) > 1: + rgsargUnbound.remove(desce[1]) + + fNewScril = True + for desce in desc: + if fNewScril: + pwKey = PwKeyHandler(pwBlock, self.HandleKey) + pwScril = PwList(pwKey, self, dxindent) + fNewScril = False + if isinstance(desce, str): + PwStatic(pwScril, desce) + elif isinstance(desce, list): + if isinstance(desce[0], str): + # selection for the scmd / scril + ProjectTypein(pwScril, self, desce[0], Scmd.Rgscmd_st, Scril.SetScmd) + else: + # mapping to sarg + fNewScril = self.ProjectSarg(pwBlock, pwScril, desce[0], desce[1]) + else: + # sproj + fNewScril = self.ProjectSarg(pwBlock, pwScril, desce, rgsargUnbound.pop(0)) + + def ProjectSarg(self, pwBlock, pwScril, sproj, sarg): + return sproj(sarg, pwBlock, pwScril) + +# pw - projected widget +# Nothing ever depends on the object identity or permanence of a pw! +# They are generated on demand, whenever we need to draw or process a +# keypress. + +class Pw(object): + def __init__(self, pwParent): + if pwParent != None: + pwParent.AddChild(self) + self.pwParent = pwParent + def PwParent(self): + return self.pwParent + def Value(self): + return None + def FSelectable(self): + return False + def FContainer(self): + return False + def RgpwChild(self): + return [] + def HandleKey(self, pov, psel, key): + return False + def PwFirstSelectable(self): + if self.FSelectable(): + return self + for pwChild in self.RgpwChild(): + pw = pwChild.PwFirstSelectable() + if pw != None: + return pw + return None + +class PwContainer(Pw): + def __init__(self, pwParent): + Pw.__init__(self, pwParent) + self.rgpwContents = [] + def AddChild(self, pw): + self.rgpwContents.append(pw) + def RgpwChild(self): + return self.rgpwContents + def FContainer(self): + return True + +class PwStatic(Pw): + def __init__(self, pwParent, stText): + Pw.__init__(self, pwParent) + self.stText = stText + def DxDyNew(self, w, dxStart, mpksel): + rgsnippet = self.Rgsnippet(w, dxStart, mpksel) + if len(rgsnippet) == 1: + return (dxStart + len(rgsnippet[0].st), 0) + else: + return (len(rgsnippet[-1].st), len(rgsnippet) - 1) + def StTextDisplay(self, ksel): + return self.stText + def Rgsnippet(self, w, dxStart, mpksel): + ksel = mpksel.Get(self) + return list(RgSnippetWrapped(self.StTextDisplay(ksel), w, ksel == Ksel.NAV, dxStart)) + def ColBg(self, ksel): + return ansi.BLACK + def ColFg(self): + return ansi.WHITE | ansi.FBRIGHT + def RgSnippetXY(self, x, y, w, dxStart, mpksel): + fFirst = True + for snippet in self.Rgsnippet(w, dxStart, mpksel): + xT = x + dxStart if fFirst else x + yield (snippet, xT, y) + y = y + 1 + fFirst = False + def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay): + if not fOverlay: + ksel = mpksel.Get(self) + for (snippet, x, y) in self.RgSnippetXY(x, y, w, dxStart, mpksel): + ascr.PutSt(snippet.st, x, y, self.ColFg(), self.ColBg(ksel)) + +class PwTypein(PwStatic): + def __init__(self, pwParent, stSobj, typeable): + if typeable.GetStTypein() == None: + stDisplay = stSobj + else: + stDisplay = None + PwStatic.__init__(self, pwParent, stDisplay) + self.typeable = typeable + def Value(self): + return self.typeable + def StTextDisplay(self, ksel): + if self.stText == None: + return self.typeable.steditTypein.StForSize(ksel == Ksel.NAV) + return self.stText + def FSelectable(self): + return True + def ColBg(self, ksel): + if ksel == Ksel.NAV: + return ansi.YELLOW + elif ksel == Ksel.OTHERNAV: + return ansi.MAGENTA + return ansi.BLUE + def HandleKey(self, pov, psel, key): + return self.typeable.HandleTypeinKey(key) + def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay): + if not fOverlay: + ksel = mpksel.Get(self) + fTextSelect = ksel == Ksel.NAV and self.typeable.GetStTypein() != None + for (snippet, x, y) in self.RgSnippetXY(x, y, w, dxStart, mpksel): + ascr.PutSt(snippet.st, x, y, self.ColFg(), self.ColBg(Ksel.NONE if fTextSelect else ksel)) + if fTextSelect: + self.typeable.steditTypein.DrawCursor(ascr, x, y, self.ColFg(), self.ColBg(ksel), snippet) + else: + for pwChild in self.RgpwChild(): + pwChild.Draw(ascr, x, y, dxStart, mpksel, fOverlay) + +class PwButton(PwStatic): + def __init__(self, pwParent, stText, value, dgEnter): + PwStatic.__init__(self, pwParent, stText) + self.value = value + self.dgEnter = dgEnter + def Value(self): + return self.value + def FSelectable(self): + return True + def ColBg(self, ksel): + if ksel == Ksel.NAV: + return ansi.YELLOW + elif ksel == Ksel.OTHERNAV: + return ansi.MAGENTA + return ansi.BLUE + def ColFg(self): + return ansi.BLUE | ansi.FBRIGHT + def HandleKey(self, pov, psel, key): + if key == ansi.K_RETURN: + self.dgEnter(self.Value()) + return True + return False + +class PwButtonHidden(PwButton): + def __init__(self, pwParent, stText, value, dgEnter, dxIndent): + PwButton.__init__(self, pwParent, stText, value, dgEnter) + self.dxIndent = dxIndent + def DxDyNew(self, w, dxStart, mpksel): + if mpksel.Get(self) == Ksel.NAV: + return PwButton.DxDyNew(self, w - self.dxIndent, dxStart + self.dxIndent, mpksel) + return (0, 0) + def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay): + if mpksel.Get(self) == Ksel.NAV: + PwButton.Draw(self, ascr, x + self.dxIndent, y, w - self.dxIndent, dxStart, mpksel, fOverlay) + +class PwKeyHandler(PwContainer): + def __init__(self, pwParent, dgHandleKey): + PwContainer.__init__(self, pwParent) + self.dgHandleKey = dgHandleKey + def DxDyNew(self, w, dxStart, mpksel): + return self.RgpwChild()[0].DxDyNew(w, dxStart, mpksel) + def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay): + self.RgpwChild()[0].Draw(ascr, x, y, w, dxStart, mpksel, fOverlay) + def HandleKey(self, pov, psel, key): + self.dgHandleKey(self, pov, psel, key) + +class PwBlock(PwContainer): + def __init__(self, pwParent, block, dxIndent = 2): + PwContainer.__init__(self, pwParent) + self.block = block + self.dxIndent = dxIndent + def Value(self): + return self.block + def DxIndent(self): + return self.dxIndent + def DxDyNew(self, w, dxStart, mpksel): + return (0, 1 + sum([pw.DxDyNew(w - self.DxIndent(), 0, mpksel)[1] + 1 for pw in self.rgpwContents])) + def RgpwChildToDraw(self): + return self.RgpwChild() + def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay): + for pw in self.RgpwChildToDraw(): + pw.Draw(ascr, x + self.DxIndent(), y, w - self.DxIndent(), 0, mpksel, fOverlay) + (dxNew, dyNew) = pw.DxDyNew(w - self.DxIndent(), 0, mpksel) + if dxNew > 0 or dyNew > 0: + y = y + dyNew + 1 + def HandleKey(self, pov, psel, key): + if key == ansi.K_UP: + return psel.Dec(self) + elif key == ansi.K_DOWN: + return psel.Inc(self) + elif key == ansi.K_DEL: + pass + return False + +class PwDropdown(PwBlock): + def __init__(self, pwParent): + PwBlock.__init__(self, pwParent, None, 0) + def WidthMax(self, mpksel): + w = -1 + for pw in self.RgpwChild(): + wPw = pw.DxDyNew(1000000, 0, mpksel)[0] + if wPw > w: + w = wPw + return w + def RgpwChildToDraw(self): + return PwBlock.RgpwChild(self)[1:] + def PwChildFirst(self): + return PwBlock.RgpwChild(self)[0] + def DxDyNew(self, w, dxStart, mpksel): + return self.PwChildFirst().DxDyNew(w, dxStart, mpksel) + def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay): + if not fOverlay: + self.PwChildFirst().Draw(ascr, x, y, w, dxStart, mpksel, False) + else: + wMax = self.WidthMax(mpksel) + w = w - dxStart + x = x + dxStart + if x + wMax > w and wMax < w: + x = w - self.WidthMax(mpksel) + PwBlock.Draw(self, ascr, x, y + 1, w, 0, mpksel, False) + +class PwList(PwContainer): + def __init__(self, pwParent, scril, dxIndent): + PwContainer.__init__(self, pwParent) + self.scril = scril + self.dxIndent = dxIndent + def Value(self): + return self.scril + def DxDyNew(self, w, dxStart, mpksel): + dx = dxStart + dy = 0 + for pw in self.rgpwContents: + (dx, dyPw) = pw.DxDyNew(w, dx, mpksel) + dy = dy + dyPw + return (dx, dy) + def DxIndent(self): + return self.dxIndent + def Draw(self, ascr, x, y, w, dx, mpksel, fOverlay): + for pw in self.rgpwContents: + pw.Draw(ascr, x + self.DxIndent(), y, w - self.DxIndent(), dx, mpksel, fOverlay) + (dx, dy) = pw.DxDyNew(w, dx, mpksel) + y = y + dy + def HandleKey(self, pov, psel, key): + if key == ansi.K_LEFT: + return psel.Dec(self) + elif key == ansi.K_RIGHT: + return psel.Inc(self) + return False + +class Ksel(object): + FOTHER = 0x2000 + NONE = FOTHER | 0x1000 # OR together all ksel flags and add 0x1000; largest possible ksel + NAV = 0 + OTHERNAV = NAV | FOTHER + + @staticmethod + def KselBest(ksel1, ksel2): + if ksel1 < ksel2: + return ksel1 + return ksel2 + +class Psel(TPrs): + def InitPersistent(self, pw, ksel): + assert len(pw.RgpwChild()) == 0 #leaf + self.rgo_ipw = self.Rgo_ipwFromPw(pw) + self.ksel = ksel + + def Rgo_ipwFromPw(self, pw): + rgo_ipw = [] + if pw != None: + while pw.PwParent() != None: + rgo_ipw.insert(0, (pw.Value(), pw.PwParent().RgpwChild().index(pw))) + pw = pw.PwParent() + return rgo_ipw + + def Validate(self, pwRoot): + assert pwRoot.PwParent() == None #root + io_ipw = 0 + pwSelected = pwRoot + while len(pwSelected.RgpwChild()) > 0 and io_ipw < len(self.rgo_ipw): + (o, ipw) = self.rgo_ipw[io_ipw] + ipwNew = 0 + for pwChild in pwSelected.RgpwChild(): + if o != None and pwChild.Value() == o: + break + ipwNew = ipwNew + 1 + if ipwNew == len(pwSelected.RgpwChild()): + ipwLim = ipw + 1 if ipw < len(pwSelected.RgpwChild()) else len(pwSelected.RgpwChild()) + ipwNew = IoDec(pwSelected.RgpwChild(), ipwLim, self.FPwValid) + if ipwNew < 0: + break + pwSelected = pwSelected.RgpwChild()[ipwNew] + io_ipw = io_ipw + 1 + + # we've made our best guess as to what the closest selectable thing is -- now make sure we point at SOMEthing selectable. + if not pwSelected.FSelectable(): + pwT = pwSelected + while pwT != None: + pwSelected = pwT.PwFirstSelectable() + if pwSelected != None: + break + pwT = pwT.PwParent() + + self.rgo_ipw = self.Rgo_ipwFromPw(pwSelected) + + def FPwValid(self, pw): + if pw.FSelectable(): + return True + for pwChild in pw.RgpwChild(): + if self.FPwValid(pwChild): + return True + return False + + def Value(self, pwRoot): + self.Validate(pwRoot) + if len(self.rgo_ipw) > 0: + return self.rgo_ipw[-1][0] + return None + + def PwSelected(self, pwRoot, clevel = None): + self.Validate(pwRoot) + return self.PwSelectedI(pwRoot, self.rgo_ipw[:clevel]) + + def PwSelectedI(self, pw, rgo_ipw): + for (_, ipw) in rgo_ipw: + pw = pw.RgpwChild()[ipw] + return pw + # contract: pwContainer is actually selected + def Inc(self, pwContainer): + return self.ChangeI(pwContainer, IoInc) + def Dec(self, pwContainer): + return self.ChangeI(pwContainer, IoDec) + + def ChangeI(self, pwContainer, fnChange): + clevelChild = 1 + pwRoot = pwContainer + while pwRoot.PwParent() != None: + clevelChild = clevelChild + 1 + pwRoot = pwRoot.PwParent() + self.Validate(pwRoot) + assert self.PwSelected(pwRoot, clevelChild).PwParent() == pwContainer + + ipw = self.rgo_ipw[clevelChild - 1][1] + rgpw = pwContainer.RgpwChild() + ipwNew = fnChange(rgpw, ipw, self.FPwValid) + self.rgo_ipw[clevelChild - 1] = (rgpw[ipwNew].Value(), ipwNew) + self.Validate(pwRoot) + return ipwNew != ipw + +# projector overlay -- handles navigation, drawing the projection, etc +class Pov(TokenClient): + def InitPersistent(self, owner, client, block): + TokenClient.InitPersistent(self, owner, client, "drawable", "overlay") + self.block = block + self.pselstate = self.game.rgtoken("pselstate")[0] + + def PwProjected(self): + return self.block.Project(None) + + def run(self): + while True: + key = self.client.evKey.receive(self) + psel = self.pselstate.PselByClient(self.client) + pwSel = psel.PwSelected(self.PwProjected()) + while pwSel != None: + if pwSel.HandleKey(self, psel, key): + break + pwSel = pwSel.PwParent() + + def draw(self, ascr, client): + if client == self.client: + pw = self.PwProjected() + mpksel = self.pselstate.GetMpksel(pw, client) + pw.Draw(ascr, 1, 1, ascr.W(), 0, mpksel, False) + pw.Draw(ascr, 1, 1, ascr.W(), 0, mpksel, True) + +class PselState(Token): + def InitPersistent(self, owner, block): + Token.InitPersistent(self, owner, "pselstate") + self.block = block + def InitTransient(self): + Token.InitTransient(self) + self.mpclient_psel = {} + def OnLeave(self, client): + del self.mpclient_psel[client] + def OnJoin(self, client): + print "client joined,", client + self.mpclient_psel[client] = Psel(self.block.Project(None).PwFirstSelectable(), Ksel.NAV) + class Mpksel(object): + def __init__(self, pwRoot, client, mpclient_psel): + self.mppw_ksel = {} + for clientT, psel in mpclient_psel.items(): + kselNew = Ksel.NAV + if clientT != client: + kselNew = kselNew | Ksel.FOTHER + pw = psel.PwSelected(pwRoot) + self.mppw_ksel[pw] = Ksel.KselBest(kselNew, self.Get(pw)) + + def Get(self, pw): + return self.mppw_ksel.get(pw, Ksel.NONE) + + def GetMpksel(self, pwRoot, client): + return self.Mpksel(pwRoot, client, self.mpclient_psel) + def PselByClient(self, client): + return self.mpclient_psel[client] + def run(self): + print "pselstate running" + with self.game.evLeave.oob(self, self.OnLeave): + while True: + self.OnJoin(self.game.evJoin.receive(self)) + +class GameScriptTest(Game): + def InitPersistent(self): + Game.InitPersistent(self) + self.block = Block(None) + self.block.InsertScrilAfter(None, ScmdHelloWorld) + self.block.InsertScrilAfter(None, ScmdBlank) + self.block.InsertScrilAfter(None, ScmdHelloWorld) + self.block.InsertScrilAfter(None, ScmdIf) + self.block.InsertScrilAfter(None, ScmdHelloWorld) + + def GetRgclsTokTrans(self): + return [[PselState, self.block], [AutoJoiner, [Pov, self.block]]] + +class RunnerScriptTest(Runner): + def InitPersistent(self): + self.game = GameScriptTest() + Runner.InitPersistent(self) + + def RunGame(self, client): + client.joinGame(self.game) + +if __name__ == "__main__": + Run(RunnerScriptTest, "script_test.marm") \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e4523fe --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20091212133112.13:@thin setup.py +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext + +setup( + cmdclass = {'build_ext': build_ext}, + ext_modules = [Extension("ansi_cython", ["ansi_cython.pyx"])] +) +#@-node:jpenner.20091212133112.13:@thin setup.py +#@-leo diff --git a/telnet.py b/telnet.py new file mode 100644 index 0000000..8d66504 --- /dev/null +++ b/telnet.py @@ -0,0 +1,233 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20090510085557.10:@thin telnet.py +#@@language python +from twisted.protocols import basic +from twisted.conch import telnet +from twisted.internet.protocol import Factory,ServerFactory +from twisted.internet import protocol, reactor +from zope.interface import implements +from twisted.python import log +import ansi +import engine +import sys +import config + +#@<> +#@+node:jpenner.20090513213413.1:<> +class AnsiTelnet(protocol.Protocol): + implements(telnet.ITelnetProtocol) + + rgoptRemote = [telnet.SGA, telnet.ECHO] #putty sez: 31 (NAWS), 32 (TERMINAL-SPEED), 24 (TERMINAL-TYPE), 39 (NEW-ENVIRON), 36 (ENVIRON), 3(SGA) + # We might support NAWS at some point -- internally we will always deal in 80x25, but it might be helpful to have + # window resizing work, and provide feedback if the window is too small. + # I don't know what we'd do in response to TERMINAL-SPEED, since we're just going to blast out data as fast as possible anyway. + # NEW-ENVIRON might be useful for sending usernames or cookies automatically from flashterm. + # ENVIRON is an older version of NEW-ENVIRON. + # TERMINAL-TYPE is client-to-server only, and the server only speaks ANSI. Again, maybe useful as a feedback mechanism. + + rgoptLocal = [telnet.SGA] #putty sez: 1 (ECHO), 3 (SGA) + + def __init__(self, client): + print "created" + self.ast = None + self.client = client + self.evKey = None + self.evArrow = None + self.Refresh() + + def connectionMade(self): + print "connection made" + rgdefr = [] + def AddCb(defr): + """ + rgdefr.append(defr) + def cbWaitForHello(result): + print "cb: ", result, len(rgdefr) + rgdefr.remove(defr) + if len(rgdefr) == 0: + self.Hello() + defr.addCallback(cbWaitForHello) + defr.addErrback(cbWaitForHello) + """ + pass + + for opt in self.rgoptRemote: + AddCb(self.transport.will(opt)) + for opt in self.rgoptLocal: + AddCb(self.transport.do(opt)) + print "end: ", len(rgdefr) + self.Hello() + + #@ <> + #@+node:jpenner.20090514181656.118:<> + def enableLocal(self, option): + print "enableLocal", ord(option) + return option in self.rgoptLocal + def enableRemote(self, option): + print "enableRemote", ord(option) + return option in self.rgoptRemote + + def disableLocal(self, option): + print "disableLocal", option + pass + def disableRemote(self, option): + print "disableRemote", option + pass + #@nonl + #@-node:jpenner.20090514181656.118:<> + #@nl + + def dataReceived(self, data): + if self.ast != None: + data = self.ast + data + ich = 0 + ichSplit = 0 + fAnsi = False + fEsc = False + for ch in data: + if fAnsi: + if not (ch == ";" or ch.isdigit()): + fAnsi = False + if ch.isalpha(): + stRgarg = data[ichSplit + 2:ich] + if len(stRgarg) == 0: + rgarg = [] + else: + rgarg = [int(stArg) for stArg in stRgarg.split(";")] + self.RcvAnsi(ch, rgarg) + ichSplit = ich + 1 + elif ch == ansi.esc[0]: + fEsc = True + elif fEsc: + fEsc = False + if ch == ansi.esc[1]: + fAnsi = True + if ichSplit < ich - 1: + self.RcvText(data[ichSplit:ich - 1]) + ichSplit = ich - 1 + ich = ich + 1 + if fEsc or fAnsi: + self.ast = data[ichSplit:ich] + else: + self.ast = None + if ichSplit < ich: + self.RcvText(data[ichSplit:ich]) + + #@ @+others + #@+node:jpenner.20090526105844.6:Hello + def Hello(self): + print "negotiated!" + self.transport.write(ansi.esc + "2J") #cls + self.transport.write(ansi.esc + "=25l") #hide the cursor + self.client.dgJoinGame = self.CreateEvents + self.client.addDgQuit(self.transport.loseConnection) + self.client.go(self.factory.fnRun, self) + #@nonl + #@-node:jpenner.20090526105844.6:Hello + #@+node:jpenner.20090526212048.1:Keys + def RcvText(self, text): + if self.client.evKey: + for ch in text: + self.client.evKey.fire(ch) + + mpcode_key = {"A": ansi.K_UP, "B": ansi.K_DOWN, "C": ansi.K_RIGHT, "D": ansi.K_LEFT, + "K": ansi.K_END, "H": ansi.K_HOME, "V": ansi.K_PGUP, "U": ansi.K_PGDN } + + def RcvAnsi(self, code, rgarg): + if self.client.evKey: + if code in self.mpcode_key: + self.client.evKey.fire(self.mpcode_key[code]) + else: + print "unrecognized code", code, rgarg + + def CreateEvents(self, client, game): + client.evKey = engine.Event(game) + #@nonl + #@-node:jpenner.20090526212048.1:Keys + #@+node:jpenner.20090527144214.1:Draw + def Draw(self, ascr): + self.transport.write(self.ascr.AstDiff(ascr)) + self.transport.write(ansi.esc + "H") #cursor stays in top-left corner? ascr should contain a cursor pos? + self.ascr = ascr + + def Refresh(self): + self.ascr = ansi.Ascr(config.W, config.H, ansi.achInvd) + #@nonl + #@-node:jpenner.20090527144214.1:Draw + #@+node:jpenner.20090529173104.4:connectionLost + def connectionLost(self, reason): + print "lost connection" + self.client.removeDgQuit(self.transport.loseConnection) + self.client.quit() + #@nonl + #@-node:jpenner.20090529173104.4:connectionLost + #@-others +#@nonl +#@-node:jpenner.20090513213413.1:<> +#@nl +#@<> +#@+node:jpenner.20090515161650.1:<> +class AnsiFactory(ServerFactory): + def __init__(self, fnRun): + self.fnRun = fnRun + def buildProtocol(self, addr): + p = telnet.TelnetTransport(AnsiTelnet, engine.Client()) + p.factory = self + print "built protocol for ", addr + return p +#@-node:jpenner.20090515161650.1:<> +#@nl + +#@<> +#@+node:jpenner.20090518103251.118:<> +class FlashPolicy(basic.LineReceiver): + delimiter = '\0' + + def lineReceived(self,line): + if line == "": + self.sendLine("") +#@-node:jpenner.20090518103251.118:<> +#@nl +#@<> +#@+node:jpenner.20090914034457.2:<> +class CmdProtocol(basic.LineReceiver): + def connectionMade(self): + if self.transport.getHost().host != "127.0.0.1": + self.transport.write("Sorry, you must connect from localhost") + self.transport.loseConnection() + + def lineReceived(self, line): + self.transport.write(self.factory.fnCmd(line) + self.delimiter) + +class CmdFactory(ServerFactory): + def __init__(self, fnCmd): + self.fnCmd = fnCmd + def buildProtocol(self, addr): + p = CmdProtocol() + p.factory = self + print "built cmd protocol for ", addr + return p +#@-node:jpenner.20090914034457.2:<> +#@nl + +#@<> +#@+node:jpenner.20090513213413.2:<> +def RunServer(fnRun): + log.startLogging(sys.stdout) + + factoryPolicy = Factory() + factoryPolicy.protocol = FlashPolicy + + reactor.listenTCP(config.PORT_TELNET, AnsiFactory(fnRun), 50, config.BIND_HOSTNAME) + if config.PORT_FLASHPOLICY: + reactor.listenTCP(config.PORT_FLASHPOLICY, factoryPolicy, 50, config.BIND_HOSTNAME) + reactor.run() + +def RunCmdServer(fnCmd): + if config.PORT_CMDSERVER: + reactor.listenTCP(config.PORT_CMDSERVER, CmdFactory(fnCmd), 50, "localhost") +#@nonl +#@-node:jpenner.20090513213413.2:<> +#@nl +#@-node:jpenner.20090510085557.10:@thin telnet.py +#@-leo diff --git a/testsig.py b/testsig.py new file mode 100644 index 0000000..4152f21 --- /dev/null +++ b/testsig.py @@ -0,0 +1,8 @@ +import signal + +def handler(signum, frame): + print "signal:", signum, frame + +signal.signal(signal.SIGINT, handler) + +input() diff --git a/tpers.py b/tpers.py new file mode 100644 index 0000000..4b1044e --- /dev/null +++ b/tpers.py @@ -0,0 +1,268 @@ +import cPickle +import pdb +import traceback + +def Decorator(dg): + def DecoratorNew(*rgarg, **mparg): + def CallDecorator(o): + return dg(o, *rgarg, **mparg) or o + return CallDecorator + return DecoratorNew + +@Decorator +def Version(cls, vr): + cls._version = vr + +def RenameFrom(stName, namespace): + def SetOldName(cls): + namespace[stName] = cls + return cls + return SetOldName + +class TransientSetter(object): + def __init__(self, obj): + self.obj = obj + try: + self._fWriteOld = obj._fWriteToPersistent + except Exception: + self._fWriteOld = True + def __enter__(self): + self.obj._fWriteToPersistent = False + return self.obj + def __exit__(self, type, value, traceback): + self.obj._fWriteToPersistent = self._fWriteOld + +@Version(1) +class TPrs(object): + def __init__(self, *rgarg, **mparg): + self._persistent = TPM() + self._fWriteToPersistent = True + self._versionCurr = self._version + self._fPersistent = True + self.InitPersistent(*rgarg, **mparg) + with self.SetTransiently(): + self.InitTransient() + + def InitPersistent(self, *rgarg, **mparg): + pass + + def InitTransient(self): + pass + + def SetTransiently(self): + return TransientSetter(self) + + def MakeTransient(self, key, value): + try: + del self._persistent[key] + except Exception: + pass + with self.SetTransiently(): + setattr(self, key, value) + + def FPersist(self): + return self._fPersistent + + def DontPersist(self): + self._fPersistent = False + + def UpgradeFrom(self, versionOld): + pass + + def __getattr__(self, key): + stPersistent = "no persistent yet" + if key != "_persistent": + try: + return self._persistent[key] + except Exception: + stPersistent = str(self._persistent) + raise AttributeError("Attribute '" + key + "' not found in object with type '" + type(self).__name__ + "':" + stPersistent + " __dict__:" + str(self.__dict__)) + + __obj_setattr = object.__setattr__ + __obj_delattr = object.__delattr__ + def __setattr__(self, key, value): + try: + if self._fWriteToPersistent or key in self._persistent: + self._persistent[key] = value + try: + self.__obj_delattr(key) + except AttributeError: + pass + return + except Exception: pass + self.__obj_setattr(key, value) + + def __delattr__(self, key): + try: + del self._persistent[key] + return + except Exception: pass + self.__obj_delattr(key) + + def __reduce__(self): + if self.FPersist(): + return (NewObjWithClass, (type(self),), self._persistent) + else: + return (NewObjWithClass, (None,)) + + def __setstate__(self, state): + self._persistent = state + self._fWriteToPersistent = True + if self._versionCurr != self._version: + Odb.rgtprsToUpgrade.append(self) + with self.SetTransiently(): + self.InitTransient() + def _Upgrade(self): + self.UpgradeFrom(self._versionCurr) + self._versionCurr = self._version + +class Odb(object): + @staticmethod + def Save(tprs, fn): + with open(fn, "w") as fl: + cPickle.dump(tprs, fl) + + @classmethod + def Load(cls, fn): + cls.rgtprsToUpgrade = [] + try: + with open(fn, "r") as fl: + tprs = cPickle.load(fl) + except Exception as e: + print "error unpickling:", e + traceback.print_exc() + return None + if len(cls.rgtprsToUpgrade) > 0: + for tprsToUpgrade in cls.rgtprsToUpgrade: + tprsToUpgrade._Upgrade() + fnUpgraded = fn + ".upgraded" + Odb.Save(tprs, fnUpgraded) + tprs = Odb.Load(fnUpgraded) + cls.rgtprsToUpgrade = None + return tprs + +clsList = list +clsDict = dict +clsSet = set +clsTuple = tuple + +def MkTransparent(value): + if type(value) == list: + return TPL(value) + elif type(value) == dict: + return TPM(value) + elif type(value) == set: + return TPS(value) + elif type(value) == tuple: + return TPT(value) + else: + return value + +def RgTransparent(rg): + if rg == None: return [] + return [MkTransparent(x) for x in rg] + +def MpTransparent(mp): + if isinstance(mp, TPM): return mp + mpNew = {} + if mp != None: + for k,v in mp.iteritems(): + mpNew[k] = MkTransparent(v) + return mpNew + +def FPersist(obj): + return (not isinstance(obj, TPrs)) or obj.FPersist() + +def MkPersistable(obj): + if FPersist(obj): + return obj + return None + +def NewObjWithClass(cls): + if cls == None: + return None + return cls.__new__(cls) + +class TPL(clsList): + __pl_setitem = clsList.__setitem__ + __pl_setslice = clsList.__setslice__ + __pl_iadd = clsList.__iadd__ + __pl_append = clsList.append + __pl_insert = clsList.insert + __pl_extend = clsList.extend + + def __init__(self, rg = None): + clsList.__init__(self, RgTransparent(rg)) + + def __setitem__(self, i, item): + return self.__pl_setitem(i, MkTransparent(item)) + + def __setslice__(self, i, j, other): + return self.__pl_setslice(i, j, RgTransparent(other)) + + def __iadd__(self, other): + return self.__pl_iadd(RgTransparent(other)) + + def append(self, item): + return self.__pl_append(MkTransparent(item)) + + def insert(self, i, item): + return self.__pl_insert(i, MkTransparent(item)) + + def extend(self, other): + return self.__pl_extend(RgTransparent(other)) + + def __reduce__(self): + return (MkTransparent, ([MkPersistable(x) for x in self],)) + +# Strip weak references rather than replacing them with None +class TPLS(TPL): + def __reduce__(self): + return (MkTransparent, ([x for x in self if FPersist(x)],)) + +class TPM(clsDict): + __pm_setitem = clsDict.__setitem__ + __pm_update = clsDict.update + + def __init__(self, *rgarg, **kwarg): + clsDict.__init__(self, MpTransparent(dict(*rgarg, **kwarg))) + + def __setitem__(self, k, v): + return self.__pm_setitem(k, MkTransparent(v)) + + def update(self, other, **kwargs): + return self.__pm_update(MpTransparent(other), **MpTransparent(kwargs)) + + def __reduce__(self): + mp = {} + for k, v in self.iteritems(): + if FPersist(k): + mp[k] = MkPersistable(v) + return (MkTransparent, (mp,)) + +class TPS(clsSet): + __ps_update = clsSet.update + __ps_ior = clsSet.__ior__ + __ps_add = clsSet.add + + def __init__(self, rg = None): + clsSet.__init__(self, RgTransparent(rg)) + + def update(self, *rgother): + return self.__ps_update(*RgTransparent(rgother)) + + def __ior__(self, other): + return self.__ps_ior(MkTransparent(other)) + + def add(self, elem): + return self.__ps_add(MkTransparent(elem)) + + def __reduce__(self): + return (MkTransparent, (set([x for x in self if FPersist(x)]),)) + +class TPT(clsTuple): + def __new__(cls, tup = None): + return clsTuple.__new__(cls, RgTransparent(tup)) + + def __reduce__(self): + return (MkTransparent, (tuple([MkPersistable(x) for x in self]),)) diff --git a/tpers_old.py b/tpers_old.py new file mode 100644 index 0000000..c068ee4 --- /dev/null +++ b/tpers_old.py @@ -0,0 +1,103 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20090714203921.6:@thin tpers_old.py +import ZODB +import persistent + +#@+others +#@+node:jpenner.20090714203921.7:def MkTransparent +def MkTransparent(value): + if type(value) == list: + return TPL(value) + elif type(value) == dict: + return TPM(value) + elif type(value) == set: + return TPS(value) + elif type(value) == tuple: + return TPT(value) + else: + return value +#@-node:jpenner.20090714203921.7:def MkTransparent +#@+node:jpenner.20090714203921.10:def RgTransparent +def RgTransparent(rg): + if rg == None: return [] + return [MkTransparent(x) for x in rg] +#@-node:jpenner.20090714203921.10:def RgTransparent +#@+node:jpenner.20090714203921.11:def MpTransparent +def MpTransparent(mp): + if isinstance(mp, TPM): return mp + mpNew = {} + if mp != None: + for k,v in mp.iteritems(): + mpNew[k] = MkTransparent(v) + return mpNew +#@-node:jpenner.20090714203921.11:def MpTransparent +#@+node:jpenner.20090714203921.8:class TPrs +class TPrs(persistent.Persistent): + __prs_setattr = persistent.Persistent.__setattr__ + + def __setattr__(self, name, value): + self.__prs_setattr(name, MkTransparent(value)) +#@-node:jpenner.20090714203921.8:class TPrs +#@+node:jpenner.20090714203921.9:class TPL +class TPL(clsList): + __pl_setitem = clsList.__setitem__ + __pl_setslice = clsList.__setslice__ + __pl_iadd = clsList.__iadd__ + __pl_append = clsList.append + __pl_insert = clsList.insert + __pl_extend = clsList.extend + + def __init__(self, rg = None): + clsList.__init__(self, RgTransparent(rg)) + + def __setitem__(self, i, item): + return self.__pl_setitem(i, MkTransparent(item)) + + def __setslice__(self, i, j, other): + return self.__pl_setslice(i, j, RgTransparent(other)) + + def __iadd__(self, other): + return self.__pl_iadd(RgTransparent(other)) + + def append(self, item): + return self.__pl_append(MkTransparent(item)) + + def insert(self, i, item): + return self.__pl_insert(i, MkTransparent(item)) + + def extend(self, other): + return self.__pl_extend(RgTransparent(other)) + + def __reduce__(self): + return (MkTransparent, ([MkPersistable(x) for x in self],)) + +# Strip weak references rather than replacing them with None +class TPLS(TPL): + def __reduce__(self): + return (MkTransparent, ([x for x in self if FPersist(x)],)) +#@-node:jpenner.20090714203921.9:class TPL +#@+node:jpenner.20090714203921.12:class TPM +class TPM(clsDict): + __pm_setitem = clsDict.__setitem__ + __pm_update = clsDict.update + + def __init__(self, *rgarg, **kwarg): + clsDict.__init__(self, MpTransparent(dict(*rgarg, **kwarg))) + + def __setitem__(self, k, v): + return self.__pm_setitem(k, MkTransparent(v)) + + def update(self, other, **kwargs): + return self.__pm_update(MpTransparent(other), **MpTransparent(kwargs)) + + def __reduce__(self): + mp = {} + for k, v in self.iteritems(): + if FPersist(k): + mp[k] = MkPersistable(v) + return (MkTransparent, (mp,)) + +#@-node:jpenner.20090714203921.12:class TPM +#@-others +#@-node:jpenner.20090714203921.6:@thin tpers_old.py +#@-leo diff --git a/util.py b/util.py new file mode 100644 index 0000000..da6b88f --- /dev/null +++ b/util.py @@ -0,0 +1,127 @@ +from tpers import TPrs +import ansi +# probably reinventing the wheel :/ +class Rgseq(TPrs): + def InitPersistent(self, *rgseq): + self.rgseq = rgseq + def __len__(self): + return sum([len(seq) for seq in self.rgseq]) + + def __getitem__(self, i): + c = 0 + for seq in self.rgseq: + cSeq = len(seq) + if i < c + cSeq: + return seq[i - c] + c = c + cSeq + raise IndexError + + def index(self, o): + c = 0 + for seq in self.rgseq: + try: + iSeq = seq.index(o) + return iSeq + c + except ValueError: + c = c + len(seq) + raise ValueError + +class Snippet(object): + def __init__(self, st, ichStart, ichLim): + self.st = st + self.ichStart = ichStart + self.ichLim = ichLim + +def RgstWrapped(st, w, fForEditing = False, dxStart = 0): + return [snippet.st for snippet in RgSnippetWrapped(st, w, fForEditing, dxStart)] + +def RgSnippetWrapped(st, w, fForEditing = False, xStart = 0): + ichWrap = -1 + ichStart = 0 + for ich, ch in enumerate(st): + if not fForEditing and ch == ' ': + ichWrap = ich + if ich == ichStart + w - xStart: + ichStartNew = ichWrap + 1 + if ichWrap < 0: + ichWrap = ich + ichStartNew = ich + elif fForEditing: # don't swallow spaces + ichWrap = ichStartNew + yield Snippet(st[ichStart:ichWrap], ichStart, ichStartNew) + ichStart = ichStartNew + ichWrap = -1 + xStart = 0 + if fForEditing and ch == ' ': + ichWrap = ich + + if ichStart < len(st) or ichStart == 0: + yield Snippet(st[ichStart:], ichStart, len(st)) + +# selection helpers +def IoInc(rgo, io, fnSelectable, fRebound = True): + io = io + 1 + while io < len(rgo) and not fnSelectable(rgo[io]): + io = io + 1 + if io >= len(rgo) or not fnSelectable(rgo[io]): + if fRebound: + return IoDec(rgo, io, fnSelectable, False) + return -1 + return io + +def IoDec(rgo, io, fnSelectable, fRebound = True): + io = io - 1 + while io > 0 and not fnSelectable(rgo[io]): + io = io - 1 + if io < 0 or not fnSelectable(rgo[io]): + if fRebound: + return IoInc(rgo, io, fnSelectable, False) + return -1 + return io + +class Stedit(TPrs): + def InitPersistent(self, dgEnter = None, stInitial = ""): + self.dgEnter = dgEnter + self.SetValue(stInitial) + def SetValue(self, st): + self.st = st + self.ich = len(st) + def GetValue(self): + return self.st + def StForSize(self, fSel = True): + if fSel and self.ich == len(self.st): + return self.st + "_" + return self.st + + def HandleKey(self, key): + if key == ansi.K_LEFT and self.ich > 0: + self.ich = self.ich - 1 + elif key == ansi.K_RIGHT and self.ich < len(self.st): + self.ich = self.ich + 1 + elif key == ansi.K_BACKSPACE and self.ich > 0: + self.st = self.st[:self.ich - 1] + self.st[self.ich:] + self.ich = self.ich - 1 + elif key == ansi.K_DEL and self.ich < len(self.st): + self.st = self.st[:self.ich] + self.st[self.ich + 1:] + elif key == ansi.K_HOME: + self.ich = 0 + elif key == ansi.K_END: + self.ich = len(self.st) + elif ansi.FKeyPrintable(key): + self.st = self.st[:self.ich] + key + self.st[self.ich:] + self.ich = self.ich + 1 + elif ansi.FEnter(key) and self.dgEnter != None: + if not self.dgEnter(self.st): + self.st = "" + self.ich = 0 + else: + return False + return True + + def DrawCursor(self, ascr, x, y, colFg, colBg, snippet): + if self.ich >= snippet.ichStart and (snippet.ichLim < 0 or self.ich < snippet.ichLim): + if self.ich == len(self.st): + ch = ' ' + else: + ch = self.st[self.ich] + ascr.PutAch(ansi.MkAch(ch, colFg, colBg), x + self.ich - snippet.ichStart, y) diff --git a/whiteboard.py b/whiteboard.py new file mode 100644 index 0000000..2322309 --- /dev/null +++ b/whiteboard.py @@ -0,0 +1,678 @@ +#@+leo-ver=4-thin +#@+node:jpenner.20090528201601.6:@thin whiteboard.py +from engine import * +from basetoken import * +from tpers import * +from datetime import * +from util import * +import telnet +import time +import login +import weakref + +#@<> +#@+node:jpenner.20090528201601.9:<> +class Layer(TPrs): + def InitPersistent(self, ascr, stName): + self.ascr = ascr + self.stName = stName + + def StName(self): + return self.stName + + def SetName(self, st): + self.stName = st + + def Draw(self, ascr): + ascr.PutAscr(self.ascr) + def FSelectable(self): + return True + +@Version(3) +class Board(Ownable): + def InitPersistent(self, owner, stName, user, rglayer=None, dtLastModified=None): + Ownable.InitPersistent(self, owner) + if rglayer == None: + rglayer = [Layer(ansi.Ascr(self.W(), self.H()), "Layer 1")] + self.rglayer = rglayer + self.stName = stName + self.user = user + self.chat = Chat() + if dtLastModified == None: + self.SetTimestamp() + else: + self.dtLastModified = dtLastModified + + def StName(self): + return self.owner.StNameBoard(self) + def StLastModified(self): + return self.dtLastModified.strftime("%b %d, %Y at %I:%M%p") + + def LayerByName(self, stName): + for layer in self.rglayer: + if layer.StName().lower() == stName.lower(): + return layer + return None + + def NewLayer(self, stName): + if len(self.rglayer) < 8: + layerNew = Layer(ansi.Ascr(self.W(), self.H()), stName) + self.rglayer.append(layerNew) + return layerNew + + def MoveLayer(self, layer, fUp): + ilayer = self.rglayer.index(layer) + if fUp and ilayer > 0: + ilayer = ilayer - 1 + elif not fUp and ilayer < len(self.rglayer) - 1: + ilayer = ilayer + 1 + self.rglayer.remove(layer) + self.rglayer.insert(ilayer, layer) + + def W(self): return config.W + def H(self): return config.H + + def Touch(self): + self.SetTimestamp() + self.owner.BoardTouched(self) + + def SetTimestamp(self): + self.dtLastModified = datetime.now() + + def draw(self, ascr): + for layer in reversed(self.rglayer): + layer.Draw(ascr) + #@ @+others + #@+node:jpenner.20090602154503.5:Save + def Save(self): + fn = config.DIR_ANSI + "/" + "_".join([str(x) for x in time.localtime()]) + ascr = ansi.Ascr(self.W(), self.H()) + self.draw(ascr) + with open(fn + ".html", "w") as fl: + fl.write("
") + fl.write(ascr.Hst()) + fl.write("
") + with open(fn + ".ans", "w") as fl: + fl.write(ascr.Ast()) + #@nonl + #@-node:jpenner.20090602154503.5:Save + #@-others + def UpgradeFrom(self, versionOld): + if versionOld < 2: + gameWB = [gameWB for gameWB in self.owner.rggameWB if gameWB.rgtoken("whiteboard")[0].board == self][0] + self.stName = gameWB.stName + self.user = gameWB.user + if versionOld < 3: + self.chat = Chat() + +@Version(5) +class Whiteboard(Token): + def InitPersistent(self, game, board): + Token.InitPersistent(self, game, "whiteboard") + self.board = board + + def UpgradeFrom(self, versionOld): + if versionOld < 3: + dt = None + else: + dt = self.dtLastModified + del self.dtLastModified + if versionOld < 4: + rglayer = [Layer(self.ascr, "Layer 1")] + del self.ascr + self.untag("drawable") + self.untag("solid") + else: + rglayer = self.rglayer + del self.rglayer + if versionOld < 5: + self.board = Board(self.game.gameLobby, self.game.stName, self.game.user, rglayer, dt) +#@-node:jpenner.20090528201601.9:<> +#@nl +#@<> +#@+node:jpenner.20090918200439.1:<> +class Chl(object): + def __init__(self, client, st): + self.user = client.cld.user + self.st = st.rstrip() + +class ChlMsg(Chl): + def StUser(self): + return "<" + self.user + ">" + def Col(self): + return ansi.WHITE + +class ChlAct(Chl): + def StUser(self): + return "* " + self.user + def Col(self): + return ansi.MAGENTA + +class ChlSys(Chl): + def StUser(self): + return self.user + def Col(self): + return ansi.BLUE + +class Chat(TPrs): + def InitTransient(self): + TPrs.InitTransient(self) + self.cchl = 100 + self.DetachFromGame() + + def InitPersistent(self): + TPrs.InitPersistent(self) + self.rgchl = [] + + def AttachToGame(self, game): + self.game = game + self.evChat = Event(game) + + def DetachFromGame(self): + self.game = None + self.evChat = None + + def Add(self, chl): + if len(chl.st) > 0: + self.rgchl.insert(0, chl) + if len(self.rgchl) > self.cchl: + self.rgchl = self.rgchl[:self.cchl] + self.evChat.fire(chl) + + def CChl(self): + return len(self.rgchl) + def CUsers(self): + return len(self.game.rgclient) + def DrawMsg(self, ascr, x, y, w, h, colBg=ansi.BLACK, ichl=0): + yDraw = y + h - 1 + for chl in self.rgchl[ichl:]: + stUser = chl.StUser() + colFg = chl.Col() + wMsg = w - len(stUser) - 1 + rgstMsg = RgstWrapped(chl.st, wMsg) + + rgstMsg.reverse() + for istMsg, stMsg in enumerate(rgstMsg): + if istMsg == len(rgstMsg) - 1: + st = "{0} {1:{2}}".format(stUser, stMsg, wMsg) + else: + st = "{0:{1}} {2:{3}}".format("", len(stUser), stMsg, wMsg) + ascr.PutSt(st, x, yDraw, colFg, colBg) + yDraw = yDraw - 1 + if yDraw == y - 1: + return + while yDraw >= y: + ascr.PutSt("{0:{1}}".format("", w), x, yDraw, ansi.WHITE, colBg) + yDraw = yDraw - 1 + + def DrawUsers(self, ascr, x, y, w, h, colBg=ansi.BLACK, iuser=0): + yDraw = y + for client in sorted(self.game.rgclient, lambda c1, c2: cmp(c1.Cldg().iclient, c2.Cldg().iclient))[iuser:]: + ascr.PutSt("{0:{1}}".format(str(client.Cldg().iclient) + ") " + client.cld.user, w), x, yDraw, ansi.WHITE, colBg) + yDraw = yDraw + 1 + if yDraw == y + h: + break + while yDraw < y + h: + ascr.PutSt("{0:{1}}".format("", w), x, yDraw, ansi.WHITE, colBg) + yDraw = yDraw + 1 +#@-node:jpenner.20090918200439.1:<> +#@nl +#@<> +#@+node:jpenner.20090528201601.10:<> +class Cursor(TokenClient): + def InitPersistent(self, game, client): + TokenClient.InitPersistent(self, game, client, "drawable", "cursor") + self.fBlink = False + self.cDontBlink = 0 + self.whiteboard = self.game.rgtoken("whiteboard")[0].board + self.pos = ansi.Pos(self.whiteboard.W(), self.whiteboard.H()) + miFg = MiColour("Foreground", ansi.rgcolorFg, ansi.WHITE) + miBg = MiColour("Background", ansi.rgcolorBg, ansi.BLACK) + miChars = MiChars([[49, 50, 51, 52, 53, 54, 55, 56, 57, 48], #digits + [176, 177, 178, 219, 223, 220, 221, 222, 254, 249], #blocks + [218, 191, 192, 217, 196, 179, 195, 180, 193, 194], #single lines + [201, 187, 200, 188, 205, 186, 204, 185, 202, 203], #double lines + [213, 184, 212, 190, 205, 179, 198, 181, 207, 209], #sing vert, doub horiz + [214, 183, 211, 189, 196, 186, 199, 182, 208, 210], #doub vert, sing horiz + [197, 206, 216, 215, 173, 168, 169, 170, 174, 175], #crosses + #[ 1, 2, 3, 4, 5, 6, 14, 15, 19, 20], #smiles + [224, 225, 226, 227, 228, 229, 230, 231, 232, 233], #greek + [234, 235, 236, 237, 238, 239, 240, 241, 242, 243], #crap + [244, 245, 246, 247, 248, 249, 251, 252, 253, 167], #more crap + [155, 156, 157, 158, 159, 171, 172, 166, 32, 32], #$ + [142, 132, 131, 143, 134, 133, 160, 145, 146, 32], #a + [128, 135, 137, 136, 138, 144, 130, 164, 165, 32], #c, e, n + [139, 140, 141, 161, 153, 148, 147, 149, 162, 32], #i, o + [154, 129, 150, 151, 163, 32, 32, 32, 32, 32], #u + ], miFg, miBg) + miQuit = MiButton("Back to Menu", self.client.leaveGame) + self.cf = MiMenu([None, miFg, miBg, None, miChars, None, MiButton("Save", self.Save), None, miQuit, None]) + self.miChat = MiMenu([None, MiChat(client, 13, self.whiteboard.chat), MiTypein(self.SendMsg), None, miQuit, None]) + self.rgmiLayer = RgmiProjected(self.whiteboard.rglayer, DgProjectMiButton(self.SelectLayer)) + self.miLayers = MiMenu(Rgseq([None], self.rgmiLayer, [None, MiButton("New Layer", self.NewLayer), MiButton("Move up", self.LayerUp), MiButton("Move down", self.LayerDown), + None, MiToggle("Show One"), None, MiStatic("Rename selected layer:"), MiTypein(self.RenameLayer, "LayerName", 80), None, miQuit, None])) + self.ovPopup = OvPopup(self, self.client, MiTab.MiTabbed(["Drawing", self.cf], ["Layers", self.miLayers], ["Chat", self.miChat])) + miStatus = MiMenuHoriz([MiStatic("Hit for menu"), MiValue(miChars)]) + miStatus.ListenForNotice(self.miChat) + self.ovStatus = OvStatic(self, self.client, miStatus, OvStatic.BOTTOM) + + self.SelectLayer(self.whiteboard.rglayer[0]) + WhiteboardDraw(self, client) + def Save(self): + self.whiteboard.Save() + msgscroller = self.game.rgtoken("msgscroller")[0] + msgscroller.evPost.fire("Saved picture") + + def SelectLayer(self, layer): + self.ovPopup.SetSelection("layer", self.rgmiLayer.MiFromValue(layer)) + miRename = self.miLayers.MiByName("LayerName").SetValue(layer.StName()) + return True + + def GetLayer(self): + return self.ovPopup.GetSelection("layer").Value() + + def NewLayer(self): + ilayer = 1 + stLayer = None + while stLayer == None: + stLayer = "Layer " + str(ilayer) + if self.whiteboard.LayerByName(stLayer) != None: + stLayer = None + ilayer = ilayer + 1 + layerNew = self.whiteboard.NewLayer(stLayer) + if layerNew != None: + self.SelectLayer(layerNew) + return True + + def LayerUp(self): + self.whiteboard.MoveLayer(self.GetLayer(), True) + return True + + def LayerDown(self): + self.whiteboard.MoveLayer(self.GetLayer(), False) + return True + + def RenameLayer(self, st): + if st != "": + self.GetLayer().SetName(st) + return True + + def blinkCursor(self, fBlink): + self.fBlink = fBlink + if not fBlink and self.cDontBlink > 0: + self.cDontBlink = self.cDontBlink - 1 + + def SendMsg(self, st): + if st.lower().startswith("/me ") and len(st.rstrip()) > 3: + self.whiteboard.chat.Add(ChlAct(self.client, st[4:])) + else: + self.whiteboard.chat.Add(ChlMsg(self.client, st)) + + def OnChat(self, chl): + if type(chl) != ChlSys: + self.miChat.FlagNotice() + + def run(self): + with self.whiteboard.chat.evChat.oob(self, self.OnChat): + while True: + key = self.client.evKey.receive(self) + fDontBlink = True + if key == ansi.K_LEFT: + self.pos.Left() + elif key == ansi.K_RIGHT: + self.pos.Right() + elif key == ansi.K_UP: + self.pos.Up() + elif key == ansi.K_DOWN: + self.pos.Down() + elif key == ansi.K_BACKSPACE: + self.pos.Left() + self.PutWb(ansi.achBlank) + elif key == ansi.K_DEL: + self.PutWb(ansi.achBlank) + self.pos.Right() + elif key == ansi.K_TAB: + fDontBlink = False + self.ovPopup.evShow.fire() + self.ovPopup.evDone.receive(self) + elif key == ansi.K_HOME: + self.Skip(self.pos.Left) + elif key == ansi.K_END: + self.Skip(self.pos.Right) + elif key == ansi.K_PGUP: + self.Skip(self.pos.Up) + elif key == ansi.K_PGDN: + self.Skip(self.pos.Down) + elif ansi.FKeyPrintable(key): + if key.isdigit(): + self.PutWb(chr(self.cf.Value("Characters")[IchFromCh(key)])) + else: + self.PutWb(key) + self.pos.Right() + else: + print "weird key:", ansi.StrKey(key) + fDontBlink = False + + if fDontBlink: + self.cDontBlink = 2 if self.fBlink else 1 + + if self.pos.Y() < self.whiteboard.H() / 2: + self.ovStatus.kplace = OvStatic.BOTTOM + else: + self.ovStatus.kplace = OvStatic.TOP + + def PutWb(self, ch): + self.Put(ch, self.GetLayer().ascr) + self.whiteboard.Touch() + + def Put(self, ch, ascr): + if type(ch) == str: + ach = ansi.MkAch(ch, self.cf.Value("Foreground"), self.cf.Value("Background")) + else: + ach = ch + ascr.PutAch(ach, self.pos.X(), self.pos.Y()) + + def GetAch(self): + return self.GetLayer().ascr.GetAch(self.pos.X(), self.pos.Y()) + + def Skip(self, dgMove): + self.SkipI(dgMove, self.GetAch()) + + def SkipI(self, dgMove, ach): + fStop = False + while not fStop: + fStop = not dgMove() or self.GetAch() != ach + + def draw(self, ascr, client): + if self.fBlink or self.cDontBlink > 0: + if client == self.client: + chCursor = chr(176) + ach = ascr.GetAch(self.pos.X(), self.pos.Y()) + if ach != None and ansi.ChFromAch(ach) == chCursor: + chCursor = chr(177) + self.Put(chCursor, ascr) + elif self.fBlink: + self.Put(str(self.client.Cldg().iclient)[0], ascr) + +class WhiteboardDraw(TokenClient): + def InitPersistent(self, owner, client): + TokenClient.InitPersistent(self, owner, client, "drawable", "solid") + + def draw(self, ascr, client): + if client == self.client: + if self.owner.miLayers.Value("Show One"): + self.owner.GetLayer().Draw(ascr) + else: + self.owner.whiteboard.draw(ascr) + +class CursorBlinker(Token): + def InitPersistent(self, owner): + Token.InitPersistent(self, owner) + Blinker(self, self.BlinkAllCursors) + + def BlinkAllCursors(self, fBlink): + for tok in self.game.rgtoken("cursor"): + tok.blinkCursor(fBlink) +#@-node:jpenner.20090528201601.10:<> +#@nl +#@<> +#@+node:jpenner.20090614150414.1:<> +#@+others +#@+node:jpenner.20090529173104.19:MiColour +class MiColour(MiList): + def InitPersistent(self, stName, rgcolor, col): + self.rgcolor = rgcolor + MiList.InitPersistent(self, rgcolor, rgcolor.index(col)) + self.stName = stName + + def StName(self): + return self.stName + + def DrawValue(self, ascr, x, y, selor): + for icol in range(len(self.rgcolor)): + col = self.rgcolor[icol] + ascr.PutAch(ansi.MkAch(chr(219) if icol != self.ival else chr(254), col, ansi.BLACK if col != ansi.BLACK else ansi.WHITE), x + icol, y) +#@-node:jpenner.20090529173104.19:MiColour +#@+node:jpenner.20090529173104.20:MiChars +class MiChars(MiList): + def InitPersistent(self, rgchar, miFg, miBg): + MiList.InitPersistent(self, rgchar) + self.miFg = miFg + self.miBg = miBg + def StName(self): + return "Characters" + def DrawValue(self, ascr, x, y, selor): + for ich, ch in enumerate(self.Value()): + ascr.PutSt(ChFromIch(ich) + ":", x, y, self.ColFg(), self.ColBg(selor)) + ascr.PutAch(ansi.MkAch(chr(ch), self.miFg.Value(), self.miBg.Value()), x + 2, y) + x = x + 4 + +def ChFromIch(ich): + if ich == 9: + return '0' + return str(ich + 1) + +def IchFromCh(ch): + if ch == '0': + return 9 + return int(ch) - 1 +#@nonl +#@-node:jpenner.20090529173104.20:MiChars +#@+node:jpenner.20090926091308.1:MiChat +class MiChat(Mi): + KWNDMSG = 0 + KWNDUSERS = 1 + def InitPersistent(self, client, height, chat): + Mi.InitPersistent(self) + self.client = client + self.chat = chat + self.height = height + assert height > 2 + self.kwnd = self.KWNDMSG + self.imsg = 0 + self.iuser = 0 + + def StName(self): + return "Chat" + + def HandleKey(self, key): + if self.kwnd == self.KWNDUSERS: + if key == ansi.K_LEFT: + self.kwnd = self.KWNDMSG + elif (key == 'a' or key == 'A') and self.iuser > 0: + self.iuser = self.iuser - 1 + elif (key == 'z' or key == 'Z') and self.iuser < self.chat.CUsers() - 1: + self.iuser = self.iuser + 1 + else: + return False + else: #KWNDMSG + if key == ansi.K_RIGHT: + self.kwnd = self.KWNDUSERS + elif (key == 'a' or key == 'A') and self.imsg < self.chat.CChl() - 1: + self.imsg = self.imsg + 1 + elif (key == 'z' or key == 'Z') and self.imsg > 0: + self.imsg = self.imsg - 1 + else: + return False + return True + + def Height(self, w): + return self.height + + def ColBgDefault(self): + return ansi.BLACK + def ColBgSelected(self, fPri): + return ansi.BLACK + def ColFg(self): + return ansi.WHITE + + def FIgnoreParent(self): + return True + + def Draw(self, ascr, x, y, w, selor): + wChat = (w / 4) * 3 + wUser = (w / 4) - 1 + self.chat.DrawMsg(ascr, x, y, wChat, self.height - 1, self.ColBg(selor), self.imsg) + for yT in range(y, y + self.height - 1): + ascr.PutAch(ansi.MkAch(chr(179)), x + wChat, yT) + ascr.PutSt("{0:{1}<{2}}{3}{0:{1}<{4}}".format("", chr(196), wChat, chr(193), wUser), x, y + self.height - 1) + self.chat.DrawUsers(ascr, x + wChat + 1, y, wUser, self.height - 1, self.ColBg(selor), self.iuser) + if selor.KSel() == Selor.PRI: + if self.kwnd == self.KWNDMSG: + xBar = x + wChat - 1 + pct = 1 - (self.imsg / float(self.chat.CChl())) + else: # KWNDUSERS + xBar = x + w - 1 + pct = self.iuser / float(self.chat.CUsers()) + yBar = int(y + (pct * (self.height - 4))) + ascr.PutAch(ansi.MkAch("A", ansi.RED, ansi.WHITE), xBar, yBar) + ascr.PutAch(ansi.MkAch(chr(186), ansi.RED, ansi.WHITE), xBar, yBar + 1) + ascr.PutAch(ansi.MkAch("Z", ansi.RED, ansi.WHITE), xBar, yBar + 2) +#@-node:jpenner.20090926091308.1:MiChat +#@-others +#@nonl +#@-node:jpenner.20090614150414.1:<> +#@nl +#@<> +#@+node:jpenner.20090529173104.3:<> +class Joiner(Token): + def InitPersistent(self, owner): + Token.InitPersistent(self, owner) + self.msgScroller = self.game.rgtoken("msgscroller")[0] + self.board = self.game.rgtoken("whiteboard")[0].board + + def OnJoin(self, client): + with client.Cldg().SetTransiently() as cldg: + cldg.iclient = -1 + iclient = 1 + rgiclient = [clientT.Cldg().iclient for clientT in self.game.rgclient] + while iclient in rgiclient: + iclient = iclient + 1 + cldg.iclient = iclient + self.msgScroller.evPost.fire(client.cld.user + "(#" + str(cldg.iclient) + ") has joined! :)") + self.board.chat.Add(ChlSys(client, "has joined")) + + def OnLeave(self, client): + self.msgScroller.evPost.fire(client.cld.user + " has left! :(") + self.board.chat.Add(ChlSys(client, "has left")) + + def run(self): + while True: + with self.game.evJoin.oob(self, self.OnJoin): + self.OnLeave(self.game.evLeave.receive(self)) +#@-node:jpenner.20090529173104.3:<> +#@nl + +#@+others +#@+node:jpenner.20091003162445.1:Lobby + +class Lobby(TokenClient): + def InitPersistent(self, owner, client): + TokenClient.InitPersistent(self, owner, client) + self.miDrawings = MiMenu(RgmiProjected(self.game.rgboard, DgProjectMiButton(self.Goto))) + self.miCmds = MiMenu([None, None, MiTypein(self.New, "DrawingName"), MiButton("New Drawing", self.NewBtn), + None, None, None, None, None, None, None, None, MiStatic("Last modified:"), MiDgText(self.StLastModified)]) + OvMenu(self, client, MiMenuHoriz([self.miDrawings, self.miCmds], [65, 35]), OvMenu.FILL) + + def New(self, stName): + if stName != "": + self.game.NewBoard(stName, self.client.cld.user) + + def NewBtn(self): + self.New(self.miCmds.Value("DrawingName")) + + def StLastModified(self): + return self.miDrawings.Value().StLastModified() + + def Goto(self, board): + self.client.leaveGame(board) +#@-node:jpenner.20091003162445.1:Lobby +#@+node:jpenner.20090529173104.25:Runner +class RunnerWB(Runner): + def InitPersistent(self): + self.gameLobby = GameLobby() + Runner.InitPersistent(self) + + def RunGame(self, client): + if (client.joinGame(GameLogin())): + while True: + board = client.joinGame(self.gameLobby) + client.joinGame(self.gameLobby.GameFromBoard(board)) + +class GameLogin(Game): + def GetRgclsTokTrans(self): + return [[AutoJoiner, login.LoginTerm]] + +@Version(5) +class GameLobby(Game): + def InitPersistent(self): + self.rgboard = [] + Game.InitPersistent(self) + + def InitTransient(self): + Game.InitTransient(self) + self.mpboard_gameWB = {} + + def GetRgclsTokTrans(self): + return [[AutoJoiner, Lobby]] + + def GameFromBoard(self, board): + if not (board in self.mpboard_gameWB): + gameWB = GameWB(self, board) + def KillBoard(): + del self.mpboard_gameWB[board] + gameWB.rgdgdie.append(KillBoard) + self.mpboard_gameWB[board] = gameWB + return gameWB + return self.mpboard_gameWB[board] + + def NewBoard(self, stName, user): + self.rgboard.insert(0, Board(self, stName, user)) + + def StNameBoard(self, board): + cclient = 0 + if board in self.mpboard_gameWB: + cclient = len(self.mpboard_gameWB[board].rgclient) + return board.stName + " (by " + board.user + ") [" + str(cclient) + "]" + + def BoardTouched(self, board): + #haaaack + if board != self.rgboard[0]: + self.rgboard.remove(board) + self.rgboard.insert(0, board) + self.ensureRun() # redraw + + def UpgradeFrom(self, versionOld): + if versionOld < 3: + for gameWB in self.rggameWB: + gameWB.gameLobby = self + if versionOld < 4: + self.rgboard = [game.rgtoken("whiteboard")[0].board for game in self.rggameWB] + for gameWB in self.rggameWB: + gameWB.finish() + if versionOld < 5: + del self.rggameWB + +class GameWB(Game): + def InitPersistent(self, gameLobby, board): + self.gameLobby = gameLobby + self.board = board + Game.InitPersistent(self) + + def InitTransient(self): + Game.InitTransient(self) + if not self.FDead(): + self.board.chat.AttachToGame(self) + self.rgdgdie.append(self.board.chat.DetachFromGame) + + def GetRgclsTokPers(self): + return [[Whiteboard, self.board]] + + def GetRgclsTokTrans(self): + return [MsgScroller, Joiner, [AutoJoiner, Cursor], [AliveWithPlayers, CursorBlinker], AutoKiller] + + def GetRgtagDraw(self): + return ["background", "solid", "cursor", "menu", "msgscroller"] + +if __name__ == "__main__": + Run(RunnerWB, "whiteboard.marm")