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

Reply via email to