| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855 |
- """ClientRepository module: contains the ClientRepository class"""
- from pandac.PandaModules import *
- from MsgTypes import *
- from direct.task import Task
- from direct.directnotify import DirectNotifyGlobal
- import CRCache
- from direct.distributed.ConnectionRepository import ConnectionRepository
- from direct.showbase import PythonUtil
- import ParentMgr
- import RelatedObjectMgr
- import time
- from ClockDelta import *
- from PyDatagram import PyDatagram
- from PyDatagramIterator import PyDatagramIterator
- class ClientRepository(ConnectionRepository):
- """
- This maintains a client-side connection with a Panda server.
- It currently supports several different versions of the server:
- within the VR Studio, we are currently in transition from the
- Toontown server to the OTP server; people outside the VR studio
- will use the Panda LAN server provided by CMU.
- """
- notify = DirectNotifyGlobal.directNotify.newCategory("ClientRepository")
- def __init__(self):
- ConnectionRepository.__init__(self, base.config)
- self.context=100000
- self.setClientDatagram(1)
- self.recorder = base.recorder
- if wantOtpServer:
- # this is used to imulate the old setzone behavior
- # with set locationa and set interest
- self.old_setzone_interest_handle = None
- self.readDCFile()
- self.cache=CRCache.CRCache()
- self.serverDelta = 0
- self.bootedIndex = None
- self.bootedText = None
- if 0: # unused:
- self.worldScale = render.attachNewNode("worldScale") # for grid zones.
- self.worldScale.setScale(base.config.GetFloat('world-scale', 100))
- self.priorWorldPos = None
- # create a parentMgr to handle distributed reparents
- # this used to be 'token2nodePath'
- self.parentMgr = ParentMgr.ParentMgr()
- # 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
- # By default, the ClientRepository is set up to respond to
- # datagrams from the CMU Panda LAN server. You can
- # reassign this member to change the response behavior
- # according to game context.
- self.handler = self.publicServerDatagramHandler
- # The DOID allocator. The CMU LAN server may choose to
- # send us a block of DOIDs. If it chooses to do so, then we
- # may create objects, using those DOIDs. These structures are
- # only used in conjunction with the CMU LAN server.
- self.DOIDbase = 0
- self.DOIDnext = 0
- self.DOIDlast = 0
- # Define uniqueName
- def uniqueName(self, desc):
- return desc
- def abruptCleanup(self):
- """
- Call this method to clean up any pending hooks or tasks on
- distributed objects, but leave the ClientRepository in a sane
- state for creating more distributed objects.
- """
- self.relatedObjectMgr.abortAllRequests()
- def sendDisconnect(self):
- if self.isConnected():
- # 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()
- if 0: # Code that became obsolete before it was used:
- def setWorldOffset(self, xOffset=0, yOffset=0):
- self.worldXOffset=xOffset
- self.worldYOffset=yOffset
- def getWorldPos(self, nodePath):
- pos = nodePath.getPos(self.worldScale)
- return (int(round(pos.getX())), int(round(pos.getY())))
- def sendWorldPos(self, x, y):
- # The server will need to know the world
- # offset of our current render node path
- # and adjust the x, y accordingly. At one
- # point I considered adding the world offset
- # here, but that would just use extra bits.
- onScreenDebug.add("worldPos", "%-4d, %-4d"%(x, y))
- return #*#
- datagram = PyDatagram()
- # Add message type
- datagram.addUint16(CLIENT_SET_WORLD_POS)
- # Add x
- datagram.addInt16(x)
- # Add y
- datagram.addSint16(y)
- # send the message
- self.send(datagram)
- def checkWorldPos(self, nodePath):
- worldPos = self.getWorldPos(nodePath)
- if self.priorWorldPos != worldPos:
- self.priorWorldPos = worldPos
- self.sendWorldPos(worldPos[0], worldPos[1])
- def allocateContext(self):
- self.context+=1
- return self.context
- def setServerDelta(self, delta):
- """
- Indicates the approximate difference in seconds between the
- client's clock and the server's clock, in universal time (not
- including timezone shifts). This is mainly useful for
- reporting synchronization information to the logs; don't
- depend on it for any precise timing requirements.
- Also see Notify.setServerDelta(), which also accounts for a
- timezone shift.
- """
- self.serverDelta = delta
- def getServerDelta(self):
- return self.serverDelta
- def getServerTimeOfDay(self):
- """
- Returns the current time of day (seconds elapsed since the
- 1972 epoch) according to the server's clock. This is in GMT,
- and hence is irrespective of timezones.
- The value is computed based on the client's clock and the
- known delta from the server's clock, which is not terribly
- precisely measured and may drift slightly after startup, but
- it should be accurate plus or minus a couple of seconds.
- """
- return time.time() + self.serverDelta
- def handleGenerateWithRequired(self, di):
- if wantOtpServer:
- parentId = di.getUint32()
- zoneId = di.getUint32()
- # Get the class Id
- classId = di.getUint16()
- # Get the DO Id
- doId = di.getUint32()
- # Look up the dclass
- dclass = self.dclassesByNumber[classId]
- dclass.startGenerate()
- # Create a new distributed object, and put it in the dictionary
- if wantOtpServer:
- distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
- else:
- distObj = self.generateWithRequiredFields(dclass, doId, di)
- dclass.stopGenerate()
- def handleGenerateWithRequiredOther(self, di):
- if wantOtpServer:
- parentId = di.getUint32()
- zoneId = di.getUint32()
- # Get the class Id
- classId = di.getUint16()
- # Get the DO Id
- doId = di.getUint32()
- # Look up the dclass
- dclass = self.dclassesByNumber[classId]
- dclass.startGenerate()
- # Create a new distributed object, and put it in the dictionary
- if wantOtpServer:
- distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
- else:
- distObj = self.generateWithRequiredOtherFields(dclass, doId, di)
- dclass.stopGenerate()
- def handleQuietZoneGenerateWithRequired(self, di):
- # Special handler for quiet zone generates -- we need to filter
- if wantOtpServer:
- parentId = di.getUint32()
- zoneId = di.getUint32()
- # Get the class Id
- classId = di.getUint16()
- # Get the DO Id
- doId = di.getUint32()
- # Look up the dclass
- dclass = self.dclassesByNumber[classId]
- dclass.startGenerate()
- # If the class is a neverDisable class (which implies uberzone) we
- # should go ahead and generate it even though we are in the quiet zone
- if not wantOtpServer:
- if dclass.getClassDef().neverDisable:
- # Create a new distributed object, and put it in the dictionary
- distObj = self.generateWithRequiredFields(dclass, doId, di)
- else:
- distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
- dclass.stopGenerate()
- def handleQuietZoneGenerateWithRequiredOther(self, di):
- # Special handler for quiet zone generates -- we need to filter
- if wantOtpServer:
- parentId = di.getUint32()
- zoneId = di.getUint32()
- # Get the class Id
- classId = di.getUint16()
- # Get the DO Id
- doId = di.getUint32()
- # Look up the dclass
- dclass = self.dclassesByNumber[classId]
- # If the class is a neverDisable class (which implies uberzone) we
- # should go ahead and generate it even though we are in the quiet zone
- dclass.startGenerate()
- if not wantOtpServer:
- if dclass.getClassDef().neverDisable:
- # Create a new distributed object, and put it in the dictionary
- distObj = self.generateWithRequiredOtherFields(dclass, doId, di)
- else:
- distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
- dclass.stopGenerate()
- # wantOtpServer: remove the None defaults when we remove this config variable
- def generateWithRequiredFields(self, dclass, doId, di, parentId = None, zoneId = None):
- if self.doId2do.has_key(doId):
- # ...it is in our dictionary.
- # Just update it.
- distObj = self.doId2do[doId]
- assert(distObj.dclass == dclass)
- distObj.generate()
- if wantOtpServer:
- distObj.setLocation(parentId, zoneId)
- distObj.updateRequiredFields(dclass, di)
- # updateRequiredFields calls announceGenerate
- elif self.cache.contains(doId):
- # ...it is in the cache.
- # Pull it out of the cache:
- distObj = self.cache.retrieve(doId)
- assert(distObj.dclass == dclass)
- # put it in the dictionary:
- self.doId2do[doId] = distObj
- # and update it.
- distObj.generate()
- if wantOtpServer:
- distObj.setLocation(parentId, zoneId)
- distObj.updateRequiredFields(dclass, di)
- # updateRequiredFields calls announceGenerate
- else:
- # ...it is not in the dictionary or the cache.
- # Construct a new one
- classDef = dclass.getClassDef()
- if classDef == None:
- self.notify.error("Could not create an undefined %s object." % (dclass.getName()))
- distObj = classDef(self)
- distObj.dclass = dclass
- # Assign it an Id
- distObj.doId = doId
- # Put the new do in the dictionary
- self.doId2do[doId] = distObj
- # Update the required fields
- distObj.generateInit() # Only called when constructed
- distObj.generate()
- if wantOtpServer:
- distObj.setLocation(parentId, zoneId)
- distObj.updateRequiredFields(dclass, di)
- # updateRequiredFields calls announceGenerate
- if wantOtpServer:
- print "New DO:%s, dclass:%s"%(doId, dclass.getName())
- return distObj
- def generateGlobalObject(self, doId, dcname):
- # Look up the dclass
- dclass = self.dclassesByName[dcname]
- # Create a new distributed object, and put it in the dictionary
- #distObj = self.generateWithRequiredFields(dclass, doId, di)
- # Construct a new one
- classDef = dclass.getClassDef()
- if classDef == None:
- self.notify.error("Could not create an undefined %s object."%(
- dclass.getName()))
- distObj = classDef(self)
- distObj.dclass = dclass
- # Assign it an Id
- distObj.doId = doId
- # Put the new do in the dictionary
- self.doId2do[doId] = distObj
- # Update the required fields
- distObj.generateInit() # Only called when constructed
- distObj.generate()
- if wantOtpServer:
- # TODO: ROGER: where should we get parentId and zoneId?
- parentId = None
- zoneId = None
- distObj.setLocation(parentId, zoneId)
- # updateRequiredFields calls announceGenerate
- return distObj
- def generateWithRequiredOtherFields(self, dclass, doId, di,
- parentId = None, zoneId = None):
- if self.doId2do.has_key(doId):
- # ...it is in our dictionary.
- # Just update it.
- distObj = self.doId2do[doId]
- assert(distObj.dclass == dclass)
- distObj.generate()
- if wantOtpServer:
- distObj.setLocation(parentId, zoneId)
- distObj.updateRequiredOtherFields(dclass, di)
- # updateRequiredOtherFields calls announceGenerate
- elif self.cache.contains(doId):
- # ...it is in the cache.
- # Pull it out of the cache:
- distObj = self.cache.retrieve(doId)
- assert(distObj.dclass == dclass)
- # put it in the dictionary:
- self.doId2do[doId] = distObj
- # and update it.
- distObj.generate()
- if wantOtpServer:
- distObj.setLocation(parentId, zoneId)
- distObj.updateRequiredOtherFields(dclass, di)
- # updateRequiredOtherFields calls announceGenerate
- else:
- # ...it is not in the dictionary or the cache.
- # Construct a new one
- classDef = dclass.getClassDef()
- if classDef == None:
- self.notify.error("Could not create an undefined %s object." % (dclass.getName()))
- distObj = classDef(self)
- distObj.dclass = dclass
- # Assign it an Id
- distObj.doId = doId
- # Put the new do in the dictionary
- self.doId2do[doId] = distObj
- # Update the required fields
- distObj.generateInit() # Only called when constructed
- distObj.generate()
- if wantOtpServer:
- distObj.setLocation(parentId, zoneId)
- distObj.updateRequiredOtherFields(dclass, di)
- # updateRequiredOtherFields calls announceGenerate
- return distObj
- def handleDisable(self, di):
- # Get the DO Id
- doId = di.getUint32()
- # disable it.
- self.disableDoId(doId)
- def disableDoId(self, doId):
- # Make sure the object exists
- if self.doId2do.has_key(doId):
- # Look up the object
- distObj = self.doId2do[doId]
- # remove the object from the dictionary
- del(self.doId2do[doId])
- # Only cache the object if it is a "cacheable" type
- # object; this way we don't clutter up the caches with
- # trivial objects that don't benefit from caching.
- if distObj.getCacheable():
- self.cache.cache(distObj)
- else:
- distObj.deleteOrDelay()
- else:
- ClientRepository.notify.warning(
- "Disable failed. DistObj "
- + str(doId) +
- " is not in dictionary")
- def handleDelete(self, di):
- # Get the DO Id
- doId = di.getUint32()
- self.deleteObject(doId)
- def deleteObject(self, doId):
- """
- Removes the object from the client's view of the world. This
- should normally not be called except in the case of error
- recovery, since the server will normally be responsible for
- deleting and disabling objects as they go out of scope.
- After this is called, future updates by server on this object
- will be ignored (with a warning message). The object will
- become valid again the next time the server sends a generate
- message for this doId.
- This is not a distributed message and does not delete the
- object on the server or on any other client.
- """
- if self.doId2do.has_key(doId):
- # If it is in the dictionary, remove it.
- obj = self.doId2do[doId]
- # Remove it from the dictionary
- del(self.doId2do[doId])
- # Disable, announce, and delete the object itself...
- # unless delayDelete is on...
- obj.deleteOrDelay()
- elif self.cache.contains(doId):
- # If it is in the cache, remove it.
- self.cache.delete(doId)
- else:
- # Otherwise, ignore it
- ClientRepository.notify.warning(
- "Asked to delete non-existent DistObj " + str(doId))
- def handleUpdateField(self, di):
- """
- This method is called when a CLIENT_OBJECT_UPDATE_FIELD
- message is received; it decodes the update, unpacks the
- arguments, and calls the corresponding method on the indicated
- DistributedObject.
- In fact, this method is exactly duplicated by the C++ method
- cConnectionRepository::handle_update_field(), which was
- written to optimize the message loop by handling all of the
- CLIENT_OBJECT_UPDATE_FIELD messages in C++. That means that
- nowadays, this Python method will probably never be called,
- since UPDATE_FIELD messages will not even be passed to the
- Python message handlers. But this method remains for
- documentation purposes, and also as a "just in case" handler
- in case we ever do come across a situation in the future in
- which python might handle the UPDATE_FIELD message.
- """
- # Get the DO Id
- doId = di.getUint32()
- #print("Updating " + str(doId))
- # Find the DO
- do = self.doId2do.get(doId)
- if do is not None:
- # Let the dclass finish the job
- do.dclass.receiveUpdate(do, di)
- else:
- ClientRepository.notify.warning(
- "Asked to update non-existent DistObj " + str(doId))
- def handleGoGetLost(self, di):
- # The server told us it's about to drop the connection on us.
- # Get ready!
- if (di.getRemainingSize() > 0):
- self.bootedIndex = di.getUint16()
- self.bootedText = di.getString()
- ClientRepository.notify.warning(
- "Server is booting us out (%d): %s" % (self.bootedIndex, self.bootedText))
- else:
- self.bootedIndex = None
- self.bootedText = None
- 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 handleSystemMessage(self, di):
- # Got a system message from the server.
- message = di.getString()
- self.notify.info('Message from server: %s' % (message))
- return message
- def handleSetDOIDrange(self, di):
- # This method is only used in conjunction with the CMU LAN
- # server.
- self.DOIDbase = di.getUint32()
- self.DOIDlast = self.DOIDbase + di.getUint32()
- self.DOIDnext = self.DOIDbase
- def handleRequestGenerates(self, di):
- # When new clients join the zone of an object, they need to hear
- # about it, so we send out all of our information about objects in
- # that particular zone.
- # This method is only used in conjunction with the CMU LAN
- # server.
- assert self.DOIDnext < self.DOIDlast
- zone = di.getUint32()
- for obj in self.doId2do.values():
- if obj.zone == zone:
- id = obj.doId
- if (self.isLocalId(id)):
- self.send(obj.dclass.clientFormatGenerate(obj, id, zone, []))
- def handleMessageType(self, msgType, di):
- if msgType == CLIENT_GO_GET_LOST:
- self.handleGoGetLost(di)
- elif msgType == CLIENT_HEARTBEAT:
- self.handleServerHeartbeat(di)
- elif msgType == CLIENT_SYSTEM_MESSAGE:
- self.handleSystemMessage(di)
- elif wantOtpServer:
- if msgType == CLIENT_CREATE_OBJECT_REQUIRED:
- self.handleGenerateWithRequired(di)
- elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER:
- self.handleGenerateWithRequiredOther(di)
- elif msgType == CLIENT_OBJECT_UPDATE_FIELD:
- self.handleUpdateField(di)
- elif msgType == CLIENT_OBJECT_DISABLE_RESP:
- self.handleDisable(di)
- elif msgType == CLIENT_OBJECT_DELETE_RESP:
- self.handleDelete(di)
- elif msgType == CLIENT_CREATE_OBJECT_REQUIRED:
- self.handleGenerateWithRequired(di)
- elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER:
- self.handleGenerateWithRequiredOther(di)
- elif msgType == CLIENT_DONE_INTEREST_RESP:
- self.handleInterestDoneMessage(di)
- elif msgType == CLIENT_QUERY_ONE_FIELD_RESP:
- self.handleQueryOneFieldResp(di)
- elif msgType == CLIENT_OBJECT_LOCATION:
- self.handleObjectLocation(di)
- else:
- currentLoginState = self.loginFSM.getCurrentState()
- if currentLoginState:
- currentLoginStateName = currentLoginState.getName()
- else:
- currentLoginStateName = "None"
- currentGameState = self.gameFSM.getCurrentState()
- if currentGameState:
- currentGameStateName = currentGameState.getName()
- else:
- currentGameStateName = "None"
- ClientRepository.notify.warning(
- "Ignoring unexpected message type: " +
- str(msgType) +
- " login state: " +
- currentLoginStateName +
- " game state: " +
- currentGameStateName)
- else:
- currentLoginState = self.loginFSM.getCurrentState()
- if currentLoginState:
- currentLoginStateName = currentLoginState.getName()
- else:
- currentLoginStateName = "None"
- currentGameState = self.gameFSM.getCurrentState()
- if currentGameState:
- currentGameStateName = currentGameState.getName()
- else:
- currentGameStateName = "None"
- ClientRepository.notify.warning(
- "Ignoring unexpected message type: " +
- str(msgType) +
- " login state: " +
- currentLoginStateName +
- " game state: " +
- currentGameStateName)
-
- def createWithRequired(self, className, zoneId = 0, optionalFields=None):
- # This method is only used in conjunction with the CMU LAN
- # server.
- if self.DOIDnext >= self.DOIDlast:
- self.notify.error(
- "Cannot allocate a distributed object ID: all IDs used up.")
- return None
- id = self.DOIDnext
- self.DOIDnext = self.DOIDnext + 1
- dclass = self.dclassesByName[className]
- classDef = dclass.getClassDef()
- if classDef == None:
- self.notify.error("Could not create an undefined %s object." % (
- dclass.getName()))
- obj = classDef(self)
- obj.dclass = dclass
- obj.zone = zoneId
- obj.doId = id
- self.doId2do[id] = obj
- obj.generateInit()
- obj.generate()
- obj.announceGenerate()
- datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields)
- self.send(datagram)
- return obj
- def sendDisableMsg(self, doId):
- # This method is only used in conjunction with the CMU LAN
- # server.
- datagram = PyDatagram()
- datagram.addUint16(CLIENT_OBJECT_DISABLE)
- datagram.addUint32(doId)
- self.send(datagram)
- def sendDeleteMsg(self, doId):
- # This method is only used in conjunction with the CMU LAN
- # server.
- datagram = PyDatagram()
- datagram.addUint16(CLIENT_OBJECT_DELETE)
- datagram.addUint32(doId)
- self.send(datagram)
- def sendRemoveZoneMsg(self, zoneId, visibleZoneList=None):
- # This method is only used in conjunction with the CMU LAN
- # server.
- datagram = PyDatagram()
- datagram.addUint16(CLIENT_REMOVE_ZONE)
- datagram.addUint32(zoneId)
- # if we have an explicit list of visible zones, add them
- if visibleZoneList is not None:
- vzl = list(visibleZoneList)
- vzl.sort()
- assert PythonUtil.uniqueElements(vzl)
- for zone in vzl:
- datagram.addUint32(zone)
- # send the message
- self.send(datagram)
- def sendSetShardMsg(self, shardId):
- datagram = PyDatagram()
- # Add message type
- datagram.addUint16(CLIENT_SET_SHARD)
- # Add shard id
- datagram.addUint32(shardId)
- # send the message
- self.send(datagram)
- def getObjectsOfClass(self, objClass):
- """ returns dict of doId:object, containing all objects
- that inherit from 'class'. returned dict is safely mutable. """
- doDict = {}
- for doId, do in self.doId2do.items():
- if isinstance(do, objClass):
- doDict[doId] = do
- return doDict
- def getObjectsOfExactClass(self, objClass):
- """ returns dict of doId:object, containing all objects that
- are exactly of type 'class' (neglecting inheritance). returned
- dict is safely mutable. """
- doDict = {}
- for doId, do in self.doId2do.items():
- if do.__class__ == objClass:
- doDict[doId] = do
- return doDict
- if wantOtpServer:
- def sendEmulateSetZone(self, zoneId, visibleZoneList=None,
- parentIdin=None, event=None):
- """
- This Will Move The avatar and set an interest to that location ..
- """
- parentId = parentIdin
- if parentId is None:
- parentId = base.localAvatar.defaultShard
- MyAvID = base.localAvatar.doId
- # move thwe avatar..
- self.sendSetLocation(MyAvID,parentId,zoneId)
- # move the interest..
- InterestZones = zoneId
- if visibleZoneList is not None:
- InterestZones = visibleZoneList
- if(self.old_setzone_interest_handle == None):
- self.old_setzone_interest_handle = self.addInterest(
- parentId, InterestZones, "OldSetZone Imulator", event)
- else:
- self.alterInterest(self.old_setzone_interest_handle,
- parentId, InterestZones, "OldSetZone Imulator", event)
- def sendEmulateSetZoneOff(self):
- MyAvID = base.localAvatar.doId
- self.sendSetLocation(MyAvID,0,0)
- if self.old_setzone_interest_handle is not None:
- self.removeInterest(self.old_setzone_interest_handle)
- self.old_setzone_interest_handle = None
- def sendSetLocation(self,doId,parentId,zoneId):
- datagram = PyDatagram()
- datagram.addUint16(CLIENT_OBJECT_LOCATION)
- datagram.addUint32(doId)
- datagram.addUint32(parentId)
- datagram.addUint32(zoneId)
- self.send(datagram)
- else:
- def sendSetZoneMsg(self, zoneId, visibleZoneList=None):
- datagram = PyDatagram()
- # Add message type
- datagram.addUint16(CLIENT_SET_ZONE)
- # Add zone id
- datagram.addUint32(zoneId)
- # if we have an explicit list of visible zones, add them
- if visibleZoneList is not None:
- vzl = list(visibleZoneList)
- vzl.sort()
- assert PythonUtil.uniqueElements(vzl)
- for zone in vzl:
- datagram.addUint32(zone)
- # send the message
- self.send(datagram)
- def handleDatagram(self, di):
- if self.notify.getDebug():
- print "ClientRepository received datagram:"
- di.getDatagram().dumpHex(ostream)
- msgType = self.getMsgType()
- if not wantOtpServer:
- if msgType == CLIENT_DONE_SET_ZONE_RESP:
- self.handleSetZoneDone()
- if self.handler == None:
- self.handleMessageType(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 publicServerDatagramHandler(self, msgType, di):
- # These are the sort of messages we may expect from the public
- # Panda server.
- if msgType == CLIENT_SET_DOID_RANGE:
- self.handleSetDOIDrange(di)
- elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_RESP:
- self.handleGenerateWithRequired(di)
- elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP:
- self.handleGenerateWithRequiredOther(di)
- elif msgType == CLIENT_OBJECT_UPDATE_FIELD_RESP:
- self.handleUpdateField(di)
- elif msgType == CLIENT_OBJECT_DELETE_RESP:
- self.handleDelete(di)
- elif msgType == CLIENT_OBJECT_DISABLE_RESP:
- self.handleDisable(di)
- elif msgType == CLIENT_REQUEST_GENERATES:
- self.handleRequestGenerates(di)
- else:
- self.handleMessageType(msgType, di)
- 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).
- self.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 sendUpdateZone(self, obj, zoneId):
- # This method is only used in conjunction with the CMU LAN
- # server.
- id = obj.doId
- assert(self.isLocalId(id))
- self.sendDeleteMsg(id, 1)
- obj.zone = zoneId
- self.send(obj.dclass.clientFormatGenerate(obj, id, zoneId, []))
- def replaceMethod(self, oldMethod, newFunction):
- return 0
- def isLocalId(self,id):
- return ((id >= self.DOIDbase) and (id < self.DOIDlast))
- def haveCreateAuthority(self):
- return (self.DOIDlast > self.DOIDnext)
- def getWorld(self, doId):
- # Get the world node for this object
- obj = self.doId2do[doId]
- worldNP = obj.getParent()
- while 1:
- nextNP = worldNP.getParent()
- if nextNP == render:
- break
- elif worldNP.isEmpty():
- return None
- return worldNP
-
-
-
|