369 lines
17 KiB
Python
369 lines
17 KiB
Python
from otp.uberdog.GameServicesManagerUD import *
|
|
from toontown.makeatoon.NameGenerator import NameGenerator
|
|
from toontown.toon.ToonDNA import ToonDNA
|
|
from toontown.toonbase import TTLocalizer
|
|
|
|
|
|
def judgeName(name):
|
|
return True # TODO: Make this useful.
|
|
|
|
|
|
class SetNamePatternOperation(AvatarOperation):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('SetNamePatternOperation')
|
|
postAccountState = 'RetrieveAvatar'
|
|
|
|
def __init__(self, gameServicesManager, target):
|
|
AvatarOperation.__init__(self, gameServicesManager, target)
|
|
self.avId = None
|
|
self.pattern = None
|
|
|
|
def enterStart(self, avId, pattern):
|
|
# Store these values.
|
|
self.avId = avId
|
|
self.pattern = pattern
|
|
|
|
# Retrieve the account.
|
|
self.demand('RetrieveAccount')
|
|
|
|
def enterRetrieveAvatar(self):
|
|
# Retrieves the avatar from the database.
|
|
if self.avId and self.avId not in self.avList:
|
|
# The avatar exists, but it's not an avatar that is
|
|
# associated with this account. Kill the connection.
|
|
self.demand('Kill', 'Tried to name an avatar not in the account!')
|
|
return
|
|
|
|
# Query the database for the avatar. self.__handleAvatar is
|
|
# our callback which will be called upon queryObject's completion.
|
|
self.gameServicesManager.air.dbInterface.queryObject(self.gameServicesManager.air.dbId, self.avId,
|
|
self.__handleAvatar)
|
|
|
|
def __handleAvatar(self, dclass, fields):
|
|
if dclass != self.gameServicesManager.air.dclassesByName['DistributedToonUD']:
|
|
# This dclass is not a valid avatar! Kill the connection.
|
|
self.demand('Kill', 'One of the account\'s avatars is invalid!')
|
|
return
|
|
|
|
if fields['WishNameState'][0] != 'OPEN':
|
|
# This avatar's wish name state is not set
|
|
# to a nameable state. Kill the connection.
|
|
self.demand('Kill', 'Avatar is not in a nameable state!')
|
|
|
|
# Otherwise, we can set the name, so let's enter the SetName state.
|
|
self.demand('SetName')
|
|
|
|
def enterSetName(self):
|
|
# Stringify this pattern.
|
|
parts = []
|
|
for p, f in self.pattern:
|
|
part = self.gameServicesManager.nameGenerator.nameDictionary.get(p, ('', ''))[1]
|
|
if f:
|
|
part = part[:1].upper() + part[1:]
|
|
else:
|
|
part = part.lower()
|
|
|
|
parts.append(part)
|
|
|
|
# This will merge 2 & 3 (the last name), as there should be no space.
|
|
parts[2] += parts.pop(3)
|
|
while '' in parts:
|
|
parts.remove('')
|
|
|
|
# Construct the final name string.
|
|
name = ' '.join(parts)
|
|
|
|
# We can now update the avatar object with the name.
|
|
self.gameServicesManager.air.dbInterface.updateObject(self.gameServicesManager.air.dbId, self.avId,
|
|
self.gameServicesManager.air.dclassesByName[
|
|
'DistributedToonUD'], {'WishNameState': ('LOCKED',),
|
|
'WishName': ('',),
|
|
'setName': (name,)})
|
|
|
|
# We're done. We can now send off the namePatternResponse update through
|
|
# the GameServicesManager, and set this operation's state to Off.
|
|
self.gameServicesManager.air.writeServerEvent('avatar-named', self.avId, name)
|
|
self.gameServicesManager.sendUpdateToAccountId(self.target, 'namePatternResponse', [self.avId, 1])
|
|
self.demand('Off')
|
|
|
|
|
|
class SetNameTypedOperation(AvatarOperation):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('SetNameTypedOperation')
|
|
postAccountState = 'RetrieveAvatar'
|
|
|
|
def __init__(self, gameServicesManager, target):
|
|
AvatarOperation.__init__(self, gameServicesManager, target)
|
|
self.avId = None
|
|
self.name = None
|
|
|
|
def enterStart(self, avId, name):
|
|
# SetNameTypedOperation judges the submitted name
|
|
# & then sets the avatars wish name state accordingly.
|
|
self.avId = avId
|
|
self.name = name
|
|
|
|
if self.avId:
|
|
# If avId is not 0, the user is submitting this name,
|
|
# so we need to retrieve the account.
|
|
self.demand('RetrieveAccount')
|
|
return
|
|
|
|
# Otherwise, avId is 0, meaning that a check request was sent.
|
|
self.demand('JudgeName')
|
|
|
|
def enterRetrieveAvatar(self):
|
|
# Retrieves the avatar from the database.
|
|
if self.avId and self.avId not in self.avList:
|
|
# The avatar exists, but it's not an avatar that is
|
|
# associated with this account. Kill the connection.
|
|
self.demand('Kill', 'Tried to name an avatar not in the account!')
|
|
return
|
|
|
|
# Query the database for the avatar. self.__handleAvatar is
|
|
# our callback which will be called upon queryObject's completion.
|
|
self.gameServicesManager.air.dbInterface.queryObject(self.gameServicesManager.air.dbId, self.avId,
|
|
self.__handleAvatar)
|
|
|
|
def __handleAvatar(self, dclass, fields):
|
|
if dclass != self.gameServicesManager.air.dclassesByName['DistributedToonUD']:
|
|
# This dclass is not a valid avatar! Kill the connection.
|
|
self.demand('Kill', 'One of the account\'s avatars is invalid!')
|
|
return
|
|
|
|
if fields['WishNameState'][0] != 'OPEN':
|
|
# This avatar's wish name state is not set
|
|
# to a nameable state. Kill the connection.
|
|
self.demand('Kill', 'Avatar is not in a nameable state!')
|
|
|
|
# Now we can move on to the judging!
|
|
self.demand('JudgeName')
|
|
|
|
def enterJudgeName(self):
|
|
# Let's see if the name is valid.
|
|
status = judgeName(self.name)
|
|
|
|
if self.avId and status:
|
|
# Cool, this is a valid name, and we have an avId.
|
|
# Let's update their avatar with the new wish name & status.
|
|
self.gameServicesManager.air.dbInterface.updateObject(self.gameServicesManager.air.dbId, self.avId,
|
|
self.gameServicesManager.air.dclassesByName[
|
|
'DistributedToonUD'],
|
|
{'WishNameState': ('PENDING',),
|
|
'WishName': (self.name,)})
|
|
|
|
if self.avId:
|
|
# If the avId is not 0, log this server event, as the avatar's
|
|
# wish name & state have been modified.
|
|
self.gameServicesManager.air.writeServerEvent('avatar-wish-name', self.avId, self.name)
|
|
|
|
# Otherwise, we're done! We can now send the response update
|
|
# through the GameServicesManager & set our state to Off.
|
|
self.gameServicesManager.sendUpdateToAccountId(self.target, 'nameTypedResponse', [self.avId, status])
|
|
self.demand('Off')
|
|
|
|
|
|
class CreateAvatarOperation(GameOperation):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('CreateAvatarOperation')
|
|
|
|
def __init__(self, gameServicesManager, target):
|
|
GameOperation.__init__(self, gameServicesManager, target)
|
|
self.index = None
|
|
self.dna = None
|
|
|
|
def enterStart(self, dna, index):
|
|
# First, perform some basic sanity checking.
|
|
if index >= 6:
|
|
# This index is invalid! Kill the connection.
|
|
self.demand('Kill', 'Invalid index specified!')
|
|
return
|
|
|
|
if not ToonDNA().isValidNetString(dna):
|
|
# This DNA string is invalid! Kill the connection.
|
|
self.demand('Kill', 'Invalid DNA specified!')
|
|
return
|
|
|
|
# Store these values.
|
|
self.index = index
|
|
self.dna = dna
|
|
|
|
# Now we can query their account.
|
|
self.demand('RetrieveAccount')
|
|
|
|
def enterRetrieveAccount(self):
|
|
# Query the sender's account. self.__handleRetrieve is the
|
|
# callback that will be called upon queryObject's competion.
|
|
self.gameServicesManager.air.dbInterface.queryObject(self.gameServicesManager.air.dbId, self.target,
|
|
self.__handleRetrieve)
|
|
|
|
def __handleRetrieve(self, dclass, fields):
|
|
if dclass != self.gameServicesManager.air.dclassesByName['AccountUD']:
|
|
# This is not an account object! Kill the connection.
|
|
self.demand('Kill', 'Your account object (%s) was not found in the database!' % dclass)
|
|
return
|
|
|
|
# Now we will get our avList.
|
|
self.account = fields
|
|
self.avList = self.account['ACCOUNT_AV_SET']
|
|
|
|
# We will now sanitize the avList.
|
|
self.avList = self.avList[:6]
|
|
self.avList += [0] * (6 - len(self.avList))
|
|
|
|
# Check if the index is open:
|
|
if self.avList[self.index]:
|
|
# This index is not open! Kill the connection.
|
|
self.demand('Kill', 'This avatar slot is already taken by another avatar!')
|
|
return
|
|
|
|
# All set, now let's create the avatar!
|
|
self.demand('CreateAvatar')
|
|
|
|
def enterCreateAvatar(self):
|
|
# We will now construct a new Toon with the given values.
|
|
dna = ToonDNA()
|
|
dna.makeFromNetString(self.dna)
|
|
colorString = TTLocalizer.NumToColor[dna.headColor]
|
|
animalType = TTLocalizer.AnimalToSpecies[dna.getAnimal()]
|
|
name = ' '.join((colorString, animalType))
|
|
toonFields = {'setName': (name,),
|
|
'WishNameState': ('OPEN',),
|
|
'WishName': ('',),
|
|
'setDNAString': (self.dna,),
|
|
'setDISLid': (self.target,)}
|
|
|
|
# Create this new Toon object in the database. self.__handleCreate is the
|
|
# callback that will be called upon the completion of createObject.
|
|
self.gameServicesManager.air.dbInterface.createObject(self.gameServicesManager.air.dbId,
|
|
self.gameServicesManager.air.dclassesByName[
|
|
'DistributedToonUD'], toonFields, self.__handleCreate)
|
|
|
|
def __handleCreate(self, avId):
|
|
if not avId:
|
|
# The database was unable to create a new avatar object! Kill the connection.
|
|
self.demand('Kill', 'Database failed to create the new avatar object!')
|
|
return
|
|
|
|
# We can now store the avatar.
|
|
self.avId = avId
|
|
self.demand('StoreAvatar')
|
|
|
|
def enterStoreAvatar(self):
|
|
# We will now associate the avatar with the account. self.__handleStoreAvatar is the
|
|
# callback which will be called upon the completion of updateObject.
|
|
self.avList[self.index] = self.avId
|
|
self.gameServicesManager.air.dbInterface.updateObject(self.gameServicesManager.air.dbId, self.target,
|
|
self.gameServicesManager.air.dclassesByName['AccountUD'],
|
|
{'ACCOUNT_AV_SET': self.avList},
|
|
{'ACCOUNT_AV_SET': self.account['ACCOUNT_AV_SET']},
|
|
self.__handleStoreAvatar)
|
|
|
|
def __handleStoreAvatar(self, fields):
|
|
if fields:
|
|
# The new avatar was not associated with the account! Kill the connection.
|
|
self.demand('Kill', 'Database failed to associate the new avatar to your account!')
|
|
return
|
|
|
|
# Otherwise, we're done! We can now send the createAvatarResponse update
|
|
# through the GameServicesManager & set this operation's state to Off.
|
|
self.gameServicesManager.air.writeServerEvent('avatar-created', self.avId, self.target, self.dna.encode('hex'),
|
|
self.index)
|
|
self.gameServicesManager.sendUpdateToAccountId(self.target, 'createAvatarResponse', [self.avId])
|
|
self.demand('Off')
|
|
|
|
|
|
class AcknowledgeNameOperation(AvatarOperation):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('AcknowledgeNameFSM')
|
|
postAccountState = 'GetTargetAvatar'
|
|
|
|
def __init__(self, gameServicesManager, target):
|
|
AvatarOperation.__init__(self, gameServicesManager, target)
|
|
self.avId = None
|
|
|
|
def enterStart(self, avId):
|
|
# Store this value & move on to RetrieveAccount.
|
|
self.avId = avId
|
|
self.demand('RetrieveAccount')
|
|
|
|
def enterGetTargetAvatar(self):
|
|
# Make sure that the target avatar is part of the account:
|
|
if self.avId not in self.avList:
|
|
# The sender tried to acknowledge name on an avatar not on the account!
|
|
# Kill the connection.
|
|
self.demand('Kill', 'Tried to acknowledge name on an avatar not in the account!')
|
|
return
|
|
|
|
# We can now query the database for the avatar. self.__handleAvatar is the
|
|
# callback which will be called upon the completion of queryObject.
|
|
self.gameServicesManager.air.dbInterface.queryObject(self.gameServicesManager.air.dbId, self.avId,
|
|
self.__handleAvatar)
|
|
|
|
def __handleAvatar(self, dclass, fields):
|
|
if dclass != self.gameServicesManager.air.dclassesByName['DistributedToonUD']:
|
|
# This dclass is not a valid avatar! Kill the connection.
|
|
self.demand('Kill', 'One of the account\'s avatars is invalid!')
|
|
return
|
|
|
|
# Process the WishNameState change.
|
|
wishNameState = fields['WishNameState'][0]
|
|
wishName = fields['WishName'][0]
|
|
name = fields['setName'][0]
|
|
|
|
if wishNameState == 'APPROVED':
|
|
wishNameState = 'LOCKED'
|
|
name = wishName
|
|
wishName = ''
|
|
elif wishNameState == 'REJECTED':
|
|
wishNameState = 'OPEN'
|
|
wishName = ''
|
|
else:
|
|
# The sender is trying to acknowledge name on avatar in invalid state! Kill the connection.
|
|
self.demand('Kill', 'Tried to acknowledge name on an avatar in invalid state (%s) !' % wishNameState)
|
|
return
|
|
|
|
# We can now update the avatar object in the database with the changes!
|
|
self.gameServicesManager.air.dbInterface.updateObject(self.gameServicesManager.air.dbId, self.avId,
|
|
self.gameServicesManager.air.dclassesByName[
|
|
'DistributedToonUD'],
|
|
{'WishNameState': (wishNameState,),
|
|
'WishName': (wishName,),
|
|
'setName': (name,)},
|
|
{'WishNameState': fields['WishNameState'],
|
|
'WishName': fields['WishName'],
|
|
'setName': fields['setName']})
|
|
|
|
# We're done. We can now send off the acknowledgeAvatarNameResponse update
|
|
# through the GameServicesManager, and set this operation's state to Off.
|
|
self.gameServicesManager.sendUpdateToAccountId(self.target, 'acknowledgeAvatarNameResponse', [])
|
|
self.demand('Off')
|
|
|
|
|
|
class TTGameServicesManagerUD(GameServicesManagerUD):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('TTGameServicesManagerUD')
|
|
avatarDclass = 'DistributedToonUD'
|
|
|
|
def __init__(self, air):
|
|
GameServicesManagerUD.__init__(self, air)
|
|
self.nameGenerator = None
|
|
|
|
def announceGenerate(self):
|
|
GameServicesManagerUD.announceGenerate(self)
|
|
|
|
# This is for processing name patterns.
|
|
self.nameGenerator = NameGenerator()
|
|
|
|
def setNamePattern(self, avId, p1, f1, p2, f2, p3, f3, p4, f4):
|
|
# Someone has used a pattern name; run a SetNamePatternOperation.
|
|
self.runOperation(SetNamePatternOperation, avId, [(p1, f1), (p2, f2),
|
|
(p3, f3), (p4, f4)])
|
|
|
|
def setNameTyped(self, avId, name):
|
|
# Someone has typed a name; run a SetNameTypedOperation.
|
|
self.runOperation(SetNameTypedOperation, avId, name)
|
|
|
|
def createAvatar(self, dna, index):
|
|
# Someone wants to create a new avatar;
|
|
# run a CreateAvatarOperation.
|
|
self.runOperation(CreateAvatarOperation, dna, index)
|
|
|
|
def acknowledgeAvatarName(self, avId):
|
|
# Someone has acknowledged their name; run an AcknowledgeNameOperation.
|
|
self.runOperation(AcknowledgeNameOperation, avId)
|