toontown-just-works/toontown/parties/GlobalPartyManagerUD.py

253 lines
12 KiB
Python
Raw Permalink Normal View History

2024-07-07 18:08:39 -05:00
from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD
from direct.distributed.PyDatagram import *
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.task import Task
from PartyGlobals import *
from datetime import datetime, timedelta
from panda3d.core import *
class GlobalPartyManagerUD(DistributedObjectGlobalUD):
notify = directNotify.newCategory('GlobalPartyManagerUD')
# This uberdog MUST be up before the AIs, as AIs talk to this UD
def announceGenerate(self):
DistributedObjectGlobalUD.announceGenerate(self)
self.notify.debug("GPMUD generated")
self.senders2Mgrs = {}
self.host2PartyId = {} # just a reference mapping
self.id2Party = {} # This should be replaced with a longterm datastore
self.party2PubInfo = {} # This should not be longterm
self.tempSlots = {}
PARTY_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
startTime = datetime.strptime('2014-01-20 11:50:00', PARTY_TIME_FORMAT)
endTime = datetime.strptime('2014-01-20 12:20:00', PARTY_TIME_FORMAT)
self.nextId = 0
#self.host2Party[100000001] = {'hostId': 100000001, 'start': startTime, 'end': endTime, 'partyId': 1717986918400000, 'decorations': [[3,5,7,6]], 'activities': [[10,13,6,18],[7,8,7,0]],'inviteTheme':1,'isPrivate':0,'inviteeIds':[]}
config = getConfigShowbase()
self.wantInstantParties = config.GetBool('want-instant-parties', 0)
# Setup tasks
self.runAtNextInterval()
# GPMUD -> PartyManagerAI messaging
def _makeAIMsg(self, field, values, recipient):
return self.air.dclassesByName['DistributedPartyManagerUD'].getFieldByName(field).aiFormatUpdate(recipient, recipient, simbase.air.ourChannel, values)
def sendToAI(self, field, values, sender=None):
if not sender:
sender = self.air.getAvatarIdFromSender()
dg = self._makeAIMsg(field, values, self.senders2Mgrs.get(sender, sender + 8))
self.air.send(dg)
# GPMUD -> toon messaging
def _makeAvMsg(self, field, values, recipient):
return self.air.dclassesByName['DistributedToonUD'].getFieldByName(field).aiFormatUpdate(recipient, recipient, simbase.air.ourChannel, values)
def sendToAv(self, avId, field, values):
dg = self._makeAvMsg(field, values, avId)
self.air.send(dg)
# Task stuff
def runAtNextInterval(self):
now = datetime.now()
howLongUntilAFive = (60 - now.second) + 60 * (4 - (now.minute % 5))
taskMgr.doMethodLater(howLongUntilAFive, self.__checkPartyStarts, 'GlobalPartyManager_checkStarts')
def canPartyStart(self, party):
now = datetime.now()
delta = timedelta(minutes=15)
endStartable = party['start'] + delta
if self.wantInstantParties:
return True
else:
return party['start'] < now# and endStartable > now
def isTooLate(self, party):
now = datetime.now()
delta = timedelta(minutes=15)
endStartable = party['start'] + delta
return endStartable > now
def __checkPartyStarts(self, task):
now = datetime.now()
for partyId in self.id2Party:
party = self.id2Party[partyId]
hostId = party['hostId']
if self.canPartyStart(party) and party['status'] == PartyStatus.Pending:
# Time to start party
party['status'] = PartyStatus.CanStart
self.sendToAv(hostId, 'setHostedParties', [[self._formatParty(party)]])
self.sendToAv(hostId, 'setPartyCanStart', [partyId])
elif self.isTooLate(party):
party['status'] = PartyStatus.NeverStarted
self.sendToAv(hostId, 'setHostedParties', [[self._formatParty(party)]])
self.runAtNextInterval()
# Format a party dict into a party struct suitable for the wire
def _formatParty(self, partyDict):
start = partyDict['start']
end = partyDict['end']
return [partyDict['partyId'],
partyDict['hostId'],
start.year,
start.month,
start.day,
start.hour,
start.minute,
end.year,
end.month,
end.day,
end.hour,
end.minute,
partyDict['isPrivate'],
partyDict['inviteTheme'],
partyDict['activities'],
partyDict['decorations'],
partyDict.get('status', PartyStatus.Pending)]
# Avatar joined the game, invoked by the CSMUD
def avatarJoined(self, avId):
# self.host2PartyId[avId] = (1337 << 32) + 10000
partyId = self.host2PartyId.get(avId, None)
if partyId:
party = self.id2Party.get(partyId, None)
if not party:
return # There's a partyId without an actual party?? What is this madness.
self.sendToAv(avId, 'setHostedParties', [[self._formatParty(party)]])
if partyId not in self.party2PubInfo and self.canPartyStart(party):
# The party hasn't started and it can start
self.sendToAv(avId, 'setPartyCanStart', [partyId])
# uberdog coordination of public party info
def __updatePartyInfo(self, partyId):
# Update all the AIs about this public party
party = self.party2PubInfo[partyId]
for sender in self.senders2Mgrs.keys():
actIds = []
for activity in self.id2Party[partyId]['activities']:
actIds.append(activity[0]) # First part of activity tuple should be actId
minLeft = int((PARTY_DURATION - (datetime.now() - party['started']).seconds) / 60)
self.sendToAI('updateToPublicPartyInfoUdToAllAi', [party['shardId'], party['zoneId'], partyId, self.id2Party[partyId]['hostId'], party['numGuests'], party['maxGuests'], party['hostName'], actIds, minLeft], sender=sender)
def __updatePartyCount(self, partyId):
# Update the party guest count
for sender in self.senders2Mgrs.keys():
self.sendToAI('updateToPublicPartyCountUdToAllAi', [self.party2PubInfo[partyId]['numGuests'], partyId], sender=sender)
def partyHasStarted(self, partyId, shardId, zoneId, hostName):
self.party2PubInfo[partyId] = {'partyId': partyId, 'shardId': shardId, 'zoneId': zoneId, 'hostName': hostName, 'numGuests': 0, 'maxGuests': MaxToonsAtAParty, 'started': datetime.now()}
self.__updatePartyInfo(partyId)
# update the host's book
if partyId not in self.id2Party:
self.notify.warning("didn't find details for starting party id %s hosted by %s" % (partyId, hostName))
return
self.id2Party[partyId]['status'] = PartyStatus.Started
party = self.id2Party.get(partyId, None)
self.sendToAv(party['hostId'], 'setHostedParties', [[self._formatParty(party)]])
def partyDone(self, partyId):
del self.party2PubInfo[partyId]
self.id2Party[partyId]['status'] = PartyStatus.Finished
party = self.id2Party.get(partyId, None)
self.sendToAv(party['hostId'], 'setHostedParties', [[self._formatParty(party)]])
del self.id2Party[partyId]
self.air.writeServerEvent('party-done', '%s')
def toonJoinedParty(self, partyId, avId):
if avId in self.tempSlots:
del self.tempSlots[avId]
return
self.party2PubInfo.get(partyId, {'numGuests': 0})['numGuests'] += 1
self.__updatePartyCount(partyId)
def toonLeftParty(self, partyId, avId):
self.party2PubInfo.get(partyId, {'numGuests': 0})['numGuests'] -= 1
self.__updatePartyCount(partyId)
def partyManagerAIHello(self, channel):
# Upon AI boot, DistributedPartyManagerAIs are supposed to say hello.
# They send along the DPMAI's doId as well, so that I can talk to them later.
print 'AI with base channel %s, will send replies to DPM %s' % (simbase.air.getAvatarIdFromSender(), channel)
self.senders2Mgrs[simbase.air.getAvatarIdFromSender()] = channel
self.sendToAI('partyManagerUdStartingUp', [])
# In addition, set up a postRemove where we inform this AI that the UD has died
self.air.addPostRemove(self._makeAIMsg('partyManagerUdLost', [], channel))
def addParty(self, avId, partyId, start, end, isPrivate, inviteTheme, activities, decorations, inviteeIds):
PARTY_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
print 'start time: %s' % start
startTime = datetime.strptime(start, PARTY_TIME_FORMAT)
endTime = datetime.strptime(end, PARTY_TIME_FORMAT)
print 'start year: %s' % startTime.year
if avId in self.host2PartyId:
# Sorry, one party at a time
self.sendToAI('addPartyResponseUdToAi', [partyId, AddPartyErrorCode.TooManyHostedParties, self._formatParty(self.id2Party[partyId])])
self.id2Party[partyId] = {'partyId': partyId, 'hostId': avId, 'start': startTime, 'end': endTime, 'isPrivate': isPrivate, 'inviteTheme': inviteTheme, 'activities': activities, 'decorations': decorations, 'inviteeIds': inviteeIds, 'status': PartyStatus.Pending}
self.host2PartyId[avId] = partyId
self.sendToAI('addPartyResponseUdToAi', [partyId, AddPartyErrorCode.AllOk, self._formatParty(self.id2Party[partyId])])
if self.wantInstantParties:
taskMgr.remove('GlobalPartyManager_checkStarts')
taskMgr.doMethodLater(15, self.__checkPartyStarts, 'GlobalPartyManager_checkStarts')
return
def queryParty(self, hostId):
# An AI is wondering if the host has a party. We'll tell em!
if hostId in self.host2PartyId:
# Yep, he has a party.
party = self.id2Party[self.host2PartyId[hostId]]
self.sendToAI('partyInfoOfHostResponseUdToAi', [self._formatParty(party), party.get('inviteeIds', [])])
return
print 'query failed, av %s isnt hosting anything' % hostId
def requestPartySlot(self, partyId, avId, gateId):
if partyId not in self.party2PubInfo:
recipient = self.GetPuppetConnectionChannel(avId)
sender = simbase.air.getAvatarIdFromSender()
dg = self.air.dclassesByName['DistributedPartyGateAI'].getFieldByName('partyRequestDenied').aiFormatUpdate(gateId, recipient, sender, [PartyGateDenialReasons.Unavailable])
self.air.send(dg)
return
party = self.party2PubInfo[partyId]
if party['numGuests'] >= party['maxGuests']:
recipient = self.GetPuppetConnectionChannel(avId)
sender = simbase.air.getAvatarIdFromSender()
dg = self.air.dclassesByName['DistributedPartyGateAI'].getFieldByName('partyRequestDenied').aiFormatUpdate(gateId, recipient, sender, [PartyGateDenialReasons.Full])
self.air.send(dg)
return
# get them a slot
party['numGuests'] += 1
self.__updatePartyCount(partyId)
# note that they might not show up
self.tempSlots[avId] = partyId
#give the client a minute to connect before freeing their slot
taskMgr.doMethodLater(60, self._removeTempSlot, 'partyManagerTempSlot%d' % avId, extraArgs=[avId])
# now format the pubPartyInfo
actIds = []
for activity in self.id2Party[partyId]['activities']:
actIds.append(activity[0])
info = [party['shardId'], party['zoneId'], party['numGuests'], party['hostName'], actIds, 0] # the param is minleft
# find the hostId
hostId = self.id2Party[party['partyId']]['hostId']
# send update to client's gate
recipient = self.GetPuppetConnectionChannel(avId)
sender = simbase.air.getAvatarIdFromSender() # try to pretend the AI sent it. ily2 cfsworks
dg = self.air.dclassesByName['DistributedPartyGateAI'].getFieldByName('setParty').aiFormatUpdate(gateId, recipient, sender, [info, hostId])
self.air.send(dg)
def _removeTempSlot(self, avId):
partyId = self.tempSlots.get(avId)
if partyId:
del self.tempSlots[avId]
self.party2PubInfo.get(partyId, {'numGuests': 0})['numGuests'] -= 1
self.__updatePartyCount(partyId)
def allocIds(self, numIds):
ids = []
while len(ids) < numIds:
ids.append(self.nextId)
self.nextId += 1
self.sendToAI('receiveId', ids)