├── DB ├── Files │ └── player.sqlite └── DatabaseHandler.py ├── Screenshots └── menu.png ├── Heart ├── Record │ ├── Debugger.py │ ├── CPPDefs.py │ ├── ByteStreamHelper.py │ ├── ChecksumEncoder.py │ └── ByteStream.py ├── Logic │ ├── LogicStringUtil.py │ ├── LogicBattlePlayerMap.py │ ├── LogicLong.py │ ├── LogicLaserMessageFactory.py │ └── LogicCommandManager.py ├── Commands │ ├── LogicServerCommand.py │ ├── Server │ │ └── ChangeAvatarNameCommand.py │ ├── Client │ │ ├── SetPlayerNameColorCommand.py │ │ ├── SetPlayerThumbnailCommand.py │ │ └── PurchaseOfferCommand.py │ └── LogicCommand.py ├── Packets │ ├── Server │ │ ├── KeepAliveServerMessage.py │ │ ├── ServerHelloMessage.py │ │ ├── AvailableServerCommandMessage.py │ │ ├── OutOfSyncMessage.py │ │ ├── LobbyInfoMessage.py │ │ ├── LoginFailedMessage.py │ │ ├── PlayerProfileMessage.py │ │ ├── LoginOkMessage.py │ │ └── OwnHomeDataMessage.py │ ├── Client │ │ ├── KeepAliveMessage.py │ │ ├── GoHomeMessage.py │ │ ├── GoHomeFromOfflinePractiseMessage.py │ │ ├── ClientHelloMessage.py │ │ ├── ChangeAvatarNameMessage.py │ │ ├── GetPlayerProfileMessage.py │ │ ├── EndClientTurnMessage.py │ │ └── LoginMessage.py │ └── PiranhaMessage.py ├── Utils │ ├── ClientsManager.py │ └── Player.py ├── Messaging.py └── Connection.py ├── Main.py ├── README.md └── LICENSE /DB/Files/player.sqlite: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Screenshots/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LkPrtctrd/BSL-V55/HEAD/Screenshots/menu.png -------------------------------------------------------------------------------- /Heart/Record/Debugger.py: -------------------------------------------------------------------------------- 1 | class Debugger: 2 | @staticmethod 3 | def error(message): 4 | print("[ERROR]", message) 5 | 6 | @staticmethod 7 | def warning(message): 8 | print("[WARNING]", message) -------------------------------------------------------------------------------- /Heart/Logic/LogicStringUtil.py: -------------------------------------------------------------------------------- 1 | class LogicStringUtil: 2 | @staticmethod 3 | def getBytes(string): 4 | return string.encode() 5 | 6 | @staticmethod 7 | def getByteLength(string): 8 | return len(string) 9 | -------------------------------------------------------------------------------- /Heart/Logic/LogicBattlePlayerMap.py: -------------------------------------------------------------------------------- 1 | from Heart.ByteStreamHelper import ByteStreamHelper 2 | from Heart.Logic.LogicLong import LogicLong 3 | 4 | class LogicBattlePlayerMap: 5 | def decode(bytestream, fields): 6 | battleMap = fields["Map"][0] 7 | fields["MapID"] = LogicLong() 8 | ByteStreamHelper.decodeLogicLong(self, fields["MapID"]) 9 | #fields[""] -------------------------------------------------------------------------------- /Main.py: -------------------------------------------------------------------------------- 1 | serverAddress = ("0.0.0.0", 9339) 2 | 3 | import socket 4 | from Heart.Connection import Connection 5 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 | server.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) 7 | server.bind(serverAddress) 8 | print(f"Heart beats on {serverAddress[0]}:{serverAddress[1]}") 9 | while True: 10 | server.listen() 11 | socket, address = server.accept() 12 | print(f"New player: {address[0]}:{address[1]}") 13 | Connection(socket, address).start() -------------------------------------------------------------------------------- /Heart/Commands/LogicServerCommand.py: -------------------------------------------------------------------------------- 1 | from Heart.Commands.LogicCommand import LogicCommand 2 | 3 | 4 | class LogicServerCommand(LogicCommand): 5 | def __init__(self, commandData): 6 | super().__init__(commandData) 7 | 8 | def addCommand(self, fields): 9 | self.writeVInt(1) 10 | LogicCommand.encode(self, fields) 11 | 12 | def decode(calling_instance, fields): 13 | fields["ID"] = calling_instance.readVInt() 14 | return LogicCommand.decode(calling_instance, fields) 15 | -------------------------------------------------------------------------------- /Heart/Packets/Server/KeepAliveServerMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Packets.PiranhaMessage import PiranhaMessage 2 | 3 | 4 | class KeepAliveServerMessage(PiranhaMessage): 5 | def __init__(self, messageData): 6 | super().__init__(messageData) 7 | self.messageVersion = 0 8 | 9 | def encode(self, fields): 10 | pass 11 | 12 | def decode(self): 13 | return {} 14 | 15 | def execute(message, calling_instance, fields): 16 | pass 17 | 18 | def getMessageType(self): 19 | return 20108 20 | 21 | def getMessageVersion(self): 22 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Utils/ClientsManager.py: -------------------------------------------------------------------------------- 1 | class ClientsManager: 2 | 3 | PlayersList = {} 4 | 5 | def AddPlayer(playerID, socket): 6 | if ClientsManager.PlayersList.keys().__contains__(playerID[1]): 7 | ClientsManager.RemovePlayer(playerID) 8 | ClientsManager.PlayersList[playerID[1]] = {"Socket": socket} 9 | 10 | def RemovePlayer(PlayerID): 11 | try: 12 | ClientsManager.PlayersList.pop(PlayerID[1]) 13 | except KeyError: 14 | print(f"Cannot remove socket with id: {PlayerID} Reason: {PlayerID} is not in the list.") 15 | 16 | def GetAll(): 17 | return ClientsManager.PlayersList 18 | 19 | def GetCount(): 20 | return len(ClientsManager.PlayersList) -------------------------------------------------------------------------------- /Heart/Packets/Client/KeepAliveMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Messaging import Messaging 2 | 3 | from Heart.Packets.PiranhaMessage import PiranhaMessage 4 | 5 | 6 | class KeepAliveMessage(PiranhaMessage): 7 | def __init__(self, messageData): 8 | super().__init__(messageData) 9 | self.messageVersion = 0 10 | 11 | def encode(self, fields): 12 | pass 13 | 14 | def decode(self): 15 | return {} 16 | 17 | def execute(message, calling_instance, fields): 18 | fields["Socket"] = calling_instance.client 19 | Messaging.sendMessage(20108, fields) 20 | 21 | def getMessageType(self): 22 | return 10108 23 | 24 | def getMessageVersion(self): 25 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Commands/Server/ChangeAvatarNameCommand.py: -------------------------------------------------------------------------------- 1 | from Heart.Commands.LogicServerCommand import LogicServerCommand 2 | 3 | 4 | class ChangeAvatarNameCommand(LogicServerCommand): 5 | def __init__(self, commandData): 6 | super().__init__(commandData) 7 | 8 | def encode(self, fields): 9 | self.writeString(fields["Name"]) 10 | self.writeVInt(0) 11 | LogicServerCommand.encode(self, fields) 12 | return self.messagePayload 13 | 14 | def decode(self, calling_instance): 15 | fields = {} 16 | fields["Name"] = calling_instance.readString() 17 | fields["Unk1"] = calling_instance.readVInt() 18 | return LogicServerCommand.decode(calling_instance, fields) 19 | 20 | def getCommandType(self): 21 | return 201 -------------------------------------------------------------------------------- /Heart/Packets/Server/ServerHelloMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Packets.PiranhaMessage import PiranhaMessage 2 | from os import urandom 3 | 4 | 5 | class ServerHelloMessage(PiranhaMessage): 6 | def __init__(self, messageData): 7 | super().__init__(messageData) 8 | self.messageVersion = 0 9 | 10 | def encode(self, fields): 11 | self.writeBytes(urandom(24), 24) 12 | 13 | def decode(self): 14 | fields = {} 15 | fields["Random"] = self.readBytesWithoutLength() 16 | super().decode(fields) 17 | return fields 18 | 19 | def execute(message, calling_instance, fields): 20 | pass 21 | 22 | def getMessageType(self): 23 | return 20100 24 | 25 | def getMessageVersion(self): 26 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Packets/Client/GoHomeMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Messaging import Messaging 2 | 3 | from Heart.Packets.PiranhaMessage import PiranhaMessage 4 | 5 | 6 | class GoHomeMessage(PiranhaMessage): 7 | def __init__(self, messageData): 8 | super().__init__(messageData) 9 | self.messageVersion = 0 10 | 11 | def encode(self, fields): 12 | pass 13 | 14 | def decode(self): 15 | fields = {} 16 | self.readBoolean() 17 | return fields 18 | 19 | def execute(message, calling_instance, fields): 20 | fields["Socket"] = calling_instance.client 21 | Messaging.sendMessage(24101, fields, calling_instance.player) 22 | 23 | def getMessageType(self): 24 | return 17750 25 | 26 | def getMessageVersion(self): 27 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Packets/Client/GoHomeFromOfflinePractiseMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Messaging import Messaging 2 | 3 | from Heart.Packets.PiranhaMessage import PiranhaMessage 4 | 5 | 6 | class GoHomeFromOfflinePractiseMessage(PiranhaMessage): 7 | def __init__(self, messageData): 8 | super().__init__(messageData) 9 | self.messageVersion = 0 10 | 11 | def encode(self, fields): 12 | pass 13 | 14 | def decode(self): 15 | fields = {} 16 | self.readBoolean() 17 | return fields 18 | 19 | def execute(message, calling_instance, fields): 20 | fields["Socket"] = calling_instance.client 21 | Messaging.sendMessage(24101, fields, calling_instance.player) 22 | 23 | def getMessageType(self): 24 | return 14109 25 | 26 | def getMessageVersion(self): 27 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Packets/PiranhaMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Record.ByteStream import ByteStream 2 | 3 | class PiranhaMessage(ByteStream): 4 | def __init__(self, messageData): 5 | super().__init__(messageData) 6 | self.messageBuffer = messageData 7 | self.fields = {} 8 | 9 | def decode(self, fields): 10 | if True: 11 | print() 12 | for typeName,value in fields.items(): 13 | print(f"{typeName}: {value}") 14 | print() 15 | 16 | def getLength(self): 17 | return len(self.messageBuffer) 18 | 19 | def isServerToClient(self): 20 | messageType = self.getMessageType() 21 | if 20000 <= messageType < 30000 or messageType == 40000: 22 | return True 23 | elif 10000 <= messageType < 20000 or messageType == 30000: 24 | return False 25 | -------------------------------------------------------------------------------- /Heart/Packets/Server/AvailableServerCommandMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Logic.LogicCommandManager import LogicCommandManager 2 | from Heart.Packets.PiranhaMessage import PiranhaMessage 3 | 4 | 5 | class AvailableServerCommandMessage(PiranhaMessage): 6 | def __init__(self, messageData): 7 | super().__init__(messageData) 8 | self.messageVersion = 0 9 | 10 | def encode(self, fields, player): 11 | self.writeVInt(fields["Command"]["ID"]) 12 | command = LogicCommandManager.createCommand(fields["Command"]["ID"], self.messagePayload) 13 | self.messagePayload = command.encode(fields) 14 | 15 | def decode(self): 16 | return {} 17 | 18 | def execute(message, calling_instance, fields): 19 | pass 20 | 21 | def getMessageType(self): 22 | return 24111 23 | 24 | def getMessageVersion(self): 25 | return self.messageVersion -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BSL-V55 2 | Python Brawl Stars server emulator for version 55 3 | 4 | ## How to play: ## 5 | 6 | ### Server ### 7 | I use a zip server from BSDS that I modified which was made by [СrazorTheCat](https://github.com/CrazorTheCat) 8 | 9 | 1: Download the server and extract it: https://github.com/LkPrtctrd/BSL-V55/archive/refs/heads/master.zip 10 | 11 | 2: Open terminal on your computer and go to server directory. 12 | 13 | 3: Type python3 Main.py and it's done, follow client instructions. 14 | 15 | ### Android Client ### 16 | 1: [Download the APK here](https://mega.nz/file/7FEhHTKB#PKxpHAGiWxyPcnBGIGT9nDR8Izo_Wc3QDQH7XNUK6JU) 17 | 18 | 2: Change redirectHost (default: 127.0.0.1) and redirectPort if you need it in the frida config (lib/arm64-v8a/libBSL.config.so) 19 | 20 | 3: Enjoy playing BSL-V55! 21 | 22 | ## Screenshots ## 23 | ![BSL-V55](https://raw.githubusercontent.com/LkPrtctrd/BSL-V55/main/Screenshots/menu.png) 24 | -------------------------------------------------------------------------------- /Heart/Packets/Server/OutOfSyncMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Packets.PiranhaMessage import PiranhaMessage 2 | 3 | 4 | class OutOfSyncMessage(PiranhaMessage): 5 | def __init__(self, messageData): 6 | super().__init__(messageData) 7 | self.messageVersion = 0 8 | 9 | def encode(self, fields): 10 | self.writeVInt(fields["ServerChecksum"]) 11 | self.writeVInt(fields["ClientChecksum"]) 12 | self.writeVInt(fields["Tick"]) 13 | 14 | def decode(self): 15 | fields = {} 16 | fields["ServerChecksum"] = self.readVInt() 17 | fields["ClientChecksum"] = self.readVInt() 18 | fields["Tick"] = self.readVInt() 19 | super().decode(fields) 20 | return fields 21 | 22 | def execute(message, calling_instance, fields): 23 | pass 24 | 25 | def getMessageType(self): 26 | return 24104 27 | 28 | def getMessageVersion(self): 29 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Commands/Client/SetPlayerNameColorCommand.py: -------------------------------------------------------------------------------- 1 | from Heart.Commands.LogicCommand import LogicCommand 2 | from Heart.Messaging import Messaging 3 | from DB.DatabaseHandler import DatabaseHandler 4 | 5 | class SetPlayerNameColorCommand(LogicCommand): 6 | def __init__(self, commandData): 7 | super().__init__(commandData) 8 | 9 | def encode(self, fields): 10 | LogicCommand.encode(self, fields) 11 | return self.messagePayload 12 | 13 | def decode(self, calling_instance): 14 | fields = {} 15 | LogicCommand.decode(calling_instance, fields, False) 16 | fields["NameColor"] = calling_instance.readDataReference()[1] 17 | 18 | LogicCommand.parseFields(fields) 19 | return fields 20 | 21 | def execute(self, calling_instance, fields): 22 | db = DatabaseHandler() 23 | pl = db.getPlayer(calling_instance.player.ID) 24 | pl["Namecolor"]=fields["NameColor"] 25 | db.updatePlayerData(pl,calling_instance) 26 | 27 | def getCommandType(self): 28 | return 527 -------------------------------------------------------------------------------- /Heart/Commands/Client/SetPlayerThumbnailCommand.py: -------------------------------------------------------------------------------- 1 | from Heart.Commands.LogicCommand import LogicCommand 2 | from Heart.Messaging import Messaging 3 | from DB.DatabaseHandler import DatabaseHandler 4 | 5 | class SetPlayerThumbnailCommand(LogicCommand): 6 | def __init__(self, commandData): 7 | super().__init__(commandData) 8 | 9 | def encode(self, fields): 10 | LogicCommand.encode(self, fields) 11 | return self.messagePayload 12 | 13 | def decode(self, calling_instance): 14 | fields = {} 15 | LogicCommand.decode(calling_instance, fields, False) 16 | fields["Thumbnail"] = calling_instance.readDataReference()[1] 17 | 18 | LogicCommand.parseFields(fields) 19 | return fields 20 | 21 | def execute(self, calling_instance, fields): 22 | db = DatabaseHandler() 23 | pl = db.getPlayer(calling_instance.player.ID) 24 | pl["Thumbnail"]=fields["Thumbnail"] 25 | db.updatePlayerData(pl,calling_instance) 26 | 27 | def getCommandType(self): 28 | return 505 -------------------------------------------------------------------------------- /Heart/Commands/Client/PurchaseOfferCommand.py: -------------------------------------------------------------------------------- 1 | from Heart.Commands.LogicCommand import LogicCommand 2 | from Heart.Messaging import Messaging 3 | 4 | class PurchaseOfferCommand(LogicCommand): 5 | def __init__(self, commandData): 6 | super().__init__(commandData) 7 | 8 | def encode(self, fields): 9 | LogicCommand.encode(self, fields) 10 | self.writeVInt(0) 11 | self.writeDataReference(0) 12 | return self.messagePayload 13 | 14 | def decode(self, calling_instance): 15 | fields = {} 16 | LogicCommand.decode(calling_instance, fields, False) 17 | fields["OfferIndex"] = calling_instance.readVInt() 18 | fields["Unk2"] = calling_instance.readDataReference() 19 | fields["Unk3"] = calling_instance.readDataReference() 20 | fields["Unk4"] = calling_instance.readVInt() 21 | 22 | LogicCommand.parseFields(fields) 23 | return fields 24 | 25 | def execute(self, calling_instance, fields): 26 | if fields["OfferIndex"] == 0: 27 | pass 28 | 29 | def getCommandType(self): 30 | return 519 -------------------------------------------------------------------------------- /Heart/Commands/LogicCommand.py: -------------------------------------------------------------------------------- 1 | from Heart.Record.ByteStream import ByteStream 2 | 3 | class LogicCommand(ByteStream): 4 | def __init__(self, commandData): 5 | super().__init__(commandData) 6 | self.messageBuffer = commandData 7 | self.messagePayload = commandData 8 | 9 | def encode(self, fields): 10 | self.writeVInt(-1) 11 | self.writeVInt(-1) 12 | self.writeVInt(0) 13 | self.writeVInt(0) 14 | #self.writeVLong(0, 0) 15 | 16 | def decode(calling_instance, fields, auto_decode=True): 17 | fields["TickWhenGiven"] = calling_instance.readVInt() 18 | fields["ExecuteTick"] = calling_instance.readVInt() 19 | fields["ExecutorAccountID"] = calling_instance.readVLong() 20 | if True: 21 | print() 22 | for typeName,value in fields.items(): 23 | print(f"{typeName}: {value}") 24 | print() 25 | return fields 26 | 27 | def parseFields(fields): 28 | print() 29 | for typeName,value in fields.items(): 30 | print(f"{typeName}: {value}") 31 | print() 32 | -------------------------------------------------------------------------------- /Heart/Packets/Client/ClientHelloMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Messaging import Messaging 2 | 3 | from Heart.Packets.PiranhaMessage import PiranhaMessage 4 | 5 | 6 | class ClientHelloMessage(PiranhaMessage): 7 | def __init__(self, messageData): 8 | super().__init__(messageData) 9 | self.messageVersion = 0 10 | 11 | def encode(self, fields): 12 | pass 13 | 14 | def decode(self): 15 | fields = {} 16 | fields["Protocol"] = self.readInt() 17 | fields["KeyVersion"] = self.readInt() 18 | fields["MajorVersion"] = self.readInt() 19 | fields["MinorVersion"] = self.readInt() 20 | fields["Build"] = self.readInt() 21 | fields["ContentHash"] = self.readString() 22 | fields["DeviceType"] = self.readInt() 23 | fields["AppStore"] = self.readInt() 24 | super().decode(fields) 25 | return fields 26 | 27 | def execute(message, calling_instance, fields): 28 | fields["Socket"] = calling_instance.client 29 | Messaging.sendMessage(20100, fields) 30 | 31 | def getMessageType(self): 32 | return 10100 33 | 34 | def getMessageVersion(self): 35 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Packets/Server/LobbyInfoMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Utils.ClientsManager import ClientsManager 2 | from Heart.Packets.PiranhaMessage import PiranhaMessage 3 | 4 | class LobbyInfoMessage(PiranhaMessage): 5 | def __init__(self, messageData): 6 | super().__init__(messageData) 7 | self.messageVersion = 0 8 | 9 | def encode(self, fields, player): 10 | self.writeVInt(ClientsManager.GetCount()) 11 | self.writeString(f"""Version: {player.ClientVersion}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n""") 12 | self.writeVInt(0) # count event 13 | self.writeVInt(0) # new timer in v51 14 | 15 | def decode(self): 16 | fields = {} 17 | fields["PlayerCount"] = self.readVInt() 18 | fields["Text"] = self.readString() 19 | fields["Unk1"] = self.readVInt() 20 | super().decode(fields) 21 | return {} 22 | 23 | def execute(message, calling_instance, fields): 24 | pass 25 | 26 | def getMessageType(self): 27 | return 23457 28 | 29 | def getMessageVersion(self): 30 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Packets/Client/ChangeAvatarNameMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Messaging import Messaging 2 | from DB.DatabaseHandler import DatabaseHandler 3 | from Heart.Packets.PiranhaMessage import PiranhaMessage 4 | 5 | 6 | class ChangeAvatarNameMessage(PiranhaMessage): 7 | def __init__(self, messageData): 8 | super().__init__(messageData) 9 | self.messageVersion = 0 10 | 11 | def encode(self, fields): 12 | self.writeString(fields["Name"]) 13 | self.writeBoolean(fields["NameSetByUser"]) 14 | 15 | def decode(self): 16 | fields = {} 17 | fields["Name"] = self.readString() 18 | fields["NameSetByUser"] = self.readBoolean() 19 | super().decode(fields) 20 | return fields 21 | 22 | def execute(message, calling_instance, fields): 23 | db_instance = DatabaseHandler() 24 | playerData = db_instance.getPlayer(calling_instance.player.ID) 25 | playerData["Name"] = fields["Name"] 26 | playerData["Registered"] = True 27 | db_instance.updatePlayerData(playerData, calling_instance) 28 | fields["Socket"] = calling_instance.client 29 | fields["Command"] = {"ID": 201} 30 | Messaging.sendMessage(24111, fields, calling_instance.player) 31 | 32 | def getMessageType(self): 33 | return 10212 34 | 35 | def getMessageVersion(self): 36 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Packets/Client/GetPlayerProfileMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Messaging import Messaging 2 | 3 | from Heart.Packets.PiranhaMessage import PiranhaMessage 4 | 5 | 6 | class GetPlayerProfileMessage(PiranhaMessage): 7 | def __init__(self, messageData): 8 | super().__init__(messageData) 9 | self.messageVersion = 0 10 | 11 | def encode(self, fields): 12 | pass 13 | 14 | def decode(self): 15 | fields = {} 16 | fields["BattleInfoBoolean"] = self.readBoolean() 17 | if fields["BattleInfoBoolean"]: 18 | fields["unk1"] = self.readVInt() 19 | fields["AnotherID"] = self.readLong() 20 | fields["unk2"] = self.readVInt() 21 | for i in self.readVInt(): 22 | fields["CsvID"] = self.readDataReference() 23 | fields["unk3"] = self.readVInt() 24 | fields["unk4"] = self.readVInt() 25 | fields["unk5"] = self.readVInt() 26 | fields["unk6"] = self.readVInt() 27 | fields["PlayerName"] = self.readString() 28 | fields["unk7"] = self.readVInt() 29 | fields["Thumbnail"] = self.readVInt() 30 | fields["NameColor"] = self.readVInt() 31 | fields["unk10"] = self.readVInt() 32 | fields["unk11"] = self.readVInt() 33 | fields["PlayerHighID"] = self.readInt() 34 | fields["PlayerLowID"] = self.readInt() 35 | super().decode(fields) 36 | 37 | 38 | return fields 39 | 40 | def execute(message, calling_instance, fields): 41 | fields["Socket"] = calling_instance.client 42 | Messaging.sendMessage(24113, fields, calling_instance.player) 43 | 44 | def getMessageType(self): 45 | return 15081 46 | 47 | def getMessageVersion(self): 48 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Record/CPPDefs.py: -------------------------------------------------------------------------------- 1 | class CPPDefs: 2 | # region Rotate Left 3 | @staticmethod 4 | def rotl8(value, count): 5 | return value << count | value >> (8 - count) 6 | 7 | @staticmethod 8 | def rotl16(value, count): 9 | return value << count | value >> (16 - count) 10 | 11 | @staticmethod 12 | def rotl32(value, count): 13 | return value << count | value >> (32 - count) 14 | 15 | @staticmethod 16 | def rotl64(value, count): 17 | return value << count | value >> (64 - count) 18 | 19 | @staticmethod 20 | def __ROL1__(value, count): 21 | return CPPDefs.rotl8(value, count) 22 | 23 | @staticmethod 24 | def __ROL2__(value, count): 25 | return CPPDefs.rotl16(value, count) 26 | 27 | @staticmethod 28 | def __ROL4__(value, count): 29 | return CPPDefs.rotl32(value, count) 30 | 31 | @staticmethod 32 | def __ROL8__(value, count): 33 | return CPPDefs.rotl64(value, count) 34 | # endregion 35 | 36 | # region Rotate Right 37 | @staticmethod 38 | def rotr8(value, count): 39 | return value >> count | value << (8 - count) 40 | 41 | @staticmethod 42 | def rotr16(value, count): 43 | return value >> count | value << (16 - count) 44 | 45 | @staticmethod 46 | def rotr32(value, count): 47 | return value >> count | value << (32 - count) 48 | 49 | @staticmethod 50 | def rotr64(value, count): 51 | return value >> count | value << (64 - count) 52 | 53 | @staticmethod 54 | def __ROR1__(value, count): 55 | return CPPDefs.rotr8(value, count) 56 | 57 | @staticmethod 58 | def __ROR2__(value, count): 59 | return CPPDefs.rotr16(value, count) 60 | 61 | @staticmethod 62 | def __ROR4__(value, count): 63 | return CPPDefs.rotr32(value, count) 64 | 65 | @staticmethod 66 | def __ROR8__(value, count): 67 | return CPPDefs.rotr64(value, count) 68 | # endregion -------------------------------------------------------------------------------- /Heart/Messaging.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | 3 | class Messaging: 4 | def writeHeader(message, payloadLen): 5 | message.messageBuffer += message.getMessageType().to_bytes(2, 'big', signed=True) 6 | message.messageBuffer += payloadLen.to_bytes(3, 'big', signed=True) 7 | message.messageBuffer += message.messageVersion.to_bytes(2, 'big', signed=True) 8 | 9 | def readHeader(headerBytes): 10 | headerData = [] 11 | headerData.append(int.from_bytes(headerBytes[:2], 'big', signed=True)) 12 | headerData.append(int.from_bytes(headerBytes[2:5], 'big', signed=True)) 13 | return headerData 14 | 15 | def sendMessage(messageType, fields, player=None): 16 | from Heart.Logic.LogicLaserMessageFactory import LogicLaserMessageFactory 17 | message = LogicLaserMessageFactory.createMessageByType(messageType, b'') 18 | if player is not None: 19 | message.encode(fields, player) 20 | else: 21 | message.encode(fields) 22 | Messaging.writeHeader(message, len(message.messagePayload)) 23 | message.messageBuffer += message.messagePayload 24 | try: 25 | fields["Socket"].send(message.messageBuffer) 26 | except Exception: 27 | print(traceback.format_exc()) 28 | 29 | class MessageManager: 30 | def receiveMessage(self, messageType, messagePayload): 31 | from Heart.Logic.LogicLaserMessageFactory import LogicLaserMessageFactory 32 | message = LogicLaserMessageFactory.createMessageByType(messageType, messagePayload) 33 | if message is not None: 34 | try: 35 | if message.isServerToClient(): 36 | message.encode() 37 | else: 38 | message.fields = message.decode() 39 | message.execute(self, message.fields) 40 | 41 | except Exception: 42 | print(traceback.format_exc()) 43 | if messageType > 10100: 44 | Messaging.sendMessage(23457, {"Socket": self.client}, self.player) -------------------------------------------------------------------------------- /Heart/Packets/Client/EndClientTurnMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Logic.LogicCommandManager import LogicCommandManager 2 | 3 | from Heart.Packets.PiranhaMessage import PiranhaMessage 4 | from Heart.Messaging import Messaging 5 | 6 | 7 | class EndClientTurnMessage(PiranhaMessage): 8 | def __init__(self, messageData): 9 | super().__init__(messageData) 10 | self.messageVersion = 0 11 | 12 | def encode(self, fields): 13 | pass 14 | 15 | def decode(self): 16 | fields = {} 17 | self.readBoolean() 18 | fields["Tick"] = self.readVInt() 19 | fields["Checksum"] = self.readVInt() 20 | fields["CommandsCount"] = self.readVInt() 21 | super().decode(fields) 22 | fields["Commands"] = [] 23 | for i in range(fields["CommandsCount"]): 24 | fields["Commands"].append({"ID": self.readVInt()}) 25 | if LogicCommandManager.commandExist(fields["Commands"][i]["ID"]): 26 | command = LogicCommandManager.createCommand(fields["Commands"][i]["ID"]) 27 | print("Command", LogicCommandManager.getCommandsName(fields["Commands"][i]["ID"])) 28 | if command is not None: 29 | fields["Commands"][i]["Fields"] = command.decode(self) 30 | fields["Commands"][i]["Instance"] = command 31 | return fields 32 | 33 | def execute(message, calling_instance, fields): 34 | fields["Socket"] = calling_instance.client 35 | for command in fields["Commands"]: 36 | if "Instance" not in command.keys(): 37 | return 38 | 39 | if hasattr(command["Instance"], 'execute'): 40 | command["Instance"].execute(calling_instance, command["Fields"]) 41 | if command["ID"] == 519: 42 | Messaging.sendMessage(24104, {"Socket": calling_instance.client, "ServerChecksum": 0, "ClientChecksum": 0, "Tick": 0}) 43 | 44 | def getMessageType(self): 45 | return 14102 46 | 47 | def getMessageVersion(self): 48 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Record/ByteStreamHelper.py: -------------------------------------------------------------------------------- 1 | import zlib 2 | from Heart.Logic.LogicLong import LogicLong 3 | 4 | class ByteStreamHelper: 5 | def readDataReference(self): 6 | result = [] 7 | result.append(self.readVInt()) 8 | if not result[0]: 9 | return None 10 | result.append(self.readVInt()) 11 | return result 12 | 13 | def writeDataReference(self, high=0, low=-1): 14 | self.writeVInt(high) 15 | if high != 0: 16 | self.writeVInt(low) 17 | 18 | def compress(self, data): 19 | compressedText = zlib.compress(data) 20 | self.writeInt(len(compressedText) + 4) 21 | self.writeIntLittleEndian(len(data)) 22 | self.buffer += compressedText 23 | 24 | def decompress(self): 25 | data_length = self.readInt() 26 | self.readIntLittleEndian() 27 | return zlib.decompress(self.readBytes(data_length - 4)) 28 | 29 | def decodeIntList(self): 30 | length = self.readVInt() 31 | intList = [] 32 | for i in range(length): 33 | intList.append(self.readVInt()) 34 | return intList 35 | 36 | def decodeLogicLong(self, logicLong=None): 37 | if logicLong is None: 38 | logicLong = LogicLong(0, 0) 39 | high = self.readVInt() 40 | logicLong.high = high 41 | low = self.readVInt() 42 | logicLong.low = low 43 | 44 | def decodeLogicLongList(self): 45 | length = self.readVInt() 46 | logicLongList = [] 47 | for i in range(length): 48 | logicLongList.append(LogicLong(self.readVInt(), self.readVInt())) 49 | return logicLongList 50 | 51 | def encodeIntList(self, intList): 52 | length = len(intList) 53 | self.writeVInt(length) 54 | for i in intList: 55 | self.writeVInt(i) 56 | 57 | def encodeLogicLong(self, logicLong): 58 | if logicLong is None: 59 | logicLong = LogicLong(0, 0) 60 | self.writeVInt(logicLong.getHigherInt(self)) 61 | self.writeVInt(logicLong.getLowerInt(self)) 62 | 63 | def encodeLogicLongList(self, logicLongList): 64 | length = len(logicLongList) 65 | self.writeVInt(self, length) 66 | for logicLong in logicLongList: 67 | self.writeVInt(logicLong.getHigherInt(self)) 68 | self.writeVInt(logicLong.getLowerInt(self)) -------------------------------------------------------------------------------- /Heart/Connection.py: -------------------------------------------------------------------------------- 1 | import time 2 | import threading 3 | import traceback 4 | 5 | from Heart.Utils.ClientsManager import ClientsManager 6 | from Heart.Utils.Player import Player 7 | from Heart.Messaging import MessageManager 8 | from Heart.Messaging import Messaging 9 | 10 | class Connection(threading.Thread): 11 | def __init__(self, socket, address): 12 | super().__init__() 13 | self.client = socket 14 | self.address = address 15 | self.player = Player() 16 | self.timeout = time.time() 17 | 18 | def recv(self, n): 19 | data = bytearray() 20 | while len(data) < n: 21 | packet = self.client.recv(n - len(data)) 22 | if not packet: 23 | return b'' 24 | data.extend(packet) 25 | return data 26 | 27 | def run(self): 28 | try: 29 | while True: 30 | time.sleep(0.001) 31 | messageHeader = self.client.recv(7) 32 | if len(messageHeader) >= 7: 33 | headerData = Messaging.readHeader(messageHeader) 34 | self.timeout = time.time() 35 | packetPayload = Connection.recv(self, headerData[1]) 36 | packetID = headerData[0] 37 | # print("Received", packetID, LogicLaserMessageFactory.getMessageName(packetID), "length", headerData[1], "data", packetPayload, '\n') 38 | MessageManager.receiveMessage(self, packetID, packetPayload) 39 | 40 | if time.time() - self.timeout > 7: 41 | print(f"Client with ip: {self.address} disconnected!") 42 | allSockets = ClientsManager.GetAll() 43 | if self.player.ID[1] in allSockets.keys() and allSockets[self.player.ID[1]]["Socket"] == self.client: 44 | ClientsManager.RemovePlayer(self.player.ID) 45 | self.client.close() 46 | break 47 | 48 | except ConnectionError: 49 | print(f"Client with ip: {self.address} disconnected!") 50 | allSockets = ClientsManager.GetAll() 51 | if self.player.ID[1] in allSockets.keys() and allSockets[self.player.ID[1]]["Socket"] == self.client: 52 | ClientsManager.RemovePlayer(self.player.ID) 53 | self.client.close() 54 | 55 | except Exception: 56 | print(traceback.format_exc()) -------------------------------------------------------------------------------- /Heart/Logic/LogicLong.py: -------------------------------------------------------------------------------- 1 | from typing import overload 2 | 3 | 4 | class LogicLong: 5 | def __init__(self): 6 | self.high = 0 7 | self.low = 0 8 | 9 | def __init__(self, high, low): 10 | self.high = high 11 | self.low = low 12 | 13 | @staticmethod 14 | def clone(logicLong): 15 | return LogicLong(logicLong.high, logicLong.low) 16 | 17 | def decode(self, bytestream): 18 | self.high = bytestream.readInt() 19 | self.low = bytestream.readInt() 20 | 21 | def encode(self, bytestream): 22 | bytestream.writeInt(self.high) 23 | bytestream.writeInt(self.low) 24 | 25 | def equals(self, logicLong): 26 | if logicLong: 27 | if self.low == logicLong.low: 28 | return self.high == logicLong.high 29 | return False 30 | 31 | @staticmethod 32 | def getHigherInt(longlong): 33 | return longlong >> 32 34 | 35 | @overload 36 | def getHigherInt(self): 37 | return self.high 38 | 39 | @staticmethod 40 | def getLowerInt(longlong): 41 | result = longlong & 0x7FFFFFFF 42 | if longlong < 0: 43 | return longlong | 0x80000000 44 | return result 45 | 46 | @overload 47 | def getLowerInt(self): 48 | return self.low 49 | 50 | def getLong(self): 51 | result = self.low 52 | if result >> 31 == -1: 53 | return result | 0x80000000 54 | return result 55 | 56 | def greaterThan(self, logicLong): 57 | result = False 58 | if logicLong: 59 | result = True 60 | if self.high <= logicLong.high: 61 | result = False 62 | if self.high == logicLong.high: 63 | return self.low > logicLong.low 64 | return result 65 | 66 | def hashCode(self): 67 | return 31 * self.high + self.low 68 | 69 | def isZero(self): 70 | if not self.low: 71 | return self.high == 0 72 | else: 73 | return False 74 | 75 | def set(self, low, high): 76 | lowerInt = low & 0x7FFFFFFF 77 | if low < 0: 78 | lowerInt = low | 0x80000000 79 | self.high = high >> 32 80 | self.low = lowerInt 81 | 82 | def toLong(high, low): 83 | lowerInt = low & 0x7FFFFFFF 84 | if low < 0: 85 | lowerInt = low | 0x80000000 86 | return lowerInt | high << 32 87 | 88 | def toString(text, logiclong): 89 | print(text, f"LogicLong({logiclong.high},{logiclong.low})") -------------------------------------------------------------------------------- /Heart/Packets/Server/LoginFailedMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Packets.PiranhaMessage import PiranhaMessage 2 | 3 | 4 | class LoginFailedMessage(PiranhaMessage): 5 | def __init__(self, messageData): 6 | super().__init__(messageData) 7 | self.messageVersion = 0 8 | 9 | def encode(self, fields): 10 | self.writeInt(fields['ErrorID']) 11 | self.writeString(fields['FingerprintData']) 12 | self.writeString() 13 | self.writeString(fields['ContentURL']) 14 | self.writeString() 15 | self.writeString(fields['Message']) 16 | self.writeInt(0) 17 | self.writeBoolean(False) 18 | self.writeInt(0) 19 | self.writeInt(0) 20 | self.writeInt(0) 21 | self.writeInt(0) 22 | self.writeString() 23 | self.writeInt(0) 24 | self.writeBoolean(True) 25 | self.writeBoolean(True) 26 | self.writeString() 27 | self.writeVInt(0) 28 | self.writeString() 29 | self.writeBoolean(False) 30 | 31 | def decode(self): 32 | fields = {} 33 | fields["ErrorCode"] = self.readInt() 34 | fields["ResourceFingerprintData"] = self.readString() 35 | fields["RedirectDomain"] = self.readString() 36 | fields["ContentURL"] = self.readString() 37 | fields["UpdateURL"] = self.readString() 38 | fields["Reason"] = self.readString() 39 | fields["SecondsUntilMaintenanceEnd"] = self.readInt() 40 | fields["ShowContactSupportForBan"] = self.readBoolean() 41 | fields["CompressedFingerprintData"] = self.readBytesWithoutLength() 42 | fields["ContentURLListCount"] = self.readInt() 43 | fields["ContentURLList"] = [] 44 | for i in range(fields["ContentURLListCount"]): 45 | fields["ContentURLList"].append(self.readString()) 46 | fields["KunlunAppStore"] = self.readInt() 47 | fields["MaintenanceType"] = self.readInt() 48 | fields["HelpshiftFaqId"] = self.readString() 49 | fields["Tier"] = self.readInt() 50 | fields["Unk1"] = self.readBoolean() 51 | fields["Unk2"] = self.readBoolean() 52 | fields["Unk3"] = self.readString() 53 | fields["Unk4"] = self.readVInt() 54 | fields["Unk5"] = self.readString() 55 | fields["OptionalTargetedAccountIdState"] = self.readBoolean() 56 | if fields["OptionalTargetedAccountIdState"] == True: 57 | fields["OptionalTargetedAccountId"] = self.readLong() 58 | super().decode(fields) 59 | return fields 60 | 61 | def execute(message, calling_instance, fields): 62 | pass 63 | 64 | def getMessageType(self): 65 | return 20103 66 | 67 | def getMessageVersion(self): 68 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Packets/Client/LoginMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Messaging import Messaging 2 | from DB.DatabaseHandler import DatabaseHandler 3 | from Heart.Packets.PiranhaMessage import PiranhaMessage 4 | import json 5 | from Heart.Utils.ClientsManager import ClientsManager 6 | 7 | class LoginMessage(PiranhaMessage): 8 | def __init__(self, messageData): 9 | super().__init__(messageData) 10 | self.messageVersion = 0 11 | 12 | def encode(self, fields): 13 | pass 14 | 15 | def decode(self): 16 | fields = {} 17 | fields["AccountID"] = self.readLong() 18 | fields["PassToken"] = self.readString() 19 | fields["ClientMajor"] = self.readInt() 20 | fields["ClientMinor"] = self.readInt() 21 | fields["ClientBuild"] = self.readInt() 22 | fields["ResourceSha"] = self.readString() 23 | fields["Device"] = self.readString() 24 | fields["PreferredLanguage"] = self.readDataReference() 25 | fields["PreferredDeviceLanguage"] = self.readString() 26 | fields["OSVersion"] = self.readString() 27 | fields["isAndroid"] = self.readBoolean() 28 | fields["IMEI"] = self.readString() 29 | fields["AndroidID"] = self.readString() 30 | fields["isAdvertisingEnabled"] = self.readBoolean() 31 | fields["AppleIFV"] = self.readString() 32 | fields["RndKey"] = self.readInt() 33 | fields["AppStore"] = self.readVInt() 34 | fields["ClientVersion"] = self.readString() 35 | fields["TencentOpenId"] = self.readString() 36 | fields["TencentToken"] = self.readString() 37 | fields["TencentPlatform"] = self.readVInt() 38 | fields["DeviceVerifierResponse"] = self.readString() 39 | fields["AppLicensingSignature"] = self.readString() 40 | fields["DeviceVerifierResponse"] = self.readString() 41 | super().decode(fields) 42 | return fields 43 | 44 | def execute(message, calling_instance, fields): 45 | if fields["ClientMajor"]==55: 46 | calling_instance.player.ClientVersion = f'{str(fields["ClientMajor"])}.{str(fields["ClientBuild"])}.{str(fields["ClientMinor"])}' 47 | fields["Socket"] = calling_instance.client 48 | db_instance = DatabaseHandler() 49 | if db_instance.playerExist(fields["PassToken"], fields["AccountID"]): 50 | player_data = json.loads(db_instance.getPlayerEntry(fields["AccountID"])[2]) 51 | db_instance.loadAccount(calling_instance.player, fields["AccountID"]) 52 | else: 53 | db_instance.createAccount(calling_instance.player.getDataTemplate(fields["AccountID"][0], fields["AccountID"][1], fields["PassToken"])) 54 | ClientsManager.AddPlayer(calling_instance.player.ID, calling_instance.client) 55 | Messaging.sendMessage(20104, fields, calling_instance.player) 56 | Messaging.sendMessage(24101, fields, calling_instance.player) 57 | 58 | def getMessageType(self): 59 | return 10101 60 | 61 | def getMessageVersion(self): 62 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Logic/LogicLaserMessageFactory.py: -------------------------------------------------------------------------------- 1 | from Heart.Packets.Client.ClientHelloMessage import ClientHelloMessage 2 | from Heart.Packets.Client.LoginMessage import LoginMessage 3 | from Heart.Packets.Client.ChangeAvatarNameMessage import ChangeAvatarNameMessage 4 | from Heart.Packets.Client.EndClientTurnMessage import EndClientTurnMessage 5 | from Heart.Packets.Client.GoHomeFromOfflinePractiseMessage import GoHomeFromOfflinePractiseMessage 6 | from Heart.Packets.Client.GoHomeMessage import GoHomeMessage 7 | from Heart.Packets.Client.GetPlayerProfileMessage import GetPlayerProfileMessage 8 | from Heart.Packets.Client.KeepAliveMessage import KeepAliveMessage 9 | from Heart.Packets.Server.LoginFailedMessage import LoginFailedMessage 10 | from Heart.Packets.Server.LoginOkMessage import LoginOkMessage 11 | from Heart.Packets.Server.OutOfSyncMessage import OutOfSyncMessage 12 | from Heart.Packets.Server.ServerHelloMessage import ServerHelloMessage 13 | from Heart.Packets.Server.AvailableServerCommandMessage import AvailableServerCommandMessage 14 | from Heart.Packets.Server.LobbyInfoMessage import LobbyInfoMessage 15 | from Heart.Packets.Server.OwnHomeDataMessage import OwnHomeDataMessage 16 | from Heart.Packets.Server.KeepAliveServerMessage import KeepAliveServerMessage 17 | from Heart.Packets.Server.PlayerProfileMessage import PlayerProfileMessage 18 | 19 | 20 | 21 | class LogicLaserMessageFactory: 22 | messagesList = { 23 | 10100: ClientHelloMessage, 24 | 10101: LoginMessage, 25 | 10108: KeepAliveMessage, 26 | 10212: ChangeAvatarNameMessage, 27 | 14101: GoHomeMessage, 28 | 14102: EndClientTurnMessage, 29 | 15081: GetPlayerProfileMessage, 30 | 17750: GoHomeFromOfflinePractiseMessage, 31 | 20100: ServerHelloMessage, 32 | 20103: LoginFailedMessage, 33 | 20104: LoginOkMessage, 34 | 20108: KeepAliveServerMessage, 35 | 23457: LobbyInfoMessage, 36 | 24101: OwnHomeDataMessage, 37 | 24104: OutOfSyncMessage, 38 | 24111: AvailableServerCommandMessage, 39 | 24113: PlayerProfileMessage, 40 | } 41 | 42 | def getMessageName(messageType): 43 | try: 44 | message = LogicLaserMessageFactory.messagesList[messageType] 45 | except KeyError: 46 | message = str(messageType) 47 | if type(message) == str: 48 | return message 49 | else: 50 | return message.__name__ 51 | 52 | def messageExist(messageType): 53 | return (messageType in LogicLaserMessageFactory.messagesList.keys()) 54 | 55 | def createMessageByType(messageType, messagePayload): 56 | messagesList = LogicLaserMessageFactory.messagesList 57 | if LogicLaserMessageFactory.messageExist(messageType): 58 | if type(messagesList[messageType]) == str: 59 | print(LogicLaserMessageFactory.getMessageName(messageType), "skipped") 60 | else: 61 | print(LogicLaserMessageFactory.getMessageName(messageType), "created") 62 | return messagesList[messageType](messagePayload) 63 | else: 64 | print(messageType, "skipped") 65 | return None 66 | -------------------------------------------------------------------------------- /DB/DatabaseHandler.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sqlite3 3 | import traceback 4 | 5 | class DatabaseHandler(): 6 | def __init__(self): 7 | self.conn = sqlite3.connect("DB/Files/player.sqlite") 8 | self.cursor = self.conn.cursor() 9 | try: 10 | self.cursor.execute("""CREATE TABLE main (ID int, Token text, Data json)""") 11 | except sqlite3.OperationalError: 12 | pass 13 | except Exception: 14 | print(traceback.format_exc()) 15 | 16 | def createAccount(self, data): 17 | try: 18 | self.cursor.execute("INSERT INTO main (ID, Token, Data) VALUES (?, ?, ?)", (data["ID"][1], data["Token"], json.dumps(data, ensure_ascii=0))) 19 | self.conn.commit() 20 | except Exception: 21 | print(traceback.format_exc()) 22 | 23 | def getAll(self): 24 | self.playersId = [] 25 | try: 26 | self.cursor.execute("SELECT * from main") 27 | self.db = self.cursor.fetchall() 28 | for i in range(len(self.db)): 29 | self.playersId.append(self.db[i][0]) 30 | return self.playersId 31 | except Exception: 32 | print(traceback.format_exc()) 33 | 34 | def getPlayer(self, plrId): 35 | try: 36 | self.cursor.execute("SELECT * from main where ID=?", (plrId[1],)) 37 | return json.loads(self.cursor.fetchall()[0][2]) 38 | except Exception: 39 | print(traceback.format_exc()) 40 | 41 | def getPlayerEntry(self, plrId): 42 | try: 43 | self.cursor.execute("SELECT * from main where ID=?", (plrId[1],)) 44 | return self.cursor.fetchall()[0] 45 | except IndexError: 46 | pass 47 | except Exception: 48 | print(traceback.format_exc()) 49 | 50 | def loadAccount(self, player, plrId): 51 | try: 52 | self.cursor.execute("SELECT * from main where ID=?", (plrId[1],)) 53 | playerData = json.loads(self.cursor.fetchall()[0][2]) 54 | player.ID = playerData["ID"] 55 | player.Name = playerData["Name"] 56 | #player.AllianceID = playerData["AllianceID"] 57 | player.Registered = playerData["Registered"] 58 | player.Thumbnail = playerData["Thumbnail"] 59 | player.Namecolor = playerData["Namecolor"] 60 | player.Region = playerData["Region"] 61 | player.ContentCreator = playerData["ContentCreator"] 62 | player.Coins = playerData["Coins"] 63 | player.Gems = playerData["Gems"] 64 | player.Blings = playerData["Blings"] 65 | player.Trophies = playerData["Trophies"] 66 | player.HighestTrophies = playerData["HighestTrophies"] 67 | player.TrophyRoadTier = playerData["TrophyRoadTier"] 68 | player.Experience = playerData["Experience"] 69 | player.Level = playerData["Level"] 70 | player.Tokens = playerData["Tokens"] 71 | player.TokensDoubler = playerData["TokensDoubler"] 72 | player.SelectedBrawlers = playerData["SelectedBrawlers"] 73 | player.OwnedPins = playerData["OwnedPins"] 74 | player.OwnedThumbnails = playerData["OwnedThumbnails"] 75 | player.OwnedBrawlers = playerData["OwnedBrawlers"] 76 | player.OwnedSkins = playerData["OwnedSkins"] 77 | except Exception: 78 | print(traceback.format_exc()) 79 | 80 | def updatePlayerData(self, data, calling_instance): 81 | try: 82 | self.cursor.execute("UPDATE main SET Data=? WHERE ID=?", (json.dumps(data, ensure_ascii=0), calling_instance.player.ID[1])) 83 | self.conn.commit() 84 | self.loadAccount(calling_instance.player, calling_instance.player.ID) 85 | except Exception: 86 | print(traceback.format_exc()) 87 | 88 | def playerExist(self, loginToken, loginID): 89 | try: 90 | if loginID[1] in self.getAll(): 91 | if loginToken != self.getPlayerEntry(loginID)[1]: 92 | return False 93 | return True 94 | return False 95 | except Exception: 96 | print(traceback.format_exc()) -------------------------------------------------------------------------------- /Heart/Record/ChecksumEncoder.py: -------------------------------------------------------------------------------- 1 | from Heart.Record.CPPDefs import CPPDefs 2 | from Heart.Record.Debugger import Debugger 3 | 4 | 5 | class ChecksumEncoder: 6 | def __init__(self): 7 | self.checksum = 0 8 | self.checksum2 = 0 9 | self.checksumEnabled = True 10 | 11 | def destruct(self): 12 | self.checksum = 0 13 | self.checksum2 = 0 14 | self.checksumEnabled = True 15 | 16 | def enableCheckSum(self, state): 17 | if not self.checksumEnabled or state: 18 | if not self.checksumEnabled and state: 19 | self.checksum = self.checksum2 20 | self.checksumEnabled = state 21 | else: 22 | self.checksum2 = self.checksum 23 | self.checksumEnabled = False 24 | 25 | def equals(self, checksum_instance): 26 | if not checksum_instance: 27 | return False 28 | 29 | if not checksum_instance.checksumEnabled: 30 | checksum = checksum_instance.checksum 31 | else: 32 | checksum2 = checksum_instance.checksum2 33 | 34 | if not self.checksumEnabled: 35 | checksum = self.checksum 36 | else: 37 | checksum2 = self.checksum2 38 | return checksum == checksum2 39 | 40 | def getCheckSum(self): 41 | if not self.checksumEnabled: 42 | checksum = self.checksum2 43 | else: 44 | checksum = self.checksum 45 | return checksum 46 | 47 | @staticmethod 48 | def hashCode(): 49 | Debugger.error("ChecksumEncoder hashCode not designed") 50 | return 42 51 | 52 | @staticmethod 53 | def isByteStream(): 54 | return False 55 | 56 | def isCheckSumEnabled(self): 57 | return self.checksumEnabled 58 | 59 | @staticmethod 60 | def isCheckSumOnlyMode(): 61 | return True 62 | 63 | def resetCheckSum(self): 64 | self.checksum = 0 65 | 66 | def writeBoolean(self, value): 67 | if value: integer = 13 68 | else: integer = 7 69 | self.checksum = integer + CPPDefs.__ROR4__(self.checksum, 31) 70 | 71 | def writeByte(self, value): 72 | self.checksum = CPPDefs.__ROR4__(self.checksum, 31) + value + 11 73 | 74 | def writeBytes(self, value, length): 75 | if value: integer = length + 38 76 | else: integer = 37 77 | self.checksum = CPPDefs.__ROR4__(self.checksum, 31) 78 | 79 | def writeInt8(self, value): 80 | if value + 0x80 >= 0x100: 81 | Debugger.error("") 82 | self.checksum = CPPDefs.__ROR4__(self.checksum, 31) + value + 11 83 | 84 | def writeInt16(self, value): 85 | if value + 0x8000 >= 0x10000: 86 | Debugger.error("") 87 | self.checksum = CPPDefs.__ROR4__(self.checksum, 31) + value + 19 88 | 89 | def writeInt24(self, value): 90 | if value + 0x800000 >= 0x1000000: 91 | Debugger.error("") 92 | self.checksum = (value & 0xFFFFFF) + CPPDefs.__ROR4__(self.checksum, 31) + value + 21 93 | 94 | def writeInt(self, value): 95 | self.checksum = CPPDefs.__ROR4__(self.checksum, 31) + value + 9 96 | 97 | @staticmethod 98 | def writeLong(bytestream, logicLong): 99 | logicLong.encode(bytestream) 100 | 101 | def writeLongLong(self, logicLong): 102 | self.checksum = logicLong.getLowerInt() + CPPDefs.__ROR4__(logicLong.getHigherInt() + CPPDefs.__ROR4__(self.checksum, 31) + 67, 31) + 91 103 | 104 | def writeShort(self, value): 105 | self.checksum = CPPDefs.__ROR4__(self.checksum, 31) + value + 19 106 | 107 | def writeString(self, value): 108 | checksum = CPPDefs.__ROR4__(self.checksum, 31) 109 | if value: 110 | self.checksum = checksum + len(value) + 28 111 | else: 112 | self.checksum = checksum + 27 113 | 114 | def writeStringReference(self, value): 115 | self.checksum = len(value) + CPPDefs.__ROR4__(self.checksum, 31) + 38 116 | 117 | def writeVInt(self, value): 118 | self.checksum = value + CPPDefs.__ROR4__(self.checksum, 31) + 33 119 | 120 | def writeVLong(self, high, low): 121 | self.checksum = low + CPPDefs.__ROR4__(high + CPPDefs.__ROR4__(self.checksum, 31) + 65, 31) + 88 -------------------------------------------------------------------------------- /Heart/Packets/Server/PlayerProfileMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Packets.PiranhaMessage import PiranhaMessage 2 | 3 | 4 | class PlayerProfileMessage(PiranhaMessage): 5 | def __init__(self, messageData): 6 | super().__init__(messageData) 7 | self.messageVersion = 0 8 | 9 | def encode(self, fields, player): 10 | self.writeVLong(fields["PlayerHighID"], fields["PlayerLowID"]) 11 | self.writeDataReference(16,11) # 12 | self.writeVInt(70) 13 | for i in range(70): 14 | self.writeDataReference(16, i) 15 | self.writeDataReference(0) 16 | self.writeVInt(500) # trophies 17 | self.writeVInt(1250) # highestTrophies 18 | self.writeVInt(11) #power level 19 | 20 | self.writeVInt(18) 21 | 22 | self.writeVInt(1) 23 | self.writeVInt(1) # 3v3 victories 24 | 25 | self.writeVInt(2) 26 | self.writeVInt(528859) # total exp 27 | 28 | self.writeVInt(3) 29 | self.writeVInt(3) # current trophies 30 | 31 | self.writeVInt(4) 32 | self.writeVInt(4) # highest trophies 33 | 34 | self.writeVInt(5) 35 | self.writeVInt(5) # unlocked brawler? 36 | 37 | self.writeVInt(8) 38 | self.writeVInt(6) # solo victories 39 | 40 | self.writeVInt(11) 41 | self.writeVInt(7) # duo victories 42 | 43 | self.writeVInt(9) 44 | self.writeVInt(8) # highest level robo rumble 45 | 46 | self.writeVInt(12) 47 | self.writeVInt(9) # highest level boss fight 48 | 49 | self.writeVInt(13) 50 | self.writeVInt(10) # highest power league points 51 | 52 | self.writeVInt(14) 53 | self.writeVInt(11) # some power league stuff 54 | 55 | self.writeVInt(15) 56 | self.writeVInt(12) # most challenge win 57 | 58 | self.writeVInt(16) #highest level city rampage 59 | self.writeVInt(13) 60 | 61 | self.writeVInt(18) #highest solo power league rank 62 | self.writeVInt(14) 63 | 64 | self.writeVInt(17) #highest team power league rank 65 | self.writeVInt(15) 66 | 67 | self.writeVInt(19) # highest Club league rank 68 | self.writeVInt(16) 69 | 70 | self.writeVInt(20) # number fame 71 | self.writeVInt(1000) 72 | 73 | self.writeVInt(21) 74 | self.writeVInt(502052) #v50 75 | 76 | self.writeString(player.Name) #PlayerInfo 77 | self.writeVInt(100) 78 | self.writeVInt(28000000 + player.Thumbnail) 79 | self.writeVInt(43000000 + player.Namecolor) 80 | self.writeVInt(14) 81 | 82 | self.writeBoolean(True) 83 | self.writeVInt(300) 84 | 85 | self.writeString("hello world") 86 | self.writeVInt(100) 87 | self.writeVInt(200) 88 | self.writeDataReference(29, 558) 89 | self.writeDataReference(0) 90 | self.writeDataReference(0) 91 | self.writeDataReference(0) 92 | self.writeDataReference(0) 93 | 94 | self.writeBoolean(True) #alliance 95 | self.writeLong(0,1) #alliance ID 96 | self.writeString("haccers") #alliance name 97 | self.writeDataReference(8,1) # alliance icon 98 | self.writeVInt(1) # type 99 | self.writeVInt(1) # member count 100 | self.writeVInt(10000) # total trophies 101 | self.writeVInt(1) # minimum trophies to enter 102 | self.writeDataReference(0) 103 | self.writeString("RU") #location 104 | self.writeVInt(4) # unknown 105 | self.writeBoolean(True) #is Family friendly 106 | self.writeVInt(0) 107 | 108 | 109 | self.writeDataReference(25, 1) #alliance role 110 | self.writeVInt(16) 111 | 112 | def decode(self): 113 | pass 114 | # fields = {} 115 | # fields["PlayerCount"] = self.readVInt() 116 | # fields["Text"] = self.readString() 117 | # fields["Unk1"] = self.readVInt() 118 | # super().decode(fields) 119 | return {} 120 | 121 | def execute(message, calling_instance, fields): 122 | pass 123 | 124 | def getMessageType(self): 125 | return 24113 126 | 127 | def getMessageVersion(self): 128 | return self.messageVersion -------------------------------------------------------------------------------- /Heart/Logic/LogicCommandManager.py: -------------------------------------------------------------------------------- 1 | from Heart.Commands.Client.PurchaseOfferCommand import PurchaseOfferCommand 2 | from Heart.Commands.Server.ChangeAvatarNameCommand import ChangeAvatarNameCommand 3 | from Heart.Commands.Client.SetPlayerThumbnailCommand import SetPlayerThumbnailCommand 4 | from Heart.Commands.Client.SetPlayerNameColorCommand import SetPlayerNameColorCommand 5 | 6 | class LogicCommandManager: 7 | commandsList = { 8 | 201: ChangeAvatarNameCommand, 9 | 202: 'DiamondsAddedCommand', 10 | 203: 'GiveDeliveryItemsCommand', 11 | 204: 'DayChangedCommand', 12 | 205: 'DecreaseHeroScoreCommand', 13 | 206: 'AddNotificationCommand', 14 | 207: 'ChangeResourcesCommand', 15 | 208: 'TransactionsRevokedCommand', 16 | 209: 'KeyPoolChangedCommand', 17 | 210: 'IAPChangedCommand', 18 | 211: 'OffersChangedCommand', 19 | 212: 'PlayerDataChangedCommand', 20 | 213: 'InviteBlockingChangedCommand', 21 | 214: 'GemNameChangeStateChangedCommand', 22 | 215: 'SetSupportedCreatorCommand', 23 | 216: 'CooldownExpiredCommand', 24 | 217: 'ProLeagueSeasonChangedCommand', 25 | 218: 'BrawlPassSeasonChangedCommand', 26 | 219: 'BrawlPassUnlockedCommand', 27 | 220: 'HerowinQuestsChangedCommand', 28 | 221: 'TeamChatMuteStateChangedCommand', 29 | 222: 'RankedSeasonChangedCommand', 30 | 223: 'CooldownAddedCommand', 31 | 224: 'SetESportsHubNotificationCommand', 32 | 228: 'RefreshRandomRewardsCommand', 33 | 500: 'GatchaCommand', 34 | 503: 'ClaimDailyRewardCommand', 35 | 504: 'SendAllianceMailCommand', 36 | 505: SetPlayerThumbnailCommand, 37 | 506: 'SelectSkinCommand', 38 | 507: 'UnlockSkinCommand', 39 | 508: 'ChangeControlModeCommand', 40 | 509: 'PurchaseDoubleCoinsCommand', 41 | 511: 'HelpOpenedCommand', 42 | 512: 'ToggleInGameHintsCommand', 43 | 514: 'DeleteNotificationCommand', 44 | 515: 'ClearShopTickersCommand', 45 | 517: 'ClaimRankUpRewardCommand', 46 | 518: 'PurchaseTicketsCommand', 47 | 519: PurchaseOfferCommand, 48 | 520: 'LevelUpCommand', 49 | 521: 'PurchaseHeroLvlUpMaterialCommand', 50 | 522: 'HeroSeenCommand', 51 | 523: 'ClaimAdRewardCommand', 52 | 524: 'VideoStartedCommand', 53 | 525: 'SelectCharacterCommand', 54 | 526: 'UnlockFreeSkinsCommand', 55 | 527: SetPlayerNameColorCommand, 56 | 528: 'ViewInboxNotificationCommand', 57 | 529: 'SelectStarPowerCommand', 58 | 530: 'SetPlayerAgeCommand', 59 | 531: 'CancelPurchaseOfferCommand', 60 | 532: 'ItemSeenCommand', 61 | 533: 'QuestSeenCommand', 62 | 534: 'PurchaseBrawlPassCommand', 63 | 535: 'ClaimTailRewardCommand', 64 | 536: 'PurchaseBrawlpassProgressCommand', 65 | 537: 'VanityItemSeenCommand', 66 | 538: 'SelectEmoteCommand', 67 | 539: 'BrawlPassAutoCollectWarningSeenCommand', 68 | 540: 'PurchaseChallengeLivesCommand', 69 | 541: 'ClearESportsHubNotificationCommand', 70 | 542: 'SelectGroupSkinCommand', 71 | 571: 'OpenRandomCommand' 72 | } 73 | 74 | def getCommandsName(commandType): 75 | try: 76 | command = LogicCommandManager.commandsList[commandType] 77 | except KeyError: 78 | command = str(commandType) 79 | if type(command) == str: 80 | return command 81 | else: 82 | return command.__name__ 83 | 84 | def commandExist(commandType): 85 | return (commandType in LogicCommandManager.commandsList.keys()) 86 | 87 | def createCommand(commandType, commandPayload=b''): 88 | commandList = LogicCommandManager.commandsList 89 | if LogicCommandManager.commandExist(commandType): 90 | print(LogicCommandManager.getCommandsName(commandType), "created") 91 | if type(commandList[commandType]) == str: 92 | pass 93 | else: 94 | return commandList[commandType](commandPayload) 95 | else: 96 | print(commandType, "skipped") 97 | return None 98 | 99 | def isServerToClient(commandType): 100 | if 200 <= commandType < 500: 101 | return True 102 | elif 500 <= commandType: 103 | return False 104 | -------------------------------------------------------------------------------- /Heart/Packets/Server/LoginOkMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Packets.PiranhaMessage import PiranhaMessage 2 | 3 | class LoginOkMessage(PiranhaMessage): 4 | def __init__(self, messageData): 5 | super().__init__(messageData) 6 | self.messageVersion = 1 7 | 8 | def encode(self, fields, player): 9 | self.writeLong(player.ID[0], player.ID[1]) 10 | self.writeLong(player.ID[0], player.ID[1]) 11 | self.writeString(player.Token) 12 | self.writeString() 13 | self.writeString() 14 | self.writeInt(55) 15 | self.writeInt(211) 16 | self.writeInt(1) 17 | self.writeString("dev") 18 | self.writeInt(0) 19 | self.writeInt(0) 20 | self.writeInt(0) 21 | self.writeString() 22 | self.writeString() 23 | self.writeString() 24 | self.writeInt(0) 25 | self.writeString() 26 | self.writeString("RU") 27 | self.writeString() 28 | self.writeInt(0) 29 | self.writeString() 30 | self.writeInt(2) 31 | self.writeString('https://game-assets.brawlstarsgame.com') 32 | self.writeString('http://a678dbc1c015a893c9fd-4e8cc3b1ad3a3c940c504815caefa967.r87.cf2.rackcdn.com') 33 | self.writeInt(2) 34 | self.writeString('https://event-assets.brawlstars.com') 35 | self.writeString('https://24b999e6da07674e22b0-8209975788a0f2469e68e84405ae4fcf.ssl.cf2.rackcdn.com/event-assets') 36 | self.writeVInt(0) 37 | self.writeCompressedString(b'') 38 | self.writeBoolean(True) 39 | self.writeBoolean(False) 40 | self.writeString() 41 | self.writeString() 42 | self.writeString() 43 | self.writeString('https://play.google.com/store/apps/details?id=com.supercell.brawlstars') 44 | self.writeString() 45 | self.writeBoolean(False) 46 | 47 | self.writeBoolean(False) 48 | if False: 49 | self.writeString() 50 | 51 | self.writeBoolean(False) 52 | if False: 53 | self.writeString() 54 | 55 | self.writeBoolean(False) 56 | if False: 57 | self.writeString() 58 | 59 | self.writeBoolean(False) 60 | if False: 61 | self.writeString() 62 | 63 | 64 | def decode(self): 65 | fields = {} 66 | fields["AccountID"] = self.readLong() 67 | fields["HomeID"] = self.readLong() 68 | fields["PassToken"] = self.readString() 69 | fields["FacebookID"] = self.readString() 70 | fields["GamecenterID"] = self.readString() 71 | fields["ServerMajorVersion"] = self.readInt() 72 | fields["ContentVersion"] = self.readInt() 73 | fields["ServerBuild"] = self.readInt() 74 | fields["ServerEnvironment"] = self.readString() 75 | fields["SessionCount"] = self.readInt() 76 | fields["PlayTimeSeconds"] = self.readInt() 77 | fields["DaysSinceStartedPlaying"] = self.readInt() 78 | fields["FacebookAppID"] = self.readString() 79 | fields["ServerTime"] = self.readString() 80 | fields["AccountCreatedDate"] = self.readString() 81 | fields["StartupCooldownSeconds"] = self.readInt() 82 | fields["GoogleServiceID"] = self.readString() 83 | fields["LoginCountry"] = self.readString() 84 | fields["KunlunID"] = self.readString() 85 | fields["Tier"] = self.readInt() 86 | fields["TencentID"] = self.readString() 87 | 88 | ContentUrlCount = self.readInt() 89 | fields["GameAssetsUrls"] = [] 90 | for i in range(ContentUrlCount): 91 | fields["GameAssetsUrls"].append(self.readString()) 92 | 93 | EventUrlCount = self.readInt() 94 | fields["EventAssetsUrls"] = [] 95 | for i in range(EventUrlCount): 96 | fields["EventAssetsUrls"].append(self.readString()) 97 | 98 | fields["SecondsUntilAccountDeletion"] = self.readVInt() 99 | fields["SupercellIDToken"] = self.readCompressedString() 100 | fields["IsSupercellIDLogoutAllDevicesAllowed"] = self.readBoolean() 101 | fields["isSupercellIDEligible"] = self.readBoolean() 102 | fields["LineID"] = self.readString() 103 | fields["SessionID"] = self.readString() 104 | fields["KakaoID"] = self.readString() 105 | fields["UpdateURL"] = self.readString() 106 | fields["YoozooPayNotifyUrl"] = self.readString() 107 | fields["UnbotifyEnabled"] = self.readBoolean() 108 | 109 | Unknown1 = self.readBoolean() 110 | fields["Unknown1"] = Unknown1 111 | if Unknown1: 112 | fields["Unknown2"] = self.readString() 113 | 114 | Unknown3 = self.readBoolean() 115 | fields["Unknown3"] = Unknown1 116 | if Unknown3: 117 | fields["Unknown4"] = self.readString() 118 | 119 | Unknown5 = self.readBoolean() 120 | fields["Unknown5"] = Unknown1 121 | if Unknown5: 122 | fields["Unknown6"] = self.readString() 123 | 124 | Unknown7 = self.readBoolean() 125 | fields["Unknown7"] = Unknown1 126 | if Unknown7: 127 | fields["Unknown8"] = self.readString() 128 | super().decode(fields) 129 | return fields 130 | 131 | def execute(message, calling_instance, fields): 132 | pass 133 | 134 | def getMessageType(self): 135 | return 20104 136 | 137 | def getMessageVersion(self): 138 | return self.messageVersion 139 | -------------------------------------------------------------------------------- /Heart/Utils/Player.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | import string 4 | 5 | 6 | class Player: 7 | ClientVersion = "0.0.0" 8 | 9 | ID = [0, 1] 10 | Token = "" 11 | Name = "Brawler" 12 | Registered = False 13 | Thumbnail = 0 14 | Namecolor = 0 15 | Region = "RU" 16 | ContentCreator = "BSL-V55" 17 | 18 | Coins = 99999 19 | Gems = 99999 20 | Blings = 0 21 | Trophies = 70000 22 | HighestTrophies = 70000 23 | TrophyRoadTier = 454 24 | Experience = 99999 25 | Level = 500 26 | Tokens = 200 27 | TokensDoubler = 1000 28 | 29 | SelectedSkins = {} 30 | SelectedBrawlers = [80, 80, 80] 31 | RandomizerSelectedSkins = [] 32 | OwnedPins = [] 33 | OwnedThumbnails = [] 34 | OwnedSkins = [] 35 | OwnedBrawlers = { 36 | 0: {'CardID': 0, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 37 | 1: {'CardID': 4, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 38 | 2: {'CardID': 8, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 39 | 3: {'CardID': 12, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 40 | 4: {'CardID': 16, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 41 | 5: {'CardID': 20, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 42 | 6: {'CardID': 24, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 43 | 7: {'CardID': 28, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 44 | 8: {'CardID': 32, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 45 | 9: {'CardID': 36, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 46 | 10: {'CardID': 40, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 47 | 11: {'CardID': 44, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 48 | 12: {'CardID': 48, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 49 | 13: {'CardID': 52, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 50 | 14: {'CardID': 56, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 51 | 15: {'CardID': 60, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 52 | 16: {'CardID': 64, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 53 | 17: {'CardID': 68, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 54 | 18: {'CardID': 72, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 55 | 19: {'CardID': 95, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 56 | 20: {'CardID': 100, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 57 | 21: {'CardID': 105, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 58 | 22: {'CardID': 110, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 59 | 23: {'CardID': 115, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 60 | 24: {'CardID': 120, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 61 | 25: {'CardID': 125, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 62 | 26: {'CardID': 130, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 63 | 27: {'CardID': 177, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 64 | 28: {'CardID': 182, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 65 | 29: {'CardID': 188, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 66 | 30: {'CardID': 194, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 67 | 31: {'CardID': 200, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 68 | 32: {'CardID': 206, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 69 | 34: {'CardID': 218, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 70 | 35: {'CardID': 224, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 71 | 36: {'CardID': 230, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 72 | 37: {'CardID': 236, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 73 | 38: {'CardID': 279, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 74 | 39: {'CardID': 296, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 75 | 40: {'CardID': 303, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 76 | 41: {'CardID': 320, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 77 | 42: {'CardID': 327, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 78 | 43: {'CardID': 334, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 79 | 44: {'CardID': 341, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 80 | 45: {'CardID': 358, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 81 | 46: {'CardID': 365, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 82 | 47: {'CardID': 372, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 83 | 48: {'CardID': 379, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 84 | 49: {'CardID': 386, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 85 | 50: {'CardID': 393, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 86 | 51: {'CardID': 410, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 87 | 52: {'CardID': 417, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 88 | 53: {'CardID': 427, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 89 | 54: {'CardID': 434, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 90 | 56: {'CardID': 448, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 91 | 57: {'CardID': 466, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 92 | 58: {'CardID': 474, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 93 | 59: {'CardID': 491, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 94 | 60: {'CardID': 499, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 95 | 61: {'CardID': 507, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 96 | 62: {'CardID': 515, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 97 | 63: {'CardID': 523, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 98 | 64: {'CardID': 531, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 99 | 65: {'CardID': 539, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 100 | 66: {'CardID': 547, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 101 | 67: {'CardID': 557, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 102 | 68: {'CardID': 565, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 103 | 69: {'CardID': 573, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 104 | 70: {'CardID': 581, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 105 | 71: {'CardID': 589, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 106 | 72: {'CardID': 597, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 107 | 73: {'CardID': 605, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 108 | 74: {'CardID': 619, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 109 | 75: {'CardID': 633, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 110 | 76: {'CardID': 642, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 111 | 77: {'CardID': 655, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 112 | 78: {'CardID': 663, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 113 | 79: {'CardID': 671, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 114 | 80: {'CardID': 730, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 115 | 81: {'CardID': 748, 'Trophies': 1250, 'HighestTrophies': 1250, 'PowerLevel': 1}, 116 | } 117 | 118 | def __init__(self): 119 | pass 120 | 121 | def getDataTemplate(self, highid, lowid, token): 122 | if highid == 0 or lowid == 0: 123 | self.ID[0] = int(''.join([str(random.randint(0, 9)) for _ in range(1)])) 124 | self.ID[1] = int(''.join([str(random.randint(0, 9)) for _ in range(8)])) 125 | self.Token = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(40)) 126 | else: 127 | self.ID[0] = highid 128 | self.ID[1] = lowid 129 | self.Token = token 130 | 131 | DBData = { 132 | 'ID': self.ID, 133 | 'Token': self.Token, 134 | 'Name': self.Name, 135 | 'Registered': self.Registered, 136 | 'Thumbnail': self.Thumbnail, 137 | 'Namecolor': self.Namecolor, 138 | 'Region': self.Region, 139 | 'ContentCreator': self.ContentCreator, 140 | 'Coins': self.Coins, 141 | 'Gems': self.Gems, 142 | 'Blings': self.Blings, 143 | 'Trophies': self.Trophies, 144 | 'HighestTrophies': self.HighestTrophies, 145 | 'TrophyRoadTier': self.TrophyRoadTier, 146 | 'Experience': self.Experience, 147 | 'Level': self.Level, 148 | 'Tokens': self.Tokens, 149 | 'TokensDoubler': self.TokensDoubler, 150 | 'SelectedBrawlers': self.SelectedBrawlers, 151 | 'OwnedPins': self.OwnedPins, 152 | 'OwnedThumbnails': self.OwnedThumbnails, 153 | 'OwnedBrawlers': self.OwnedBrawlers, 154 | 'OwnedSkins': self.OwnedSkins, 155 | } 156 | return DBData 157 | 158 | def toJSON(self): 159 | return json.loads(json.dumps(self, default=lambda o: o.__dict__, 160 | sort_keys=True, indent=4)) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Heart/Packets/Server/OwnHomeDataMessage.py: -------------------------------------------------------------------------------- 1 | from Heart.Record.ByteStreamHelper import ByteStreamHelper 2 | from Heart.Packets.PiranhaMessage import PiranhaMessage 3 | 4 | class OwnHomeDataMessage(PiranhaMessage): 5 | def __init__(self, messageData): 6 | super().__init__(messageData) 7 | self.messageVersion = 0 8 | 9 | def encode(self, fields, player): 10 | self.writeVInt(1688816070) 11 | self.writeVInt(1191532375) 12 | self.writeVInt(2023189) 13 | self.writeVInt(73530) 14 | 15 | self.writeVInt(player.Trophies) 16 | self.writeVInt(player.HighestTrophies) 17 | self.writeVInt(player.HighestTrophies) 18 | self.writeVInt(player.TrophyRoadTier) 19 | self.writeVInt(player.Experience) 20 | self.writeDataReference(28, player.Thumbnail) 21 | self.writeDataReference(43, player.Namecolor) 22 | 23 | self.writeVInt(26) 24 | for x in range(26): 25 | self.writeVInt(x) 26 | 27 | self.writeVInt(0) 28 | 29 | self.writeVInt(0) 30 | 31 | self.writeVInt(0) 32 | 33 | self.writeVInt(len(player.OwnedSkins)) 34 | for x in player.OwnedSkins: 35 | self.writeDataReference(29, x) 36 | 37 | self.writeVInt(0) 38 | 39 | self.writeVInt(0) 40 | 41 | self.writeVInt(0) 42 | self.writeVInt(player.HighestTrophies) 43 | self.writeVInt(0) 44 | self.writeVInt(2) 45 | self.writeBoolean(True) 46 | self.writeVInt(0) 47 | self.writeVInt(115) 48 | self.writeVInt(335442) 49 | self.writeVInt(1001442) 50 | self.writeVInt(5778642) 51 | 52 | self.writeVInt(120) 53 | self.writeVInt(200) 54 | self.writeVInt(0) 55 | 56 | self.writeBoolean(True) 57 | self.writeVInt(2) 58 | self.writeVInt(2) 59 | self.writeVInt(2) 60 | self.writeVInt(0) 61 | self.writeVInt(0) 62 | 63 | self.writeVInt(1) # Shop Offers 64 | 65 | self.writeVInt(1) # RewardCount 66 | 67 | self.writeVInt(45) # ItemType 68 | self.writeVInt(1337) # Amount 69 | self.writeDataReference(0) # CsvID 70 | self.writeVInt(0) # SkinID 71 | 72 | self.writeVInt(0) # Currency(0-Gems, 1-Gold, 3-StarpoInts) 73 | self.writeVInt(0) # Cost 74 | self.writeVInt(0) # Time 75 | self.writeVInt(0) 76 | self.writeVInt(0) 77 | self.writeBoolean(False) 78 | self.writeVInt(0) 79 | self.writeVInt(0) 80 | self.writeBoolean(False) # Daily Offer 81 | self.writeVInt(0) # Old price 82 | self.writeString('Offer') # Text 83 | self.writeVInt(0) 84 | self.writeVInt(0) 85 | self.writeString("offer_bgr_squad") # Background 86 | self.writeVInt(0) 87 | self.writeBoolean(False) # This purchase is already being processed 88 | self.writeVInt(0) # Type Benefit 89 | self.writeVInt(0) # Benefit 90 | self.writeString() 91 | self.writeBoolean(False) # One time offer 92 | self.writeBoolean(False) # Claimed 93 | self.writeDataReference(0) 94 | self.writeDataReference(0) 95 | self.writeBoolean(False) 96 | self.writeBoolean(False) 97 | self.writeBoolean(False) 98 | self.writeVInt(0) 99 | self.writeVInt(0) 100 | self.writeVInt(0) 101 | self.writeBoolean(False) 102 | self.writeBoolean(False) 103 | self.writeVInt(0) 104 | self.writeVInt(0) 105 | self.writeBoolean(False) 106 | self.writeVInt(0) 107 | self.writeVInt(0) 108 | self.writeVInt(0) 109 | self.writeVInt(0) 110 | self.writeVInt(0) 111 | self.writeBoolean(False) 112 | self.writeBoolean(False) 113 | 114 | self.writeVInt(20) 115 | self.writeVInt(1428) 116 | 117 | self.writeVInt(0) 118 | 119 | self.writeVInt(1) 120 | self.writeVInt(30) 121 | 122 | self.writeByte(1) # count brawlers selected 123 | self.writeDataReference(16, player.SelectedBrawlers[0]) # selected brawler 124 | self.writeString(player.Region) # location 125 | self.writeString(player.ContentCreator) # supported creator 126 | 127 | self.writeVInt(6) 128 | self.writeVInt(1) 129 | self.writeVInt(9) 130 | self.writeVInt(1) 131 | self.writeVInt(22) 132 | self.writeVInt(3) 133 | self.writeVInt(25) 134 | self.writeVInt(1) 135 | self.writeVInt(24) 136 | self.writeVInt(0) 137 | self.writeVInt(15) 138 | self.writeVInt(32447) 139 | self.writeVInt(28) 140 | 141 | 142 | self.writeVInt(0) 143 | 144 | self.writeVInt(1) 145 | for season in range(1): 146 | self.writeVInt(26-1) 147 | self.writeVInt(40000) 148 | self.writeBoolean(True) 149 | self.writeVInt(0) 150 | self.writeBoolean(False) 151 | self.writeBoolean(True) 152 | self.writeInt(0) 153 | self.writeInt(0) 154 | self.writeInt(0) 155 | self.writeInt(0) 156 | self.writeBoolean(True) 157 | self.writeInt(0) 158 | self.writeInt(0) 159 | self.writeInt(0) 160 | self.writeInt(0) 161 | self.writeBoolean(True) 162 | self.writeBoolean(True) 163 | self.writeInt(0) 164 | self.writeInt(0) 165 | self.writeInt(0) 166 | self.writeInt(0) 167 | 168 | self.writeVInt(0) 169 | 170 | self.writeBoolean(True) 171 | self.writeVInt(0) 172 | self.writeVInt(1) 173 | self.writeVInt(2) 174 | self.writeVInt(0) 175 | 176 | self.writeBoolean(True) # Vanity items 177 | self.writeVInt(len(player.OwnedThumbnails)+len(player.OwnedPins)) 178 | for x in player.OwnedThumbnails: 179 | self.writeVInt(28) 180 | self.writeVInt(x) 181 | self.writeVInt(0) 182 | for x in player.OwnedPins: 183 | self.writeVInt(52) 184 | self.writeVInt(x) 185 | self.writeVInt(0) 186 | 187 | 188 | self.writeBoolean(False) # Power league season data 189 | 190 | self.writeInt(0) 191 | self.writeVInt(0) 192 | self.writeDataReference(16, 80) 193 | self.writeBoolean(False) 194 | self.writeVInt(0) 195 | self.writeVInt(0) 196 | self.writeVInt(0) 197 | 198 | self.writeVInt(2023189) 199 | 200 | self.writeVInt(36) # event slot id 201 | self.writeVInt(1) 202 | self.writeVInt(2) 203 | self.writeVInt(3) 204 | self.writeVInt(4) 205 | self.writeVInt(5) 206 | self.writeVInt(6) 207 | self.writeVInt(7) 208 | self.writeVInt(8) 209 | self.writeVInt(9) 210 | self.writeVInt(10) 211 | self.writeVInt(11) 212 | self.writeVInt(12) 213 | self.writeVInt(13) 214 | self.writeVInt(14) 215 | self.writeVInt(15) 216 | self.writeVInt(16) 217 | self.writeVInt(17) 218 | self.writeVInt(18) 219 | self.writeVInt(19) 220 | self.writeVInt(20) 221 | self.writeVInt(21) 222 | self.writeVInt(22) 223 | self.writeVInt(23) 224 | self.writeVInt(24) 225 | self.writeVInt(25) 226 | self.writeVInt(26) 227 | self.writeVInt(27) 228 | self.writeVInt(28) 229 | self.writeVInt(29) 230 | self.writeVInt(30) 231 | self.writeVInt(31) 232 | self.writeVInt(32) 233 | self.writeVInt(33) 234 | self.writeVInt(34) 235 | self.writeVInt(35) 236 | self.writeVInt(36) 237 | 238 | self.writeVInt(1) 239 | 240 | self.writeVInt(-1) 241 | self.writeVInt(1) 242 | self.writeVInt(1) 243 | self.writeVInt(0) 244 | self.writeVInt(72292) 245 | self.writeVInt(10) 246 | self.writeDataReference(15, 636) # map id 247 | self.writeVInt(-1) 248 | self.writeVInt(2) 249 | self.writeString("") 250 | self.writeVInt(0) 251 | self.writeVInt(0) 252 | self.writeVInt(0) 253 | self.writeVInt(0) 254 | self.writeVInt(0) 255 | self.writeVInt(0) 256 | self.writeBoolean(False) # MapMaker map structure array 257 | self.writeVInt(0) 258 | self.writeBoolean(False) # Power League array entry 259 | self.writeVInt(0) 260 | self.writeVInt(0) 261 | self.writeBoolean(False) 262 | self.writeBoolean(False) 263 | self.writeBoolean(False) 264 | self.writeBoolean(False) 265 | self.writeVInt(-1) 266 | self.writeBoolean(False) 267 | self.writeBoolean(False) 268 | self.writeVInt(-1) 269 | self.writeVInt(0) 270 | self.writeVInt(0) 271 | self.writeVInt(0) 272 | self.writeBoolean(False) 273 | self.writeBoolean(False) 274 | 275 | self.writeVInt(0) 276 | 277 | ByteStreamHelper.encodeIntList(self, [20, 35, 75, 140, 290, 480, 800, 1250, 1875, 2800]) 278 | ByteStreamHelper.encodeIntList(self, [30, 80, 170, 360]) # Shop Coins Price 279 | ByteStreamHelper.encodeIntList(self, [300, 880, 2040, 4680]) # Shop Coins Amount 280 | 281 | self.writeVInt(0) 282 | 283 | self.writeVInt(1) 284 | self.writeVInt(41000098) # theme 285 | self.writeVInt(1) 286 | 287 | self.writeVInt(0) 288 | self.writeVInt(0) 289 | self.writeVInt(0) 290 | self.writeVInt(0) 291 | 292 | self.writeVInt(2) 293 | self.writeVInt(1) 294 | self.writeVInt(2) 295 | self.writeVInt(2) 296 | self.writeVInt(1) 297 | self.writeVInt(-1) 298 | self.writeVInt(2) 299 | self.writeVInt(1) 300 | self.writeVInt(4) 301 | 302 | ByteStreamHelper.encodeIntList(self, [0, 29, 79, 169, 349, 699]) 303 | ByteStreamHelper.encodeIntList(self, [0, 160, 450, 500, 1250, 2500]) 304 | 305 | self.writeLong(0, 1) # Player ID 306 | 307 | self.writeVInt(0) # Notification factory 308 | 309 | self.writeVInt(1) 310 | self.writeBoolean(False) 311 | self.writeVInt(0) 312 | self.writeVInt(0) 313 | self.writeVInt(0) 314 | self.writeBoolean(False) # Login Calendar 315 | self.writeVInt(0) 316 | self.writeBoolean(True) # Starr Road 317 | for i in range(7): 318 | self.writeVInt(0) 319 | 320 | self.writeVInt(0) # Mastery 321 | 322 | #BattleCard 323 | self.writeVInt(0) 324 | self.writeVInt(0) 325 | self.writeVInt(0) 326 | self.writeVInt(0) 327 | self.writeVInt(0) 328 | self.writeBoolean(False) 329 | self.writeBoolean(False) 330 | self.writeBoolean(False) 331 | self.writeBoolean(False) 332 | 333 | self.writeVInt(0) #Brawler's BattleCards 334 | 335 | self.writeVInt(5) 336 | for i in range(5): 337 | self.writeDataReference(80, i) 338 | self.writeVInt(-1) 339 | self.writeVInt(0) 340 | 341 | self.writeVInt(0) 342 | self.writeVInt(0) 343 | self.writeInt(0) 344 | self.writeVInt(0) 345 | self.writeVInt(0) 346 | self.writeVInt(86400*24) 347 | self.writeVInt(0) 348 | self.writeVInt(0) 349 | self.writeVInt(0) 350 | self.writeVInt(0) 351 | self.writeVInt(0) 352 | self.writeVInt(0) 353 | self.writeVInt(0) 354 | 355 | self.writeBoolean(False) 356 | 357 | # end LogicClientHome 358 | 359 | self.writeVLong(player.ID[0], player.ID[1]) 360 | self.writeVLong(player.ID[0], player.ID[1]) 361 | self.writeVLong(player.ID[0], player.ID[1]) 362 | self.writeStringReference(player.Name) 363 | self.writeBoolean(player.Registered) 364 | self.writeInt(-1) 365 | 366 | self.writeVInt(17) 367 | unlocked_brawler = [i['CardID'] for x,i in player.OwnedBrawlers.items()] 368 | self.writeVInt(len(unlocked_brawler) + 3) 369 | for x in unlocked_brawler: 370 | self.writeDataReference(23, x) 371 | self.writeVInt(-1) 372 | self.writeVInt(1) 373 | 374 | self.writeDataReference(5, 8) 375 | self.writeVInt(-1) 376 | self.writeVInt(player.Coins) 377 | 378 | self.writeDataReference(5, 21) 379 | self.writeVInt(-1) 380 | self.writeVInt(149100) 381 | 382 | self.writeDataReference(5, 23) 383 | self.writeVInt(-1) 384 | self.writeVInt(player.Blings) 385 | 386 | self.writeVInt(len(player.OwnedBrawlers)) # HeroScore 387 | for x,i in player.OwnedBrawlers.items(): 388 | self.writeDataReference(16, x) 389 | self.writeVInt(-1) 390 | self.writeVInt(i["Trophies"]) 391 | 392 | self.writeVInt(len(player.OwnedBrawlers)) # HeroHighScore 393 | for x,i in player.OwnedBrawlers.items(): 394 | self.writeDataReference(16, x) 395 | self.writeVInt(-1) 396 | self.writeVInt(i["HighestTrophies"]) 397 | 398 | self.writeVInt(0) # Array 399 | 400 | self.writeVInt(0) # HeroPower 401 | 402 | self.writeVInt(len(player.OwnedBrawlers)) # HeroLevel 403 | for x,i in player.OwnedBrawlers.items(): 404 | self.writeDataReference(16, x) 405 | self.writeVInt(-1) 406 | self.writeVInt(i["PowerLevel"]-1) 407 | 408 | self.writeVInt(0) # hero star power gadget and hypercharge 409 | 410 | self.writeVInt(len(player.OwnedBrawlers)) # HeroSeenState 411 | for x,i in player.OwnedBrawlers.items(): 412 | self.writeDataReference(16, x) 413 | self.writeVInt(-1) 414 | self.writeVInt(2) 415 | 416 | self.writeVInt(0) # Array 417 | self.writeVInt(0) # Array 418 | self.writeVInt(0) # Array 419 | self.writeVInt(0) # Array 420 | self.writeVInt(0) # Array 421 | self.writeVInt(0) # Array 422 | self.writeVInt(0) # Array 423 | self.writeVInt(0) # Array 424 | self.writeVInt(0) # Array 425 | 426 | self.writeVInt(player.Gems) # Diamonds 427 | self.writeVInt(player.Gems) # Free Diamonds 428 | self.writeVInt(10) # Player Level 429 | self.writeVInt(100) 430 | self.writeVInt(0) # CumulativePurchasedDiamonds or Avatar User Level Tier | 10000 < Level Tier = 3 | 1000 < Level Tier = 2 | 0 < Level Tier = 1 431 | self.writeVInt(100) # Battle Count 432 | self.writeVInt(10) # WinCount 433 | self.writeVInt(80) # LoseCount 434 | self.writeVInt(50) # WinLooseStreak 435 | self.writeVInt(20) # NpcWinCount 436 | self.writeVInt(0) # NpcLoseCount 437 | self.writeVInt(2) # TutorialState | shouldGoToFirstTutorialBattle = State == 0 438 | self.writeVInt(12) 439 | self.writeVInt(0) 440 | self.writeVInt(0) 441 | self.writeString() 442 | self.writeVInt(0) 443 | self.writeVInt(0) 444 | self.writeVInt(1) 445 | 446 | def decode(self): 447 | fields = {} 448 | return fields 449 | 450 | def execute(message, calling_instance, fields): 451 | pass 452 | 453 | def getMessageType(self): 454 | return 24101 455 | 456 | def getMessageVersion(self): 457 | return self.messageVersion 458 | -------------------------------------------------------------------------------- /Heart/Record/ByteStream.py: -------------------------------------------------------------------------------- 1 | import zlib 2 | 3 | from Heart.Record.ByteStreamHelper import ByteStreamHelper 4 | from Heart.Record.ChecksumEncoder import ChecksumEncoder 5 | from Heart.Logic.LogicStringUtil import LogicStringUtil 6 | from Heart.Record.Debugger import Debugger 7 | from Heart.Logic.LogicLong import LogicLong 8 | 9 | 10 | class ByteStream(ChecksumEncoder): 11 | def __init__(self, messageBuffer, unknown=0): 12 | super().__init__() 13 | self.messagePayload = messageBuffer 14 | self.bitoffset = 0 15 | self.offset = 0 16 | self.length = len(self.messagePayload) 17 | 18 | def clear(self, length): 19 | if self.messagePayload: 20 | self.messagePayload = b'' 21 | self.bitoffset = 0 22 | self.offset = 0 23 | 24 | def destroy(self): 25 | self.messagePayload = None 26 | self.bitoffset = 0 27 | self.offset = 0 28 | self.length = 0 29 | 30 | def ensureCapacity(self, length): 31 | offset = self.offset 32 | if len(self.messagePayload) < offset + length: 33 | buffer_copy = self.messagePayload 34 | buf_len = length 35 | self.length = buf_len 36 | self.messagePayload += bytes([0] * buf_len) 37 | 38 | def writeHexa(self, data, length): 39 | self.bitoffset = 0 40 | if data: 41 | if data.startswith('0x'): 42 | data = data[2:] 43 | 44 | self.messagePayload += bytes.fromhex(''.join(data.split()).replace('-', '')) 45 | self.offset += length 46 | 47 | def getBitOffset(self): 48 | return self.bitoffset 49 | 50 | def getByteArray(self): 51 | return self.messagePayload 52 | 53 | def getCapacityIncrement(self): 54 | return 100 55 | 56 | def getDataPointer(self): 57 | return self.messagePayload[self.offset] 58 | 59 | def getLength(self): 60 | length = self.length 61 | if self.length <= self.offset: 62 | length = self.offset 63 | return length 64 | 65 | def getOffset(self): 66 | return self.offset 67 | 68 | @staticmethod 69 | def getVIntSizeInBytes(value): 70 | if value < 0: 71 | if value > -64: 72 | return 1 73 | elif value > -8192: 74 | return 2 75 | elif value > -1048576: 76 | return 3 77 | elif value > -134217727: 78 | return 4 79 | else: 80 | return 5 81 | else: 82 | if value < 64: 83 | return 1 84 | elif value < 8192: 85 | return 2 86 | elif value < 1048576: 87 | return 3 88 | elif value < 134217727: 89 | return 4 90 | else: 91 | return 5 92 | 93 | @staticmethod 94 | def getVLongSizeInBytes(value): 95 | if value < 0: 96 | if value > -64: 97 | return 1 98 | elif value > -8192: 99 | return 2 100 | elif value > -1048576: 101 | return 3 102 | elif value > -134217727: 103 | return 4 104 | elif value > -17179869184: 105 | return 5 106 | elif value > -2199023255552: 107 | return 6 108 | elif value > -281474976710656: 109 | return 7 110 | elif value > -36028797018963968: 111 | return 8 112 | elif value > -4611686018427387903: 113 | return 9 114 | else: 115 | return 10 116 | else: 117 | if value < 64: 118 | return 1 119 | elif value < 8192: 120 | return 2 121 | elif value < 1048576: 122 | return 3 123 | elif value < 134217727: 124 | return 4 125 | elif value < 17179869184: 126 | return 5 127 | elif value < 2199023255552: 128 | return 6 129 | elif value < 281474976710656: 130 | return 7 131 | elif value < 36028797018963968: 132 | return 8 133 | elif value < 4611686018427387903: 134 | return 9 135 | else: 136 | return 10 137 | 138 | def isAtEnd(self): 139 | return len(self.messagePayload) <= self.offset 140 | 141 | @staticmethod 142 | def isByteStream(): 143 | return True 144 | 145 | @staticmethod 146 | def isCheckSumOnlyMode(): 147 | return False 148 | 149 | def readBoolean(self): 150 | bitoffset = self.bitoffset 151 | offset = self.offset + (8 - bitoffset >> 3) 152 | self.offset = offset 153 | self.bitoffset = bitoffset + 1 & 7 154 | return (1 << (bitoffset & 31) & self.messagePayload[offset - 1]) != 0 155 | 156 | def readByte(self): 157 | self.bitoffset = 0 158 | result = self.messagePayload[self.offset] 159 | self.offset += 1 160 | return result 161 | 162 | def readBytes(self, length, max=1000): 163 | self.bitoffset = 0 164 | if (length & 0x80000000) != 0: 165 | if length != -1: 166 | Debugger.warning("Negative readBytes length encountered.") 167 | elif length <= max: 168 | result = self.messagePayload[self.offset:self.offset + length] 169 | self.offset += length 170 | return bytes(result) 171 | else: 172 | Debugger.warning("readBytes too long array, max", max) 173 | return b'' 174 | 175 | def readBytesLength(self): 176 | self.bitoffset = 0 177 | result = (self.messagePayload[self.offset] << 24) 178 | result += (self.messagePayload[self.offset + 1] << 16) 179 | result += (self.messagePayload[self.offset + 2] << 8) 180 | result += (self.messagePayload[self.offset + 3]) 181 | self.offset += 4 182 | return result 183 | 184 | def readInt8(self): 185 | self.bitoffset = 0 186 | result = (self.messagePayload[self.offset]) 187 | self.offset += 1 188 | return result 189 | 190 | def readInt16(self): 191 | self.bitoffset = 0 192 | result = (self.messagePayload[self.offset] << 8) 193 | result += (self.messagePayload[self.offset + 1]) 194 | self.offset += 2 195 | return result 196 | 197 | def readInt24(self): 198 | self.bitoffset = 0 199 | result = (self.messagePayload[self.offset] << 16) 200 | result += (self.messagePayload[self.offset + 1] << 8) 201 | result += (self.messagePayload[self.offset + 2]) 202 | self.offset += 3 203 | return result 204 | 205 | def readInt(self): 206 | self.bitoffset = 0 207 | result = (self.messagePayload[self.offset] << 24) 208 | result += (self.messagePayload[self.offset + 1] << 16) 209 | result += (self.messagePayload[self.offset + 2] << 8) 210 | result += (self.messagePayload[self.offset + 3]) 211 | self.offset += 4 212 | return result 213 | 214 | def readIntLittleEndian(self): 215 | self.bitoffset = 0 216 | result = (self.messagePayload[self.offset]) 217 | result += (self.messagePayload[self.offset + 1] << 8) 218 | result += (self.messagePayload[self.offset + 2] << 16) 219 | result += (self.messagePayload[self.offset + 3] << 24) 220 | self.offset += 4 221 | return result 222 | 223 | def readLong(self, logicLong=None): 224 | if not logicLong: 225 | logicLong = LogicLong(0, 0) 226 | logicLong.decode(self) 227 | return [logicLong.high, logicLong.low] 228 | 229 | def readLongLong(self): 230 | self.bitoffset = 0 231 | high = (self.messagePayload[self.offset] << 24) 232 | high += (self.messagePayload[self.offset + 1] << 16) 233 | high += (self.messagePayload[self.offset + 2] << 8) 234 | high += (self.messagePayload[self.offset + 3]) 235 | self.offset += 4 236 | low = (self.messagePayload[self.offset] << 24) 237 | low += (self.messagePayload[self.offset + 1] << 16) 238 | low += (self.messagePayload[self.offset + 2] << 8) 239 | low += (self.messagePayload[self.offset + 3]) 240 | self.offset += 4 241 | return LogicLong.toLong(high, low) 242 | 243 | def readShort(self): 244 | self.bitoffset = 0 245 | result = (self.messagePayload[self.offset] << 8) 246 | result += (self.messagePayload[self.offset + 1]) 247 | self.offset += 2 248 | return result 249 | 250 | def readString(self, max=900000): 251 | self.bitoffset = 0 252 | length = (self.messagePayload[self.offset] << 24) 253 | length += (self.messagePayload[self.offset + 1] << 16) 254 | length += (self.messagePayload[self.offset + 2] << 8) 255 | length += (self.messagePayload[self.offset + 3]) 256 | self.offset += 4 257 | if length <= -1: 258 | if length != -1: 259 | Debugger.warning("Negative String length encountered.") 260 | return b'' 261 | elif length > max: 262 | Debugger.warning(f"Too long String encountered, length {length}, max {max}") 263 | return b'' 264 | result = bytes(self.messagePayload[self.offset:self.offset + length]).decode('utf-8') 265 | self.offset += length 266 | return result 267 | 268 | def readStringReference(self, max): 269 | self.bitoffset = 0 270 | length = (self.messagePayload[self.offset] << 24) 271 | length += (self.messagePayload[self.offset + 1] << 16) 272 | length += (self.messagePayload[self.offset + 2] << 8) 273 | length += (self.messagePayload[self.offset + 3]) 274 | self.offset += 4 275 | if length <= -1: 276 | if length != -1: 277 | Debugger.warning("Negative String length encountered.") 278 | return b'' 279 | elif length > max: 280 | Debugger.warning(f"Too long String encountered, length {length}, max {max}") 281 | return b'' 282 | result = self.messagePayload[self.offset].decode('utf-8') 283 | self.offset += length 284 | return result 285 | 286 | def readVInt(self): 287 | offset = self.offset 288 | self.bitoffset = 0 289 | v4 = offset + 1 290 | self.offset = offset + 1 291 | result = self.messagePayload[offset] & 0x3F 292 | if (self.messagePayload[offset] & 0x40) != 0: 293 | if (self.messagePayload[offset] & 0x80) != 0: 294 | self.offset = offset + 2 295 | v7 = self.messagePayload[v4] 296 | v8 = result & 0xFFFFE03F | ((v7 & 0x7F) << 6) 297 | if (v7 & 0x80) != 0: 298 | self.offset = offset + 3 299 | v9 = v8 & 0xFFF01FFF | ((self.messagePayload[offset + 2] & 0x7F) << 13) 300 | if (self.messagePayload[offset + 2] & 0x80) != 0: 301 | self.offset = offset + 4 302 | v10 = v9 & 0xF80FFFFF | ((self.messagePayload[offset + 3] & 0x7F) << 20) 303 | if (self.messagePayload[offset + 3] & 0x80) != 0: 304 | self.offset = offset + 5 305 | return v10 & 0x7FFFFFF | (self.messagePayload[offset + 4] << 27) | 0x80000000 306 | else: 307 | return v10 | 0xF8000000 308 | else: 309 | return v9 | 0xFFF00000 310 | else: 311 | return v8 | 0xFFFFE000 312 | else: 313 | return self.messagePayload[offset] | 0xFFFFFFC0 314 | elif (self.messagePayload[offset] & 0x80) != 0: 315 | self.offset = offset + 2 316 | v6 = self.messagePayload[v4] 317 | result = result & 0xFFFFE03F | ((v6 & 0x7F) << 6) 318 | if (v6 & 0x80) != 0: 319 | self.offset = offset + 3 320 | result = result & 0xFFF01FFF | ((self.messagePayload[offset + 2] & 0x7F) << 13) 321 | if (self.messagePayload[offset + 2] & 0x80) != 0: 322 | self.offset = offset + 4 323 | result = result & 0xF80FFFFF | ((self.messagePayload[offset + 3] & 0x7F) << 20) 324 | if (self.messagePayload[offset + 3] & 0x80) != 0: 325 | self.offset = offset + 5 326 | return result & 0x7FFFFFF | (self.messagePayload[offset + 4] << 27) 327 | 328 | return result 329 | 330 | def readVLong(self): 331 | result = [] 332 | result.append(self.readVInt()) 333 | result.append(self.readVInt()) 334 | return result 335 | 336 | def removeByteArray(self): 337 | self.messagePayload = b'' 338 | 339 | def reset(self): 340 | self.length = 0 341 | self.offset = 0 342 | 343 | def resetOffset(self): 344 | self.offset = 0 345 | self.bitoffset = 0 346 | 347 | def rewind(self): 348 | self.bitoffset = 0 349 | length = self.length 350 | if self.length <= self.offset: 351 | length = self.offset 352 | self.offset = 0 353 | self.length = length 354 | 355 | def setOffset(self, offset, bitoffset): 356 | self.offset = offset 357 | self.bitoffset = bitoffset 358 | 359 | def skip(self, bytes_to_skip): 360 | self.bitoffset = 0 361 | self.offset = self.offset + bytes_to_skip 362 | 363 | def writeBoolean(self, value): 364 | ChecksumEncoder.writeBoolean(self, value & 1) 365 | tempBuf = list(self.messagePayload) 366 | if self.bitoffset == 0: 367 | offset = self.offset 368 | self.offset += 1 369 | tempBuf.append(0) 370 | if (value & 1) != 0: 371 | tempBuf[self.offset - 1] = tempBuf[self.offset - 1] | 1 << (self.bitoffset & 31) 372 | self.bitoffset = self.bitoffset + 1 & 7 373 | self.messagePayload = bytes(tempBuf) 374 | 375 | def writeByte(self, value): 376 | ChecksumEncoder.writeByte(self, value) 377 | self.bitoffset = 0 378 | tempBuf = list(self.messagePayload) 379 | tempBuf.append(value & 0xFF) 380 | self.messagePayload = bytes(tempBuf) 381 | self.offset += 1 382 | 383 | def writeBytes(self, value, length): 384 | ChecksumEncoder.writeBytes(self, value, length) 385 | self.bitoffset = 0 386 | if value != 0: 387 | ByteStream.writeIntToByteArray(self, length) 388 | self.messagePayload += value 389 | self.offset += length 390 | else: 391 | ByteStream.writeIntToByteArray(self, -1) 392 | 393 | def writeInt8(self, value): 394 | ChecksumEncoder.writeInt(self, value) 395 | self.bitoffset = 0 396 | tempBuf = list(self.messagePayload) 397 | tempBuf.append(value & 0xFF) 398 | self.messagePayload = bytes(tempBuf) 399 | self.offset += 1 400 | 401 | def writeInt16(self, value): 402 | ChecksumEncoder.writeInt(self, value) 403 | self.bitoffset = 0 404 | tempBuf = list(self.messagePayload) 405 | tempBuf.append(value >> 8 & 0xFF) 406 | tempBuf.append(value & 0xFF) 407 | self.messagePayload = bytes(tempBuf) 408 | self.offset += 2 409 | 410 | def writeInt24(self, value): 411 | ChecksumEncoder.writeInt(self, value) 412 | self.bitoffset = 0 413 | tempBuf = list(self.messagePayload) 414 | tempBuf.append(value >> 16 & 0xFF) 415 | tempBuf.append(value >> 8 & 0xFF) 416 | tempBuf.append(value & 0xFF) 417 | self.messagePayload = bytes(tempBuf) 418 | self.offset += 3 419 | 420 | def writeInt(self, value): 421 | ChecksumEncoder.writeInt(self, value) 422 | ByteStream.writeIntToByteArray(self, value) 423 | 424 | def writeIntLittleEndian(self, value): 425 | self.bitoffset = 0 426 | tempBuf = list(self.messagePayload) 427 | tempBuf.append(value & 0xFF) 428 | tempBuf.append(value >> 8 & 0xFF) 429 | tempBuf.append(value >> 16 & 0xFF) 430 | tempBuf.append(value >> 24 & 0xFF) 431 | self.messagePayload = bytes(tempBuf) 432 | self.offset += 4 433 | 434 | def writeIntToByteArray(self, value): 435 | self.bitoffset = 0 436 | tempBuf = list(self.messagePayload) 437 | tempBuf.append(value >> 24 & 0xFF) 438 | tempBuf.append(value >> 16 & 0xFF) 439 | tempBuf.append(value >> 8 & 0xFF) 440 | tempBuf.append(value & 0xFF) 441 | self.messagePayload = bytes(tempBuf) 442 | self.offset += 4 443 | 444 | def writeLongLong(self, longlong): 445 | ChecksumEncoder.writeLongLong(self, longlong) 446 | self.bitoffset = 0 447 | high = LogicLong.getHigherInt(longlong) 448 | ByteStream.writeIntToByteArray(self, high) 449 | low = LogicLong.getLowerInt(longlong) 450 | ByteStream.writeIntToByteArray(self, low) 451 | 452 | def writeLong(self, high, low): 453 | self.writeIntToByteArray(high) 454 | self.writeIntToByteArray(low) 455 | 456 | def writeShort(self, value): 457 | ChecksumEncoder.writeShort(self, value) 458 | self.bitoffset = 0 459 | tempBuf = list(self.messagePayload) 460 | tempBuf.append(value >> 8 & 0xFF) 461 | tempBuf.append(value & 0xFF) 462 | self.messagePayload = bytes(tempBuf) 463 | self.offset += 2 464 | 465 | def writeString(self, value=None): 466 | ChecksumEncoder.writeString(self, value) 467 | self.bitoffset = 0 468 | if value != None: 469 | str_bytes = LogicStringUtil.getBytes(value) 470 | str_length = LogicStringUtil.getByteLength(str_bytes) 471 | if str_length < 900001: 472 | ByteStream.writeIntToByteArray(self, str_length) 473 | self.messagePayload += str_bytes 474 | self.offset += str_length 475 | else: 476 | Debugger.warning(f"ByteStream::writeString invalid string length {str_length}") 477 | ByteStream.writeIntToByteArray(self, -1) 478 | else: 479 | ByteStream.writeIntToByteArray(self, -1) 480 | 481 | def writeStringReference(self, value): 482 | ChecksumEncoder.writeStringReference(self, value) 483 | self.bitoffset = 0 484 | str_bytes = LogicStringUtil.getBytes(value) 485 | str_length = LogicStringUtil.getByteLength(str_bytes) 486 | if str_length < 900001: 487 | ByteStream.writeIntToByteArray(self, str_length) 488 | self.messagePayload += str_bytes 489 | self.offset += str_length 490 | else: 491 | Debugger.warning(f"ByteStream::writeString invalid string length {str_length}") 492 | ByteStream.writeIntToByteArray(self, -1) 493 | 494 | def writeVInt(self, data): 495 | self.bitoffset = 0 496 | if type(data) == str: 497 | data = int(data) 498 | final = b'' 499 | if (data & 2147483648) != 0: 500 | if data >= -63: 501 | final += (data & 0x3F | 0x40).to_bytes(1, 'big', signed=False) 502 | self.offset += 1 503 | elif data >= -8191: 504 | final += (data & 0x3F | 0xC0).to_bytes(1, 'big', signed=False) 505 | final += ((data >> 6) & 0x7F).to_bytes(1, 'big', signed=False) 506 | self.offset += 2 507 | elif data >= -1048575: 508 | final += (data & 0x3F | 0xC0).to_bytes(1, 'big', signed=False) 509 | final += ((data >> 6) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 510 | final += ((data >> 13) & 0x7F).to_bytes(1, 'big', signed=False) 511 | self.offset += 3 512 | elif data >= -134217727: 513 | final += (data & 0x3F | 0xC0).to_bytes(1, 'big', signed=False) 514 | final += ((data >> 6) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 515 | final += ((data >> 13) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 516 | final += ((data >> 20) & 0x7F).to_bytes(1, 'big', signed=False) 517 | self.offset += 4 518 | else: 519 | final += (data & 0x3F | 0xC0).to_bytes(1, 'big', signed=False) 520 | final += ((data >> 6) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 521 | final += ((data >> 13) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 522 | final += ((data >> 20) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 523 | final += ((data >> 27) & 0xF).to_bytes(1, 'big', signed=False) 524 | self.offset += 5 525 | else: 526 | if data <= 63: 527 | final += (data & 0x3F).to_bytes(1, 'big', signed=False) 528 | self.offset += 1 529 | elif data <= 8191: 530 | final += (data & 0x3F | 0x80).to_bytes(1, 'big', signed=False) 531 | final += ((data >> 6) & 0x7F).to_bytes(1, 'big', signed=False) 532 | self.offset += 2 533 | elif data <= 1048575: 534 | final += (data & 0x3F | 0x80).to_bytes(1, 'big', signed=False) 535 | final += ((data >> 6) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 536 | final += ((data >> 13) & 0x7F).to_bytes(1, 'big', signed=False) 537 | self.offset += 3 538 | elif data <= 134217727: 539 | final += (data & 0x3F | 0x80).to_bytes(1, 'big', signed=False) 540 | final += ((data >> 6) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 541 | final += ((data >> 13) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 542 | final += ((data >> 20) & 0x7F).to_bytes(1, 'big', signed=False) 543 | self.offset += 4 544 | else: 545 | final += (data & 0x3F | 0x80).to_bytes(1, 'big', signed=False) 546 | final += ((data >> 6) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 547 | final += ((data >> 13) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 548 | final += ((data >> 20) & 0x7F | 0x80).to_bytes(1, 'big', signed=False) 549 | final += ((data >> 27) & 0xF).to_bytes(1, 'big', signed=False) 550 | self.offset += 5 551 | 552 | self.messagePayload += final 553 | 554 | def writeVLong(self, high, low): 555 | ChecksumEncoder.writeVLong(self, high, low) 556 | self.bitoffset = 0 557 | self.writeVInt(high) 558 | self.writeVInt(low) 559 | 560 | def writeCompressedString(self, data): 561 | self.bitoffset = 0 562 | compressedText = zlib.compress(data) 563 | self.writeInt(len(compressedText) + 4) 564 | self.writeIntLittleEndian(len(data)) 565 | self.messagePayload += compressedText 566 | 567 | def readCompressedString(self): 568 | data_length = self.readInt() 569 | self.readIntLittleEndian() 570 | return zlib.decompress(self.readBytes(data_length - 4)) 571 | 572 | #region ByteStreamHelper lazy call 573 | 574 | def readDataReference(self): 575 | return ByteStreamHelper.readDataReference(self) 576 | 577 | def writeDataReference(self, high=0, low=-1): 578 | ByteStreamHelper.writeDataReference(self, high, low) 579 | 580 | def decodeIntList(self): 581 | return ByteStreamHelper.decodeIntList(self) 582 | 583 | def encodeIntList(self, intList): 584 | ByteStreamHelper.encodeIntList(self, intList) 585 | 586 | def decodeLogicLong(self, logicLong=None): 587 | return ByteStreamHelper.decodeLogicLong(self, logicLong) 588 | 589 | def encodeLogicLong(self, logicLong): 590 | ByteStreamHelper.encodeLogicLong(self, logicLong) 591 | 592 | def decodeLogicLongList(self): 593 | return ByteStreamHelper.decodeLogicLongList(self) 594 | 595 | def encodeLogicLongList(self, logicLongList): 596 | ByteStreamHelper.encodeLogicLongList(self, logicLongList) 597 | 598 | #endregion --------------------------------------------------------------------------------