commit 5d61e939aa78271adbc1cabbed4245e07ba7e38c Author: Jeremy Penner Date: Fri Mar 18 17:10:02 2011 -0700 initial commit 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")