381 lines
14 KiB
Python
381 lines
14 KiB
Python
"""
|
|
The Uber Distributed Obeject Globals server.
|
|
"""
|
|
|
|
from direct.directnotify.DirectNotifyGlobal import directNotify
|
|
from direct.fsm.ClassicFSM import ClassicFSM
|
|
from direct.fsm.State import State
|
|
from otp.otpbase import OTPGlobals
|
|
from otp.distributed import OtpDoGlobals
|
|
from otp.distributed.OtpDoGlobals import *
|
|
from otp.ai.AIRepository import AIRepository
|
|
from otp.ai import TimeManagerAI
|
|
from pandac.PandaModules import *
|
|
from otp.uberdog.AccountDetailRecord import AccountDetailRecord, SubDetailRecord
|
|
|
|
from otp.ai.AIMsgTypes import *
|
|
|
|
|
|
class UberDog(AIRepository):
|
|
notify = directNotify.newCategory("UberDog")
|
|
|
|
def __init__(
|
|
self, mdip, mdport, esip, esport, dcFileNames,
|
|
serverId, minChannel, maxChannel):
|
|
AIRepository.__init__(
|
|
self, mdip, mdport, esip, esport, dcFileNames,
|
|
serverId, minChannel, maxChannel, dcSuffix = 'UD')
|
|
|
|
# We're responsible for keeping track of who's online with which avatar
|
|
self.onlineAccountDetails = {}
|
|
self.onlineAvatars = {}
|
|
self.onlinePlayers = {}
|
|
|
|
self.pending={}
|
|
self.doId2doCache={}
|
|
|
|
if hasattr(self, 'setVerbose'):
|
|
if self.config.GetBool('verbose-uberrepository'):
|
|
self.setVerbose(1)
|
|
|
|
# The AI State machine
|
|
self.fsm = ClassicFSM(
|
|
'UberDog', [
|
|
State('off',
|
|
self.enterOff,
|
|
self.exitOff,
|
|
['connect']),
|
|
State('connect',
|
|
self.enterConnect,
|
|
self.exitConnect,
|
|
['noConnection', 'playGame',]),
|
|
State('playGame',
|
|
self.enterPlayGame,
|
|
self.exitPlayGame,
|
|
['noConnection']),
|
|
State('noConnection',
|
|
self.enterNoConnection,
|
|
self.exitNoConnection,
|
|
['connect'])],
|
|
# initial state
|
|
'off',
|
|
# final state
|
|
'off',
|
|
)
|
|
self.fsm.enterInitialState()
|
|
self.fsm.request("connect")
|
|
|
|
def _connected(self):
|
|
"""
|
|
Callback for when we successfully connect to the otp_server cluster.
|
|
"""
|
|
self.setConnectionName("UberDog")
|
|
AIRepository._connected(self)
|
|
# Listen for Account and Avatar online/offline messages
|
|
self.registerForChannel(CHANNEL_PUPPET_ACTION)
|
|
self.fsm.request("playGame")
|
|
|
|
def dispatchUpdateToDoId(self, dclassName, fieldName, doId, args, channelId=None):
|
|
# dispatch immediately to local object if it's local, otherwise send
|
|
# it over the wire
|
|
obj = self.doId2do.get(doId)
|
|
if obj is not None:
|
|
assert obj.__class__.__name__ == (dclassName + self.dcSuffix)
|
|
method = getattr(obj, fieldName)
|
|
apply(method, args)
|
|
else:
|
|
self.sendUpdateToDoId(dclassName, fieldName, doId, args, channelId)
|
|
|
|
def dispatchUpdateToGlobalDoId(self, dclassName, fieldName, doId, args):
|
|
# dispatch immediately to local object if it's local, otherwise send
|
|
# it over the wire
|
|
obj = self.doId2do.get(doId)
|
|
if obj is not None:
|
|
assert obj.__class__.__name__ == dclassName
|
|
method = getattr(obj, fieldName)
|
|
apply(method, args)
|
|
else:
|
|
self.sendUpdateToGlobalDoId(dclassName, fieldName, doId, args)
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def handleAccountUsage(self, di):
|
|
priorAccount = di.getUint32() # Historic - used only in __dev__ atm
|
|
newAccount = di.getUint32()
|
|
|
|
if priorAccount == 0 and newAccount == 0:
|
|
assert self.notify.debug("priorAccount==0 and newAccount==0, ignoring accountUsage message")
|
|
return
|
|
|
|
accountDetailRecord = AccountDetailRecord()
|
|
accountDetailRecord.openChatEnabled = (di.getString() == "YES")
|
|
accountDetailRecord.createFriendsWithChat = (di.getString() == "YES")
|
|
accountDetailRecord.chatCodeCreation = (di.getString() == "YES")
|
|
access = di.getString()
|
|
if access == "VELVET":
|
|
access = OTPGlobals.AccessVelvetRope
|
|
elif access == "FULL":
|
|
access = OTPGlobals.AccessFull
|
|
else:
|
|
access = OTPGlobals.AccessUnknown
|
|
accountDetailRecord.piratesAccess = access
|
|
accountDetailRecord.familyAccountId = di.getInt32()
|
|
accountDetailRecord.playerAccountId = di.getInt32()
|
|
accountDetailRecord.playerName = di.getString()
|
|
accountDetailRecord.playerNameApproved = di.getInt8()
|
|
accountDetailRecord.maxAvatars = di.getInt32()
|
|
accountDetailRecord.numFamilyMembers = di.getInt16()
|
|
accountDetailRecord.familyMembers = []
|
|
for i in range(accountDetailRecord.numFamilyMembers):
|
|
accountDetailRecord.familyMembers.append(di.getInt32())
|
|
|
|
logoutReason = di.getInt32()
|
|
|
|
# Now retrieve the subscription information
|
|
accountDetailRecord.numSubs = di.getUint16()
|
|
|
|
for i in range(accountDetailRecord.numSubs):
|
|
subDetailRecord = SubDetailRecord()
|
|
subDetailRecord.subId = di.getUint32()
|
|
subDetailRecord.subOwnerId = di.getUint32()
|
|
subDetailRecord.subName = di.getString()
|
|
subDetailRecord.subActive = di.getString()
|
|
access = di.getString()
|
|
if access == "VELVET":
|
|
access = OTPGlobals.AccessVelvetRope
|
|
elif access == "FULL":
|
|
access = OTPGlobals.AccessFull
|
|
else:
|
|
access = OTPGlobals.AccessUnknown
|
|
subDetailRecord.subAccess = access
|
|
subDetailRecord.subLevel = di.getUint8()
|
|
subDetailRecord.subNumAvatars = di.getUint8()
|
|
subDetailRecord.subNumConcur = di.getUint8()
|
|
subDetailRecord.subFounder = (di.getString() == "YES")
|
|
# Add this subscription to the dict on the account record
|
|
accountDetailRecord.subDetails[subDetailRecord.subId] = subDetailRecord
|
|
|
|
# How many avatar slots total do you get in this game?
|
|
accountDetailRecord.maxAvatarSlots = di.getInt8()
|
|
|
|
assert self.notify.debug("accountDetailRecord: %s" % accountDetailRecord)
|
|
|
|
if priorAccount:
|
|
# Send any previous account offline
|
|
self.accountOffline(priorAccount)
|
|
pass
|
|
|
|
if newAccount:
|
|
# Set up the new guy
|
|
self.accountOnline(newAccount, accountDetailRecord)
|
|
pass
|
|
pass
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def handleAvatarUsage(self, di):
|
|
priorAvatar = di.getUint32()
|
|
newAvatar = di.getUint32()
|
|
|
|
if priorAvatar == 0 and newAvatar == 0:
|
|
assert self.notify.debug("priorAvatar==0 and newAvatar==0, ignoring avatarUsage message")
|
|
return
|
|
|
|
newAvatarType = di.getUint16()
|
|
|
|
accountId = di.getUint32()
|
|
|
|
openChatEnabled = di.getString()
|
|
createFriendsWithChat = di.getString()
|
|
chatCodeCreation = di.getString()
|
|
piratesAccess = di.getString()
|
|
familyAccountId = di.getInt32()
|
|
playerAccountId = di.getInt32()
|
|
playerName = di.getString()
|
|
playerNameApproved = di.getInt8()
|
|
maxAvatars = di.getInt32()
|
|
numFamilyMembers = di.getInt16()
|
|
familyMembers = []
|
|
for i in range(numFamilyMembers):
|
|
familyMembers.append(di.getInt32())
|
|
|
|
if openChatEnabled == "YES":
|
|
openChatEnabled = 1
|
|
else:
|
|
openChatEnabled = 0
|
|
|
|
if priorAvatar:
|
|
# Send any previous avatar offline
|
|
self.avatarOffline(accountId, priorAvatar)
|
|
pass
|
|
|
|
if newAvatar:
|
|
# Set up the new guy
|
|
self.avatarOnline(newAvatar, newAvatarType,
|
|
playerAccountId,
|
|
playerName,
|
|
playerNameApproved,
|
|
openChatEnabled,
|
|
createFriendsWithChat,
|
|
chatCodeCreation)
|
|
pass
|
|
pass
|
|
|
|
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def accountOnline(self, accountId, accountDetailRecord):
|
|
self.writeServerEvent('accountOnline', accountId, '')
|
|
self.onlineAccountDetails[accountId] = accountDetailRecord
|
|
messenger.send('accountOnline', [accountId])
|
|
pass
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def accountOffline(self, accountId):
|
|
self.writeServerEvent('accountOffline', accountId, '')
|
|
self.onlineAccountDetails.pop(accountId, None)
|
|
self.onlinePlayers.pop(accountId, None)
|
|
messenger.send('accountOffline', [accountId])
|
|
pass
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def getAccountDetails(self, accountId):
|
|
return self.onlineAccountDetails.get(accountId)
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def isAccountOnline(self, accountId):
|
|
return accountId in self.onlineAccountDetails
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def isAvatarOnline(self, avatarId):
|
|
return avatarId in self.onlineAvatars
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def getAvatarAccountOnline(self, avatarId):
|
|
return self.onlineAvatars.get(avatarId, 0)
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def getAccountOnlineAvatar(self, accountId):
|
|
return self.onlinePlayers.get(accountId, 0)
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def checkAccountId(self, accountId):
|
|
if not accountId:
|
|
# SUSPICIOUS
|
|
self.notify.warning("Bogus accountId: %s" % accountId)
|
|
self.writeServerEvent('suspicious', accountId, 'bogus accountId in OtpAvatarManagerUD')
|
|
elif not self.isAccountOnline(accountId):
|
|
# SUSPICIOUS
|
|
self.notify.warning("Got request from account not online: %s" % accountId)
|
|
self.writeServerEvent('suspicious', accountId, 'request from offline account in OtpAvatarManagerUD')
|
|
else:
|
|
# Everything checks out
|
|
return True
|
|
return False
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def avatarOnline(self, avatarId, avatarType, accountId, playerName, playerNameApproved,
|
|
openChatEnabled, createFriendsWithChat, chatCodeCreation):
|
|
self.writeServerEvent('avatarOnline', avatarId, '%s|%s|%s|%s|%s|%s' % (
|
|
accountId, playerName, playerNameApproved, openChatEnabled,
|
|
createFriendsWithChat, chatCodeCreation))
|
|
|
|
self.onlineAvatars[avatarId] = accountId
|
|
self.onlinePlayers[accountId] = avatarId
|
|
|
|
simpleInfo = [avatarId, avatarType]
|
|
fullInfo = [avatarId,
|
|
accountId,
|
|
playerName,
|
|
playerNameApproved,
|
|
openChatEnabled,
|
|
createFriendsWithChat,
|
|
chatCodeCreation]
|
|
|
|
# necessary for local UD manager objects
|
|
messenger.send("avatarOnline", simpleInfo)
|
|
messenger.send("avatarOnlinePlusAccountInfo", fullInfo)
|
|
pass
|
|
|
|
@report(types = ['args'], dConfigParam = 'avatarmgr')
|
|
def avatarOffline(self, accountId, avatarId):
|
|
self.writeServerEvent('avatarOffline', avatarId, '')
|
|
|
|
self.onlinePlayers.pop(accountId, None)
|
|
self.onlineAvatars.pop(avatarId, None)
|
|
|
|
# necessary for local UD manager objects
|
|
messenger.send("avatarOffline", [avatarId])
|
|
pass
|
|
|
|
###################################
|
|
# Assumed Obsolete as of 6/29/09
|
|
#
|
|
# If you're reading this and there
|
|
# haven't been any strange UD crashes
|
|
# here lately, you can probably delete
|
|
# the next few functions.
|
|
###################################
|
|
def _addObject(self, context, distributedObject):
|
|
"""
|
|
Handle a new distributed object arriving by adding
|
|
it to the cache calling self.handleGotDo().
|
|
"""
|
|
assert False, 'JCW: Testing for obsolete functions. If this crashes, let Josh know'
|
|
doId=distributedObject.getDoId()
|
|
assert not self.doId2doCache.has_key(doId)
|
|
if not self.doId2doCache.has_key(doId):
|
|
self.doId2doCache[doId]=distributedObject
|
|
self.handleGotDo(distributedObject)
|
|
|
|
def handleGotDo(self, distributedObject):
|
|
"""
|
|
This allows derived classes to override the handling
|
|
of new distributed objects arriving in the cache.
|
|
By default, this will loop through the pending calls
|
|
for that object and make the function calls. It
|
|
will also remove the handled calls from the pending set.
|
|
"""
|
|
assert False, 'JCW: Testing for obsolete functions. If this crashes, let Josh know'
|
|
assert self.doId2doCache.has_key(doId)
|
|
pending=self.pending.get(doId)
|
|
if pending is not None:
|
|
del self.pending[doId]
|
|
for i in pending:
|
|
apply(i[0], i[2])
|
|
|
|
def deleteObject(self, doId):
|
|
"""
|
|
Ask for the object to be removed from the private
|
|
distributed object cache.
|
|
"""
|
|
assert False, 'JCW: Testing for obsolete functions. If this crashes, let Josh know'
|
|
if self.doId2doCache.had_key(doId):
|
|
self.unregisterForChannel(doId)
|
|
#self.deleteObject(doId)
|
|
del self.doId2doCache[doId]
|
|
#HACK:
|
|
self.unregisterForChannel(doId)
|
|
AIRepository.deleteObject(self.doId)
|
|
|
|
def uniqueName(self, desc):
|
|
return desc
|
|
|
|
if __dev__:
|
|
"""
|
|
Early warning system for unsupported use of the Uberdog Repository
|
|
"""
|
|
def deleteObjects(self):
|
|
assert 0
|
|
|
|
def createDistrict(self, districtId, districtName):
|
|
assert 0
|
|
|
|
def deleteDistrict(self, districtId):
|
|
assert 0
|
|
|
|
def enterDistrictReset(self):
|
|
assert 0
|
|
|
|
def exitDistrictReset(self):
|
|
assert 0
|
|
|