152 lines
6.9 KiB
Python
152 lines
6.9 KiB
Python
from .AIBaseGlobal import *
|
|
from pandac.PandaModules import *
|
|
from direct.distributed.ClockDelta import *
|
|
from direct.task import Task
|
|
from direct.distributed import DistributedObjectAI
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from direct.showbase import GarbageReport
|
|
from otp.otpbase import OTPGlobals
|
|
from otp.ai.GarbageLeakServerEventAggregatorAI import GarbageLeakServerEventAggregatorAI
|
|
import time
|
|
|
|
class TimeManagerAI(DistributedObjectAI.DistributedObjectAI):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory("TimeManagerAI")
|
|
|
|
def __init__(self, air):
|
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
|
if not __dev__:
|
|
# double-check that we're not implementing a client-sendable debug DC method in production
|
|
if hasattr(self, 'checkForGarbageLeaks'):
|
|
self.notify.error('checkForGarbageLeaks should not be defined outside of __dev__')
|
|
|
|
def requestServerTime(self, context):
|
|
"""requestServerTime(self, int8 context)
|
|
|
|
This message is sent from the client to the AI to initiate a
|
|
synchronization phase. The AI should immediately report back
|
|
with its current time. The client will then measure the round
|
|
trip.
|
|
"""
|
|
timestamp = globalClockDelta.getRealNetworkTime(bits=32)
|
|
requesterId = self.air.getAvatarIdFromSender()
|
|
timeOfDay = int(time.time())
|
|
self.sendUpdateToAvatarId(requesterId, "serverTime",
|
|
[context, timestamp, timeOfDay])
|
|
|
|
def setDisconnectReason(self, disconnectCode):
|
|
"""setDisconnectReason(self, uint8 disconnectCode)
|
|
|
|
This method is called by the client just before it leaves a
|
|
shard to alert the AI as to the reason it's going. If the AI
|
|
doesn't get this message, it can assume the client aborted
|
|
messily or its internet connection was dropped.
|
|
"""
|
|
requesterId = self.air.getAvatarIdFromSender()
|
|
self.notify.info("Client %s leaving for reason %s (%s)." % (
|
|
requesterId, disconnectCode,
|
|
OTPGlobals.DisconnectReasons.get(disconnectCode,
|
|
'invalid reason')))
|
|
|
|
if disconnectCode in OTPGlobals.DisconnectReasons:
|
|
self.air.setAvatarDisconnectReason(requesterId, disconnectCode)
|
|
else:
|
|
self.air.writeServerEvent(
|
|
'suspicious', requesterId, 'invalid disconnect reason: %s' % disconnectCode)
|
|
|
|
def setExceptionInfo(self, info):
|
|
"""setExceptionInfo(self, string info)
|
|
|
|
In the case of the client leaving for a Python exception, we
|
|
also follow up the above message with this one, which just
|
|
sends a text string describing the exception for the AI log.
|
|
"""
|
|
requesterId = self.air.getAvatarIdFromSender()
|
|
self.notify.info("Client %s exception: %s" % (requesterId, info))
|
|
serverVersion = simbase.config.GetString('server-version','')
|
|
self.air.writeServerEvent('client-exception', requesterId, '%s|%s' % (serverVersion,info))
|
|
|
|
def setSignature(self, signature, hash, pyc):
|
|
"""
|
|
This method is called by the client at startup time, to send
|
|
the xrc signature and the prc hash to the AI for logging in
|
|
case the client does anything suspicious.
|
|
"""
|
|
if signature:
|
|
requesterId = self.air.getAvatarIdFromSender()
|
|
prcHash = HashVal()
|
|
prcHash.setFromBin(hash)
|
|
info = '%s|%s' % (signature, prcHash.asHex())
|
|
self.notify.info('Client %s signature: %s' % (requesterId, info))
|
|
self.air.writeServerEvent('client-signature', requesterId, info)
|
|
|
|
pycHash = HashVal()
|
|
pycHash.setFromBin(pyc)
|
|
if pycHash != HashVal():
|
|
info = pycHash.asHex()
|
|
self.notify.info('Client %s py signature: %s' % (requesterId, info))
|
|
self.air.writeServerEvent('client-py-signature', requesterId, info)
|
|
|
|
def setCpuInfo(self, info, cacheStatus):
|
|
"""
|
|
This method is called by the client at startup time, to send
|
|
the detailed CPU information to the server for logging.
|
|
"""
|
|
requesterId = self.air.getAvatarIdFromSender()
|
|
|
|
self.notify.info('client-cpu %s|%s' % (requesterId, info))
|
|
self.air.writeServerEvent('client-cpu', requesterId, info)
|
|
# We call this cacheStatus, but really it's the mac address or
|
|
# other client fingerprint information, in a simple
|
|
# obfuscating cipher. Decode it.
|
|
key = 'outrageous'
|
|
p = 0
|
|
fingerprint = ''
|
|
for ch in cacheStatus:
|
|
ic = ord(ch) ^ ord(key[p])
|
|
p += 1
|
|
if p >= len(key):
|
|
p = 0
|
|
fingerprint += chr(ic)
|
|
|
|
self.notify.info('client-fingerprint %s|%s' % (requesterId, fingerprint))
|
|
self.air.writeServerEvent('client-fingerprint', requesterId, fingerprint)
|
|
if hasattr(self.air, 'cpuInfoMgr'):
|
|
self.air.cpuInfoMgr.sendCpuInfoToUd(info, fingerprint)
|
|
|
|
|
|
def setFrameRate(self, fps, deviation, numAvs,
|
|
locationCode, timeInLocation, timeInGame,
|
|
gameOptionsCode, vendorId, deviceId,
|
|
processMemory, pageFileUsage, physicalMemory,
|
|
pageFaultCount, osInfo, cpuSpeed,
|
|
numCpuCores, numLogicalCpus, apiName):
|
|
""" This method is called by the client at the interval
|
|
specified by getFrameRateInterval(), to report its current
|
|
frame rate. """
|
|
|
|
requesterId = self.air.getAvatarIdFromSender()
|
|
info = '%0.1f fps|%0.3fd|%s avs|%s|%d|%d|%s|0x%04x|0x%04x|%0.1fMB|%0.1fMB|%0.1fMB|%d|%s|%s|%s cpus|%s' % (
|
|
fps, deviation, numAvs, locationCode, timeInLocation,
|
|
timeInGame, gameOptionsCode,
|
|
vendorId, deviceId, processMemory, pageFileUsage, physicalMemory,
|
|
pageFaultCount, '%s.%d.%d.%d' % osInfo, '%0.03f,%0.03f' % cpuSpeed,
|
|
'%d,%d' % (numCpuCores, numLogicalCpus),
|
|
apiName)
|
|
self.notify.info('client-fps %s|%s' % (requesterId, info))
|
|
self.air.writeServerEvent('client-fps', requesterId, info)
|
|
|
|
if __dev__:
|
|
def checkForGarbageLeaks(self, wantReply):
|
|
senderId = self.air.getAvatarIdFromSender()
|
|
self.notify.info("checking for garbage leaks requested by %s" % senderId)
|
|
# okay checking for garbage leaks should only be done by devs, it's rare enough i'll flag it
|
|
# as suspicious
|
|
self.air.writeServerEvent('suspicious', senderId, 'checkForGarbageLeaks')
|
|
numLeaks = GarbageReport.checkForGarbageLeaks()
|
|
if wantReply:
|
|
requesterId = self.air.getAvatarIdFromSender()
|
|
self.sendUpdateToAvatarId(requesterId, 'setNumAIGarbageLeaks', [numLeaks])
|
|
|
|
def setClientGarbageLeak(self, num, description):
|
|
messenger.send(GarbageLeakServerEventAggregatorAI.ClientLeakEvent, [num, description])
|