summaryrefslogtreecommitdiff
path: root/http2/httppages.py
diff options
context:
space:
mode:
Diffstat (limited to 'http2/httppages.py')
-rw-r--r--http2/httppages.py705
1 files changed, 705 insertions, 0 deletions
diff --git a/http2/httppages.py b/http2/httppages.py
new file mode 100644
index 0000000..3f90093
--- /dev/null
+++ b/http2/httppages.py
@@ -0,0 +1,705 @@
+import os, sys, random
+from cStringIO import StringIO
+import socket, time
+
+PLAYERNAMES = ['Bub', 'Bob', 'Boob', 'Beb',
+ 'Biob', 'Bab', 'Bib',
+ 'Baub', 'Beab', 'Biab']
+
+try:
+ FILE = __file__
+except NameError:
+ FILE = sys.argv[0]
+LOCALDIR = os.path.abspath(os.path.dirname(FILE))
+
+sys.path.insert(0, os.path.abspath(os.path.join(LOCALDIR, os.pardir)))
+sys.path.insert(0, os.path.abspath(os.path.join(LOCALDIR, os.pardir,'common')))
+import gamesrv, httpserver, hostchooser
+from metaserver import metaclient
+from httpserver import HTTPRequestError
+
+
+class Options:
+ def __init__(self, dict={}):
+ self.update(dict)
+ def dict(self):
+ return self.__dict__.copy()
+ def update(self, dict):
+ self.__dict__.update(dict)
+ def copy(self):
+ return Options(self.__dict__)
+ def clear(self):
+ self.__dict__.clear()
+ def __getattr__(self, attr):
+ if not attr.startswith('_'):
+ return None
+ else:
+ raise AttributeError, attr
+
+
+class PageServer:
+ CONFIGFILE = 'config.txt'
+ localservers = None
+
+ def __init__(self, Game):
+ self.Game = Game
+ self.seed = hex(random.randrange(0x1000, 0x10000))
+ #self.unique_actions = {}
+ self.localhost = gamesrv.HOSTNAME
+ self.filename = os.path.join(LOCALDIR, self.CONFIGFILE)
+ data = self.loadoptionfile()
+ self.globaloptions = Options(data.get('*', {}))
+ self.localoptions = Options(data.get(self.localhost, {}))
+ self.reloadports()
+ #self.inetserverlist = None
+ #self.inetservers = {}
+ #self.has_been_published = 0
+
+ def registerpages(self):
+ prefix = '%s/' % self.seed
+ #httpserver.register('controlcenter.html', self.controlcenterloader)
+ httpserver.register(prefix, self.indexloader)
+ httpserver.register(prefix+'index.html', self.indexloader)
+ #httpserver.register(prefix+'list.html', self.listloader)
+ httpserver.register(prefix+'new.html', self.newloader)
+ httpserver.register(prefix+'run.html', self.runloader)
+ httpserver.register(prefix+'stop.html', self.stoploader)
+ httpserver.register(prefix+'join.html', self.joinloader)
+ #httpserver.register(prefix+'register.html',self.registerloader)
+ httpserver.register(prefix+'options.html', self.optionsloader)
+ httpserver.register(prefix+'name.html', self.nameloader)
+ for fn in os.listdir(os.path.join(LOCALDIR, 'data')):
+ path = prefix + fn
+ if not httpserver.is_registered(path):
+ httpserver.register(path, httpserver.fileloader(
+ os.path.join(LOCALDIR, 'data', fn)))
+
+ def opensocket(self):
+ hs = gamesrv.openhttpsocket()
+ if hs is None:
+ return 0
+ self.httpport = port = gamesrv.displaysockport(hs)
+ self.indexurl = 'http://127.0.0.1:%d/%s/' % (port, self.seed)
+ if self.Game:
+ print self.Game.FnDesc,
+ print 'server is ready at', self.indexurl
+ return 1
+
+ def getlocalservers(self):
+ if self.localservers is None:
+ self.searchlocalservers()
+ return self.localservers
+
+ def searchlocalservers(self):
+ servers = hostchooser.find_servers().items()
+ servers = filter(self.filterserver, servers)
+ servers.sort()
+ self.localservers = servers
+
+## def parse_inetserv(self, s):
+## try:
+## host, port, udpport, httpport = s.split(':')
+## return host, int(port), int(udpport)
+## except (ValueError, IndexError):
+## return None, None, None
+
+## def getinetservers(self):
+## if self.inetserverlist is None:
+## return None
+## result = []
+## for s in self.inetserverlist:
+## host, port, udpport = self.parse_inetserv(s)
+## addr = host, port
+## if addr in self.inetservers:
+## result.append((addr, self.inetservers[addr]))
+## return result
+
+## def setinetserverlist(self, lst):
+## self.inetserverlist = lst
+
+## def checkinetserverlist(self):
+## ulist = []
+## for s in self.inetserverlist:
+## host, port, udpport = self.parse_inetserv(s)
+## if host is not None:
+## ulist.append((host, udpport))
+## srvs = hostchooser.find_servers(ulist, delay=0.8)
+## self.inetservers = {}
+## for srv in srvs.items():
+## if not self.filterserver(srv):
+## continue
+## (host, port), info = srv
+## try:
+## host = socket.gethostbyaddr(host)[0]
+## except socket.error:
+## pass
+## self.inetservers[host, port] = info
+## #print 'hostchooser:', self.inetserverlist, '->', self.inetservers
+
+ def filterserver(self, ((host, port), info)):
+ for c in host+str(port):
+ if c not in "-.0123456789:@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_abcdefghijklmnopqrstuvwxyz":
+ return 0
+ return 1
+
+## def statusservers(self):
+## result = [], []
+## for s in self.inetserverlist:
+## host, port, udpport = self.parse_inetserv(s)
+## addr = host, port
+## found = addr in self.inetservers
+## result[found].append(s)
+## return result
+
+ def loadoptionfile(self):
+ try:
+ f = open(self.filename, 'r')
+ data = f.read().strip()
+ f.close()
+ except IOError:
+ data = None
+ return eval(data or '{}', {}, {})
+
+ def saveoptions(self):
+ data = self.loadoptionfile()
+ data['*'] = self.globaloptions.dict()
+ data[self.localhost] = self.localoptions.dict()
+ try:
+ f = open(self.filename, 'w')
+ print >> f, `data`
+ f.close()
+ except IOError, e:
+ print >> sys.stderr, "! Cannot save config file: " + str(e)
+
+ def reloadports(self):
+ import msgstruct
+ msgstruct.PORTS.clear()
+ for key, value in self.localoptions.dict().items():
+ if key.startswith('port_'):
+ key = key[5:]
+ if key == 'CLIENT' and type(value) == str and ':' in value:
+ udphostname, value = value.split(':')
+ msgstruct.PORTS['sendudpto'] = udphostname
+ try:
+ value = int(value)
+ except:
+ continue
+ msgstruct.PORTS[key] = value
+
+ def startgame(self):
+ self.reloadports()
+ options = self.globaloptions
+ kwds = {}
+ if options.beginboard is not None:
+ kwds['beginboard'] = int(options.beginboard)
+ if options.lvlend is not None and options.lvlend.startswith('n'):
+ kwds['finalboard'] = int(options.finalboard)
+ if options.stepboard is not None:
+ kwds['stepboard'] = int(options.stepboard)
+ if options.limit is not None and options.limit.startswith('y'):
+ kwds['limitlives'] = int(options.lives)
+ if options.limitlifegain is not None:
+ kwds['lifegainlimit'] = int(options.lifegainlimit)
+ if options.extralife is not None:
+ kwds['extralife'] = int(options.extralife)
+ if options.autoreset is not None:
+ kwds['autoreset'] = options.autoreset.startswith('y')
+ if options.metapublish is not None:
+ kwds['metaserver'] = options.metapublish.startswith('y')
+ self.Game(options.file, **kwds)
+
+ ### loaders ###
+
+ def metaserverpage(self, headers):
+ metaserver_url = metaclient.METASERVER_URL
+ myhost = my_host(headers)
+ joinurl = quote_plus('%s/%s' % (myhost, self.seed))
+ return metaserver_url + '?join=%s&time=%s' % (joinurl, time.time())
+
+ def mainpage(self, headers, juststarted=0, justconnected=0):
+ running = my_server()
+ count = len(gamesrv.clients)
+ tim = time.time()
+ #if running:
+ # metapublish = my_server_meta_address()
+ # fndesc = quote_plus(gamesrv.game.FnDesc)
+ #else:
+ # metapublish = None
+ return httpserver.load(os.path.join(LOCALDIR, 'data', 'index.html'),
+ 'text/html', locals=locals())
+
+ def indexloader(self, headers, cheat=[], **options):
+ if cheat:
+ import __builtin__
+ for c in cheat:
+ getattr(__builtin__, '__cheat')(c)
+ else:
+ self.localservers = None
+ return self.mainpage(headers, juststarted=('juststarted' in options))
+
+ def controlcenterloader(self, headers, **options):
+ host = headers['remote host']
+ host = socket.gethostbyname(host)
+ if host != '127.0.0.1':
+ raise HTTPRequestError, "Access denied"
+ return None, self.indexurl
+
+## def listloader(self, headers, s=[], **options):
+## self.setinetserverlist(s)
+## self.checkinetserverlist()
+## query = []
+## missing, found = self.statusservers()
+## for s in missing:
+## query.append('d=' + s)
+## for s in found:
+## query.append('a=' + s)
+## return self.mainpage(headers, query)
+
+ def newloader(self, headers, **options):
+ if not self.Game:
+ raise HTTPRequestError, "Complete bub-n-bros installation needed"
+ locals = {
+ 'Game': self.Game,
+ 'options': self.globaloptions,
+ 'running': gamesrv.game is not None,
+ }
+ return httpserver.load(os.path.join(LOCALDIR, 'data', 'new.html'),
+ 'text/html', locals=locals)
+
+ def runloader(self, headers, **options):
+ self.globaloptions.metapublish = 'n'
+ self.globaloptions.autoreset = 'n'
+ for key, value in options.items():
+ if len(value) == 1:
+ setattr(self.globaloptions, key, value[0])
+ self.saveoptions()
+ self.startgame()
+ return None, 'index.html?juststarted=%s' % time.time()
+
+ def stoploader(self, headers, really=[], **options):
+ count = len(gamesrv.clients)
+ if count == 0 or really:
+ locals = {
+ 'self': self,
+ #'metaserver': METASERVER,
+ #'metapublish': gamesrv.game and my_server_meta_address(),
+ #'localdir': LOCALDIR,
+ }
+ gamesrv.closeeverything()
+ return httpserver.load(os.path.join(LOCALDIR, 'data', 'stop.html'),
+ 'text/html', locals=locals)
+ else:
+ locals = {
+ 'count': count,
+ }
+ return httpserver.load(os.path.join(LOCALDIR, 'data', 'confirm.html'),
+ 'text/html', locals=locals)
+
+## def registerloader(self, headers, a=[], d=[], **options):
+## if a: # the lists 'a' and 'd' contain dummies !!
+## self.globaloptions.metapublish = 'y'
+## self.has_been_published = 1
+## kwd = 'a'
+## else:
+## self.globaloptions.metapublish = 'n'
+## kwd = 'd'
+## url = "%s?cmd=register&%s=%s" % (METASERVER,
+## kwd, my_server_meta_address())
+## if a and gamesrv.game:
+## url += '&desc=' + quote_plus(gamesrv.game.FnDesc)
+## return None, url
+
+ def joinloader(self, headers, host=[], port=[], httpport=[],
+ m=[], **options):
+ args = self.buildclientoptions()
+ assert len(host) == 1
+ host = host[0]
+ if len(port) == 1:
+ port = port[0]
+ else:
+ try:
+ host, port = host.split(':')
+ except:
+ port = None
+ if args is None:
+ # redirect to the Java applet
+ try:
+ httpport = int(httpport[0])
+ except (ValueError, IndexError):
+ if port:
+ raise HTTPRequestError, "This server is not running HTTP."
+ else:
+ raise HTTPRequestError, "Sorry, I cannot connect the Java applet to a server using this field."
+ return None, 'http://%s:%s/' % (host, httpport)
+
+ # now is a good time to generate the color files if we can
+ file = os.path.join(LOCALDIR, os.pardir, 'bubbob', 'images',
+ 'buildcolors.py')
+ if os.path.exists(file):
+ g = {'__name__': '__auto__', '__file__': file}
+ execfile(file, g)
+
+ if port:
+ address = '%s:%s' % (host, port)
+ else:
+ address = host
+ nbclients = len(gamesrv.clients)
+ script = os.path.join(LOCALDIR, os.pardir, 'display', 'Client.py')
+ script = no_quote_worries(script)
+ if m:
+ args.insert(0, '-m')
+ args = [script] + args + [address]
+ schedule_launch(args)
+ if m:
+ time.sleep(0.5)
+ s = 'Connecting to %s.' % address
+ return None, self.metaserverpage(headers) + '&head=' + quote_plus(s)
+ #elif my_server_address() == address:
+ # endtime = time.time() + 3.0
+ # while gamesrv.recursiveloop(endtime, []):
+ # if len(gamesrv.clients) > nbclients:
+ # break
+ return self.mainpage(headers, justconnected=1)
+
+ def optionsloader(self, headers, reset=[], savetime=[], **options):
+ if reset:
+ self.localoptions.clear()
+ self.globaloptions.clear()
+ self.saveoptions()
+ elif savetime:
+ self.localoptions.port_CLIENT = None
+ self.localoptions.port_LISTEN = None
+ self.localoptions.port_HTTP = None
+ for key, value in options.items():
+ setattr(self.localoptions, key, value[0])
+ self.saveoptions()
+ locals = {
+ 'self' : self,
+ 'options': self.localoptions,
+ }
+ return httpserver.load(os.path.join(LOCALDIR, 'data', 'options.html'),
+ 'text/html', locals=locals)
+
+ def nameloader(self, headers, **options):
+ MAX = len(PLAYERNAMES)
+ if options:
+ anyname = None
+ for id in range(MAX):
+ keyid = 'player%d' % id
+ if keyid in options:
+ value = options[keyid][0]
+ anyname = anyname or value
+ teamid = 'team%d' % id
+ if teamid in options:
+ team = options[teamid][0]
+ if len(team) == 1:
+ value = '%s (%s)' % (value, team)
+ setattr(self.localoptions, keyid, value)
+ if 'c' in options:
+ for id in range(MAX):
+ keyid = 'player%d' % id
+ try:
+ delattr(self.localoptions, keyid)
+ except AttributeError:
+ pass
+ if 'f' in options:
+ for id in range(MAX):
+ keyid = 'player%d' % id
+ if not getattr(self.localoptions, keyid):
+ setattr(self.localoptions, keyid,
+ anyname or PLAYERNAMES[id])
+ else:
+ anyname = getattr(self.localoptions, keyid)
+ self.saveoptions()
+ if 's' in options:
+ return self.mainpage(headers)
+ locals = {
+ 'options': self.localoptions.dict(),
+ }
+ return httpserver.load(os.path.join(LOCALDIR, 'data', 'name.html'),
+ 'text/html', locals=locals)
+
+ def graphicmodeslist(self):
+ try:
+ return self.GraphicModesList
+ except AttributeError:
+ import display.modes
+ self.GraphicModesList = display.modes.graphicmodeslist()
+ javamode = display.modes.GraphicMode(
+ 'java', 'Java Applet (for Java browsers)', [])
+ javamode.low_priority = 1
+ javamode.getmodule = lambda : None
+ self.GraphicModesList.insert(0, javamode)
+ return self.GraphicModesList
+
+ def soundmodeslist(self):
+ try:
+ return self.SoundModesList
+ except AttributeError:
+ import display.modes
+ self.SoundModesList = display.modes.soundmodeslist()
+ return self.SoundModesList
+
+ def localmodes(self):
+ import display.modes
+ currentmodes = []
+ options = self.localoptions
+ for name, lst in [(options.dpy_, self.graphicmodeslist()),
+ (options.snd_, self.soundmodeslist())]:
+ try:
+ mode = display.modes.findmode(name, lst)
+ except KeyError:
+ try:
+ mode = display.modes.findmode(None, lst)
+ except KeyError, e:
+ print >> sys.stderr, str(e) # no mode!
+ mode = None
+ currentmodes.append(mode)
+ return currentmodes
+
+ def buildclientoptions(self):
+ dpy, snd = self.localmodes()
+ if dpy.getmodule() is None:
+ return None # redirect to the Java applet
+ if dpy is None or snd is None:
+ raise HTTPRequestError, "No installed graphics or sounds drivers. See the settings page."
+ options = self.localoptions
+ result = ['--cfg='+no_quote_worries(self.filename)]
+ for key, value in options.dict().items():
+ if key.startswith('port_') and value:
+ result.append('--port')
+ result.append('%s=%s' % (key[5:], value))
+ if options.datachannel == 'tcp': result.append('--tcp')
+ if options.datachannel == 'udp': result.append('--udp')
+ if options.music == 'no': result.append('--music=no')
+ for optname, mode in [('--display', dpy),
+ ('--sound', snd)]:
+ result.append(optname + '=' + mode.name)
+ uid = mode.unique_id() + '_'
+ for key, value in options.dict().items():
+ if key.startswith(uid):
+ result.append('--%s=%s' % (key[len(uid):], value))
+ return result
+
+def my_host(headers):
+ return headers.get('host') or httpserver.my_host()
+
+def my_server():
+ if gamesrv.game:
+ s = gamesrv.opentcpsocket()
+ return ((gamesrv.HOSTNAME, gamesrv.displaysockport(s)),
+ gamesrv.game.FnDesc)
+ else:
+ return None
+
+def my_server_address():
+ running = my_server()
+ if running:
+ (host, port), info = running
+ return '%s:%d' % (host, port)
+ else:
+ return None
+
+##def my_server_meta_address():
+## s = gamesrv.opentcpsocket()
+## ps = gamesrv.openpingsocket()
+## hs = gamesrv.openhttpsocket()
+## fullname = gamesrv.HOSTNAME
+## try:
+## fullname = socket.gethostbyaddr(fullname)[0]
+## except socket.error:
+## pass
+## return '%s:%s:%s:%s' % (fullname,
+## gamesrv.displaysockport(s),
+## gamesrv.displaysockport(ps),
+## gamesrv.displaysockport(hs))
+
+##def meta_register():
+## # Note: this tries to open a direct HTTP connection to the meta-server
+## # which may not work if the proxy is not configured in $http_proxy
+## try:
+## import urllib
+## except ImportError:
+## print >> sys.stderr, "cannot register with the meta-server: Python's urllib missing"
+## return
+## print "registering with the meta-server...",
+## sys.stdout.flush()
+## addr = my_server_meta_address()
+## try:
+## f = urllib.urlopen('%s?a=%s&desc=%s' % (
+## METASERVER, addr, quote_plus(gamesrv.game.FnDesc)))
+## f.close()
+## except Exception, e:
+## print
+## print >> sys.stderr, "cannot contact the meta-server (check $http_proxy):"
+## print >> sys.stderr, "%s: %s" % (e.__class__.__name__, e)
+## else:
+## print "ok"
+## unregister_at_exit(addr)
+
+##def meta_unregister(addr):
+## import urllib
+## print "unregistering from the meta-server...",
+## sys.stdout.flush()
+## try:
+## f = urllib.urlopen(METASERVER + '?d=' + addr)
+## f.close()
+## except Exception, e:
+## print "failed"
+## else:
+## print "ok"
+
+##def unregister_at_exit(addr, firsttime=[1]):
+## if firsttime:
+## import atexit
+## atexit.register(meta_unregister, addr)
+## del firsttime[:]
+
+QuoteTranslation = {}
+for c in ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ 'abcdefghijklmnopqrstuvwxyz'
+ '0123456789' '_.-'):
+ QuoteTranslation[c] = c
+del c
+QuoteTranslation[' '] = '+'
+
+def quote_plus(s):
+ """Quote the query fragment of a URL; replacing ' ' with '+'"""
+ getter = QuoteTranslation.get
+ return ''.join([getter(c, '%%%02X' % ord(c)) for c in s])
+
+
+def main(Game, save_url_to=None, quiet=0):
+ #gamesrv.openpingsocket(0) # try to reserve the standard UDP port
+ srv = PageServer(Game)
+ srv.registerpages()
+ if not srv.opensocket():
+ print >> sys.stderr, "server aborted."
+ sys.exit(1)
+ if quiet:
+ if Game:
+ Game.Quiet = 1
+ import stdlog
+ f = stdlog.LogFile()
+ if f:
+ print "Logging to", f.filename
+ sys.stdout = sys.stderr = f
+ if save_url_to:
+ data = srv.indexurl + '\n'
+ def try_to_unlink(fn):
+ try:
+ os.unlink(fn)
+ except:
+ pass
+ import atexit
+ atexit.register(try_to_unlink, save_url_to)
+ try:
+ fno = os.open(save_url_to, os.O_CREAT | os.O_TRUNC | os.O_WRONLY,
+ 0600)
+ if os.write(fno, data) != len(data):
+ raise OSError
+ os.close(fno)
+ except:
+ f = open(save_url_to, 'w')
+ f.write(data)
+ f.close()
+ #if webbrowser:
+ # srv.launchbrowser()
+
+
+# ____________________________________________________________
+# Hack hack hack - workaround for the fact that on Windows
+# the socket is inherited by the subprocess, which is quite
+# bad because it keeps the browser-server connexion alive
+# and the browser gets confused
+
+
+def schedule_launch(args):
+ httpserver.actions_when_finished.append(lambda args=args: launch(args))
+
+def launch(args):
+ # platform-specific hacks
+ print 'Running client -> ', ' '.join(args)
+ if 0: # OLD CODE sys.platform == 'darwin': # must start as a UI process
+ import tempfile
+ cmdname = tempfile.mktemp('_BubBob.py')
+ f = open(cmdname, 'w')
+ print >> f, 'import sys, os'
+ print >> f, 'try: os.unlink(%r)' % cmdname
+ print >> f, 'except OSError: pass'
+ print >> f, 'sys.argv[:] = %r' % (args,)
+ print >> f, '__file__ = %r' % cmdname
+ print >> f, 'execfile(%r)' % args[0]
+ f.close()
+ os.system('/usr/bin/open -a PythonLauncher "%s"' % cmdname)
+ else:
+ args.insert(0, sys.executable)
+ # try to close the open fds first
+ if hasattr(os, 'fork'):
+ try:
+ from resource import getrlimit, RLIMIT_NOFILE, error
+ except ImportError:
+ pass
+ else:
+ try:
+ soft, hard = getrlimit(RLIMIT_NOFILE)
+ except error:
+ pass
+ else:
+ if os.fork():
+ return # in parent -- done, continue
+ # in child
+ for fd in range(3, min(16384, hard)):
+ try:
+ os.close(fd)
+ except OSError:
+ pass
+ os.execv(args[0], args)
+ # this point should never be reached
+ # fall-back
+ # (quoting sucks on Windows) ** 42
+ if sys.platform == 'win32':
+ args[0] = '"%s"' % (args[0],)
+ if hasattr(os, 'P_DETACH'):
+ mode = os.P_DETACH
+ elif hasattr(os, 'P_NOWAIT0'):
+ mode = os.P_NOWAIT0
+ else:
+ mode = os.P_NOWAIT
+ os.spawnv(mode, sys.executable, args)
+
+if sys.platform != "win32":
+ def no_quote_worries(s):
+ return s
+else:
+ def no_quote_worries(s): # quoting !&?+*:-(
+ s = os.path.normpath(os.path.abspath(s))
+ absroot = os.path.join(LOCALDIR, os.pardir)
+ absroot = os.path.normpath(os.path.abspath(absroot))
+ ROOTDIR = os.curdir
+ while os.path.normpath(os.path.abspath(ROOTDIR)) != absroot:
+ if ROOTDIR == os.curdir:
+ ROOTDIR = os.pardir
+ else:
+ ROOTDIR = os.path.join(ROOTDIR, os.pardir)
+ if len(ROOTDIR) > 200:
+ # cannot find relative path! try with absolute one anyway
+ ROOTDIR = absroot
+ break
+ assert s.startswith(absroot)
+ if absroot.endswith(os.sep): # 'C:\'
+ absroot = absroot[:-1]
+ assert s[len(absroot)] == os.sep
+ relpath = s[len(absroot)+1:]
+ result = os.path.join(ROOTDIR, relpath)
+ print "no_quote_worries %r => %r" % (s, result)
+ return result
+
+
+if __name__ == '__main__':
+ if (len(sys.argv) != 3 or sys.argv[1] != '--quiet' or
+ not sys.argv[2].startswith('--saveurlto=')):
+ print >> sys.stderr, "This script should only be launched by BubBob.py."
+ sys.exit(2)
+ main(None, sys.argv[2][len('--saveurlto='):], quiet=1)
+ gamesrv.mainloop()