Poodletooth-iLand/toontown/friends/TTUFriendsManagerUD.py
2015-03-03 17:10:12 -05:00

422 lines
16 KiB
Python

from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD
from direct.distributed.PyDatagram import *
from direct.task import Task
from direct.directnotify.DirectNotifyGlobal import directNotify
import string
import random
import functools
import time
from direct.fsm.FSM import FSM
# -- FSMS --
class OperationFSM(FSM):
def __init__(self, mgr, air, senderAvId, targetAvId=None, callback=None):
FSM.__init__(self, 'OperationFSM-%s' % senderAvId)
self.mgr = mgr
self.air = air
self.sender = senderAvId
self.result = None
self.target = targetAvId
self.callback = callback
def enterOff(self):
if self.callback:
if self.result is not None:
self.callback(self.sender, self.result)
else:
self.callback()
if self in self.mgr.operations:
self.mgr.operations.remove(self)
def enterError(self, message=None):
self.mgr.notify.warning("An error has occurred in a '%s'. Message: %s" %
(type(self).__name__, message) )
if self.sender in self.mgr.operations:
del self.mgr.operations[self.sender]
# -- Friends list --
class FriendsListOperation(OperationFSM):
def enterStart(self):
self.air.dbInterface.queryObject(self.air.dbId, self.sender,
self.handleRetrieveSender)
def handleRetrieveSender(self, dclass, fields):
if dclass != self.air.dclassesByName['DistributedToonUD']:
self.demand('Error', 'Distributed Class was not a Toon.')
return
self.demand('Retrieved', fields['setFriendsList'][0])
def enterRetrieved(self, friendsList):
self.friendsList = friendsList
if len(self.friendsList) <= 0:
self.result = []
self.demand('Off')
return
self.friendIndex = 0
self.realFriendsList = []
self.air.dbInterface.queryObject(self.air.dbId, self.friendsList[0][0],
self.addFriend)
def addFriend(self, dclass, fields):
if dclass != self.air.dclassesByName['DistributedToonUD']:
self.demand('Error', 'Friend was not a Toon')
return
friendId = self.friendsList[self.friendIndex][0]
self.realFriendsList.append([friendId, fields['setName'][0],
fields['setDNAString'][0], fields['setPetId'][0]])
if len(self.realFriendsList) >= len(self.friendsList):
self.result = self.realFriendsList
self.demand('Off')
return
self.friendIndex += 1
self.air.dbInterface.queryObject(self.air.dbId,
self.friendsList[self.friendIndex][0], self.addFriend)
# -- Remove Friends --
class RemoveFriendOperation(OperationFSM):
def __init__(self, mgr, air, senderAvId, targetAvId=None, callback=None, alert=False):
OperationFSM.__init__(self, mgr, air, senderAvId, targetAvId, callback)
self.alert = alert
def enterStart(self):
self.air.dbInterface.queryObject(self.air.dbId, self.sender,
self.handleRetrieve)
def handleRetrieve(self, dclass, fields):
if dclass != self.air.dclassesByName['DistributedToonUD']:
self.demand('Error', 'Distributed Class was not a Toon.')
return
self.demand('Retrieved', fields['setFriendsList'][0])
def enterRetrieved(self, friendsList):
newList = []
for i in xrange(len(friendsList)):
if friendsList[i][0] == self.target:
continue
newList.append(friendsList[i])
if self.sender in self.mgr.onlineToons:
dg = self.air.dclassesByName['DistributedToonUD'].aiFormatUpdate(
'setFriendsList', self.sender, self.sender,
self.air.ourChannel, [newList])
self.air.send(dg)
if self.alert:
dg = self.air.dclassesByName['DistributedToonUD'].aiFormatUpdate(
'friendsNotify', self.sender, self.sender,
self.air.ourChannel, [self.target, 1])
self.air.send(dg)
self.demand('Off')
return
self.air.dbInterface.updateObject(self.air.dbId, self.sender,
self.air.dclassesByName['DistributedToonUD'],
{'setFriendsList' : [newList]})
self.demand('Off')
# -- Avatar Details --
class FriendDetailsOperation(OperationFSM):
def __init__(self, mgr, air, senderAvId, targetAvId=None, callback=None, friendIds=None):
OperationFSM.__init__(self, mgr, air, senderAvId, targetAvId, callback)
self.friendIds = friendIds
def enterStart(self):
self.air.dbInterface.queryObject(self.air.dbId, self.sender,
self.handleRetrieve)
def handleRetrieve(self, dclass, fields):
if dclass != self.air.dclassesByName['DistributedToonUD']:
self.demand('Error', 'Distributed Class was not a Toon.')
return
self.demand('Retrieved', fields['setFriendsList'][0])
def enterRetrieved(self, friendsList):
self.currId = 0
for id in self.friendIds:
for friend in friendsList:
if friend[0] == id:
self.currId = id
self.air.dbInterface.queryObject(self.air.dbId, id,
self.handleFriend)
break
self.demand('Off')
def handleFriend(self, dclass, fields):
if dclass != self.air.dclassesByName['DistributedToonUD']:
self.demand('Error', 'Distributed Class was not a Toon.')
return
name = fields['setName'][0]
dna = fields['setDNAString'][0]
petId = fields['setPetId'][0]
self.mgr.sendUpdateToAvatarId(self.sender, 'friendInfo',
[self.currId, name, dna, petId])
# -- Clear List --
class ClearListOperation(OperationFSM):
def enterStart(self):
self.air.dbInterface.queryObject(self.air.dbId, self.sender,
self.handleRetrieved)
def handleRetrieved(self, dclass, fields):
if dclass != self.air.dclassesByName['DistributedToonUD']:
self.demand('Error', 'Distributed Class was not a Toon.')
return
self.demand('Retrieved', fields['setFriendsList'][0])
def enterRetrieved(self, friendsList):
for friend in friendsList:
newOperation = RemoveFriendOperation(self.mgr, self.air, friend[0],
targetAvId=self.sender, alert=True)
self.mgr.operations.append(newOperation)
newOperation.demand('Start')
self.demand('Off')
# -- FriendsManager --
class TTUFriendsManagerUD(DistributedObjectGlobalUD):
notify = directNotify.newCategory('TTUFriendsManagerUD')
def announceGenerate(self):
DistributedObjectGlobalUD.announceGenerate(self)
self.onlineToons = []
self.tpRequests = {}
self.whisperRequests = {}
self.operations = []
self.secret2avId = {}
self.delayTime = 1.0
# -- Friends list --
def requestFriendsList(self):
avId = self.air.getAvatarIdFromSender()
newOperation = FriendsListOperation(self, self.air, avId,
callback = self.sendFriendsList)
self.operations.append(newOperation)
newOperation.demand('Start')
def sendFriendsList(self, sender, friendsList):
self.sendUpdateToAvatarId(sender, 'friendList', [friendsList])
if sender not in self.onlineToons:
self.toonOnline(sender, friendsList)
# -- Remove Friend --
def removeFriend(self, friendId):
avId = self.air.getAvatarIdFromSender()
# Sender remove Friend
newOperation = RemoveFriendOperation(self, self.air, avId, friendId)
self.operations.append(newOperation)
newOperation.demand('Start')
# Friend remove Sender
newOperation = RemoveFriendOperation(self, self.air, friendId, avId,
alert=True)
self.operations.append(newOperation)
newOperation.demand('Start')
# -- Avatar Info --
def requestAvatarInfo(self, friendIdList):
avId = self.air.getAvatarIdFromSender()
newOperation = FriendDetailsOperation(self, self.air, avId,
friendIds = friendIdList)
self.operations.append(newOperation)
newOperation.demand('Start')
def getAvatarDetails(self, avId):
senderId = self.air.getAvatarIdFromSender()
def handleToon(dclass, fields):
if dclass != self.air.dclassesByName['DistributedToonUD']:
return
inventory = fields['setInventory'][0]
trackAccess = fields['setTrackAccess'][0]
trophies = 0 # fields['setTrophyScore'][0] is not db
hp = fields['setHp'][0]
maxHp = fields['setMaxHp'][0]
defaultShard = fields['setDefaultShard'][0]
lastHood = fields['setLastHood'][0]
dnaString = fields['setDNAString'][0]
experience = fields['setExperience'][0]
trackBonusLevel = fields['setTrackBonusLevel'][0]
# We need an actual way to send the fields to the client...............
# Inventory, trackAccess, trophies, Hp, maxHp, defaultshard, lastHood, dnastring
self.sendUpdateToAvatarId(senderId, 'friendDetails', [avId, inventory, trackAccess, trophies, hp, maxHp, defaultShard, lastHood, dnaString, experience, trackBonusLevel])
self.air.dbInterface.queryObject(self.air.dbId, avId, handleToon)
# -- Toon Online/Offline --
def toonOnline(self, doId, friendsList):
self.onlineToons.append(doId)
channel = self.GetPuppetConnectionChannel(doId)
dgcleanup = self.dclass.aiFormatUpdate('goingOffline', self.doId, self.doId, self.air.ourChannel, [doId])
dg = PyDatagram()
dg.addServerHeader(channel, self.air.ourChannel, CLIENTAGENT_ADD_POST_REMOVE)
dg.addString(dgcleanup.getMessage())
self.air.send(dg)
for friend in friendsList:
friendId = friend[0]
if friend[0] in self.onlineToons:
self.sendUpdateToAvatarId(doId, 'friendOnline', [friendId, 0, 0])
self.sendUpdateToAvatarId(friendId, 'friendOnline', [doId, 0, 0])
def goingOffline(self, avId):
self.toonOffline(avId)
def toonOffline(self, doId):
if doId not in self.onlineToons:
return
def handleToon(dclass, fields):
if dclass != self.air.dclassesByName['DistributedToonUD']:
return
friendsList = fields['setFriendsList'][0]
for friend in friendsList:
friendId = friend[0]
if friendId in self.onlineToons:
self.sendUpdateToAvatarId(friendId, 'friendOffline', [doId])
if doId in self.onlineToons:
self.onlineToons.remove(doId)
self.air.dbInterface.queryObject(self.air.dbId, doId, handleToon)
# -- Clear List --
def clearList(self, doId):
newOperation = ClearListOperation(self, self.air, doId)
self.operations.append(newOperation)
newOperation.demand('Start')
# -- Teleport and Whispers --
def routeTeleportQuery(self, toId):
fromId = self.air.getAvatarIdFromSender()
if fromId in self.tpRequests.values():
return
self.tpRequests[fromId] = toId
self.sendUpdateToAvatarId(toId, 'teleportQuery', [fromId])
taskMgr.doMethodLater(5, self.giveUpTeleportQuery, 'tp-query-timeout-%d' % fromId, extraArgs=[fromId, toId])
def giveUpTeleportQuery(self, fromId, toId):
# The client didn't respond to the query within the set time,
# So we will tell the query sender that the toon is unavailable.
if fromId in self.tpRequests:
del self.tpRequests[fromId]
self.sendUpdateToAvatarId(fromId, 'setTeleportResponse', [toId, 0, 0, 0, 0])
self.notify.warning('Teleport request that was sent by %d to %d timed out.' % (fromId, toId))
def teleportResponse(self, toId, available, shardId, hoodId, zoneId):
# Here is where the toId and fromId swap (because we are now sending it back)
fromId = self.air.getAvatarIdFromSender()
# We got the query response, so no need to give up!
if taskMgr.hasTaskNamed('tp-query-timeout-%d' % toId):
taskMgr.remove('tp-query-timeout-%d' % toId)
if toId not in self.tpRequests:
return
if self.tpRequests.get(toId) != fromId:
self.air.writeServerEvent('suspicious', fromId, 'toon tried to send teleportResponse for a query that isn\'t theirs!')
return
self.sendUpdateToAvatarId(toId, 'setTeleportResponse', [fromId, available, shardId, hoodId, zoneId])
del self.tpRequests[toId]
def whisperSCTo(self, toId, msgIndex):
fromId = self.air.getAvatarIdFromSender()
currStamp = time.time()
if fromId in self.whisperRequests:
elapsed = currStamp - self.whisperRequests[fromId]
if elapsed < self.delayTime:
self.whisperRequests[fromId] = currStamp
return
self.whisperRequests[fromId] = currStamp
self.sendUpdateToAvatarId(toId, 'setWhisperSCFrom', [fromId, msgIndex])
def whisperSCCustomTo(self, toId, msgIndex):
fromId = self.air.getAvatarIdFromSender()
currStamp = time.time()
if fromId in self.whisperRequests:
elapsed = currStamp - self.whisperRequests[fromId]
if elapsed < self.delayTime:
self.whisperRequests[fromId] = currStamp
return
self.whisperRequests[fromId] = currStamp
self.sendUpdateToAvatarId(toId, 'setWhisperSCCustomFrom', [fromId, msgIndex])
def whisperSCEmoteTo(self, toId, msgIndex):
fromId = self.air.getAvatarIdFromSender()
currStamp = time.time()
if fromId in self.whisperRequests:
elapsed = currStamp - self.whisperRequests[fromId]
if elapsed < self.delayTime:
self.whisperRequests[fromId] = currStamp
return
self.whisperRequests[fromId] = currStamp
self.sendUpdateToAvatarId(toId, 'setWhisperSCEmoteFrom', [fromId, msgIndex])
def sendTalkWhisper(self, toId, message):
fromId = self.air.getAvatarIdFromSender()
currStamp = time.time()
if fromId in self.whisperRequests:
elapsed = currStamp - self.whisperRequests[fromId]
if elapsed < self.delayTime:
self.whisperRequests[fromId] = currStamp
return
self.whisperRequests[fromId] = currStamp
self.sendUpdateToAvatarId(toId, 'receiveTalkWhisper', [fromId, message])
self.air.writeServerEvent('whisper-said', fromId, toId, message)
# -- Secret Friends --
def requestSecret(self):
avId = self.air.getAvatarIdFromSender()
allowed = string.lowercase + string.digits
secret = ''
for i in xrange(6):
secret += random.choice(allowed)
if i == 2:
secret += ' '
self.secret2avId[secret] = avId
self.sendUpdateToAvatarId(avId, 'requestSecretResponse', [1, secret])
def submitSecret(self, secret):
requester = self.air.getAvatarIdFromSender()
owner = self.secret2avId.get(secret)
if not owner:
self.sendUpdateToAvatarId(requester, 'submitSecretResponse', [0, 0])
return
if owner == requester:
del self.secret2avId[secret]
self.sendUpdateToAvatarId(requester, 'submitSecretResponse', [3, 0])
return
self.sendUpdateToAvatarId(requester, 'submitSecretResponse', [5, 0])
# -- Routes --
def battleSOS(self, toId):
requester = self.air.getAvatarIdFromSender()
self.sendUpdateToAvatarId(toId, 'setBattleSOS', [requester])
def teleportGiveup(self, toId):
requester = self.air.getAvatarIdFromSender()
self.sendUpdateToAvatarId(toId, 'setTeleportGiveup', [requester])
def whisperSCToontaskTo(self, toId, taskId, toNpcId, toonProgress, msgIndex):
requester = self.air.getAvatarIdFromSender()
self.sendUpdateToAvatarId(toId, 'setWhisperSCToontaskFrom', [requester,
taskId, toNpcId, toonProgress, msgIndex]
)
def sleepAutoReply(self, toId):
requester = self.air.getAvatarIdFromSender()
self.sendUpdateToAvatarId(toId, 'setSleepAutoReply', [requester])