diff --git a/etc/toon.dc b/etc/toon.dc index ccaebef..b4b29ed 100755 --- a/etc/toon.dc +++ b/etc/toon.dc @@ -2874,7 +2874,9 @@ dclass ToontownFriendsManager : DistributedObject { makeFriendsResponse(uint32, uint32, uint8, uint32) airecv; removeFriend(uint32) clsend; requestSecret(uint32); + submitSecret(uint32, string); requestSecretResponse(uint8, string, uint32) airecv; + submitSecretResponse(uint8, uint32, uint32) airecv; }; dclass TTSpeedchatRelay : SpeedchatRelay { diff --git a/otp/friends/FriendManagerAI.py b/otp/friends/FriendManagerAI.py index 3321330..b3c3bcd 100644 --- a/otp/friends/FriendManagerAI.py +++ b/otp/friends/FriendManagerAI.py @@ -18,6 +18,11 @@ class FriendManagerAI(DistributedObjectGlobalAI): # forget who has declined friendships from whom. DeclineFriendshipTimeout = 600.0 + # This is the length of time, in seconds, to sit on a secret guess + # # before processing it. This serves to make it difficult to guess + # # passwords at random. + SecretDelay = 1.0 + # This subclass is used to record currently outstanding # in-the-game invitation requests. class Invite: @@ -51,10 +56,12 @@ class FriendManagerAI(DistributedObjectGlobalAI): # via the AIR. self.accept("makeFriendsReply", self.makeFriendsReply) self.accept("requestSecretReply", self.requestSecretReply) + self.accept('submitSecretReply', self.submitSecretReply) def delete(self): self.ignore("makeFriendsReply") self.ignore("requestSecretReply") + self.ignore('submitSecretReply') DistributedObjectGlobalAI.delete(self) ### Messages sent from inviter client to AI @@ -223,6 +230,51 @@ class FriendManagerAI(DistributedObjectGlobalAI): self.sendUpdateToAvatarId(recipient, "friendResponse", [yesNoMaybe, context]) self.notify.debug("AI: friendResponse(%d, %d)" % (yesNoMaybe, context)) + def submitSecret(self, secret): + """submitSecret(self, string secret) + + Sent by the client to the AI to submit a "secret" typed in by + the user. + """ + avId = self.air.getAvatarIdFromSender() + + # We have to sit on this request for a few seconds before + # processing it. This delay is solely to discourage password + # guessing. + taskName = "secret-" + str(avId) + taskMgr.remove(taskName) + if FriendManagerAI.SecretDelay: + taskMgr.doMethodLater(FriendManagerAI.SecretDelay, + self.continueSubmission, + taskName, + extraArgs = (avId, secret)) + else: + # No delay + self.continueSubmission(avId, secret) + + def continueSubmission(self, avId, secret): + """continueSubmission(self, avId, secret) + Finishes the work of submitSecret, a short time later. + """ + self.air.submitSecret(avId, secret) + + def submitSecretReply(self, result, recipient, avId): + self.down_submitSecretResponse(recipient, result, avId) + + def down_submitSecretResponse(self, recipient, result, avId): + """submitSecret(self, int8 result, int32 avId) + + Sent by the AI to the client in response to submitSecret(). + result is one of: + + 0 - Failure. The secret is unknown or has timed out. + 1 - Success. You are now friends with the indicated avId. + 2 - Failure. One of the avatars has too many friends already. + 3 - Failure. You just used up your own secret. + + """ + self.sendUpdateToAvatarId(recipient, 'submitSecretResponse', [result, avId]) + ### Support methods def newInvite(self, inviterId, inviteeId): @@ -414,7 +466,7 @@ class FriendManagerAI(DistributedObjectGlobalAI): def requestSecretReply(self, result, secret, requesterId): self.down_requestSecretResponse(requesterId, result, secret) - + def down_requestSecretResponse(self, recipient, result, secret): """requestSecret(self, int8 result, string secret) diff --git a/toontown/ai/ToontownAIRepository.py b/toontown/ai/ToontownAIRepository.py index 2d8bb31..4349269 100644 --- a/toontown/ai/ToontownAIRepository.py +++ b/toontown/ai/ToontownAIRepository.py @@ -547,6 +547,19 @@ class ToontownAIRepository(ToontownInternalRepository): if __astron__: self.toontownFriendsManager.sendRequestSecret(requesterId) + def submitSecret(self, avId, secret): + """ + avId: the avatar id of the friend who requested the secret + secret: the secret that was requested + Submits a secret that was previously requested. + Will submit through the friends manager. + When the secret is submitted, a "submitSecretReply" message will + be thrown with two parameters: the result code (0 or 1, + indicating failure or success), and the requesterId again. + """ + if __astron__: + self.toontownFriendsManager.sendSubmitSecret(avId, secret) + def setupFiles(self): if not os.path.exists(self.dataFolder): os.mkdir(self.dataFolder) diff --git a/toontown/friends/ToontownFriendsManagerAI.py b/toontown/friends/ToontownFriendsManagerAI.py index 7a73003..cca30e2 100644 --- a/toontown/friends/ToontownFriendsManagerAI.py +++ b/toontown/friends/ToontownFriendsManagerAI.py @@ -27,3 +27,9 @@ class ToontownFriendsManagerAI(DistributedObjectGlobalAI): def sendRequestSecret(self, requesterId): self.sendUpdate('requestSecret', [requesterId]) + + def sendSubmitSecret(self, avId, secret): + self.sendUpdate('submitSecret', [avId, secret]) + + def submitSecretResponse(self, result, recipient, requesterId): + messenger.send("submitSecretReply", [result, recipient, requesterId]) \ No newline at end of file diff --git a/toontown/friends/ToontownFriendsManagerUD.py b/toontown/friends/ToontownFriendsManagerUD.py index 42f6402..f572f6e 100644 --- a/toontown/friends/ToontownFriendsManagerUD.py +++ b/toontown/friends/ToontownFriendsManagerUD.py @@ -1,7 +1,7 @@ from direct.directnotify import DirectNotifyGlobal from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD from direct.distributed.PyDatagram import * - +from direct.distributed.MsgTypes import * from otp.otpbase import OTPGlobals import random import string @@ -709,8 +709,7 @@ class GenerateSecretOperation(FriendsOperation): TT 3 random letters/numbers 3 random letters/numbers """ self.requesterId = requesterId - # first check if friends list is full - + self.friendsManager.loadSecretsDB() # check if too many secrets # TODO self.secret = '' @@ -728,6 +727,7 @@ class GenerateSecretOperation(FriendsOperation): Handle the completion of the operation. """ FriendsOperation._handleDone(self) + self.friendsManager.updateSecretsDB() # send the response to the requester that it was a success, the result and the secret self.friendsManager.sendRequestSecretResponse(self.requesterId, 1, self.secret) @@ -907,6 +907,32 @@ class ToontownFriendsManagerUD(DistributedObjectGlobalUD): """ self.runServerOperation(GenerateSecretOperation, requesterId) + def submitSecret(self, avId, secret): + """ + Handle a request to submit a secret. + + :param avId: The avatar ID. + :param secret: The secret. + """ + self.loadSecretsDB() + recipient = self.secrets.get(secret, None) + if recipient: + self.runServerOperation(MakeFriendsOperation, avId, recipient, 0, 0) + self.sendSubmitSecretResponse(avId, 1, recipient) + else: + self.sendSubmitSecretResponse(avId, 0, recipient) + + def sendSubmitSecretResponse(self, requesterId, success, recipient): + """ + Send a response to a secret submission. + + :param requesterId: The requester's ID. + :param success: Whether the operation was successful. + :param secret: The secret. + :param recipient: The recipient's ID. + """ + self.sendUpdate('submitSecretResponse', [success, recipient, requesterId]) + def comingOnline(self, avId, friendsList): """ Handle a request for an avatar coming online. @@ -935,6 +961,16 @@ class ToontownFriendsManagerUD(DistributedObjectGlobalUD): self.sendUpdate('requestSecretResponse', [success, secret, requesterId]) + def updateSecretsDB(self): + with open('astron/databases/secrets.json', 'w') as f: + json.dump(self.secrets, f) + self.notify.debug('Updated secrets database') - + def loadSecretsDB(self): + try: + with open('astron/databases/secrets.json', 'r') as f: + self.secrets = json.load(f) + self.notify.debug('Loaded secrets database') + except: + self.notify.warning('Failed to load secrets database') \ No newline at end of file