diff --git a/direct/src/distributed/ClientRepository.py b/direct/src/distributed/ClientRepository.py index b7ae21fff0..4036ca0148 100644 --- a/direct/src/distributed/ClientRepository.py +++ b/direct/src/distributed/ClientRepository.py @@ -11,7 +11,9 @@ import PythonUtil import ParentMgr import RelatedObjectMgr import time +from ClockDelta import * from PyDatagram import PyDatagram +from PyDatagramIterator import PyDatagramIterator class ClientRepository(ConnectionRepository.ConnectionRepository): notify = DirectNotifyGlobal.directNotify.newCategory("ClientRepository") @@ -39,6 +41,12 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # The RelatedObjectMgr helps distributed objects find each # other. self.relatedObjectMgr = RelatedObjectMgr.RelatedObjectMgr(self) + + # Keep track of how recently we last sent a heartbeat message. + # We want to keep these coming at heartbeatInterval seconds. + self.heartbeatInterval = base.config.GetDouble('heartbeat-interval', 10) + self.heartbeatStarted = 0 + self.lastHeartbeat = 0 def abruptCleanup(self): """ @@ -48,6 +56,18 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): """ self.relatedObjectMgr.abortAllRequests() + def sendDisconnect(self): + if self.tcpConn: + # Tell the game server that we're going: + datagram = PyDatagram() + # Add message type + datagram.addUint16(CLIENT_DISCONNECT) + # Send the message + self.send(datagram) + self.notify.info("Sent disconnect message to server") + self.disconnect() + self.stopHeartbeat() + def setServerDelta(self, delta): """ Indicates the approximate difference in seconds between the @@ -316,12 +336,10 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): ClientRepository.notify.warning( "Server is booting us out with no explanation.") - def handleServerHeartbeat(self, di): # Got a heartbeat message from the server. if base.config.GetBool('server-heartbeat-info', 1): ClientRepository.notify.info("Server heartbeat.") - def handleUnexpectedMsgType(self, msgType, di): if msgType == CLIENT_GO_GET_LOST: @@ -373,6 +391,69 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # send the message self.send(datagram) + + def handleDatagram(self, datagram): + if self.notify.getDebug(): + print "ClientRepository received datagram:" + datagram.dumpHex(ostream) + di = PyDatagramIterator(datagram) + msgType = di.getUint16() + if self.notify.getDebug(): + self.notify.debug("handleDatagram: msgType: " + `msgType`) + # watch for setZoneDones + if msgType == CLIENT_DONE_SET_ZONE_RESP: + self.handleSetZoneDone() + if self.handler == None: + self.handleUnexpectedMsgType(msgType, di) + else: + self.handler(msgType, di) + # If we're processing a lot of datagrams within one frame, we + # may forget to send heartbeats. Keep them coming! + self.considerHeartbeat() + + def sendHeartbeat(self): + datagram = PyDatagram() + # Add message type + datagram.addUint16(CLIENT_HEARTBEAT) + # Send it! + self.send(datagram) + self.lastHeartbeat = globalClock.getRealTime() + # This is important enough to consider flushing immediately + # (particularly if we haven't run readerPollTask recently). + if self.tcpConn: + self.tcpConn.considerFlush() + + def considerHeartbeat(self): + """Send a heartbeat message if we haven't sent one recently.""" + if not self.heartbeatStarted: + self.notify.debug("Heartbeats not started; not sending.") + return + + elapsed = globalClock.getRealTime() - self.lastHeartbeat + if elapsed < 0 or elapsed > self.heartbeatInterval: + # It's time to send the heartbeat again (or maybe someone + # reset the clock back). + self.notify.info("Sending heartbeat mid-frame.") + self.startHeartbeat() + + def stopHeartbeat(self): + taskMgr.remove("heartBeat") + self.heartbeatStarted = 0 + + def startHeartbeat(self): + self.stopHeartbeat() + self.heartbeatStarted = 1 + self.sendHeartbeat() + self.waitForNextHeartBeat() + + def sendHeartbeatTask(self, task): + self.sendHeartbeat() + self.waitForNextHeartBeat() + return Task.done + + def waitForNextHeartBeat(self): + taskMgr.doMethodLater(self.heartbeatInterval, self.sendHeartbeatTask, + "heartBeat") def sendUpdate(self, do, fieldName, args, sendToId = None): # Get the DO id diff --git a/direct/src/distributed/MsgTypes.py b/direct/src/distributed/MsgTypes.py index 3a23140b40..79c100206e 100644 --- a/direct/src/distributed/MsgTypes.py +++ b/direct/src/distributed/MsgTypes.py @@ -1,7 +1,25 @@ """MsgTypes module: contains distributed object message types""" +# 2 new params: passwd, char bool 0/1 1 = new account +# 2 new return values: 129 = not found, 12 = bad passwd, +CLIENT_LOGIN = 1 +CLIENT_LOGIN_RESP = 2 +CLIENT_GET_AVATARS = 3 # Sent by the server when it is dropping the connection deliberately. CLIENT_GO_GET_LOST = 4 +CLIENT_GET_AVATARS_RESP = 5 +CLIENT_CREATE_AVATAR = 6 +CLIENT_CREATE_AVATAR_RESP = 7 +CLIENT_GET_SHARD_LIST = 8 +CLIENT_GET_SHARD_LIST_RESP = 9 +CLIENT_GET_FRIEND_LIST = 10 +CLIENT_GET_FRIEND_LIST_RESP = 11 +CLIENT_GET_FRIEND_DETAILS = 12 +CLIENT_GET_FRIEND_DETAILS_RESP = 13 +CLIENT_GET_AVATAR_DETAILS = 14 +CLIENT_GET_AVATAR_DETAILS_RESP = 15 +CLIENT_LOGIN_2 = 16 +CLIENT_LOGIN_2_RESP = 17 CLIENT_OBJECT_UPDATE_FIELD = 24 CLIENT_OBJECT_UPDATE_FIELD_RESP = 24 @@ -9,10 +27,36 @@ CLIENT_OBJECT_DISABLE_RESP = 25 CLIENT_OBJECT_DELETE_RESP = 27 CLIENT_SET_ZONE = 29 CLIENT_SET_SHARD = 31 +CLIENT_SET_AVATAR = 32 CLIENT_CREATE_OBJECT_REQUIRED = 34 CLIENT_CREATE_OBJECT_REQUIRED_OTHER = 35 +CLIENT_DISCONNECT = 37 + +CLIENT_CHANGE_IP_ADDRESS_RESP = 45 +CLIENT_GET_STATE = 46 +CLIENT_GET_STATE_RESP = 47 +CLIENT_DONE_SET_ZONE_RESP = 48 +CLIENT_DELETE_AVATAR = 49 +CLIENT_DELETE_AVATAR_RESP = 50 + CLIENT_HEARTBEAT = 52 +CLIENT_FRIEND_ONLINE = 53 +CLIENT_FRIEND_OFFLINE = 54 +CLIENT_REMOVE_FRIEND = 56 + +CLIENT_SERVER_UP = 57 +CLIENT_SERVER_DOWN = 58 + +CLIENT_CHANGE_PASSWORD = 65 + +CLIENT_SET_NAME_PATTERN = 67 +CLIENT_SET_NAME_PATTERN_ANSWER = 68 + +CLIENT_SET_WISHNAME = 70 +CLIENT_SET_WISHNAME_RESP = 71 +CLIENT_SET_WISHNAME_CLEAR = 72 +CLIENT_SET_SECURITY = 73 # These messages are ignored when the client is headed to the quiet zone QUIET_ZONE_IGNORED_LIST = [ @@ -27,3 +71,9 @@ QUIET_ZONE_IGNORED_LIST = [ #CLIENT_CREATE_OBJECT_REQUIRED_OTHER, ] + +# The following is a different set of numbers from above. +# These are the sub-message types for CLIENT_LOGIN_2. +CLIENT_LOGIN_2_GREEN = 1 # Disney's GoReg subscription token, not used. +CLIENT_LOGIN_2_PLAY_TOKEN = 2 # VR Studio PlayToken. +CLIENT_LOGIN_2_BLUE = 3 # The international GoReg token. diff --git a/direct/src/level/DistributedLevel.py b/direct/src/level/DistributedLevel.py index 434db19ffc..7574d26301 100755 --- a/direct/src/level/DistributedLevel.py +++ b/direct/src/level/DistributedLevel.py @@ -6,6 +6,7 @@ from PythonUtil import Functor, sameElements, list2dict, uniqueElements from IntervalGlobal import * from ToontownMsgTypes import * import ToontownGlobals +import OTPGlobals import DistributedObject import Level import LevelConstants @@ -633,7 +634,7 @@ class DistributedLevel(DistributedObject.DistributedObject, # always include Toontown and factory uberZones uberZone = self.getZoneId(LevelConstants.UberZoneEntId) # the level itself is in the 'level zone' - visibleZoneIds = [ToontownGlobals.UberZone, self.levelZone, uberZone] + visibleZoneIds = [OTPGlobals.UberZone, self.levelZone, uberZone] for vz in vizList: visibleZoneIds.append(self.getZoneId(vz)) assert(uniqueElements(visibleZoneIds))