WIP robot creation in board editor!

- refactor client - now has a _stack_ of current Games, the top of which
  is active - prevents shutdown of whiteboard when "leaving" to edit
  source code
- add tpers.StaticIndex / inspector.py for interactively querying object
  graphs
This commit is contained in:
Jeremy Penner 2020-07-19 21:31:54 -04:00
parent f7c33a23a6
commit 10edbacf86
7 changed files with 353 additions and 90 deletions

View file

@ -73,7 +73,7 @@ class Terminal(Token):
self.fBlink = fBlink
def getKey(self):
return self.client.evKey.receive(self)
return self.client.EvKey(self.game).receive(self)
def getPrintableKey(self):
while True:
@ -199,9 +199,14 @@ class Terminal(Token):
class LeaveJoinToken(Token):
def run(self):
evLeave = self.game.evSuspend if self.FOnActivate() else self.game.evLeave
evJoin = self.game.evActivate if self.FOnActivate() else self.game.evJoin
while True:
Event.selectDg(self, (self.game.evLeave, self.OnLeave), (self.game.evJoin, self.OnJoin))
Event.selectDg(self, (evLeave, self.OnLeave), (evJoin, self.OnJoin))
def FOnActivate(self):
return False
class AutoJoiner(LeaveJoinToken):
def InitPersistent(self, owner, *rgclsTok):
Token.InitPersistent(self, owner)
@ -234,6 +239,9 @@ class AliveWithPlayers(LeaveJoinToken):
Token.InitTransient(self)
self.rgclient = []
def FOnActivate(self):
return True
def OnLeave(self, client):
self.rgclient.remove(client)
if len(self.rgclient) == 0:
@ -705,9 +713,18 @@ class MiMenuHoriz(MiMenuI):
mi.DrawWithParent(selor, ascr, x, y, wT, miSelected == mi)
x = x + wT
def DgProjectMiButton(dgExec):
class ProjectedValue(TPrs):
def InitPersistent(self, value, dgStName):
self.value = value
self.dgStName = dgStName
def StName(self):
return self.dgStName(self.value)
def Value(self):
return self.value
def DgProjectMiButton(dgExec, dgStName = None):
def DgProject(value):
return MiButton(None, lambda: dgExec(value), value)
return MiButton(None, lambda: dgExec(value), ProjectedValue(value, dgStName) if dgStName else value)
return DgProject
class RgmiProjected(TPrs):
@ -781,13 +798,15 @@ class OvStatic(TokenClient):
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))
self.miMenu.HandleKey(self.client.EvKey(self.game).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")
@ -800,7 +819,7 @@ class OvPopup(OvStatic):
self.evShow.receive(self)
self.fAwake = True
while self.fAwake:
key = self.client.evKey.receive(self)
key = self.client.EvKey(self.game).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

112
engine.py
View file

@ -184,7 +184,7 @@ class Ownable(TPrs):
tok.die()
self.DontPersist()
@Version(2)
@Version(3)
class Game(Ownable):
def InitPersistent(self):
Ownable.InitPersistent(self)
@ -194,12 +194,16 @@ class Game(Ownable):
self.rgdgdie = []
self.evJoin = Event(self)
self.evLeave = Event(self)
self.evActivate = Event(self)
self.evSuspend = Event(self)
self.mpuser_cldg = {}
self.CreateRgtok(self.GetRgclsTokPers(), True)
def InitTransient(self):
self.MakeTransient("fScheduled", False)
self.rgclient = []
self.rgclientActive = []
self.rgclientSuspended = []
self.rgclientLeaving = []
self.rgproc = [PTimer(), PEvent(), PDraw(*self.GetRgtagDraw())]
for proc in self.rgproc:
proc.setGame(self)
@ -240,6 +244,9 @@ class Game(Ownable):
if not fPersist:
tok.DontPersist()
def RgclientConnected(self):
return self.rgclientActive + self.rgclientSuspended
def tagToken(self, token, tag):
if not (tag in self.mptagTokenset):
self.mptagTokenset[tag] = set([token])
@ -268,10 +275,12 @@ class Game(Ownable):
return rgtoken
def quit(self):
assert len(self.rgclientSuspended) == 0
self.running = False
self.fQuit = True
def finish(self, rc = None):
assert len(self.rgclientSuspended) == 0
self.running = False
self.rc = rc
class QuitException(Exception):
@ -291,22 +300,44 @@ class Game(Ownable):
proc.process()
if not self.running:
self.die()
for client in self.rgclient:
client.leaveGame(self.rc)
for client in self.rgclient:
for client in self.rgclientActive:
if client not in self.rgclientLeaving:
client.leaveGame(self.rc)
deadClients = list(self.rgclientLeaving)
del self.rgclientLeaving[:]
for client in deadClients:
if client in self.rgclientActive:
self.rgclientActive.remove(client)
if client in self.rgclientSuspended:
self.rgclientSuspended.remove(client)
client.postRunStep()
finally:
self.fScheduled = False
return self.running
def joinGame(self,client):
self.rgclient.append(client)
self.rgclientActive.append(client)
self.evJoin.fire(client)
self.evActivate.fire(client)
def leaveGame(self, client):
self.evLeave.fire(client)
if self.running:
if client in self.rgclientActive:
self.evSuspend.fire(client)
self.evLeave.fire(client)
self.rgclientLeaving.append(client)
def reactivateClient(self, client):
assert client in self.rgclientSuspended
self.rgclientSuspended.remove(client)
self.rgclientActive.append(client)
self.evActivate.fire(client)
def suspendClient(self, client):
assert client in self.rgclientActive
self.rgclientActive.remove(client)
self.rgclientSuspended.append(client)
self.evSuspend.fire(client)
def leaveGameI(self, client):
self.rgclient.remove(client)
def ensureRun(self):
self.running = True
if not self.fScheduled:
@ -315,6 +346,9 @@ class Game(Ownable):
def UpgradeFrom(self, versionOld):
if versionOld < 2:
self.mpuser_cldg = {}
if versionOld < 3:
self.evActivate = Event(self)
self.evSuspend = Event(self)
# Client data
@Version(2)
@ -336,9 +370,9 @@ class Client(TPrs):
def InitTransient(self):
self.chRun = stackless.channel()
self.gameCurr = None
self.mpgame_evKey = {}
self.rggameSuspended = []
self.chCont = None
self.fRunning = False
self.dgJoinGame = None
self.rgdgQuit = []
self.fQuit = False
self.cld = None
@ -353,19 +387,33 @@ class Client(TPrs):
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()
if not self.gameCurr:
chCont = stackless.channel()
self.chRun.send((game, chCont))
return chCont.receive()
else:
self.gameCurr.suspendClient(self)
self.rggameSuspended.append(self.gameCurr)
self.gameCurr = game
self.gameCurr.joinGame(self)
def leaveGame(self, rc = None):
self.fRunning = False
self.gameCurr.leaveGame(self)
self.rc = rc
if self.gameCurr in self.mpgame_evKey:
del self.mpgame_evKey[self.gameCurr]
if len(self.rggameSuspended) == 0:
self.gameCurr = None
self.rc = rc
else:
self.gameCurr = self.rggameSuspended.pop()
self.gameCurr.reactivateClient(self)
def quit(self):
self.leaveGame(None)
while self.gameCurr:
self.leaveGame(None)
self.fQuit = True
def _runTskRun(self):
"run self.tskRun after it has been scheduled, and await results"
stackless.run()
@ -373,20 +421,17 @@ class Client(TPrs):
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.gameCurr:
if not self.fQuit:
self.chCont.send(self.rc)
self.fQuit = not self._runTskRun()
if self.fQuit:
for dgQuit in self.rgdgQuit:
dgQuit()
del self.rgdgQuit[:]
return self.fQuit
def beep(self):
pass
@ -401,9 +446,18 @@ class Client(TPrs):
def removeDgQuit(self, dgquit):
self.rgdgQuit.remove(dgquit)
def Cldg(self, game = None):
if game == None:
game = self.gameCurr
def EvKey(self, game):
if game:
if game not in self.mpgame_evKey:
self.mpgame_evKey[game] = Event(game)
return self.mpgame_evKey[game]
def KeyPressed(self, ch):
if self.gameCurr:
self.EvKey(self.gameCurr).fire(ch)
def Cldg(self, game):
return game.GetCldg(self.cld.user)
class Processor(TPrs):
@ -463,7 +517,7 @@ class PDraw(Processor):
def process(self):
if self.game.ms >= self.msDrawAgain:
for client in self.game.rgclient:
for client in self.game.rgclientActive:
ascr = ansi.Ascr()
for tok in self.game.rgtoken("drawable", self.rgprio):
try:
@ -552,8 +606,8 @@ class Token(Taggable):
def runI(self):
try:
self.run()
#except greenlet.GreenletExit:
# raise
except greenlet.GreenletExit:
raise
except Exception:
print "token script crashed:"
traceback.print_exc()

7
inspector.py Normal file
View file

@ -0,0 +1,7 @@
# to use:
# python -i inspector.py
# >>> i.Rgclass() ...
from whiteboard import *
o = tpers.Odb.Load('whiteboard.marm')
i = tpers.StaticIndex().Populate(o)

View file

@ -28,9 +28,10 @@ def RegStmt(clsStmt):
class Botdef(TPrs):
def InitPersistent(self, stName, ach, x, y):
self.stName = stName
self.ast = ast
self.ach = ach
self.x = x
self.y = y
self.fPlayer = False
self.syntOnTouch = SyntBlock()
class Flagdef(TPrs):
@ -62,6 +63,9 @@ class Vm(TPrs):
def FFlagSet(self, flagdef):
return self.mpflagdef[flagdef]
def Fail(self, stMessage, synt):
self.Log(Fail(stMessage, synt))
def Log(self, fail):
print(fail.stMessage, fail.synt)
@ -155,7 +159,7 @@ class Synt(Typeable):
pass
def Eval(self, vm):
"Execute yourself, in the context of vm."
return Fail("Missing information", self)
return vm.Fail("Missing information", self)
def StypeForChild(self, syntChild):
"Return the stype that should be applied to the given child."
@ -361,7 +365,7 @@ class SyntFlagRef(Synt):
try:
return vm.FFlagSet(self.flagdef)
except:
return Fail("No such flag", self)
return vm.Fail("No such flag", self)
class SyntIsFlagSet(SyntDesc):
desc = [["set"]]
@ -422,10 +426,9 @@ class Fail(TPrs):
edit a script while the game is running and see the effect of their changes immediately; this suggests that failures
should be logged and the script should continue to run.
"""
def InitPersistent(self, vm, stFailure, synt):
def InitPersistent(self, stFailure, synt):
self.stFailure = stFailure
self.synt = synt
vm.Log(self)
# projection cursor
class Pcur(object):
@ -880,7 +883,7 @@ class Pov(TokenClient):
def run(self):
while True:
key = self.client.evKey.receive(self)
key = self.client.EvKey(self.game).receive(self)
if key == ansi.K_PGDN:
self.block.Eval(Vm(self.defs))
psel = self.pselstate.PselByClient(self.client)

View file

@ -41,8 +41,6 @@ class AnsiTelnet(protocol.Protocol):
print "created"
self.ast = None
self.client = client
self.evKey = None
self.evArrow = None
self.Refresh()
def connectionMade(self):
@ -122,27 +120,21 @@ class AnsiTelnet(protocol.Protocol):
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)
def RcvText(self, text):
if self.client.evKey:
for ch in text:
self.client.evKey.fire(ch)
for ch in text:
self.client.KeyPressed(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)
if code in self.mpcode_key:
self.client.KeyPressed(self.mpcode_key[code])
else:
print "unrecognized code", code, rgarg
def Draw(self, ascr):
self.transport.write(self.ascr.AstDiff(ascr))

View file

@ -319,3 +319,61 @@ def _unpickle_method(func_name, obj, cls):
import copy_reg
import types
copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method)
def hashable(v):
try:
hash(v)
return True
except:
return False
class StaticIndex(TPrs):
def InitPersistent(self, fIncludeTemp = False):
self.mpv_a_rge = {}
self.rgv = set()
self.fIncludeTemp = fIncludeTemp
def FPersist(self):
return False
def AddEAV(self, e, a, v):
if v not in self.mpv_a_rge:
self.mpv_a_rge[v] = {}
a_rge = self.mpv_a_rge[v]
if a not in a_rge:
a_rge[a] = []
a_rge[a].append(e)
def Populate(self, v, a = (), e = None):
if hashable(v):
if e:
self.AddEAV(e, a, v)
if v in self.rgv:
return self
self.rgv.add(v)
if isinstance(v, TPrs):
attrs = set(v._persistent.keys())
if self.fIncludeTemp:
attrs = attrs.union(set(v.__dict__.keys()))
for attr in attrs:
child = getattr(v, attr)
self.Populate(child, (attr,), v)
elif isinstance(v, clsList) or isinstance(v, clsTuple):
for ichild, child in enumerate(v):
self.Populate(child, a + (ichild,), e)
elif isinstance(v, clsSet):
for child in v:
self.Populate(child, a, e)
elif isinstance(v, clsDict):
for k, child in v.items():
self.Populate(child, a + (k,), e)
return self
def Rgclass(self):
return set([v.__class__ for v in self.rgv])
def RgvWithClass(self, cls):
return [v for v in self.rgv if isinstance(v, cls)]
def Rgref(self, v):
return self.mpv_a_rge.get(v)

View file

@ -20,6 +20,7 @@ from basetoken import *
from tpers import *
from datetime import *
from util import *
from scripting import Botdef, Flagdef, Defs, PselState, Pov
import telnet
import time
import login
@ -129,8 +130,13 @@ class Drawing(Ownable):
def SetTimestamp(self):
self.dtLastModified = datetime.now()
@Version(3)
class Board(Drawing):
@Version(4)
class Board(Drawing):
def InitPersistent(self, owner, stName, user, rglayer=None, dtLastModified=None):
super(Board, self).InitPersistent(owner, stName, user, rglayer=rglayer, dtLastModified=dtLastModified)
self.defs = Defs()
self.layerObj = self.rglayer[0]
def StType(self):
return "Board"
def W(self): return config.W
@ -139,6 +145,10 @@ class Board(Drawing):
def draw(self, ascr):
for layer in reversed(self.rglayer):
ascr.PutAscr(layer.ascr)
def DrawObjectDefs(self, ascr):
for botdef in self.defs.rgbotdef:
ascr.PutAch(botdef.ach, botdef.x, botdef.y)
def Save(self):
fn = config.DIR_ANSI + "/" + "_".join([str(x) for x in time.localtime()])
@ -158,6 +168,9 @@ class Board(Drawing):
self.user = gameWB.user
if versionOld < 3:
self.chat = Chat()
if versionOld < 4:
self.defs = Defs()
self.layerObj = self.rglayer[0]
class Sprite(Drawing):
def InitPersistent(self, owner, stName, user, w, h):
@ -225,19 +238,22 @@ class Chat(TPrs):
def InitTransient(self):
TPrs.InitTransient(self)
self.cchl = 100
self.DetachFromGame()
self.MakeTransient("game", None)
self.MakeTransient("evChat", None)
def InitPersistent(self):
TPrs.InitPersistent(self)
self.rgchl = []
def AttachToGame(self, game):
self.game = game
self.evChat = Event(game)
with self.SetTransiently():
self.game = game
self.evChat = Event(game)
def DetachFromGame(self):
self.game = None
self.evChat = None
with self.SetTransiently():
self.game = None
self.evChat = None
def Add(self, chl):
if len(chl.st) > 0:
@ -248,8 +264,10 @@ class Chat(TPrs):
def CChl(self):
return len(self.rgchl)
def CUsers(self):
return len(self.game.rgclient)
return len(self.game.RgclientConnected())
def DrawMsg(self, ascr, x, y, w, h, colBg=ansi.BLACK, ichl=0):
yDraw = y + h - 1
for chl in self.rgchl[ichl:]:
@ -274,8 +292,8 @@ class Chat(TPrs):
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)
for client in sorted(self.game.RgclientConnected(), lambda c1, c2: cmp(c1.Cldg(self.game).iclient, c2.Cldg(self.game).iclient))[iuser:]:
ascr.PutSt("{0:{1}}".format(str(client.Cldg(self.game).iclient) + ") " + client.cld.user, w), x, yDraw, ansi.WHITE, colBg)
yDraw = yDraw + 1
if yDraw == y + h:
break
@ -320,19 +338,38 @@ class Cursor(TokenClient):
miQuit = MiButton("Back to Menu", self.client.leaveGame)
self.cf = self.MenuDrawing(miFg, miBg, miChars, miQuit)
self.miChat = MiMenu([None, MiChat(self.client, 13, self.game.drawing.chat), MiTypein(self.SendMsg), None, miQuit, None])
self.rgmiLayer = RgmiProjected(self.game.drawing.rglayer, DgProjectMiButton(self.SelectLayer))
self.rgmiLayer = RgmiProjected(self.game.drawing.rglayer, DgProjectMiButton(self.SelectLayer, self.StLayerName))
self.miLayers = self.MenuLayers(miQuit)
self.ovPopup = OvPopup(self, self.client, MiTab.MiTabbed(["Drawing", self.cf], [self.StLayerTab(), self.miLayers], ["Chat", self.miChat]))
miStatus = MiMenuHoriz([MiStatic("Hit <Tab> for menu"), MiValue(miChars)])
miStatus = MiMenuHoriz([MiDgText(self.StStatus), MiValue(miChars)])
miStatus.ListenForNotice(self.miChat)
self.ovStatus = OvStatic(self, self.client, miStatus, OvStatic.BOTTOM)
def StLayerName(self, layer):
return ('[Bot] ' if layer == self.game.drawing.layerObj else '') + layer.StName()
def StStatus(self):
stStatus = "<Tab>: Menu"
if self.GetLayer() == self.game.drawing.layerObj:
botdef = self.BotdefAtPos(self.pos)
if botdef:
stStatus += " | <Enter>: Edit bot"
elif self.game.drawing.layerObj.ascr.GetAch(self.pos.X(), self.pos.Y()) != ansi.achBlank:
stStatus += " | <Enter>: Make bot"
return stStatus
def BotdefAtPos(self, pos):
for botdef in self.game.drawing.defs.rgbotdef:
if botdef.x == pos.X() and botdef.y == pos.Y():
return botdef
def MenuDrawing(self, miFg, miBg, miChars, miQuit):
return MiMenu([None, miFg, miBg, None, miChars, None, MiButton("Save", self.Save), None, miQuit, None])
def MenuLayers(self, miQuit):
return 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]))
None, MiToggle("Show One"), MiButton("Set as bot layer", self.SetBotLayer), None,
MiStatic("Rename selected layer:"), MiTypein(self.RenameLayer, "LayerName", 80), None, miQuit, None]))
def StLayerTab(self): return "Layers"
def Save(self):
self.game.drawing.Save()
@ -345,7 +382,8 @@ class Cursor(TokenClient):
return True
def GetLayer(self):
return self.ovPopup.GetSelection("layer").Value()
selLayer = self.ovPopup.GetSelection("layer")
return selLayer.Value().Value() if selLayer else None
def NewLayer(self):
ilayer = 1
@ -373,6 +411,9 @@ class Cursor(TokenClient):
self.GetLayer().SetName(st)
return True
def SetBotLayer(self):
self.game.drawing.layerObj = self.GetLayer()
def blinkCursor(self, fBlink):
self.fBlink = fBlink
if not fBlink and self.cDontBlink > 0:
@ -391,7 +432,7 @@ class Cursor(TokenClient):
def run(self):
with self.game.drawing.chat.evChat.oob(self, self.OnChat):
while True:
key = self.client.evKey.receive(self)
key = self.client.EvKey(self.game).receive(self)
fDontBlink = True
if key == ansi.K_LEFT:
self.pos.Left()
@ -419,6 +460,8 @@ class Cursor(TokenClient):
self.Skip(self.pos.Up)
elif key == ansi.K_PGDN:
self.Skip(self.pos.Down)
elif key == ansi.K_NEWLINE or key == ansi.K_RETURN:
self.EditBot()
elif ansi.FKeyPrintable(key):
if key.isdigit():
self.PutWb(chr(self.cf.Value("Characters")[IchFromCh(key)]))
@ -466,6 +509,41 @@ class Cursor(TokenClient):
def FDrawCursor(self):
return self.fBlink or self.cDontBlink > 0
def EditBot(self):
if self.GetLayer() == self.game.drawing.layerObj:
botdef = self.BotdefAtPos(self.pos)
ach = self.game.drawing.layerObj.ascr.GetAch(self.pos.X(), self.pos.Y())
fNewBot = ach != ansi.achBlank if not botdef else False
if not fNewBot and not botdef:
return
if fNewBot:
botdef = Botdef('Bot ' + str(len(self.game.drawing.defs.rgbotdef) + 1) + ' (' + ansi.Ach(ach).ch + ')', ach, self.pos.X(), self.pos.Y())
evDone = Event(self.game)
def DgDone(val):
return lambda: evDone.fire(val)
miBotName = MiTypein(None, "BotName", 80)
miBotName.SetValue(botdef.stName)
miMenu = MiMenu([None,
MiStatic("Bot Name"), miBotName, None,
MiToggle("Is Player", botdef.fPlayer), None,
MiButton("Save", DgDone(True)), MiButton("Cancel", DgDone(False)), None,
MiButton("Edit Code (live)", lambda: self.client.joinGame(self.game.GameSyntEdit(botdef.syntOnTouch))), None])
ov = OvMenu(self, self.client, miMenu, OvMenu.MIDDLE)
try:
result = evDone.receive(self)
finally:
ov.die()
if result:
botdef.stName = miBotName.Value()
botdef.fPlayer = miMenu.MiByName("Is Player").Value()
if fNewBot:
self.game.drawing.defs.rgbotdef.append(botdef)
def draw(self, ascr, client):
if self.FDrawCursor():
if client == self.client:
@ -475,7 +553,7 @@ class Cursor(TokenClient):
chCursor = chr(177)
self.Put(chCursor, ascr, *self.XzYzScreenPic())
elif self.fBlink:
self.Put(str(self.client.Cldg().iclient)[0], ascr, *self.XzYzScreenPic())
self.Put(str(self.client.Cldg(self.game).iclient)[0], ascr, *self.XzYzScreenPic())
class CursorSprite(Cursor):
def InitPersistent(self, game, client):
@ -492,7 +570,9 @@ class CursorSprite(Cursor):
return MiMenu(Rgseq([None], self.rgmiLayer, [None, MiButton("New Frame", self.NewLayer), MiButton("Move up", self.LayerUp), MiButton("Move down", self.LayerDown),
None, MiStatic("Rename selected frame:"), MiTypein(self.RenameLayer, "LayerName", 80), None, MiButton("Animate", self.Animate), None, miQuit, None]))
def StStatus(self): return "Hit <Tab> for menu"
def StLayerTab(self): return "Frames"
def NewLayer(self):
stLayer = "Frame " + str(len(self.game.drawing.rglayer) + 1)
layer = self.GetLayer()
@ -521,10 +601,14 @@ class WhiteboardDraw(TokenClient):
def draw(self, ascr, client):
if client == self.client:
fDrawObjects = True
if self.owner.miLayers.Value("Show One"):
ascr.PutAscr(self.owner.GetLayer().ascr)
fDrawObjects = self.owner.GetLayer() == self.owner.layerObj
else:
self.game.drawing.draw(ascr)
if fDrawObjects:
self.game.drawing.DrawObjectDefs(ascr)
class SpriteDraw(TokenClient):
def InitPersistent(self, owner, client):
@ -671,10 +755,10 @@ class Joiner(LeaveJoinToken):
self.board = self.game.drawing
def OnJoin(self, client):
with client.Cldg().SetTransiently() as cldg:
with client.Cldg(self.game).SetTransiently() as cldg:
cldg.iclient = -1
iclient = 1
rgiclient = [clientT.Cldg().iclient for clientT in self.game.rgclient]
rgiclient = [clientT.Cldg(self.game).iclient for clientT in self.game.RgclientConnected()]
while iclient in rgiclient:
iclient = iclient + 1
cldg.iclient = iclient
@ -813,7 +897,29 @@ class GameLogin(Game):
def GetRgclsTokTrans(self):
return [[AutoJoiner, login.LoginTerm]]
@Version(6)
class Mpsubgame(TPrs):
def InitPersistent(self, game, dgMakeGame):
self.game = game
self.dgMakeGame = dgMakeGame
self.mpkey_subgame = {}
def SubgameFromKey(self, key):
if not (key in self.mpkey_subgame):
subgame = self.dgMakeGame(key)
subgame.rgdgdie.append(Partial(self.KillSubgame, key))
self.mpkey_subgame[key] = subgame
return self.mpkey_subgame[key]
def KillSubgame(self, key):
del self.mpkey_subgame[key]
def __contains__(self, key):
return key in self.mpkey_subgame
def __getitem__(self, key):
return self.mpkey_subgame[key]
@Version(7)
class GameLobby(Game):
def InitPersistent(self):
self.rgproject = []
@ -821,30 +927,37 @@ class GameLobby(Game):
def InitTransient(self):
Game.InitTransient(self)
self.mpdrawing_game = {}
self.mpdrawing_game = Mpsubgame(self, self.MakeGameFromDrawing)
self.mpdrawingsynt_game = Mpsubgame(self, self.MakeGameSyntEdit)
def GetRgclsTokTrans(self):
return [[AutoJoiner, Lobby]]
def GameFromDrawing(self, drawing):
if not (drawing in self.mpdrawing_game):
if drawing.StType() == "Board":
game = GameWB(self, drawing)
elif drawing.StType() == "Sprite":
game = GameSprEdit(self, drawing)
else:
assert False, "Lobby not prepared to deal with drawing of type " + drawing.StType()
game.rgdgdie.append(Partial(self.KillBoard, drawing))
self.mpdrawing_game[drawing] = game
return game
return self.mpdrawing_game[drawing]
return self.mpdrawing_game.SubgameFromKey(drawing)
def MakeGameFromDrawing(self, drawing):
if drawing.StType() == "Board":
return GameWB(self, drawing)
elif drawing.StType() == "Sprite":
return GameSprEdit(self, drawing)
else:
assert False, "Lobby not prepared to deal with drawing of type " + drawing.StType()
# we accidentally persisted references to this method that are immediately removed by Chat.InitTransient()
def KillBoard(self, drawing):
del self.mpdrawing_game[drawing]
pass
def GameFromSynt(self, drawing, synt):
return self.mpdrawingsynt_game.SubgameFromKey((drawing, synt))
def MakeGameSyntEdit(self, drawing_synt):
drawing, synt = drawing_synt
return GameSyntEdit(drawing, synt)
def CclientDrawing(self, drawing):
if drawing in self.mpdrawing_game:
return len(self.mpdrawing_game[drawing].rgclient)
return len(self.mpdrawing_game[drawing].RgclientConnected())
return 0
def StNameProject(self, project):
@ -883,7 +996,12 @@ class GameLobby(Game):
project.rgdrawing.append(board)
del self.rgboard
self.rgproject = [project]
self.mpuser_cldg = {} # ugh, upgrading parent classes is broken in our model :(
# ugh, upgrading parent classes is broken in our model :(
if versionOld < 6:
Game.UpgradeFrom(self, 1)
elif versionOld < 7:
Game.UpgradeFrom(self, 2)
class GameWB(Game):
def InitPersistent(self, gameLobby, board):
@ -905,6 +1023,18 @@ class GameWB(Game):
def GetRgtagDraw(self):
return ["background", "solid", "cursor", "menu", "msgscroller"]
def GameSyntEdit(self, synt):
return self.gameLobby.GameFromSynt(self.drawing, synt)
class GameSyntEdit(Game):
def InitPersistent(self, board, synt):
Game.InitPersistent(self)
self.board = board
self.synt = synt
def GetRgclsTokTrans(self):
return [[PselState, self.board.defs, self.synt], [AutoJoiner, [Pov, self.board.defs, self.synt]]]
class GameSprEdit(GameWB):
def ClsCursor(self):