Browse Source

server-failover

David Rose 23 years ago
parent
commit
2623dabc13
1 changed files with 50 additions and 118 deletions
  1. 50 118
      direct/src/distributed/ClientRepository.py

+ 50 - 118
direct/src/distributed/ClientRepository.py

@@ -89,7 +89,7 @@ class ClientRepository(DirectObject.DirectObject):
             self.name2cdc[dcClass.getName()]=clientDistClass
         return None
 
-    def connect(self, serverURL,
+    def connect(self, serverList,
                 successCallback = None, successArgs = [],
                 failureCallback = None, failureArgs = []):
         """
@@ -113,77 +113,56 @@ class ClientRepository(DirectObject.DirectObject):
             self.connectHttp = 0
         else:
             self.connectHttp = self.hasProxy
-        
+
         self.bootedIndex = None
         self.bootedText = None
         if self.connectHttp:
-            self.notify.info("Connecting via HTTP interface.")
+            # In the HTTP case, we can't just iterate through the list
+            # of servers, because each server attempt requires
+            # spawning a request and then coming back later to check
+            # the success or failure.  Instead, we start the ball
+            # rolling by calling the connect callback, which will call
+            # itself repeatedly until we establish a connection (or
+            # run out of servers).
+            
             ch = self.http.makeChannel(0)
-            ch.beginConnectTo(DocumentSpec(serverURL))
-            ch.spawnTask(name = 'connect-to-server',
-                         callback = self.httpConnectCallback,
-                         extraArgs = [ch, successCallback, successArgs,
-                                      failureCallback, failureArgs])
+            self.httpConnectCallback(ch, serverList, 0,
+                                     successCallback, successArgs,
+                                     failureCallback, failureArgs)
+
         else:
-            self.notify.info("Connecting via NSPR interface.")
             self.qcm = QueuedConnectionManager()
             # A big old 20 second timeout.
             gameServerTimeoutMs = base.config.GetInt("game-server-timeout-ms",
                                                      20000)
-            if self.hasProxy:
-                url = self.proxy
-            else:
-                url = serverURL
-            self.tcpConn = self.qcm.openTCPClientConnection(
-                url.getServer(), url.getPort(),
-                gameServerTimeoutMs)
-
-            if self.tcpConn:
-                self.tcpConn.setNoDelay(1)
-                self.qcr=QueuedConnectionReader(self.qcm, 0)
-                self.qcr.addConnection(self.tcpConn)
-                minLag = config.GetFloat('min-lag', 0.)
-                maxLag = config.GetFloat('max-lag', 0.)
-                if minLag or maxLag:
-                    self.qcr.startDelay(minLag, maxLag)
-                self.cw=ConnectionWriter(self.qcm, 0)
-                if self.hasProxy:
-                    # Now we send an http CONNECT message on that
-                    # connection to initiate a connection to the real
-                    # game server
-                    realGameServer = (serverURL.getServer() + ":" + str(serverURL.getPort()))
-                    connectString = "CONNECT " + realGameServer + " HTTP/1.0\012\012"
-                    datagram = Datagram()
-                    # Use appendData and sendRaw so we do not send the length of the string
-                    datagram.appendData(connectString)
-                    self.notify.info("Sending CONNECT string: " + connectString)
-                    self.cw.setRawMode(1)
-                    self.qcr.setRawMode(1)
-                    self.notify.info("done set raw mode")
-                    self.send(datagram)
-                    self.notify.info("done send datagram")
-                    # Find the end of the http response, then call callback
-                    self.findRawString(["\015\012", "\015\015"],
-                                       self.proxyConnectCallback, [successCallback, successArgs])
-                    self.notify.info("done find raw string")
-                    # Now start the raw reader poll task and look for
-                    # the HTTP response When this is finished, it will
-                    # call the connect callback just like the non
-                    # proxy case
-                    self.startRawReaderPollTask()
-                    self.notify.info("done start raw reader poll task")
-
-                else:
-                    # no proxy.  We're done connecting.
+
+            # Try each of the servers in turn.
+            for url in serverList:
+                self.notify.info("Connecting to %s via NSPR interface." % (url.cStr()))
+                self.tcpConn = self.qcm.openTCPClientConnection(
+                    url.getServer(), url.getPort(),
+                    gameServerTimeoutMs)
+
+                if self.tcpConn:
+                    self.tcpConn.setNoDelay(1)
+                    self.qcr=QueuedConnectionReader(self.qcm, 0)
+                    self.qcr.addConnection(self.tcpConn)
+                    minLag = config.GetFloat('min-lag', 0.)
+                    maxLag = config.GetFloat('max-lag', 0.)
+                    if minLag or maxLag:
+                        self.qcr.startDelay(minLag, maxLag)
+                    self.cw=ConnectionWriter(self.qcm, 0)
                     self.startReaderPollTask()
                     if successCallback:
                         successCallback(*successArgs)
-            else:
-                # Failed to connect.
-                if failureCallback:
-                    failureCallback(0, *failureArgs)
+                    return
 
-    def httpConnectCallback(self, ch, successCallback, successArgs,
+            # Failed to connect.
+            if failureCallback:
+                failureCallback(0, *failureArgs)
+
+    def httpConnectCallback(self, ch, serverList, serverIndex,
+                            successCallback, successArgs,
                             failureCallback, failureArgs):
         if ch.isConnectionReady():
             self.tcpConn = ch.getConnection()
@@ -192,68 +171,21 @@ class ClientRepository(DirectObject.DirectObject):
             if successCallback:
                 successCallback(*successArgs)
 
+        elif serverIndex < len(serverList):
+            # No connection yet, but keep trying.
+            
+            url = serverList[serverIndex]
+            self.notify.info("Connecting to %s via HTTP interface." % (url.cStr()))
+            ch.beginConnectTo(DocumentSpec(url))
+            ch.spawnTask(name = 'connect-to-server',
+                         callback = self.httpConnectCallback,
+                         extraArgs = [ch, serverList, serverIndex + 1,
+                                      successCallback, successArgs,
+                                      failureCallback, failureArgs])
         else:
-            # Failed to connect.
+            # No more servers to try; we have to give up now.
             if failureCallback:
                 failureCallback(ch.getStatusCode(), *failureArgs)
-    
-    def proxyConnectCallback(self, successCallback, successArgs):
-        # Make sure we are not in raw mode anymore
-        self.cw.setRawMode(0)
-        self.qcr.setRawMode(0)
-        self.stopRawReaderPollTask()
-        if successCallback:
-            successCallback(*successArgs)
-
-    def startRawReaderPollTask(self):
-        # Stop any tasks we are running now
-        self.stopRawReaderPollTask()
-        self.stopReaderPollTask()
-        task = Task.Task(self.rawReaderPollUntilEmpty)
-        # Start with empty string
-        task.currentRawString = ""
-        taskMgr.add(task, "rawReaderPollTask", priority=self.TASK_PRIORITY)
-        return None
-
-    def stopRawReaderPollTask(self):
-        taskMgr.remove("rawReaderPollTask")
-        return None
-
-    def rawReaderPollUntilEmpty(self, task):
-        while self.rawReaderPollOnce():
-            pass
-        return Task.cont
-
-    def rawReaderPollOnce(self):
-        self.notify.debug("rawReaderPollOnce")
-        self.ensureValidConnection()
-        availGetVal = self.qcr.dataAvailable()
-        if availGetVal:
-            datagram = NetDatagram()
-            readRetVal = self.qcr.getData(datagram)
-            if readRetVal:
-                str = datagram.getMessage()
-                self.notify.debug("rawReaderPollOnce: found str: " + str)
-                self.handleRawString(str)
-            else:
-                ClientRepository.notify.warning("getData returned false")
-        return availGetVal
-
-    def handleRawString(self, str):
-        self.notify.info("handleRawString: str = <%s>" % (str))
-        self.currentRawString += str
-        self.notify.info("currentRawString = <%s>" % (self.currentRawString))
-        # Look in all the match strings to see if we got it yet
-        for matchString in self.rawStringMatchList:
-            if (self.currentRawString.find(matchString) >= 0):
-                self.rawStringCallback(*self.rawStringExtraArgs)
-                return
-
-    def findRawString(self, matchList, callback, extraArgs = []):
-        self.currentRawString = ""
-        self.rawStringMatchList = matchList
-        self.rawStringCallback = callback
-        self.rawStringExtraArgs = extraArgs
             
     def startReaderPollTask(self):
         # Stop any tasks we are running now