URL:
<http://gna.org/patch/?893>
Summary: rewrite of the python gameserver
Project: Warzone Resurrection Project
Submitted by: gschaden
Submitted on: Samstag 22.12.2007 um 08:19
Category: None
Priority: 5 - Normal
Status: None
Privacy: Public
Assigned to: None
Originator Email:
Open/Closed: Open
Discussion Lock: Any
_______________________________________________________
Details:
Index: wzmasterserver.py
===================================================================
--- wzmasterserver.py (revision 3113)
+++ wzmasterserver.py (working copy)
@@ -17,9 +17,12 @@
import sys
import SocketServer
-import thread
import struct
import socket
+from threading import Lock, Thread, Event, Timer
+import select
+import logging
+import cmd
#
################################################################################
@@ -30,11 +33,98 @@
lobbyDbg = True # Enable debugging.
gsSize = 112 # Size of GAMESTRUCT in byte.
ipOffset = 64+4+4 # 64 byte StringSize + SDWORD + SDWORD
-gameList = set() # Holds the list.
-listLock = thread.allocate_lock()
+logging.basicConfig(level=logging.INFO)
+
#
################################################################################
+# Support functions
+
+def synchronized(lock):
+ """ Synchronization decorator. """
+ def wrap(f):
+ def newFunction(*args, **kw):
+ lock.acquire()
+ try:
+ return f(*args, **kw)
+ finally:
+ lock.release()
+ return newFunction
+ return wrap
+
+#
+################################################################################
+# Game DB
+
+gdb=None
+gamedblock = Lock()
+class GameDB:
+ def __init__(self):
+ self.list = set()
+
+ @synchronized(gamedblock)
+ def addGame(self, g):
+ self.list.add(g)
+
+ @synchronized(gamedblock)
+ def removeGame(self, g):
+ try:
+ self.list.remove(g)
+ except KeyError:
+ return False
+ return True
+
+ # only games with a valid description
+ def getGames(self):
+ return filter(lambda x: x.description, self.list)
+
+ def getAllGames(self):
+ return self.list
+
+ def getGamesByHost(self, host):
+ return filter(lambda x: x.host == host, self.getGames())
+
+
+#
+################################################################################
+# Game class
+
+class Game:
+ def __init__(self, rh):
+ self.data = None
+ self.requestHandler = rh
+ self.description = None
+ self.size = None
+ self.flags = None
+ self.host = None
+ self.maxPlayers = None
+ self.currentPlayers = None
+ self.user1 = None
+ self.user2 = None
+ self.user3 = None
+ self.user4 = None
+
+ def setData(self, d):
+ logging.debug("setData")
+ (self.description, self.size, self.flags, self.host,
self.maxPlayers, self.currentPlayers,
+ self.user1, self.user2, self.user3, self.user4 ) =
struct.unpack("64sII16sIIIIII", d)
+ self.description=self.description.strip("\x00")
+ self.host=self.host.strip("\x00")
+ self.data = d
+ logging.debug("Game: %s %s %s %s" % ( self.host,
self.description,
self.maxPlayers, self.currentPlayers))
+
+ def getData(self):
+ return struct.pack("64sII16sIIIIII",
+ self.description.ljust(64, "\x00"),
+ self.size, self.flags,
+ self.host.ljust(16, "\x00"),
+ self.maxPlayers, self.currentPlayers, self.user1,
self.user2, self.user3,
self.user4)
+
+ def __str__(self):
+ return "Game: %16s %s %s %s" % ( self.host, self.description,
self.maxPlayers, self.currentPlayers)
+
+#
+################################################################################
# Socket Handler.
class RequestHandler(SocketServer.ThreadingMixIn,
SocketServer.StreamRequestHandler):
@@ -53,8 +143,7 @@
if netCommand == 'addg':
# Debug
- if lobbyDbg:
- print "<- addg"
+ logging.debug("<- addg")
# Fix the server address.
gameHost = self.client_address[0]
@@ -62,94 +151,178 @@
# Check we can connect to the host
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
- if lobbyDbg:
- print " \- Checking gameserver's vitality..."
+ logging.debug(" \- Checking gameserver's vitality...")
s.settimeout(10.0)
s.connect((gameHost, gamePort))
except:
- if lobbyDbg:
- print " \- Gameserver did not respond!"
+ logging.debug(" \- Gameserver did not respond!")
s.close()
return
# The host is valid, close the socket and continue
- if lobbyDbg:
- print " \- Adding gameserver."
+ logging.debug(" \- Adding gameserver.")
s.close()
while len(gameHost) < 16:
gameHost += '\0'
- currentGameData = None
-
- # and start receiving updates about the game
- while True:
- # Receive the gamestruct.
- try:
- newGameData = self.rfile.read(gsSize)
- except:
- newGameData = None
-
- # remove the previous data from the list
- if currentGameData:
- listLock.acquire()
+ try:
+ g=Game(self)
+ gdb.addGame(g)
+
+ # and start receiving updates about the game
+ while True:
+ #wait for data or an error
+ rdlist=[self.rfile]
+ while True:
+ (r, w, e) = select.select(rdlist,[],rdlist,1)
+ if len(e) > 0:
+ raise Exception("Communication Error")
+ if len(r) > 0:
+ break
+ logging.debug("end select, start reading")
+ # Receive the gamestruct.
try:
- if lobbyDbg:
- print "Removing game from",
self.client_address[0]
- gameList.remove(currentGameData)
- finally:
- listLock.release()
-
- if not newGameData:
- # incomplete data
- break
-
- # Update the new gameData whith the gameHost
- currentGameData = newGameData[:ipOffset] + gameHost +
newGameData[ipOffset+16:]
-
- # Put the game in the database
- listLock.acquire()
- try:
- if lobbyDbg:
- print " \- Adding game from",
self.client_address[0]
- gameList.add(currentGameData)
- finally:
- listLock.release()
-
+ newGameData = self.rfile.read(gsSize)
+ except:
+ newGameData = None
+
+ if not newGameData:
+ break
+
+ # Update the new gameData whith the gameHost
+ g.setData(newGameData[:ipOffset] + gameHost +
newGameData[ipOffset+16:])
+
+ except:
+ logging.warning("Communication error with %s" % g )
+ finally:
+ if g:
+ gdb.removeGame(g)
# Get a game list.
elif netCommand == 'list':
# Debug
- if lobbyDbg:
- print "<- list"
- print " \- I know ", len(gameList), " games."
+ logging.debug("<- list")
# Lock the gamelist to prevent new games while output.
- listLock.acquire()
+ gamedblock.acquire()
+ gamesCount=len(gdb.getGames())
+ logging.debug(" \- I know %i games" % gamesCount)
# Transmit the length of the following list as unsigned integer
(in network byte-order: big-endian).
- count = struct.pack('!I', len(gameList))
+ count = struct.pack('!I', gamesCount)
self.wfile.write(count)
# Transmit the single games.
- for game in gameList:
- self.wfile.write(game)
+ for game in gdb.getGames():
+ #self.wfile.write(game.data)
+ self.wfile.write(game.getData())
# Remove the lock.
- listLock.release()
+ gamedblock.release()
# If something unknown apperas.
else:
- print "Recieved a unknown command: ", netCommand
+ logging.warning("Recieved a unknown command: %s" % netCommand)
#
################################################################################
+# Cmd Interface
+
+def startCmdInterface():
+ x=SimpleInterface()
+ x.cmdloop("""
+Warzone 2100 Resurrection Project, Lobby server
+use "help" to get available commands
+""")
+
+class SimpleInterface(cmd.Cmd):
+ def emptyline(self):
+ pass
+
+ def do_list(self, arg):
+ print "Currently registered games"
+ print "".rjust(79,"=")
+ for g in gdb.getGames():
+ print "%16s %s %s %s" % (g.host, g.description,
g.maxPlayers,
g.currentPlayers)
+ print "".rjust(79,"=")
+
+ def do_info(self, arg):
+ print """
+Warzone 2100 Resurrection Project
+Lobby Server
+"""
+
+ def do_exit(self, arg):
+ return self.do_quit(arg)
+
+ def do_quit(self, arg):
+ stopServer()
+ return True
+
+ def do_kill(self, arg):
+ games=gdb.getGamesByHost(arg)
+ for g in games:
+ print "killing %s " % str(g)
+ g.requestHandler.finish()
+
+
+ def do_loglevel(self, arg):
+ level=None
+ try:
+ level=int(arg)
+ logging.getLogger().setLevel(level)
+ except:
+ print "wrong argument"
+
+ def help_loglevel(self):
+ print """
+Sets the loglevel
+CRITICAL = 50
+FATAL = CRITICAL
+ERROR = 40
+WARNING = 30
+WARN = WARNING
+INFO = 20
+DEBUG = 10
+NOTSET = 0
+"""
+
+#
+################################################################################
+# Code for shutdown
+
+shutdownPending = False
+
+def stopServer():
+ global shutdownPending
+ tcpserver.socket.shutdown(2)
+ shutdownPending = True
+
+
+
+#
+################################################################################
# The legendary Main.
if __name__ == '__main__':
- print "Starting Warzone 2100 lobby server on port ", lobbyPort
+ logging.info("Starting Warzone 2100 lobby server on port %d" %
lobbyPort)
+ gdb=GameDB()
+
+ t = Timer(1, startCmdInterface)
+ t.start()
+
SocketServer.ThreadingTCPServer.allow_reuse_address = True
tcpserver = SocketServer.ThreadingTCPServer(('0.0.0.0', lobbyPort),
RequestHandler)
- tcpserver.serve_forever()
+ try:
+ while not shutdownPending:
+ tcpserver.handle_request()
+ except KeyboardInterrupt:
+ pass
+ logging.info("Shutting down lobby server")
+ t.cancel()
+ for game in gdb.getAllGames():
+ game.requestHandler.finish()
+ tcpserver.server_close()
_______________________________________________________
Reply to this item at:
<http://gna.org/patch/?893>
_______________________________________________
Nachricht geschickt von/durch Gna!
http://gna.org/
_______________________________________________
Warzone-dev mailing list
[email protected]
https://mail.gna.org/listinfo/warzone-dev