├── Crypto ├── __init__.py ├── RSA.py └── RC4.py ├── gameVersion.txt ├── Resources └── version.txt ├── requirements.txt ├── .gitignore ├── Constants ├── GameIds.py ├── ClassIds.py ├── ApiPoints.py ├── Servers.py ├── StatusEffects.py ├── StatTypes.py └── PacketIds.py ├── Models ├── CharData.py ├── ConditionEffect.py └── PlayerData.py ├── Networking ├── Packets │ ├── Incoming │ │ ├── __init__.py │ │ ├── VerifyEmailPacket.py │ │ ├── PingPacket.py │ │ ├── RealmScorePacket.py │ │ ├── Unknown165Packet.py │ │ ├── TradeRequestedPacket.py │ │ ├── NewAbilityPacket.py │ │ ├── NewCharacterInformationPacket.py │ │ ├── ClaimBattlepassResponsePacket.py │ │ ├── PasswordPromptPacket.py │ │ ├── ReskinUnlockPacket.py │ │ ├── RealmHeroesResponsePacket.py │ │ ├── FilePacket.py │ │ ├── ClientStatPacket.py │ │ ├── TradeDonePacket.py │ │ ├── InvitedToGuildPacket.py │ │ ├── NameResultPacket.py │ │ ├── PlaySoundPacket.py │ │ ├── QuestRedeemResponsePacket.py │ │ ├── BuyResultPacket.py │ │ ├── FailurePacket.py │ │ ├── GuildResultPacket.py │ │ ├── QueueInformationPacket.py │ │ ├── GlobalNotificationPacket.py │ │ ├── TradeChangedPacket.py │ │ ├── CreateSuccessPacket.py │ │ ├── KeyInfoResponsePacket.py │ │ ├── PicPacket.py │ │ ├── ClaimDailyLoginResponsePacket.py │ │ ├── QuestObjIdPacket.py │ │ ├── GotoPacket.py │ │ ├── BlueprintInfoPacket.py │ │ ├── ForgeResponsePacket.py │ │ ├── QuestFetchResponsePacket.py │ │ ├── AccountListPacket.py │ │ ├── AllyShootPacket.py │ │ ├── ReconnectPacket.py │ │ ├── TradeAcceptedPacket.py │ │ ├── Unknown139Packet.py │ │ ├── CrucibleResponsePacket.py │ │ ├── InvResultPacket.py │ │ ├── TradeStartPacket.py │ │ ├── DamagePacket.py │ │ ├── NewTickPacket.py │ │ ├── AoePacket.py │ │ ├── TextPacket.py │ │ ├── DeathPacket.py │ │ ├── EnemyShootPacket.py │ │ ├── ExaltationUpdatePacket.py │ │ ├── ServerPlayerShootPacket.py │ │ ├── UpdatePacket.py │ │ ├── ShowEffectPacket.py │ │ ├── MapInfoPacket.py │ │ ├── NotificationPacket.py │ │ └── VaultInfoPacket.py │ ├── Outgoing │ │ ├── __init__.py │ │ ├── EscapePacket.py │ │ ├── QuestFetchAskPacket.py │ │ ├── UpdateAckPacket.py │ │ ├── CheckCreditsPacket.py │ │ ├── GoToQuestRoomPacket.py │ │ ├── ResetDailyQuestsPacket.py │ │ ├── CancelTradePacket.py │ │ ├── GuildRemovePacket.py │ │ ├── ReskinPacket.py │ │ ├── JoinGuildPacket.py │ │ ├── ShootAckPacket.py │ │ ├── ChooseNamePacket.py │ │ ├── CreateGuildPacket.py │ │ ├── GuildInvitePacket.py │ │ ├── PlayerTextPacket.py │ │ ├── RequestTradePacket.py │ │ ├── ShowAllyShootPacket.py │ │ ├── UsePortalPacket.py │ │ ├── KeyInfoRequestPacket.py │ │ ├── ClaimBattlepassPacket.py │ │ ├── PongPacket.py │ │ ├── BuyPacket.py │ │ ├── GotoAckPacket.py │ │ ├── LoadPacket.py │ │ ├── AoeAckPacket.py │ │ ├── ChangeGuildRankPacket.py │ │ ├── TeleportPacket.py │ │ ├── ClaimDailyLoginRewardPacket.py │ │ ├── GroundDamagePacket.py │ │ ├── EnemyShootAckPacket.py │ │ ├── PlayerHitPacket.py │ │ ├── ChangeTradePacket.py │ │ ├── ActivePetUpdateRequestPacket.py │ │ ├── InvDropPacket.py │ │ ├── SetConditionPacket.py │ │ ├── SendEmotePacket.py │ │ ├── SquareHitPacket.py │ │ ├── ChangePetSkinPacket.py │ │ ├── EditAccountListPacket.py │ │ ├── OtherHitPacket.py │ │ ├── CreatePacket.py │ │ ├── ForgeRequestPacket.py │ │ ├── CrucibleRequestPacket.py │ │ ├── UseItemPacket.py │ │ ├── InvSwapPacket.py │ │ ├── AcceptTradePacket.py │ │ ├── MovePacket.py │ │ ├── EnemyHitPacket.py │ │ ├── CreatePartyPacket.py │ │ ├── PlayerShootPacket.py │ │ └── HelloPacket.py │ ├── Packet.py │ └── PacketTypes.py ├── PacketHelper.py ├── Writer.py ├── Reader.py └── SocketManager.py ├── Accounts_ex.json ├── Plugins ├── ReloadPlugin.py ├── PlayerTrackerPlugin.py └── ReplyPlugin.py ├── Data ├── GroundTileData.py ├── MoveRecord.py ├── FameData.py ├── ObjectData.py ├── SlotObjectData.py ├── TradeItem.py ├── ObjectStatusData.py ├── WorldPosData.py ├── QuestData.py └── StatData.py ├── Helpers ├── Random.py ├── Servers.py └── Equip.py ├── README.md ├── LICENSE ├── pyrelay.py ├── ClientManager.py ├── PluginManager.py └── Client └── Client.py /Crypto/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gameVersion.txt: -------------------------------------------------------------------------------- 1 | 6.2.0.0.0 -------------------------------------------------------------------------------- /Resources/version.txt: -------------------------------------------------------------------------------- 1 | 1599033809 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodomex 2 | requests 3 | pysocks 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | Resources/Equip.xml 3 | test* 4 | Accounts.json -------------------------------------------------------------------------------- /Constants/GameIds.py: -------------------------------------------------------------------------------- 1 | tutorial = -1 2 | nexus = -2 3 | randomRealm = -3 4 | vault = -5 5 | mapTest = -6 6 | vaultExplanation = -8 7 | nexusExplanation = -9 8 | questRoomt = -11 9 | cheatersQuarantine = -13 10 | -------------------------------------------------------------------------------- /Models/CharData.py: -------------------------------------------------------------------------------- 1 | 2 | class CharData: 3 | def __init__(self): 4 | self.charIds = [] 5 | self.currentCharId = -1 6 | self.nextCharId = -1 7 | self.maxNumChars = -1 8 | 9 | -------------------------------------------------------------------------------- /Models/ConditionEffect.py: -------------------------------------------------------------------------------- 1 | from Constants.StatusEffects import * 2 | 3 | def hasEffect(condition, *effects): 4 | bits = 0 5 | for effect in effects: 6 | bits |= 1 << effect-1 7 | return (condition & bits) != 0 8 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | 4 | path = __file__.replace("__init__.py", "") 5 | 6 | for file in os.listdir(path): 7 | if "__" not in file: 8 | globals()[file[:-3]] = getattr(importlib.import_module(__name__+"."+file[:-3]), file[:-3]) 9 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | 4 | path = __file__.replace("__init__.py", "") 5 | 6 | for file in os.listdir(path): 7 | if "__" not in file: 8 | globals()[file[:-3]] = getattr(importlib.import_module(__name__+"."+file[:-3]), file[:-3]) 9 | -------------------------------------------------------------------------------- /Networking/Packets/Packet.py: -------------------------------------------------------------------------------- 1 | class Packet: 2 | send = True 3 | def __init__(self): 4 | pass 5 | 6 | def read(self, reader): 7 | pass 8 | 9 | def write(self, writer): 10 | pass 11 | 12 | def __str__(self): 13 | return str(self.__dict__) -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/EscapePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class EscapePacket(Packet): 4 | def __init__(self): 5 | self.type = "ESCAPE" 6 | 7 | def write(self, writer): 8 | pass 9 | 10 | def read(self, reader): 11 | pass 12 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/QuestFetchAskPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class QuestFetchAskPacket(Packet): 4 | def __init__(self): 5 | self.type = "QUESTFETCHASK" 6 | 7 | def write(self, writer): 8 | pass 9 | 10 | def read(self, reader): 11 | pass 12 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/VerifyEmailPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class VerifyEmailPacket(Packet): 4 | def __init__(self): 5 | self.type = "VERIFYEMAIL" 6 | 7 | def read(self, reader): 8 | pass 9 | 10 | def write(self, writer): 11 | pass 12 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/UpdateAckPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class UpdateAckPacket(Packet): 4 | def __init__(self): 5 | self.type = "UPDATEACK" 6 | 7 | def write(self, writer): 8 | return 9 | 10 | def read(self, reader): 11 | return 12 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/CheckCreditsPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class CheckCreditsPacket(Packet): 4 | def __init__(self): 5 | self.type = "CHECKCREDITS" 6 | 7 | def write(self, writer): 8 | pass 9 | 10 | def read(self, reader): 11 | pass 12 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/GoToQuestRoomPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class GoToQuestRoomPacket(Packet): 4 | def __init__(self): 5 | self.type = "GOTOQUESTROOM" 6 | 7 | def write(self, writer): 8 | pass 9 | 10 | def read(self, reader): 11 | pass 12 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ResetDailyQuestsPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ResetDailyQuestsPacket(Packet): 4 | def __init__(self): 5 | self.type = "RESETDAILYQUESTS" 6 | 7 | def write(self, writer): 8 | pass 9 | 10 | def read(self, reader): 11 | pass 12 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/CancelTradePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class CancelTradePacket(Packet): 4 | def __init__(self): 5 | self.type = "CANCELTRADE" 6 | self.objectId = 0 7 | 8 | def write(self, writer): 9 | pass 10 | 11 | def read(self, reader): 12 | pass 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/PingPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class PingPacket(Packet): 4 | def __init__(self): 5 | self.type = "PING" 6 | self.serial = 0 7 | 8 | def read(self, reader): 9 | self.serial = reader.readInt32() 10 | 11 | def write(self, writer): 12 | writer.writeInt32(self.serial) 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/RealmScorePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class RealmScorePacket(Packet): 4 | def __init__(self): 5 | self.type = "REALMSCORE" 6 | self.score = 0 7 | 8 | def read(self, reader): 9 | self.score = reader.readInt32() 10 | 11 | def write(self, writer): 12 | writer.writeInt32(self.score) 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/GuildRemovePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class GuildRemovePacket(Packet): 4 | def __init__(self): 5 | self.type = "GUILDREMOVE" 6 | self.name = "" 7 | 8 | def write(self, writer): 9 | writer.writeStr(self.name) 10 | 11 | def read(self, reader): 12 | self.name = reader.readStr() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ReskinPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ReskinPacket(Packet): 4 | def __init__(self): 5 | self.type = "RESKIN" 6 | self.skinID = 0 7 | 8 | def write(self, writer): 9 | writer.writeInt32(self.skinID) 10 | 11 | def read(self, reader): 12 | self.skinID = reader.readInt32() -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/JoinGuildPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class JoinGuildPacket(Packet): 4 | def __init__(self): 5 | self.type = "JOINGUILD" 6 | self.name = "" 7 | 8 | def write(self, writer): 9 | writer.writeStr(self.name) 10 | 11 | def read(self, reader): 12 | self.name = reader.readStr() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ShootAckPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ShootAckPacket(Packet): 4 | def __init__(self): 5 | self.type = "SHOOTACK" 6 | self.time = 0 7 | 8 | def write(self, writer): 9 | writer.writeInt32(self.time) 10 | 11 | def read(self, reader): 12 | self.time = reader.readInt32() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ChooseNamePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ChooseNamePacket(Packet): 4 | def __init__(self): 5 | self.type = "CHOOSENAME" 6 | self.name = "" 7 | 8 | def write(self, writer): 9 | writer.writeStr(self.name) 10 | 11 | def read(self, reader): 12 | self.name = reader.readStr() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/CreateGuildPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class CreateGuildPacket(Packet): 4 | def __init__(self): 5 | self.type = "CREATEGUILD" 6 | self.name = "" 7 | 8 | def write(self, writer): 9 | writer.writeStr(self.name) 10 | 11 | def read(self, reader): 12 | self.name = reader.readStr() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/GuildInvitePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class GuildInvitePacket(Packet): 4 | def __init__(self): 5 | self.type = "GUILDINVITE" 6 | self.name = "" 7 | 8 | def write(self, writer): 9 | writer.writeStr(self.name) 10 | 11 | def read(self, reader): 12 | self.name = reader.readStr() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/PlayerTextPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class PlayerTextPacket(Packet): 4 | def __init__(self): 5 | self.type = "PLAYERTEXT" 6 | self.text = "" 7 | 8 | def write(self, writer): 9 | writer.writeStr(self.text) 10 | 11 | def read(self, reader): 12 | self.text = reader.readStr() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/Unknown165Packet.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class Unknown165Packet(Packet): 4 | def __init__(self): 5 | self.type = "UNKNOWN165" 6 | self.unknownStr = "" 7 | 8 | def read(self, reader): 9 | self.unknownStr = reader.readStr() 10 | 11 | def write(self, writer): 12 | writer.writeStr(self.unknownStr) 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/RequestTradePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class RequestTradePacket(Packet): 4 | def __init__(self): 5 | self.type = "REQUESTTRADE" 6 | self.name = "" 7 | 8 | def write(self, writer): 9 | writer.writeStr(self.name) 10 | 11 | def read(self, reader): 12 | self.name = reader.readStr() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ShowAllyShootPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ShowAllyShootPacket(Packet): 4 | def __init__(self): 5 | self.type = "SHOWALLYSHOOT" 6 | self.toggle = 0 7 | 8 | def write(self, writer): 9 | writer.writeInt32(self.toggle) 10 | 11 | def read(self, reader): 12 | self.toggle = reader.readInt32() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/TradeRequestedPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class TradeRequestedPacket(Packet): 4 | def __init__(self): 5 | self.type = "TRADEREQUESTED" 6 | self.name = "" 7 | 8 | def read(self, reader): 9 | self.name = reader.readStr() 10 | 11 | def write(self, writer): 12 | writer.writeStr(self.name) 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/UsePortalPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class UsePortalPacket(Packet): 4 | def __init__(self): 5 | self.type = "USEPORTAL" 6 | self.objectId = 0 7 | 8 | def write(self, writer): 9 | writer.writeInt32(self.objectId) 10 | 11 | def read(self, reader): 12 | self.objectId = reader.readInt32() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/NewAbilityPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class NewAbilityPacket(Packet): 4 | def __init__(self): 5 | self.type = "NEWABILITY" 6 | self.abilityType = 0 7 | 8 | def read(self, reader): 9 | self.abilityType = reader.readInt32() 10 | 11 | def write(self, writer): 12 | writer.writeInt32(self.abilityType) 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/KeyInfoRequestPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class KeyInfoRequestPacket(Packet): 4 | def __init__(self): 5 | self.type = "KEYINFOREQUEST" 6 | self.itemType = 0 7 | 8 | def write(self, writer): 9 | writer.writeInt32(self.itemType) 10 | 11 | def read(self, reader): 12 | self.itemType = reader.readInt32() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ClaimBattlepassPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ClaimBattlepassPacket(Packet): 4 | def __init__(self): 5 | self.type = "CLAIMBATTLEPASS" 6 | self.item = -1#Index of item or -1 for all 7 | 8 | def write(self, writer): 9 | writer.writeByte(self.item) 10 | 11 | def read(self, reader): 12 | self.item = reader.readByte() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/NewCharacterInformationPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class NewCharacterInformationPacket(Packet): 4 | def __init__(self): 5 | self.type = "NEWCHARACTERINFORMATION" 6 | self.charXML = "" 7 | 8 | def read(self, reader): 9 | self.charXML = reader.readStr() 10 | 11 | def write(self, writer): 12 | writer.writeStr(self.charXML) 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ClaimBattlepassResponsePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ClaimBattlepassResponsePacket(Packet): 4 | def __init__(self): 5 | self.type = "CLAIMBATTLEPASSRESPONSE" 6 | self.success = False 7 | 8 | def write(self, writer): 9 | writer.writeBool(self.success) 10 | 11 | def read(self, reader): 12 | self.success = reader.readBool() 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/PasswordPromptPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class PasswordPromptPacket(Packet): 4 | def __init__(self): 5 | self.type = "PASSWORDPROMPT" 6 | self.cleanPasswordStatus = 0 7 | 8 | def read(self, reader): 9 | self.cleanPasswordStatus = reader.readInt32() 10 | 11 | def write(self, writer): 12 | writer.writeInt32(self.cleanPasswordStatus) 13 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ReskinUnlockPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ReskinUnlockPacket(Packet): 4 | def __init__(self): 5 | self.type = "RESKINUNLOCK" 6 | self.isPetSkin = 0 7 | 8 | def read(self, reader): 9 | self.isPetSkin = reader.readInt32() 10 | 11 | def write(self, writer): 12 | writer.writeInt32(self.skinID) 13 | writer.writeInt32(self.isPetSkin) 14 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/RealmHeroesResponsePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class RealmHeroesResponsePacket(Packet): 4 | def __init__(self): 5 | self.type = "REALMHEROESRESPONSE" 6 | self.numberOfRealmHeros = 0 7 | 8 | def read(self, reader): 9 | self.numberOfRealmHeros = reader.readInt32() 10 | 11 | def write(self, writer): 12 | writer.writeInt32(self.numberOfRealmHeros) 13 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/PongPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class PongPacket(Packet): 4 | def __init__(self): 5 | self.type = "PONG" 6 | self.serial = 0 7 | self.time = 0 8 | 9 | def write(self, writer): 10 | writer.writeInt32(self.serial) 11 | writer.writeInt32(self.time) 12 | 13 | def read(self, reader): 14 | self.serial = reader.readInt32() 15 | self.time = reader.readInt32() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/FilePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class FilePacket(Packet): 4 | def __init__(self): 5 | self.type = "FILE" 6 | self.fileName = "" 7 | self.file = "" 8 | 9 | def read(self, reader): 10 | self.fileName = reader.readStr() 11 | self.file = reader.readStr32() 12 | 13 | def write(self, writer): 14 | writer.writeStr(self.fileName) 15 | writer.writeStr32(self.file) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ClientStatPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ClientStatPacket(Packet): 4 | def __init__(self): 5 | self.type = "CLIENTSTAT" 6 | self.name = "" 7 | self.value = 0 8 | 9 | def read(self, reader): 10 | self.name = reader.readStr() 11 | self.value = reader.readInt32() 12 | 13 | def write(self, writer): 14 | writer.writeStr(self.name) 15 | writer.writeInt32(self.value) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/BuyPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class BuyPacket(Packet): 4 | def __init__(self): 5 | self.type = "BUY" 6 | self.objectId = 0 7 | self.quantity = 0 8 | 9 | def write(self, writer): 10 | writer.writeInt32(self.objectId) 11 | writer.writeInt32(self.quantity) 12 | 13 | def read(self, reader): 14 | self.objectId = reader.readInt32() 15 | self.quantity = reader.readInt32() 16 | -------------------------------------------------------------------------------- /Constants/ClassIds.py: -------------------------------------------------------------------------------- 1 | 2 | ROGUE = 768 3 | ARCHER = 775 4 | WIZARD = 782 5 | PRIEST = 784 6 | WARRIOR = 797 7 | KNIGHT = 798 8 | PALADIN = 799 9 | ASSASSIN = 800 10 | NECROMANCER = 801 11 | HUNTRESS = 802 12 | MYSTIC = 803 13 | TRICKSTER = 804 14 | SORCERER = 805 15 | NINJA = 806 16 | SAMURAI = 785 17 | BARD = 796 18 | SUMMONER = 817 19 | KENSEI = 818 20 | 21 | ALL = [ROGUE, ARCHER, WIZARD, PRIEST, WARRIOR, KNIGHT, PALADIN, ASSASSIN, NECROMANCER, HUNTRESS, MYSTIC, TRICKSTER, SORCERER, NINJA, SAMURAI, BARD, SUMMONER, KENSEI] -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/GotoAckPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class GotoAckPacket(Packet): 4 | def __init__(self): 5 | self.type = "GOTOACK" 6 | self.time = 0 7 | self.unknownByte = 0 8 | 9 | def write(self, writer): 10 | writer.writeInt32(self.time) 11 | writer.writeByte(self.unknownByte) 12 | 13 | def read(self, reader): 14 | self.time = reader.readInt32() 15 | self.unknownByte = reader.readByte() 16 | -------------------------------------------------------------------------------- /Accounts_ex.json: -------------------------------------------------------------------------------- 1 | [ 2 | {//Normal account 3 | "alias": "0", 4 | "guid": "", 5 | "password": "", 6 | "server": "EUEast" 7 | }, 8 | {//For steam 9 | "alias": "1", 10 | "guid": "", 11 | "secret": "", 12 | "server": "EUEast" 13 | }, 14 | {//Proxies 15 | "alias": "2", 16 | "guid": "", 17 | "password": "", 18 | "server": "EUEast", 19 | "proxy": { 20 | "host": "127.0.0.1", 21 | "port": 8080, 22 | "type": 5, 23 | "username": "username", 24 | "password": "password" 25 | } 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/TradeDonePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class TradeDonePacket(Packet): 4 | def __init__(self): 5 | self.type = "TRADEDONE" 6 | self.code = 0 7 | self.description = "" 8 | 9 | def read(self, reader): 10 | self.code = reader.readInt32() 11 | self.description = reader.readStr() 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.code) 15 | writer.writeStr(self.description) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/LoadPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class LoadPacket(Packet): 4 | def __init__(self): 5 | self.type = "LOAD" 6 | self.charId = 0 7 | self.isFromArena = False 8 | 9 | def write(self, writer): 10 | writer.writeInt32(self.charId) 11 | writer.writeBool(self.isFromArena) 12 | 13 | def read(self, reader): 14 | self.charId = reader.readInt32() 15 | self.isFromArena = reader.readBool() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/InvitedToGuildPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class InvitedToGuildPacket(Packet): 4 | def __init__(self): 5 | self.type = "INVITEDTOGUILD" 6 | self.name = "" 7 | self.guildName = "" 8 | 9 | def read(self, reader): 10 | self.name = reader.readStr() 11 | self.guildName = reader.readStr() 12 | 13 | def write(self, writer): 14 | writer.writeStr(self.name) 15 | writer.writeStr(self.guildName) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/NameResultPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class NameResultPacket(Packet): 4 | def __init__(self): 5 | self.type = "NAMERESULT" 6 | self.success = False 7 | self.errorText = "" 8 | 9 | def read(self, reader): 10 | self.success = reader.readBool() 11 | self.errorText = reader.readStr() 12 | 13 | def write(self, writer): 14 | writer.writeBool(self.success) 15 | writer.writeStr(self.errorText) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/AoeAckPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class AoeAckPacket(Packet): 5 | def __init__(self): 6 | self.type = "AOEACK" 7 | self.time = 0 8 | self.pos = WorldPosData() 9 | 10 | def write(self, writer): 11 | writer.writeInt32(self.time) 12 | self.pos.write(writer) 13 | 14 | def read(self, reader): 15 | self.time = reader.readInt32() 16 | self.pos.read(reader) 17 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ChangeGuildRankPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ChangeGuildRankPacket(Packet): 4 | def __init__(self): 5 | self.type = "CHANGEGUILDRANK" 6 | self.name = "" 7 | self.guildRank = 0 8 | 9 | def write(self, writer): 10 | writer.writeStr(self.name) 11 | writer.writeByte(self.guildRank) 12 | 13 | def read(self, reader): 14 | self.name = reader.readStr() 15 | self.guildRank = reader.readByte() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/TeleportPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class TeleportPacket(Packet): 4 | def __init__(self): 5 | self.type = "TELEPORT" 6 | self.objectId = 0 7 | self.playerName = "" 8 | 9 | def write(self, writer): 10 | writer.writeInt32(self.objectId) 11 | writer.writeStr(self.playerName) 12 | 13 | def read(self, reader): 14 | self.objectId = reader.readInt32() 15 | self.playerName = reader.readStr() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/PlaySoundPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class PlaySoundPacket(Packet): 4 | def __init__(self): 5 | self.type = "PLAYSOUND" 6 | self.ownerId = 0 7 | self.soundId = 0 8 | 9 | def read(self, reader): 10 | self.ownerId = reader.readInt32() 11 | self.soundId = reader.readUnsignedByte() 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.ownerId) 15 | writer.writeUnsignedByte(self.soundId) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/QuestRedeemResponsePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class QuestRedeemResponsePacket(Packet): 4 | def __init__(self): 5 | self.type = "QUESTREDEEMRESPONSE" 6 | self.ok = False 7 | self.message = "" 8 | 9 | def read(self, reader): 10 | self.ok = reader.readBool() 11 | self.message = reader.readStr() 12 | 13 | def write(self, writer): 14 | writer.writeBool(self.ok) 15 | writer.writeStr(self.message) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/BuyResultPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class BuyResultPacket(Packet): 4 | def __init__(self): 5 | self.type = "BUYRESULT" 6 | self.result = 0 7 | self.resultString = "" 8 | 9 | def read(self, reader): 10 | self.result = reader.readInt32() 11 | self.resultString = reader.readStr() 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.result) 15 | writer.writeStr(self.resultString) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/FailurePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class FailurePacket(Packet): 4 | def __init__(self): 5 | self.type = "FAILURE" 6 | self.errorId = 0 7 | self.errorDescription = "" 8 | 9 | def read(self, reader): 10 | self.errorId = reader.readInt32() 11 | self.errorDescription = reader.readStr() 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.errorId) 15 | writer.writeStr(self.errorDescription) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ClaimDailyLoginRewardPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ClaimDailyLoginRewardPacket(Packet): 4 | def __init__(self): 5 | self.type = "CLAIMDAILYLOGINREWARD" 6 | self.claimStr = "" 7 | self.claimType = "" 8 | 9 | def write(self, writer): 10 | writer.writeStr(self.claimStr) 11 | writer.writeStr(self.claimType) 12 | 13 | def read(self, reader): 14 | self.claimStr = reader.readStr() 15 | self.claimType = reader.readStr() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/GuildResultPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class GuildResultPacket(Packet): 4 | def __init__(self): 5 | self.type = "GUILDRESULT" 6 | self.success = False 7 | self.lineBuilderJSON = "" 8 | 9 | def read(self, reader): 10 | self.success = reader.readBool() 11 | self.lineBuilderJSON = reader.readStr() 12 | 13 | def write(self, writer): 14 | writer.writeBool(self.success) 15 | writer.writeStr(self.lineBuilderJSON) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/GroundDamagePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class GroundDamagePacket(Packet): 5 | def __init__(self): 6 | self.type = "GROUNDDAMAGE" 7 | self.time = 0 8 | self.pos = WorldPosData() 9 | 10 | def write(self, writer): 11 | writer.writeInt32(self.time) 12 | self.pos.write(writer) 13 | 14 | def read(self, reader): 15 | self.time = reader.readInt32() 16 | self.pos.read(reader) 17 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/EnemyShootAckPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class EnemyShootAckPacket(Packet): 4 | def __init__(self): 5 | self.type = "ENEMYSHOOTACK" 6 | self.time = 0 7 | self.numEnemies = 0#How many enemies shot since last ack 8 | 9 | def write(self, writer): 10 | writer.writeInt32(self.time) 11 | writer.writeShort(self.numEnemies) 12 | 13 | def read(self, reader): 14 | self.time = reader.readInt32() 15 | self.numEnemies = reader.readShort() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/QueueInformationPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | 4 | class QueueInformationPacket(Packet): 5 | def __init__(self): 6 | self.type = "QUEUEINFORMATION" 7 | self.curPos = -1 8 | self.maxPos = -1 9 | 10 | def read(self, reader): 11 | self.curPos = reader.readUnsignedShort() 12 | self.maxPos = reader.readUnsignedShort() 13 | 14 | def write(self, writer): 15 | writer.writeUnsignedShort(self.curPos) 16 | writer.writeUnsignedShort(self.maxPos) 17 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/GlobalNotificationPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class GlobalNotificationPacket(Packet): 4 | def __init__(self): 5 | self.type = "GLOBALNOTIFICATION" 6 | self.notificationType = 0 7 | self.text = "" 8 | 9 | def read(self, reader): 10 | self.notificationType = reader.readInt32() 11 | self.text = reader.readStr() 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.notificationType) 15 | writer.writeStr(self.text) 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/PlayerHitPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class PlayerHitPacket(Packet): 5 | def __init__(self): 6 | self.type = "PLAYERHIT" 7 | self.bulletId = 0 8 | self.objectId = 0 9 | 10 | def write(self, writer): 11 | writer.writeShort(self.bulletId) 12 | writer.writeInt32(self.objectId) 13 | 14 | def read(self, reader): 15 | self.bulletId = reader.readShort() 16 | self.objectId = reader.readInt32() 17 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ChangeTradePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ChangeTradePacket(Packet): 4 | def __init__(self): 5 | self.type = "CHANGETRADE" 6 | self.offer = [] 7 | 8 | def write(self, writer): 9 | writer.writeShort(len(self.offer)) 10 | for i in self.offer: 11 | writer.writeBool(i) 12 | 13 | def read(self, reader): 14 | offerLen = reader.readShort() 15 | for i in range(offerLen): 16 | self.offer.append(reader.readBool()) 17 | -------------------------------------------------------------------------------- /Plugins/ReloadPlugin.py: -------------------------------------------------------------------------------- 1 | from PluginManager import hook, plugin, reloadPlugins 2 | 3 | 4 | #Reloads all plugins, keep in mind all class variables of plugins gets reset 5 | @plugin(active=True) 6 | class ReloadPlugin: 7 | def __init__(self): 8 | self.owner = "" 9 | 10 | @hook("text") 11 | def onText(self, client, packet): 12 | if packet.name == self.owner and packet.recipient == client.playerData.name: 13 | if packet.text.lower() == "reload": 14 | print("Reloading plugins...") 15 | reloadPlugins() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ActivePetUpdateRequestPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ActivePetUpdateRequestPacket(Packet): 4 | def __init__(self): 5 | self.type = "ACTIVEPETUPDATEREQUEST" 6 | self.commandType = 0 7 | self.instanceId = 0 8 | 9 | def write(self, writer): 10 | writer.writeByte(self.commandType) 11 | writer.writeInt32(self.instanceId) 12 | 13 | def read(self, reader): 14 | self.commandType = reader.readByte() 15 | self.instanceId = reader.readInt32() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/InvDropPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.SlotObjectData import * 3 | 4 | class InvDropPacket(Packet): 5 | def __init__(self): 6 | self.type = "INVDROP" 7 | self.slotObject = SlotObjectData() 8 | self.unknownByte = -1 9 | 10 | def write(self, writer): 11 | self.slotObject.write(writer) 12 | writer.writeByte(self.unknownByte) 13 | 14 | def read(self, reader): 15 | self.slotObject.read(reader) 16 | self.unknownByte = reader.readByte() 17 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/SetConditionPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class SetConditionPacket(Packet): 4 | def __init__(self): 5 | self.type = "SETCONDITION" 6 | self.conditionEffect = 0 7 | self.conditionDuration = 0 8 | 9 | def write(self, writer): 10 | writer.writeByte(self.conditionEffect) 11 | writer.writeFloat(self.conditionDuration) 12 | 13 | def read(self, reader): 14 | self.conditionEffect = reader.readByte() 15 | self.conditionDuration = reader.readFloat() 16 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/TradeChangedPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class TradeChangedPacket(Packet): 4 | def __init__(self): 5 | self.type = "TRADECHANGED" 6 | self.offer = [] 7 | 8 | def read(self, reader): 9 | offer_len = reader.readShort() 10 | for i in range(offer_len): 11 | self.offer.append(reader.readBool()) 12 | 13 | def write(self, writer): 14 | writer.writeShort(len(self.offer)) 15 | for i in range(len(self.offer)): 16 | writer.writeBool(self.offer[i]) 17 | -------------------------------------------------------------------------------- /Data/GroundTileData.py: -------------------------------------------------------------------------------- 1 | 2 | class GroundTileData: 3 | def __init__(self, x=0, y=0, type=0): 4 | self.x = x 5 | self.y = y 6 | self.type = type 7 | 8 | def read(self, reader): 9 | self.x = reader.readShort() 10 | self.y = reader.readShort() 11 | self.type = reader.readUnsignedShort() 12 | 13 | def write(self, writer): 14 | writer.writeShort(self.x) 15 | writer.writeShort(self.y) 16 | writer.writeUnsignedShort(self.type) 17 | 18 | def clone(self): 19 | return GroundTileData(self.x, self.y, self.type) 20 | -------------------------------------------------------------------------------- /Networking/PacketHelper.py: -------------------------------------------------------------------------------- 1 | import Networking.Packets.PacketTypes as PacketTypes 2 | 3 | packetTypes = PacketTypes.PacketTypes() 4 | 5 | def createPacket(packetType): 6 | if type(packetType) != str: 7 | raise ValueError("Packet type have to be of type str not " + str(type(packetType))) 8 | packetType = packetType.upper() 9 | if not isValidPacket(packetType): 10 | raise ValueError("Invalid Packet type: " + packetType) 11 | return packetTypes.packet_dict[packetType]() 12 | 13 | def isValidPacket(packetType): 14 | return packetType in packetTypes.packet_dict.keys() 15 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/SendEmotePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class SendEmotePacket(Packet): 4 | def __init__(self): 5 | self.type = "SENDEMOTE" 6 | self.emoteId = -1 7 | self.time = -1 8 | self.unknownByte = -1 9 | 10 | def write(self, writer): 11 | writer.writeInt32(self.emoteId) 12 | writer.writeInt32(self.time) 13 | writer.writeByte(self.unknownByte) 14 | 15 | def read(self, reader): 16 | self.emoteId = reader.readInt32() 17 | self.time = reader.readInt32() 18 | self.unknownByte = reader.readByte() 19 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/SquareHitPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class SquareHitPacket(Packet): 4 | def __init__(self): 5 | self.type = "SQUAREHIT" 6 | self.time = 0 7 | self.bulletId = 0 8 | self.objectId = 0 9 | 10 | def write(self, writer): 11 | writer.writeInt32(self.time) 12 | writer.writeShort(self.bulletId) 13 | writer.writeInt32(self.objectId) 14 | 15 | def read(self, reader): 16 | self.time = reader.readInt32() 17 | self.bulletId = reader.readShort() 18 | self.objectId = reader.readInt32() 19 | -------------------------------------------------------------------------------- /Helpers/Random.py: -------------------------------------------------------------------------------- 1 | 2 | class Random: 3 | def __init__(self, seed=0): 4 | self.seed = seed 5 | 6 | def nextInt(self, min, max): 7 | if min == max: 8 | return min 9 | return min + self.generate() % (max-min) 10 | 11 | def generate(self): 12 | lo = 16807 * (self.seed & 65535) 13 | hi = 16807 * (self.seed >> 16) 14 | lo += (hi & 32767) << 16 15 | lo += hi >> 15 16 | if lo > 2147483647: 17 | lo -= 2147483647 18 | self.seed = lo 19 | return self.seed 20 | 21 | def setSeed(self, seed): 22 | self.seed = seed 23 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/CreateSuccessPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class CreateSuccessPacket(Packet): 4 | def __init__(self): 5 | self.type = "CREATESUCCESS" 6 | self.objectId = 0 7 | self.charId = 0 8 | self.PCStats = "" 9 | 10 | def read(self, reader): 11 | self.objectId = reader.readInt32() 12 | self.charId = reader.readInt32() 13 | self.PCStats = reader.readStr() 14 | 15 | def write(self, writer): 16 | writer.writeInt32(self.objectId) 17 | writer.writeInt32(self.charId) 18 | writer.writeStr(self.PCStats) 19 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/KeyInfoResponsePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class KeyInfoResponsePacket(Packet): 4 | def __init__(self): 5 | self.type = "KEYINFORESPONSE" 6 | self.name = "" 7 | self.description = "" 8 | self.creator = "" 9 | 10 | def read(self, reader): 11 | self.name = reader.readStr() 12 | self.description = reader.readStr() 13 | self.creator = reader.readStr() 14 | 15 | def write(self, writer): 16 | writer.writeStr(self.name) 17 | writer.writeStr(self.description) 18 | writer.writeStr(self.creator) 19 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/PicPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class PicPacket(Packet): 4 | def __init__(self): 5 | self.type = "PIC" 6 | self.width = 0 7 | self.height = 0 8 | self.bitmapData = [] 9 | 10 | def read(self, reader): 11 | self.width = reader.readInt32() 12 | self.height = reader.readInt32() 13 | self.bitmapData = reader.readBytes(self.width * self.height * 4) 14 | 15 | def write(self, writer): 16 | writer.writeInt32(self.width) 17 | writer.writeInt32(self.height) 18 | writer.writeBytes(self.bitmapData) 19 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ChangePetSkinPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ChangePetSkinPacket(Packet): 4 | def __init__(self): 5 | self.type = "CHANGEPETSKIN" 6 | self.petId = 0 7 | self.skinType = 0 8 | self.currency = 0 9 | 10 | def write(self, writer): 11 | writer.writeInt32(self.petId) 12 | writer.writeInt32(self.skinType) 13 | writer.writeInt32(self.currency) 14 | 15 | def read(self, reader): 16 | self.petId = reader.readInt32() 17 | self.skinType = reader.readInt32() 18 | self.currency = reader.readInt32() 19 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ClaimDailyLoginResponsePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ClaimDailyLoginResponsePacket(Packet): 4 | def __init__(self): 5 | self.type = "CLAIMDAILYLOGINRESPONSE" 6 | self.itemId = 0 7 | self.quantity = 0 8 | self.gold = 0 9 | 10 | def read(self, reader): 11 | self.itemId = reader.readInt32() 12 | self.quantity = reader.readInt32() 13 | self.gold = reader.readInt32() 14 | 15 | def write(self, writer): 16 | writer.writeInt32(self.itemId) 17 | writer.writeInt32(self.quantity) 18 | writer.writeInt32(self.gold) 19 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/QuestObjIdPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class QuestObjIdPacket(Packet): 4 | def __init__(self): 5 | self.type = "QUESTOBJID" 6 | self.objectId = 0 7 | self.unknownBytes = [] 8 | 9 | def read(self, reader): 10 | self.objectId = reader.readInt32() 11 | for i in range(reader.bytesAvailable()): 12 | self.unknownBytes.append(reader.readByte()) 13 | 14 | def write(self, writer): 15 | writer.writeInt32(self.objectId) 16 | for i in range(len(self.unknownBytes)): 17 | writer.writeByte(self.unknownBytes[i]) 18 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/EditAccountListPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class EditAccountListPacket(Packet): 4 | def __init__(self): 5 | self.type = "EDITACCOUNTLIST" 6 | self.accountListId = 0 7 | self.add = False 8 | self.objectId = 0 9 | 10 | def write(self, writer): 11 | writer.writeInt32(self.accountListId) 12 | writer.writeBool(self.add) 13 | writer.writeInt32(self.objectId) 14 | 15 | def read(self, reader): 16 | self.accountListId = reader.readInt32() 17 | self.add = reader.readBool() 18 | self.objectId = reader.readInt32() 19 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/GotoPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class GotoPacket(Packet): 5 | def __init__(self): 6 | self.type = "GOTO" 7 | self.objectId = 0 8 | self.position = WorldPosData() 9 | self.unknownInt = 0 10 | 11 | def read(self, reader): 12 | self.objectId = reader.readInt32() 13 | self.position.read(reader) 14 | self.unknownInt = reader.readInt32() 15 | 16 | def write(self, writer): 17 | writer.writeInt32(self.objectId) 18 | self.position.write(writer) 19 | writer.writeInt32(self.unknownInt) 20 | -------------------------------------------------------------------------------- /Crypto/RSA.py: -------------------------------------------------------------------------------- 1 | from Cryptodome.PublicKey import RSA 2 | from Cryptodome.Cipher import PKCS1_v1_5 3 | import base64 4 | 5 | PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n" + \ 6 | "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCKFctVrhfF3m2Kes0FBL/JFeO" + \ 7 | "cmNg9eJz8k/hQy1kadD+XFUpluRqa//Uxp2s9W2qE0EoUCu59ugcf/p7lGuL99Uo" + \ 8 | "SGmQEynkBvZct+/M40L0E0rZ4BVgzLOJmIbXMp0J4PnPcb6VLZvxazGcmSfjauC7" + \ 9 | "F3yWYqUbZd/HCBtawwIDAQAB\n" + \ 10 | "-----END PUBLIC KEY-----" 11 | 12 | def encrypt(msg): 13 | keyPub = RSA.importKey(PUBLIC_KEY) 14 | cipher = PKCS1_v1_5.new(keyPub) 15 | cipher_text = cipher.encrypt(msg.encode()) 16 | return base64.b64encode(cipher_text).decode() 17 | -------------------------------------------------------------------------------- /Data/MoveRecord.py: -------------------------------------------------------------------------------- 1 | from Data.WorldPosData import WorldPosData 2 | 3 | class MoveRecord: 4 | def __init__(self, time=0, x=0.0, y=0.0): 5 | self.time = time 6 | self.pos = WorldPosData(x, y) 7 | 8 | def read(self, reader): 9 | self.time = reader.readInt32() 10 | self.pos.read(reader) 11 | 12 | def write(self, writer): 13 | writer.writeInt32(self.time) 14 | self.pos.write(writer) 15 | 16 | def clone(self): 17 | return MoveRecord(self.time, self.pos.x, self.pos.y) 18 | 19 | def __str__(self): 20 | return f"time: {self.time}, pos: {self.pos}" 21 | 22 | def __repr__(self): 23 | return str(self) -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/OtherHitPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class OtherHitPacket(Packet): 4 | def __init__(self): 5 | self.type = "OTHERHIT" 6 | self.time = 0 7 | self.bulletId = 0 8 | self.objectId = 0 9 | self.targetId = 0 10 | 11 | def write(self, writer): 12 | writer.writeInt32(self.time) 13 | writer.writeShort(self.bulletId) 14 | writer.writeInt32(self.objectId) 15 | writer.writeInt32(self.targetId) 16 | 17 | def read(self, reader): 18 | self.time = reader.readInt32() 19 | self.bulletId = reader.readShort() 20 | self.objectId = reader.readInt32() 21 | self.targetId = reader.readInt32() 22 | -------------------------------------------------------------------------------- /Networking/Packets/PacketTypes.py: -------------------------------------------------------------------------------- 1 | import Networking.Packets.Incoming as Incoming 2 | import Networking.Packets.Outgoing as Outgoing 3 | 4 | class PacketTypes: 5 | def __init__(self): 6 | self.packet_dict = {} 7 | for file in dir(Incoming): 8 | if "Packet" in file: 9 | packet = getattr(Incoming, file) 10 | packetName = file.replace("Packet", "") 11 | self.packet_dict[packetName.upper()] = packet 12 | 13 | for file in dir(Outgoing): 14 | if "Packet" in file: 15 | packet = getattr(Outgoing, file) 16 | packetName = file.replace("Packet", "") 17 | self.packet_dict[packetName.upper()] = packet 18 | -------------------------------------------------------------------------------- /Data/FameData.py: -------------------------------------------------------------------------------- 1 | #Fame bonuses given on death 2 | 3 | class FameData: 4 | def __init__(self, name=0, rank=0, fame=0): 5 | self.name = name 6 | self.rank = rank 7 | self.fame = fame 8 | 9 | def read(self, reader): 10 | self.name = reader.readStr() 11 | self.rank = reader.readCompressedInt() 12 | self.fame = reader.readCompressedInt() 13 | 14 | def write(self, writer): 15 | writer.writeStr(self.name) 16 | writer.writeCompressedInt(self.rank) 17 | writer.writeCompressedInt(self.fame) 18 | 19 | def clone(self): 20 | return StatData(self.statType, self.statValue, self.strStatValue, self.secondaryValue) 21 | 22 | def __str__(self): 23 | return f"{self.name}-{self.rank} {self.fame}" 24 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/BlueprintInfoPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class BlueprintInfoPacket(Packet): 4 | def __init__(self): 5 | self.type = "BLUEPRINTINFO" 6 | self.unknownByte = -1 7 | self.blueprints = [] 8 | 9 | def read(self, reader): 10 | self.unknownByte = reader.readByte() 11 | numBlueprints = reader.readCompressedInt() 12 | for i in range(numBlueprints): 13 | self.blueprints.append(reader.readCompressedInt()) 14 | 15 | def write(self, writer): 16 | writer.writeByte(self.unknownByte) 17 | writer.writeCompressedInt(len(self.blueprints)) 18 | for i in range(len(self.blueprints)): 19 | writer.writeCompressedInt(self.blueprints[i]) 20 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ForgeResponsePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.SlotObjectData import SlotObjectData 3 | 4 | class ForgeResponsePacket(Packet): 5 | def __init__(self): 6 | self.type = "FORGERESPONSE" 7 | self.success = False 8 | self.slots = [] 9 | 10 | def read(self, reader): 11 | self.success = reader.readBool() 12 | slotLen = reader.readByte() 13 | for i in range(slotLen): 14 | slot = SlotObjectData() 15 | slot.read(reader) 16 | self.slots.append(slot) 17 | 18 | def write(self, writer): 19 | writer.writeBool(self.success) 20 | writer.writeByte(len(self.slots)) 21 | for slot in self.slots: 22 | slot.write(writer) 23 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/CreatePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class CreatePacket(Packet): 4 | def __init__(self): 5 | self.type = "CREATE" 6 | self.classType = 0 7 | self.skinType = 0 8 | self.isChallenger = False 9 | self.isSeasonal = False 10 | 11 | def write(self, writer): 12 | writer.writeShort(self.classType) 13 | writer.writeShort(self.skinType) 14 | writer.writeBool(self.isChallenger) 15 | writer.writeBool(self.isSeasonal) 16 | 17 | def read(self, reader): 18 | self.classType = reader.readShort() 19 | self.skinType = reader.readShort() 20 | self.isChallenger = reader.readBool() 21 | self.isSeasonal = reader.readBool() 22 | -------------------------------------------------------------------------------- /Constants/ApiPoints.py: -------------------------------------------------------------------------------- 1 | 2 | VERSION = "https://static.drips.pw/rotmg/production/current/version.txt" 3 | EQUIP = "https://static.drips.pw/rotmg/production/current/xml/Equip.xml" 4 | VERIFY = "https://www.realmofthemadgod.com/account/verify" 5 | VERIFYTOKEN = "https://www.realmofthemadgod.com/account/verifyAccessTokenClient" 6 | CHAR = "https://www.realmofthemadgod.com/char/list" 7 | SERVERS = "https://www.realmofthemadgod.com/account/servers" 8 | 9 | launcherHeaders = { 10 | "User-Agent": "UnityPlayer/2021.3.16f1 (UnityWebRequest/1.0, libcurl/7.84.0-DEV)", 11 | "X-Unity-Version": "2021.3.16f1" 12 | } 13 | 14 | exaltHeaders = { 15 | "User-Agent": "UnityPlayer/2021.3.16f1 (UnityWebRequest/1.0, libcurl/7.84.0-DEV)", 16 | "X-Unity-Version": "2021.3.16f1" 17 | } 18 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/ForgeRequestPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.SlotObjectData import SlotObjectData 3 | 4 | class ForgeRequestPacket(Packet): 5 | def __init__(self): 6 | self.type = "FORGEREQUEST" 7 | self.itemId = -1 8 | self.offers = [] 9 | 10 | def write(self, writer): 11 | writer.writeInt32(self.itemId) 12 | writer.writeInt32(len(self.offers)) 13 | for offer in self.offers: 14 | offer.write(writer) 15 | 16 | def read(self, reader): 17 | self.itemId = reader.readInt32() 18 | offerLen = reader.readInt32() 19 | for i in range(offerLen): 20 | slotObject = SlotObjectData() 21 | slotObject.read(reader) 22 | self.offers.append(slotObject) 23 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/CrucibleRequestPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class CrucibleRequestPacket(Packet): 4 | def __init__(self): 5 | self.type = "CRUCIBLEREQUEST" 6 | self.unknownShort = 0 7 | self.unknownInt1 = 0 8 | self.unknownInt2 = 0 9 | self.unknownInt3 = 0 10 | 11 | def write(self, writer): 12 | writer.writeShort(self.unknownShort) 13 | writer.writeInt32(self.unknownInt1) 14 | writer.writeInt32(self.unknownInt2) 15 | writer.writeInt32(self.unknownInt3) 16 | 17 | def read(self, reader): 18 | self.unknownShort = reader.readShort() 19 | self.unknownInt1 = reader.readInt32() 20 | self.unknownInt2 = reader.readInt32() 21 | self.unknownInt3 = reader.readInt32() 22 | -------------------------------------------------------------------------------- /Data/ObjectData.py: -------------------------------------------------------------------------------- 1 | from .ObjectStatusData import * 2 | 3 | class ObjectData: 4 | def __init__(self, objectType=0, status=None): 5 | self.objectType = objectType 6 | if status is None: 7 | self.status = ObjectStatusData() 8 | else: 9 | self.status = status.clone() 10 | 11 | def read(self, reader): 12 | self.objectType = reader.readUnsignedShort() 13 | self.status.read(reader) 14 | 15 | def write(self, writer): 16 | writer.writeUnsignedShort(self.objectType) 17 | self.status.write(writer) 18 | 19 | def clone(self): 20 | return ObjectData(self.objectType, self.status.clone()) 21 | 22 | def __str__(self): 23 | return "ObjectType: {}\nStatus: \n{}".format(self.objectType, self.status) 24 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/QuestFetchResponsePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.QuestData import * 3 | 4 | class QuestFetchResponsePacket(Packet): 5 | def __init__(self): 6 | self.type = "QUESTFETCHRESPONSE" 7 | self.quests = [] 8 | self.nextRefreshPrice = 0 9 | 10 | def read(self, reader): 11 | num_quests = reader.readShort() 12 | for i in range(num_quests): 13 | quest = QuestData() 14 | quest.read(reader) 15 | self.quests.append(quest) 16 | self.nextRefreshPrice = reader.readShort() 17 | 18 | def write(self, writer): 19 | writer.writeShort(len(self.quests)) 20 | for quest in self.quests: 21 | quest.write(writer) 22 | writer.writeShort(self.nextRefreshPrice) 23 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/UseItemPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.SlotObjectData import * 3 | from Data.WorldPosData import * 4 | 5 | class UseItemPacket(Packet): 6 | def __init__(self): 7 | self.type = "USEITEM" 8 | self.time = 0 9 | self.slotObject = SlotObjectData() 10 | self.pos = WorldPosData() 11 | self.useType = 0 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.time) 15 | self.slotObject.write(writer) 16 | self.pos.write(writer) 17 | writer.writeByte(self.useType) 18 | 19 | def read(self, reader): 20 | self.time = reader.readInt32() 21 | self.slotObject.read(reader) 22 | self.pos.read(reader) 23 | self.useType = reader.readByte() 24 | -------------------------------------------------------------------------------- /Data/SlotObjectData.py: -------------------------------------------------------------------------------- 1 | 2 | class SlotObjectData: 3 | def __init__(self, objectId=0, slotId=0, objectType=0): 4 | self.objectId = objectId 5 | self.slotId = slotId 6 | self.objectType = objectType 7 | 8 | def read(self, reader): 9 | self.objectId = reader.readInt32() 10 | self.slotId = reader.readInt32() 11 | self.objectType = reader.readInt32() 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.objectId) 15 | writer.writeInt32(self.slotId) 16 | writer.writeInt32(self.objectType) 17 | 18 | def __str__(self): 19 | return "{} {} {}".format(self.objectId, self.slotId, self.objectType) 20 | 21 | def clone(self): 22 | return SlotObjectData(self.objectId, self.slotId, self.objectType) 23 | 24 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/InvSwapPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | from Data.SlotObjectData import * 4 | 5 | class InvSwapPacket(Packet): 6 | def __init__(self): 7 | self.type = "INVSWAP" 8 | self.time = 0 9 | self.pos = WorldPosData() 10 | self.slotObject1 = SlotObjectData() 11 | self.slotObject2 = SlotObjectData() 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.time) 15 | self.pos.write(writer) 16 | self.slotObject1.write(writer) 17 | self.slotObject2.write(writer) 18 | 19 | def read(self, reader): 20 | self.time = reader.readInt32() 21 | self.pos.read(reader) 22 | self.slotObject1.read(reader) 23 | self.slotObject2.read(reader) 24 | 25 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/AccountListPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class AccountListPacket(Packet): 4 | def __init__(self): 5 | self.type = "ACCOUNTLIST" 6 | self.accountListId = 0 7 | self.accountIds = [] 8 | self.lockAction = 0 9 | 10 | def read(self, reader): 11 | self.accountListId = reader.readInt32() 12 | accountIdsNum = reader.readShort() 13 | for i in range(accountIdsNum): 14 | self.accountIds.append(reader.readStr()) 15 | self.lockAction = reader.readInt32() 16 | 17 | def write(self, writer): 18 | writer.writeInt32(self.accountListId) 19 | writer.writeShort(len(self.accountIds)) 20 | for i in range(len(self.accountIds)): 21 | writer.writeStr(self.accountIds[i]) 22 | writer.writeInt32(self.lockAction) 23 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/AcceptTradePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class AcceptTradePacket(Packet): 4 | def __init__(self): 5 | self.type = "ACCEPTTRADE" 6 | self.clientOffer = [] 7 | self.partnerOffer = [] 8 | 9 | def write(self, writer): 10 | writer.writeShort(len(self.clientOffer)) 11 | for i in self.clientOffer: 12 | writer.writeBool(i) 13 | writer.writeShort(len(self.partnerOffer)) 14 | for i in self.partnerOffer: 15 | writer.writeBool(i) 16 | 17 | def read(self, reader): 18 | offerLen = reader.readShort() 19 | for i in range(offerLen): 20 | self.clientOffer.append(reader.readBool()) 21 | offerLen = reader.readShort() 22 | for i in range(offerLen): 23 | self.partnerOffer.append(reader.readBool()) 24 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/AllyShootPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class AllyShootPacket(Packet): 4 | def __init__(self): 5 | self.type = "ALLYSHOOT" 6 | self.bulletId = 0 7 | self.ownerId = [] 8 | self.containerType = 0 9 | self.angle = 0 10 | self.bard = False 11 | 12 | def read(self, reader): 13 | self.bulletId = reader.readUnsignedShort() 14 | self.ownerId = reader.readInt32() 15 | self.containerType = reader.readShort() 16 | self.angle = reader.readFloat() 17 | self.bard = reader.readBool() 18 | 19 | def write(self, writer): 20 | writer.writeUnsignedShort(self.bulletId) 21 | writer.writeInt32(self.ownerId) 22 | writer.writeShort(self.containerType) 23 | writer.writeFloat(self.angle) 24 | writer.writeBool(self.bard) 25 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/MovePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | from Data.MoveRecord import * 4 | 5 | class MovePacket(Packet): 6 | def __init__(self): 7 | self.type = "MOVE" 8 | self.tickId = 0 9 | self.time = 0 10 | self.records = [] 11 | 12 | def write(self, writer): 13 | writer.writeInt32(self.tickId) 14 | writer.writeUInt32(self.time) 15 | writer.writeShort(len(self.records)) 16 | for record in self.records: 17 | record.write(writer) 18 | 19 | def read(self, reader): 20 | self.tickId = reader.readInt32() 21 | self.time = reader.readUInt32() 22 | recordLen = reader.readShort() 23 | for i in range(recordLen): 24 | record = MoveRecord() 25 | record.read(reader) 26 | self.records.append(record) 27 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ReconnectPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ReconnectPacket(Packet): 4 | def __init__(self): 5 | self.type = "RECONNECT" 6 | self.name = "" 7 | self.host = "" 8 | self.port = 0 9 | self.gameId = 0 10 | self.keyTime = 0 11 | self.key = [] 12 | 13 | def read(self, reader): 14 | self.name = reader.readStr() 15 | self.host = reader.readStr() 16 | self.port = reader.readShort() 17 | self.gameId = reader.readInt32() 18 | self.keyTime = reader.readInt32() 19 | self.key = reader.readBytes() 20 | 21 | def write(self, writer): 22 | writer.writeStr(self.name) 23 | writer.writeStr(self.host) 24 | writer.writeShort(self.port) 25 | writer.writeInt32(self.gameId) 26 | writer.writeInt32(self.keyTime) 27 | writer.writeBytes(self.key) 28 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/EnemyHitPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class EnemyHitPacket(Packet): 4 | def __init__(self): 5 | self.type = "ENEMYHIT" 6 | self.time = 0 7 | self.bulletId = 0 8 | self.id1 = 0 9 | self.targetId = 0 10 | self.kill = False 11 | self.id2 = 0 12 | 13 | def write(self, writer): 14 | writer.writeInt32(self.time) 15 | writer.writeShort(self.bulletId) 16 | writer.writeInt32(self.id1) 17 | writer.writeInt32(self.targetId) 18 | writer.writeBool(self.kill) 19 | writer.writeInt32(self.id2) 20 | 21 | def read(self, reader): 22 | self.time = reader.readInt32() 23 | self.bulletId = reader.readShort() 24 | self.id1 = reader.readInt32() 25 | self.targetId = reader.readInt32() 26 | self.kill = reader.readBool() 27 | self.id2 = reader.readInt32() 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyrelay 2 | 3 | pyrelay is a client which can connect to Realm of the Mad God written in [Python](https://www.python.org/). 4 | 5 | This project is inspired by [nrelay](https://github.com/thomas-crane/nrelay) 6 | 7 | ## Installation 8 | 9 | Use the package manager [pip](https://pip.pypa.io/en/stable/) to install the packages in requirements.txt 10 | 11 | ```bash 12 | pip install -r requirements.txt 13 | ``` 14 | 15 | ## Setup 16 | 17 | Copy the file `Accounts_ex.json`, rename the copy to `Accounts.json` and remove the comments 18 | 19 | Then in the file `Accounts.json` insert the email+password for the account(s) you want to use 20 | 21 | Look in `Accounts_ex.json` to see how different accounts and proxies are used. 22 | 23 | ### Plugins 24 | 25 | The folder `Plugins` is where you make the plugins the clients will use. 26 | 27 | ### Running 28 | 29 | To start the bot simply run pyrelay.py from the cmd 30 | 31 | ```bash 32 | pyrelay.py 33 | ``` -------------------------------------------------------------------------------- /Networking/Packets/Incoming/TradeAcceptedPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class TradeAcceptedPacket(Packet): 4 | def __init__(self): 5 | self.type = "TRADEACCEPTED" 6 | self.clientOffer = [] 7 | self.partnerOffer = [] 8 | 9 | def read(self, reader): 10 | clientOffer_len = reader.readShort() 11 | for i in range(clientOffer_len): 12 | self.clientOffer.append(reader.readBool()) 13 | partnerOffer_len = reader.readShort() 14 | for i in range(partnerOffer_len): 15 | self.partnerOffer.append(reader.readBool()) 16 | 17 | def write(self, writer): 18 | writer.writeShort(len(self.clientOffer)) 19 | for i in range(len(self.clientOffer)): 20 | writer.writeBool(self.clientOffer[i]) 21 | writer.writeShort(len(self.partnerOffer)) 22 | for i in range(len(self.partnerOffer)): 23 | writer.writeBool(self.partnerOffer[i]) 24 | -------------------------------------------------------------------------------- /Constants/Servers.py: -------------------------------------------------------------------------------- 1 | 2 | nameToIp = {'EUEast': '18.184.218.174', 'EUSouthWest': '35.180.67.120', 'USEast2': '54.209.152.223', 'EUNorth': '18.159.133.120', 'USEast': '54.234.226.24', 'USWest4': '54.235.235.140', 'EUWest2': '52.16.86.215', 'Asia': '3.0.147.127', 'USSouth3': '52.207.206.31', 'EUWest': '15.237.60.223', 'USWest': '54.86.47.176', 'USMidWest2': '3.140.254.133', 'USMidWest': '18.221.120.59', 'USSouth': '3.82.126.16', 'USWest3': '18.144.30.153', 'USSouthWest': '54.153.13.68', 'USNorthWest': '34.238.176.119', 'Australia': '13.236.87.250'} 3 | ipToName = {'18.184.218.174': 'EUEast', '35.180.67.120': 'EUSouthWest', '54.209.152.223': 'USEast2', '18.159.133.120': 'EUNorth', '54.234.226.24': 'USEast', '54.235.235.140': 'USWest4', '52.16.86.215': 'EUWest2', '3.0.147.127': 'Asia', '52.207.206.31': 'USSouth3', '15.237.60.223': 'EUWest', '54.86.47.176': 'USWest', '3.140.254.133': 'USMidWest2', '18.221.120.59': 'USMidWest', '3.82.126.16': 'USSouth', '18.144.30.153': 'USWest3', '54.153.13.68': 'USSouthWest', '34.238.176.119': 'USNorthWest', '13.236.87.250': 'Australia'} -------------------------------------------------------------------------------- /Networking/Packets/Incoming/Unknown139Packet.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class Unknown139Packet(Packet): 4 | def __init__(self): 5 | self.type = "UNKNOWN139" 6 | self.unknownByte1 = 0 7 | self.unknownByte2 = 0 8 | self.unknownByte3 = 0 9 | self.unknownInt1 = 0 10 | self.unknownInt2 = 0 11 | self.byte3 = False 12 | 13 | def read(self, reader): 14 | self.unknownByte1 = reader.readByte() 15 | self.unknownByte2 = reader.readByte() 16 | if reader.bytesAvailable() == 9: 17 | self.unknownByte3 = reader.readByte() 18 | self.byte3 = True 19 | self.unknownInt1 = reader.readInt32() 20 | self.unknownInt2 = reader.readInt32() 21 | 22 | def write(self, writer): 23 | writer.writeByte(self.unknownByte1) 24 | writer.writeByte(self.unknownByte2) 25 | if self.byte3: 26 | writer.writeByte(self.unknownByte3) 27 | writer.writeInt32(self.unknownInt1) 28 | writer.writeInt32(self.unknownInt2) 29 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/CrucibleResponsePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class CrucibleResponsePacket(Packet): 4 | def __init__(self): 5 | self.type = "CRUCIBLERESPONSE" 6 | self.int1 = 0 7 | self.int2 = 0 8 | self.int3 = 0 9 | self.int4 = 0 10 | 11 | self.json1 = "" 12 | self.json2 = "" 13 | self.json3 = "" 14 | 15 | def write(self, writer): 16 | writer.writeInt32(self.int1) 17 | writer.writeInt32(self.int2) 18 | writer.writeInt32(self.int3) 19 | writer.writeInt32(self.int4) 20 | 21 | writer.writeStr(self.json1) 22 | writer.writeStr(self.json2) 23 | writer.writeStr(self.json3) 24 | 25 | def read(self, reader): 26 | self.int1 = reader.readInt32() 27 | self.int2 = reader.readInt32() 28 | self.int3 = reader.readInt32() 29 | self.int4 = reader.readInt32() 30 | 31 | self.json1 = reader.readStr() 32 | self.json2 = reader.readStr() 33 | self.json3 = reader.readStr() 34 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/InvResultPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.SlotObjectData import * 3 | 4 | class InvResultPacket(Packet): 5 | def __init__(self): 6 | self.type = "INVRESULT" 7 | self.unknownBool = False#Prob success bool 8 | self.unknownByte = 0 9 | self.fromSlot = SlotObjectData() 10 | self.toSlot = SlotObjectData() 11 | self.unknownInt1 = 0 12 | self.unknownInt2 = 0 13 | 14 | def read(self, reader): 15 | self.unknownBool = reader.readBool() 16 | self.unknownByte = reader.readByte() 17 | self.fromSlot.read(reader) 18 | self.toSlot.read(reader) 19 | self.unknownInt1 = reader.readInt32() 20 | self.unknownInt2 = reader.readInt32() 21 | 22 | def write(self, writer): 23 | writer.writeBool(self.unknownBool) 24 | writer.writeByte(self.unknownByte) 25 | self.fromSlot.write(writer) 26 | self.toSlot.write(writer) 27 | writer.writeInt32(self.unknownInt1) 28 | writer.writeInt32(self.unknownInt2) 29 | -------------------------------------------------------------------------------- /Helpers/Servers.py: -------------------------------------------------------------------------------- 1 | import urllib.parse 2 | import Constants.ApiPoints as ApiPoints 3 | 4 | def getXML(accessToken, proxies={}): 5 | import requests 6 | return requests.post(ApiPoints.SERVERS, data={"accessToken": accessToken, 7 | "game_net": "Unity", "play_platform": "Unity", "game_net_user_id": ""}, headers=ApiPoints.exaltHeaders, proxies=proxies).text 8 | 9 | def parseServers(xml): 10 | from xml.etree import ElementTree 11 | nameToIp = {} 12 | ipToName = {} 13 | root = ElementTree.fromstring(xml) 14 | for tag in root.findall("Server"): 15 | name, ip = tag.find("Name").text, tag.find("DNS").text 16 | nameToIp[name] = ip 17 | ipToName[ip] = name 18 | return nameToIp, ipToName 19 | 20 | def writeServers(servers): 21 | text = "\nnameToIp = {}\nipToName = {}".format(servers[0], servers[1]) 22 | 23 | with open("Constants/Servers.py", "w") as file: 24 | file.write(text) 25 | 26 | def update(accessToken, proxies={}): 27 | writeServers(parseServers(getXML(accessToken, proxies={}))) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Maximilian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Constants/StatusEffects.py: -------------------------------------------------------------------------------- 1 | NOTHING = 0 2 | DEAD = 1 3 | QUIET = 2 4 | WEAK = 3 5 | SLOWED = 4 6 | SICK = 5 7 | DAZED = 6 8 | STUNNED = 7 9 | BLIND = 8 10 | HALLUCINATING = 9 11 | DRUNK = 10 12 | CONFUSED = 11 13 | STUNIMMUNE = 12 14 | INVISIBLE = 13 15 | PARALYZED = 14 16 | SPEEDY = 15 17 | BLEEDING = 16 18 | ARMORBROKENIMMUNE = 17 19 | HEALING = 18 20 | DAMAGING = 19 21 | BERSERK = 20 22 | PAUSED = 21 23 | STASIS = 22 24 | STASISIMMUNE = 23 25 | INVINCIBLE = 24 26 | INVULNERABLE = 25 27 | ARMORED = 26 28 | ARMORBROKEN = 27 29 | HEXED = 28 30 | NINJASPEEDY = 29 31 | UNSTABLE = 30 32 | DARKNESS = 31 33 | SLOWEDIMMUNE = 32 34 | DAZEDIMMUNE = 33 35 | PARALYZEDIMMUNE = 34 36 | PETRIFIED = 35 37 | PETRIFIEDIMMUNE = 36 38 | PETEFFECTICON = 37 39 | CURSE = 38 40 | CURSEIMMUNE = 39 41 | HPBOOST = 40 42 | MPBOOST = 41 43 | ATTBOOST = 42 44 | DEFBOOST = 43 45 | SPDBOOST = 44 46 | VITBOOST = 45 47 | WISBOOST = 46 48 | DEXBOOST = 47 49 | SILENCED = 48 50 | EXPOSED = 49 51 | ENERGIZED = 50 52 | HPDEBUFF = 51 53 | MPDEBUFF = 52 54 | ATTDEBUFF = 53 55 | DEFDEBUFF = 54 56 | SPDDEBUFF = 55 57 | VITDEBUFF = 56 58 | WISDEBUFF = 57 59 | DEXDEBUFF = 58 60 | INSPIRED = 59 61 | GROUNDDAMAGE = 99 62 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/CreatePartyPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class CreatePartyPacket(Packet): 4 | def __init__(self): 5 | self.type = "CREATEPARTY" 6 | self.description = "" 7 | self.minPowerLevel = 0 8 | self.maxPartySize = 0 9 | self.activity = 0 10 | self.maxedStatReq = 0 11 | self.privacy = 0 12 | self.server = 0 13 | 14 | def write(self, writer): 15 | writer.writeStr(self.description) 16 | writer.writeShort(self.minPowerLevel) 17 | writer.writeByte(self.maxPartySize) 18 | writer.writeByte(self.activity) 19 | writer.writeByte(self.maxedStatReq) 20 | writer.writeByte(self.privacy) 21 | writer.writeByte(self.server) 22 | 23 | def read(self, reader): 24 | self.description = reader.readStr() 25 | self.minPowerLevel = reader.readShort() 26 | self.maxPartySize = reader.readByte() 27 | #0=dungeons, 1=realm, 2=other 28 | self.activity = reader.readByte() 29 | self.maxedStatReq = reader.readByte() 30 | #1=public, 2=private 31 | self.privacy = reader.readByte() 32 | self.server = reader.readByte() -------------------------------------------------------------------------------- /Networking/Packets/Incoming/TradeStartPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.TradeItem import * 3 | 4 | class TradeStartPacket(Packet): 5 | def __init__(self): 6 | self.type = "TRADESTART" 7 | self.clientItems = [] 8 | self.partnerName = "" 9 | self.partnerItems = [] 10 | 11 | def read(self, reader): 12 | clientItems_len = reader.readShort() 13 | for i in range(clientItems_len): 14 | item = TradeItem() 15 | item.read(reader) 16 | self.clientItems.append(item) 17 | self.partnerName = reader.readStr() 18 | partnerItems_len = reader.readShort() 19 | for i in range(partnerItems_len): 20 | item = TradeItem() 21 | item.read(reader) 22 | self.partnerItems.append(item) 23 | 24 | def write(self, writer): 25 | writer.writeShort(len(self.clientItems)) 26 | for i in range(len(self.clientItems)): 27 | self.clientItems[i].write(writer) 28 | writer.writeStr(self.partnerName) 29 | writer.writeShort(len(self.partnerItems)) 30 | for i in range(len(self.partnerItems)): 31 | self.partnerItems[i].write(writer) 32 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/DamagePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | 4 | class DamagePacket(Packet): 5 | def __init__(self): 6 | self.type = "DAMAGE" 7 | self.targetId = 0 8 | self.effects = [] 9 | self.damageAmount = 0 10 | self.info = 0 11 | self.bulletId = 0 12 | self.objectId = 0 13 | 14 | def read(self, reader): 15 | self.targetId = reader.readInt32() 16 | effectNum = reader.readUnsignedByte() 17 | for i in range(effectNum): 18 | self.effects.append(reader.readUnsignedByte()) 19 | self.damageAmount = reader.readUnsignedShort() 20 | self.info = reader.readByte() 21 | self.bulletId = reader.readUnsignedShort() 22 | self.objectId = reader.readInt32() 23 | 24 | def write(self, writer): 25 | writer.writeInt32(self.targetId) 26 | writer.writeUnsignedByte(len(self.effects)) 27 | for i in range(len(self.effects)): 28 | writer.writeUnsignedByte(self.effects[i]) 29 | writer.writeUnsignedShort(self.damageAmount) 30 | writer.writeByte(self.info) 31 | writer.writeUnsignedShort(self.bulletId) 32 | writer.writeInt32(self.objectId) 33 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/NewTickPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.ObjectStatusData import * 3 | 4 | class NewTickPacket(Packet): 5 | def __init__(self): 6 | self.type = "NEWTICK" 7 | self.tickId = 0 8 | self.tickTime = 0 9 | self.serverRealTimeMS = 0 10 | self.serverLastTimeRTTMS = 0 11 | self.statuses = [] 12 | 13 | def read(self, reader): 14 | self.tickId = reader.readInt32() 15 | self.tickTime = reader.readInt32() 16 | self.serverRealTimeMS = reader.readUInt32() 17 | self.serverLastTimeRTTMS = reader.readUnsignedShort() 18 | statuses_num = reader.readShort() 19 | for i in range(statuses_num): 20 | status = ObjectStatusData() 21 | status.read(reader) 22 | self.statuses.append(status) 23 | 24 | def write(self, writer): 25 | writer.writeInt32(self.tickId) 26 | writer.writeInt32(self.tickTime) 27 | writer.writeUInt32(self.serverRealTimeMS) 28 | writer.writeUnsignedShort(self.serverLastTimeRTTMS) 29 | writer.writeShort(len(self.statuses)) 30 | for i in range(len(self.statuses)): 31 | self.statuses[i].write(writer) 32 | -------------------------------------------------------------------------------- /Data/TradeItem.py: -------------------------------------------------------------------------------- 1 | 2 | class TradeItem: 3 | def __init__(self, item=0, slotType=0, tradeable=False, included=False, enchantment=""): 4 | self.item = item 5 | self.slotType = slotType 6 | self.tradeable = tradeable 7 | self.included = included 8 | self.enchantment = enchantment 9 | 10 | def read(self, reader): 11 | self.item = reader.readInt32() 12 | self.slotType = reader.readInt32() 13 | self.tradeable = reader.readBool() 14 | self.included = reader.readBool() 15 | self.enchantment = reader.readStr() 16 | 17 | def write(self, writer): 18 | writer.writeInt32(self.item) 19 | writer.writeInt32(self.slotType) 20 | writer.writeBool(self.tradeable) 21 | writer.writeBool(self.included) 22 | writer.writeStr(self.enchantment) 23 | 24 | def clone(self): 25 | return TradeItem(self.item, self.slotType, self.tradeable, self.included, self.enchantment) 26 | 27 | def __str__(self): 28 | return "Item: {}, slotType: {}, tradeable: {}, included: {}, enchantment: {}".format(self.item, self.slotType, self.tradeable, self.included, self.enchantment) 29 | 30 | def __repr__(self): 31 | return str(self.__dict__) 32 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/AoePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class AoePacket(Packet): 5 | def __init__(self): 6 | self.type = "AOE" 7 | self.pos = WorldPosData() 8 | self.radius = 0 9 | self.damage = 0 10 | self.effect = 0 11 | self.duration = 0 12 | self.origType = 0 13 | self.color = 0 14 | self.armorPierce = False 15 | 16 | def read(self, reader): 17 | self.pos.read(reader) 18 | self.radius = reader.readFloat() 19 | self.damage = reader.readUnsignedShort() 20 | self.effect = reader.readUnsignedByte() 21 | self.duration = reader.readFloat() 22 | self.origType = reader.readUnsignedShort() 23 | self.color = reader.readInt32() 24 | self.armorPierce = reader.readBool() 25 | 26 | def write(self, writer): 27 | self.pos.write(writer) 28 | writer.writeFloat(self.radius) 29 | writer.writeUnsignedShort(self.damage) 30 | writer.writeUnsignedByte(self.effect) 31 | writer.writeFloat(self.duration) 32 | writer.writeUnsignedShort(self.origType) 33 | writer.writeInt32(self.color) 34 | writer.writeBool(self.armorPierce) 35 | 36 | -------------------------------------------------------------------------------- /Crypto/RC4.py: -------------------------------------------------------------------------------- 1 | OUTGOING_KEY = "5a4d2016bc16dc64883194ffd9" 2 | INCOMING_KEY = "c91d9eec420160730d825604e0" 3 | STATE_LENGTH = 256 4 | 5 | class RC4: 6 | def __init__(self, key): 7 | self.key = bytearray.fromhex(key) 8 | self.x = 0 9 | self.y = 0 10 | self.state = [0] * STATE_LENGTH 11 | self.reset() 12 | 13 | def process(self, data): 14 | if isinstance(data, str): 15 | data = bytearray.fromhex(data) 16 | elif isinstance(data, bytes): 17 | data = bytearray(data) 18 | for i in range(len(data)): 19 | self.x = (self.x + 1) % STATE_LENGTH 20 | self.y = (self.y + self.state[self.x]) % STATE_LENGTH 21 | self.state[self.x], self.state[self.y] = self.state[self.y], self.state[self.x] 22 | k = self.state[(self.state[self.x] + self.state[self.y]) % STATE_LENGTH] 23 | data[i] = data[i] ^ k 24 | return data 25 | 26 | def reset(self): 27 | self.x = 0 28 | self.y = 0 29 | for i in range(STATE_LENGTH): 30 | self.state[i] = i 31 | i2 = 0 32 | for i in range(STATE_LENGTH): 33 | i2 = (i2 + self.state[i] + self.key[i%len(self.key)]) % STATE_LENGTH 34 | self.state[i], self.state[i2] = self.state[i2], self.state[i] 35 | 36 | -------------------------------------------------------------------------------- /Data/ObjectStatusData.py: -------------------------------------------------------------------------------- 1 | from .WorldPosData import * 2 | from .StatData import * 3 | 4 | class ObjectStatusData: 5 | def __init__(self, objectId=0, pos=None, stats=None): 6 | self.objectId = objectId 7 | if pos is None: 8 | self.pos = WorldPosData() 9 | else: 10 | self.pos = pos.clone() 11 | if stats is None: 12 | self.stats = [] 13 | else: 14 | self.stats = [stat.clone() for stat in stats] 15 | 16 | def read(self, reader): 17 | self.objectId = reader.readCompressedInt() 18 | self.pos.read(reader) 19 | stats_len = reader.readCompressedInt() 20 | for i in range(stats_len): 21 | stat = StatData() 22 | stat.read(reader) 23 | self.stats.append(stat) 24 | 25 | def write(self, writer): 26 | writer.writeCompressedInt(self.objectId) 27 | self.pos.write(writer) 28 | writer.writeCompressedInt(len(self.stats)) 29 | for stat in self.stats: 30 | stat.write(writer) 31 | 32 | def clone(self): 33 | return ObjectStatusData(self.objectId, self.pos, [stat.clone() for stat in self.stats]) 34 | 35 | def __str__(self): 36 | return "ObjectId: {}\nPos: {}\nStats: [\n{}\n]".format(self.objectId, self.pos, "\n\n".join(map(str, self.stats))) 37 | -------------------------------------------------------------------------------- /pyrelay.py: -------------------------------------------------------------------------------- 1 | from ClientManager import ClientManager 2 | from PluginManager import loadPlugins 3 | import json 4 | import time 5 | import threading 6 | import argparse 7 | 8 | VERSION_PATH = "Resources/version.txt" 9 | EQUIP_PATH = "Resources/equip.xml" 10 | 11 | parser = argparse.ArgumentParser(description="pyrelay") 12 | 13 | parser.add_argument("-s", "--servers", action="store_true", help="Update the server ips") 14 | 15 | args = parser.parse_args() 16 | accounts = [] 17 | try: 18 | with open("Accounts.json", "r", encoding="utf-8") as file: 19 | accounts = json.load(file) 20 | except IOError: 21 | print("Missing Accounts.json file") 22 | exit(1) 23 | 24 | loadPlugins() 25 | clientMan = ClientManager() 26 | 27 | if args.servers: 28 | clientMan.updateServers = True 29 | 30 | account_threads = [] 31 | for account in accounts: 32 | thread = threading.Thread(target=clientMan.addClient, args=(account,)) 33 | thread.daemon = True 34 | thread.start() 35 | account_threads.append(thread) 36 | 37 | for thread in account_threads: 38 | thread.join() 39 | 40 | try: 41 | while 1: 42 | if clientMan.reconnectIfNeeded(): 43 | print("No clients are active - exiting") 44 | break 45 | time.sleep(0.5) 46 | except (KeyboardInterrupt, SystemExit): 47 | clientMan.stop() 48 | 49 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/TextPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class TextPacket(Packet): 4 | def __init__(self): 5 | self.type = "TEXT" 6 | self.name = "" 7 | self.objectId = 0 8 | self.numStars = 0 9 | self.bubbleTime = 0 10 | self.recipient = "" 11 | self.text = "" 12 | self.cleanText = "" 13 | self.isSupporter = False 14 | self.starBg = 0 15 | 16 | def read(self, reader): 17 | self.name = reader.readStr() 18 | self.objectId = reader.readInt32() 19 | self.numStars = reader.readUnsignedShort() 20 | self.bubbleTime = reader.readUnsignedByte() 21 | self.recipient = reader.readStr() 22 | self.text = reader.readStr() 23 | self.cleanText = reader.readStr() 24 | self.isSupporter = reader.readBool() 25 | self.starBg = reader.readInt32() 26 | 27 | def write(self, writer): 28 | writer.writeStr(self.name) 29 | writer.writeInt32(self.objectId) 30 | writer.writeUnsignedShort(self.numStars) 31 | writer.writeUnsignedByte(self.bubbleTime) 32 | writer.writeStr(self.recipient) 33 | writer.writeStr(self.text) 34 | writer.writeStr(self.cleanText) 35 | writer.writeBool(self.isSupporter) 36 | writer.writeInt32(self.starBg) 37 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/PlayerShootPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class PlayerShootPacket(Packet): 5 | def __init__(self): 6 | self.type = "PLAYERSHOOT" 7 | self.time = 0 8 | self.bulletId = 0 9 | self.containerType = 0 10 | self.unknownByte = 0 11 | self.shotPos = WorldPosData() 12 | self.angle = 0 13 | self.isBurst = False 14 | self.unknownShort = 0 15 | self.pos = WorldPosData() 16 | 17 | def write(self, writer): 18 | writer.writeInt32(self.time) 19 | writer.writeShort(self.bulletId) 20 | writer.writeShort(self.containerType) 21 | writer.writeByte(self.unknownByte) 22 | self.shotPos.write(writer) 23 | writer.writeFloat(self.angle) 24 | writer.writeBool(self.isBurst) 25 | writer.writeShort(self.unknownShort) 26 | self.pos.write(writer) 27 | 28 | def read(self, reader): 29 | self.time = reader.readInt32() 30 | self.bulletId = reader.readShort() 31 | self.containerType = reader.readShort() 32 | self.unknownByte = reader.readByte() 33 | self.shotPos.read(reader) 34 | self.angle = reader.readFloat() 35 | self.isBurst = reader.readBool() 36 | self.unknownShort = reader.readShort() 37 | self.pos.read(reader) 38 | -------------------------------------------------------------------------------- /Data/WorldPosData.py: -------------------------------------------------------------------------------- 1 | 2 | class WorldPosData: 3 | def __init__(self, x=0, y=0): 4 | self.x = x 5 | self.y = y 6 | 7 | def distTo(self, a, b=None): 8 | if isinstance(a, self): 9 | return self.dist(a) 10 | elif not b is None: 11 | return ((self.x-a)**2 + (self.y-b)**2)**0.5 12 | raise ValueError("Wrong arguments for \"distTo\"") 13 | 14 | def read(self, reader): 15 | self.x = reader.readFloat() 16 | self.y = reader.readFloat() 17 | 18 | def write(self, writer): 19 | writer.writeFloat(self.x) 20 | writer.writeFloat(self.y) 21 | 22 | def clone(self): 23 | return WorldPosData(self.x, self.y) 24 | 25 | def squareDist(self, other): 26 | return (self.x-other.x)**2 + (self.y-other.y)**2 27 | 28 | def dist(self, other): 29 | return self.squareDist(other)**0.5 30 | 31 | def __str__(self): 32 | return "{}, {}".format(round(self.x, 2), round(self.y, 2)) 33 | 34 | def __add__(self, other): 35 | pos = self.clone() 36 | if type(other) == WorldPosData: 37 | pos.x += other.x 38 | pos.y += other.y 39 | elif type(other) in [list, tuple]:#Should prob do some checks 40 | pos.x += other[0] 41 | pos.y += other[1] 42 | elif type(other) == dict: 43 | pos.x += other["x"] 44 | pos.y += other["y"] 45 | return pos 46 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/DeathPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.FameData import * 3 | 4 | class DeathPacket(Packet): 5 | def __init__(self): 6 | self.type = "DEATH" 7 | self.accountId = "" 8 | self.charId = 0 9 | self.killedBy = "" 10 | self.unknownInt = 0 11 | self.fameEarned = 0 12 | self.fameBonuses = [] 13 | self.PCStats = "" 14 | 15 | def read(self, reader): 16 | self.accountId = reader.readStr() 17 | self.charId = reader.readCompressedInt() 18 | self.killedBy = reader.readStr() 19 | self.unknownInt = reader.readInt32() 20 | self.fameEarned = reader.readCompressedInt() 21 | fameBonuses = reader.readCompressedInt() 22 | for i in range(fameBonuses): 23 | fameData = FameData() 24 | fameData.read(reader) 25 | self.fameBonuses.append(fameData) 26 | self.PCStats = reader.readStr() 27 | 28 | 29 | def write(self, writer): 30 | writer.writeStr(self.accountId) 31 | writer.writeCompressedInt(self.charId) 32 | writer.writeStr(self.killedBy) 33 | writer.writeInt32(self.unknownInt) 34 | writer.writeCompressedInt(self.fameEarned) 35 | writer.writeCompressedInt(len(self.fameBonuses)) 36 | for fameData in self.fameBonuses: 37 | fameData.write(writer) 38 | writer.writeStr(self.PCStats) 39 | -------------------------------------------------------------------------------- /Plugins/PlayerTrackerPlugin.py: -------------------------------------------------------------------------------- 1 | from Models.PlayerData import PlayerData 2 | from PluginManager import hook, plugin 3 | from Constants import ClassIds 4 | 5 | @plugin(active=True) 6 | class PlayerTrackerPlugin: 7 | def __init__(self): 8 | self.players = {} 9 | 10 | @hook("update") 11 | def onUpdate(self, client, packet): 12 | if not client.guid in self.players: 13 | self.players[client.guid] = [] 14 | for obj in packet.newObjs: 15 | if obj.objectType in ClassIds.ALL: 16 | 17 | pd = PlayerData() 18 | pd.parse(obj) 19 | 20 | self.players[client.guid].append(pd) 21 | 22 | self.players[client.guid] = [player for player in self.players[client.guid] 23 | if not player.objectId in packet.drops] 24 | 25 | @hook("mapinfo") 26 | def onMapInfo(self, client, packet): 27 | self.players[client.guid] = [] 28 | 29 | @hook("newTick") 30 | def onNewtick(self, client, packet): 31 | if not client.guid in self.players: 32 | self.players[client.guid] = [] 33 | for status in packet.statuses: 34 | for i in range(len(self.players[client.guid])): 35 | if self.players[client.guid][i].objectId == status.objectId: 36 | self.players[client.guid][i].parseStats(status.stats) 37 | self.players[client.guid][i].pos = status.pos 38 | -------------------------------------------------------------------------------- /Networking/Packets/Outgoing/HelloPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class HelloPacket(Packet): 4 | def __init__(self): 5 | self.type = "HELLO" 6 | self.gameId = 0 7 | self.buildVersion = "" 8 | self.accessToken = "" 9 | self.keyTime = 0 10 | self.key = [] 11 | self.userPlatform = "" 12 | self.playPlatform = "" 13 | self.platformToken = "" 14 | self.userToken = "" 15 | self.token = "XQpu8CWkMehb5rLVP3DG47FcafExRUvg" 16 | 17 | def write(self, writer): 18 | writer.writeInt32(self.gameId) 19 | writer.writeStr(self.buildVersion) 20 | writer.writeStr(self.accessToken) 21 | writer.writeInt32(self.keyTime) 22 | writer.writeBytes(self.key) 23 | writer.writeStr(self.userPlatform) 24 | writer.writeStr(self.playPlatform) 25 | writer.writeStr(self.platformToken) 26 | writer.writeStr(self.userToken) 27 | writer.writeStr(self.token) 28 | 29 | def read(self, reader): 30 | self.gameId = reader.readInt32() 31 | self.buildVersion = reader.readStr() 32 | self.accessToken = reader.readStr() 33 | self.keyTime = reader.readInt32() 34 | self.key = reader.readBytes() 35 | self.userPlatform = reader.readStr() 36 | self.playPlatform = reader.readStr() 37 | self.platformToken = reader.readStr() 38 | self.userToken = reader.readStr() 39 | self.token = reader.readStr() 40 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/EnemyShootPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class EnemyShootPacket(Packet): 5 | def __init__(self): 6 | self.type = "ENEMYSHOOT" 7 | self.bulletId = 0 8 | self.ownerId = 0 9 | self.bulletType = 0 10 | self.startingPos = WorldPosData() 11 | self.angle = 0 12 | self.damage = 0 13 | self.numShots = 0 14 | self.angleInc = 0 15 | 16 | def read(self, reader): 17 | self.bulletId = reader.readUnsignedShort() 18 | self.ownerId = reader.readInt32() 19 | self.bulletType = reader.readUnsignedByte() 20 | self.startingPos.read(reader) 21 | self.angle = reader.readFloat() 22 | self.damage = reader.readShort() 23 | if (reader.bytesAvailable() > 0): 24 | self.numShots = reader.readUnsignedByte() 25 | self.angleInc = reader.readFloat() 26 | else: 27 | self.numShots = 1 28 | self.angleInc = 0 29 | 30 | def write(self, writer): 31 | writer.writeUnsignedShort(self.bulletId) 32 | writer.writeInt32(self.ownerId) 33 | writer.writeUnsignedByte(self.bulletType) 34 | self.startingPos.write(writer) 35 | writer.writeFloat(self.angle) 36 | writer.writeShort(self.damage) 37 | if self.angleInc != 0 or self.numShots != 1: 38 | writer.writeUnsignedByte(self.numShots) 39 | writer.writeFloat(self.angleInc) 40 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ExaltationUpdatePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class ExaltationUpdatePacket(Packet): 4 | def __init__(self): 5 | self.type = "EXALTATIONUPDATE" 6 | self.objType = -1 7 | self.dexProgress = -1 8 | self.spdProgress = -1 9 | self.vitProgress = -1 10 | self.wisProgress = -1 11 | self.defProgress = -1 12 | self.attProgress = -1 13 | self.manaProgress = -1 14 | self.lifeProgress = -1 15 | 16 | def read(self, reader): 17 | self.objType = reader.readShort() 18 | self.dexProgress = reader.readCompressedInt() 19 | self.spdProgress = reader.readCompressedInt() 20 | self.vitProgress = reader.readCompressedInt() 21 | self.wisProgress = reader.readCompressedInt() 22 | self.defProgress = reader.readCompressedInt() 23 | self.attProgress = reader.readCompressedInt() 24 | self.manaProgress = reader.readCompressedInt() 25 | self.lifeProgress = reader.readCompressedInt() 26 | 27 | def write(self, writer): 28 | writer.writeShort(self.objType) 29 | writer.writeCompressedInt(self.dexProgress) 30 | writer.writeCompressedInt(self.spdProgress) 31 | writer.writeCompressedInt(self.vitProgress) 32 | writer.writeCompressedInt(self.wisProgress) 33 | writer.writeCompressedInt(self.defProgress) 34 | writer.writeCompressedInt(self.attProgress) 35 | writer.writeCompressedInt(self.manaProgress) 36 | writer.writeCompressedInt(self.lifeProgress) 37 | -------------------------------------------------------------------------------- /Helpers/Equip.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __init__(self, obj): 3 | self.parse(obj) 4 | 5 | def parse(self, obj): 6 | self.name = obj.attrib["id"] 7 | self.itemId = int(obj.attrib["type"], 16) 8 | self.rof = float(obj.find("RateOfFire").text) 9 | numProj = obj.find("NumProjectiles") 10 | if not numProj is None: 11 | self.numProjectiles = int(numProj.text) 12 | else: 13 | self.numProjectiles = 1 14 | arcGap = obj.find("ArcGap") 15 | if not arcGap is None: 16 | self.arcGap = int(arcGap.text) 17 | else: 18 | self.arcGap = 11.25 19 | self.projectile = Projectile(obj.find("Projectile")) 20 | 21 | 22 | class Projectile: 23 | def __init__(self, proj): 24 | self.parse(proj) 25 | 26 | def parse(self, proj): 27 | self.speed = float(proj.find("Speed").text) 28 | self.lifetime = float(proj.find("LifetimeMS").text) 29 | dmg = proj.find("Damage") 30 | if not dmg is None: 31 | self.minDmg = int(proj.find("Damage").text) 32 | self.maxDmg = self.minDmg 33 | else: 34 | self.minDmg = int(proj.find("MinDamage").text) 35 | self.maxDmg = int(proj.find("MaxDamage").text) 36 | 37 | 38 | from xml.etree import ElementTree 39 | 40 | WEAPONIDS = [17, 8, 1, 24, 3, 2] 41 | 42 | def parseWeapons(path): 43 | idToWeapon = {} 44 | tree = ElementTree.parse(path) 45 | root = tree.getroot() 46 | for obj in root: 47 | slotType = obj.find("SlotType") 48 | if not slotType is None: 49 | slotType = int(slotType.text) 50 | if slotType in WEAPONIDS: 51 | weapon = Weapon(obj) 52 | idToWeapon[weapon.itemId] = weapon 53 | return idToWeapon 54 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ServerPlayerShootPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class ServerPlayerShootPacket(Packet): 5 | def __init__(self): 6 | self.type = "SERVERPLAYERSHOOT" 7 | self.bulletId = 0 8 | self.ownerId = 0 9 | self.containerType = 0 10 | self.startingPos = WorldPosData() 11 | self.angle = 0 12 | self.damage = 0 13 | self.unknownInt = 0 14 | self.unknownByte = 0 15 | self.spellBomb = False 16 | self.bulletCount = 0 17 | self.bulletAngle = 0 18 | 19 | def read(self, reader): 20 | self.bulletId = reader.readUnsignedShort() 21 | self.ownerId = reader.readInt32() 22 | self.containerType = reader.readInt32() 23 | self.startingPos.read(reader) 24 | self.angle = reader.readFloat() 25 | self.damage = reader.readShort() 26 | self.unknownInt = reader.readInt32() 27 | self.unknownByte = reader.readByte() 28 | if reader.bytesAvailable() > 0: 29 | self.spellBomb = True 30 | self.bulletCount = reader.readByte() 31 | self.bulletAngle = reader.readFloat() 32 | else: 33 | self.bulletCount = 0 34 | self.bulletAngle = 0 35 | 36 | def write(self, writer): 37 | writer.writeUnsignedShort(self.bulletId) 38 | writer.writeInt32(self.ownerId) 39 | writer.writeInt32(self.containerType) 40 | self.startingPos.write(writer) 41 | writer.writeFloat(self.angle) 42 | writer.writeShort(self.damage) 43 | writer.writeInt32(self.unknownInt) 44 | writer.writeByte(self.unknownByte) 45 | if self.spellBomb: 46 | writer.writeByte(self.bulletCount) 47 | writer.writeFloat(self.bulletAngle) 48 | -------------------------------------------------------------------------------- /Data/QuestData.py: -------------------------------------------------------------------------------- 1 | 2 | class QuestData: 3 | def __init__(self): 4 | self.id = "" 5 | self.name = "" 6 | self.description = "" 7 | self.expiration = "" 8 | self.category = 0 9 | self.type = 0#<7 = quest tab, 7 = event tab 10 | self.itemsNeeded = [] 11 | self.rewards = [] 12 | self.completed = False 13 | self.choice = False 14 | self.repeatable = False 15 | 16 | def read(self, reader): 17 | self.id = reader.readStr() 18 | self.name = reader.readStr() 19 | self.description = reader.readStr() 20 | self.expiration = reader.readStr() 21 | self.category = reader.readInt32() 22 | self.type = reader.readInt32() 23 | 24 | num_items_needed = reader.readShort() 25 | for i in range(num_items_needed): 26 | self.itemsNeeded.append(reader.readInt32()) 27 | 28 | num_rewards = reader.readShort() 29 | for i in range(num_rewards): 30 | self.rewards.append(reader.readInt32()) 31 | 32 | self.completed = reader.readBool() 33 | self.choice = reader.readBool() 34 | self.repeatable = reader.readBool() 35 | 36 | def write(self, writer): 37 | writer.writeStr(self.id) 38 | writer.writeStr(self.name) 39 | writer.writeStr(self.description) 40 | writer.writeStr(self.expiration) 41 | writer.writeInt32(self.category) 42 | writer.writeInt32(self.type) 43 | 44 | writer.writeShort(len(self.itemsNeeded)) 45 | for item in self.itemsNeeded: 46 | writer.writeInt32(item) 47 | 48 | writer.writeShort(len(self.rewards)) 49 | for reward in self.rewards: 50 | writer.writeInt32(reward) 51 | 52 | writer.writeBool(self.completed) 53 | writer.writeBool(self.choice) 54 | writer.writeBool(self.repeatable) 55 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/UpdatePacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.GroundTileData import * 3 | from Data.ObjectData import * 4 | from Data.WorldPosData import * 5 | 6 | class UpdatePacket(Packet): 7 | def __init__(self): 8 | self.type = "UPDATE" 9 | self.pos = WorldPosData() 10 | self.levelType = 0 11 | self.tiles = [] 12 | self.newObjs = [] 13 | self.drops = [] 14 | self.unknownByte = -1 15 | 16 | def read(self, reader): 17 | self.pos.read(reader) 18 | self.levelType = reader.readByte() 19 | tiles_len = reader.readCompressedInt() 20 | for i in range(tiles_len): 21 | tile = GroundTileData() 22 | tile.read(reader) 23 | self.tiles.append(tile) 24 | 25 | objects_len = reader.readCompressedInt() 26 | for i in range(objects_len): 27 | data = ObjectData() 28 | data.read(reader) 29 | self.newObjs.append(data) 30 | 31 | drops_len = reader.readCompressedInt() 32 | for i in range(drops_len): 33 | self.drops.append(reader.readCompressedInt()) 34 | 35 | if reader.bytesAvailable() > 0: 36 | self.unknownByte = reader.readUnsignedByte() 37 | 38 | def write(self, writer): 39 | self.pos.write(writer) 40 | writer.writeByte(self.levelType) 41 | writer.writeCompressedInt(len(self.tiles)) 42 | for i in range(len(self.tiles)): 43 | self.tiles[i].write(writer) 44 | 45 | writer.writeCompressedInt(len(self.newObjs)) 46 | for i in range(len(self.newObjs)): 47 | self.newObjs[i].write(writer) 48 | 49 | writer.writeCompressedInt(len(self.drops)) 50 | for i in range(len(self.drops)): 51 | writer.writeCompressedInt(self.drops[i]) 52 | 53 | if self.unknownByte != -1: 54 | writer.writeUnsignedByte(self.unknownByte) 55 | -------------------------------------------------------------------------------- /Data/StatData.py: -------------------------------------------------------------------------------- 1 | from Constants.StatTypes import StatTypes, nameOf 2 | 3 | class StatData: 4 | def __init__(self, statType=0, statValue=0, strStatValue="", secondaryValue=0): 5 | self.statType = statType 6 | self.statValue = statValue 7 | self.strStatValue = strStatValue 8 | self.secondaryValue = secondaryValue 9 | 10 | def isStringStat(self): 11 | return self.statType in [StatTypes.EXPSTAT, StatTypes.NAMESTAT, StatTypes.ACCOUNTIDSTAT, StatTypes.GUILDNAMESTAT, 12 | StatTypes.PETNAMESTAT, StatTypes.GRAVEACCOUNTID, StatTypes.OWNERACCOUNTIDSTAT, 13 | StatTypes.ENCHANTMENTS, StatTypes.UNKNOWN121, StatTypes.MATERIALAMOUNTSTAT, StatTypes.CRUCIBLESTAT, 14 | StatTypes.DUSTCAPSTAT, StatTypes.DUSTAMOUNTSTAT, StatTypes.MATERIALCAPSTAT, StatTypes.UNKNOWN155] 15 | 16 | def statToName(self, statType=None): 17 | if statType is None: 18 | return nameOf(self.statType) 19 | else: 20 | return nameOf(statType) 21 | 22 | def read(self, reader): 23 | self.statType = reader.readUnsignedByte() 24 | if self.isStringStat(): 25 | self.strStatValue = reader.readStr() 26 | else: 27 | self.statValue = reader.readCompressedInt() 28 | self.secondaryValue = reader.readCompressedInt() 29 | 30 | def write(self, writer): 31 | writer.writeUnsignedByte(self.statType) 32 | if self.isStringStat(): 33 | writer.writeStr(self.strStatValue) 34 | else: 35 | writer.writeCompressedInt(self.statValue) 36 | writer.writeCompressedInt(self.secondaryValue) 37 | 38 | def clone(self): 39 | return StatData(self.statType, self.statValue, self.strStatValue, self.secondaryValue) 40 | 41 | def __str__(self): 42 | if self.isStringStat(): 43 | return "statType: {}\nstrStatValue: {}\nsecondaryValue: {}".format(self.statType, self.strStatValue, self.secondaryValue) 44 | else: 45 | return "statType: {}\nstatValue: {}\nsecondaryValue: {}".format(self.statType, self.statValue, self.secondaryValue) 46 | 47 | -------------------------------------------------------------------------------- /Plugins/ReplyPlugin.py: -------------------------------------------------------------------------------- 1 | from Networking.PacketHelper import createPacket 2 | from PluginManager import hook, plugin 3 | 4 | shouldEnter = False 5 | 6 | #All plugins should have a plugin decorater 7 | @plugin(active=True) 8 | class ReplyPlugin: 9 | def __init__(self): 10 | self.toReply = "" 11 | 12 | #To hook a packet use the hook decorator with the packet type you wish to hook 13 | @hook("text") 14 | def onText(self, client, packet): 15 | global shouldEnter 16 | if packet.name == self.toReply and packet.recipient == client.playerData.name: 17 | replyPacket = createPacket("PLAYERTEXT") 18 | if packet.text.lower() == "hello": 19 | replyPacket.text = f"/tell {packet.name} Hey!" 20 | elif packet.text.lower() == "pos": 21 | replyPacket.text = f"/tell {packet.name} My posision is {client.pos}" 22 | elif packet.text.lower() == "nexus": 23 | client.nexus() 24 | elif packet.text.lower() == "reconnect": 25 | client.connect() 26 | elif packet.text.lower().startswith("server"): 27 | client.changeServer(packet.text.split()[1]) 28 | elif packet.text.lower() == "enter vault": 29 | shouldEnter = True 30 | replyPacket.text = f"/tell {packet.name} Entering vault..." 31 | else: 32 | replyPacket.text = f"/tell {packet.name} Unknown command" 33 | client.send(replyPacket) 34 | 35 | #There can be multiple plugins in one file, be aware that all plugins 36 | #will be used on all clients 37 | @plugin(active=True) 38 | class PortalPlugin: 39 | def __init__(self): 40 | self.vaultPortal = None 41 | 42 | @hook("ping") 43 | def onPing(self, client, packet): 44 | global shouldEnter 45 | if shouldEnter and not self.vaultPortal is None: 46 | self.enterVault(client) 47 | 48 | #To hook a packet use the hook decorator with the packet type you wish to hook 49 | @hook("update") 50 | def onUpdate(self, client, packet): 51 | for e in packet.newObjs: 52 | if e.objectType == 1824: 53 | self.vaultPortal = e 54 | 55 | @hook("mapInfo") 56 | def onMapInfo(self, client, packet): 57 | global shouldEnter 58 | shouldEnter = False 59 | 60 | def enterVault(self, client): 61 | if client.pos.dist(self.vaultPortal.status.pos) < 0.25: 62 | usePortal = createPacket("USEPORTAL") 63 | usePortal.objectId = self.vaultPortal.status.objectId 64 | client.send(usePortal) 65 | else: 66 | client.nextPos = [self.vaultPortal.status.pos] 67 | -------------------------------------------------------------------------------- /Networking/Writer.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | class Writer: 4 | def __init__(self): 5 | self.index = 5#5 header bytes 6 | self.buffer = bytearray() 7 | 8 | def writeByte(self, value): 9 | self.index += 1 10 | self.buffer.extend(struct.pack("!b", value)) 11 | 12 | def writeUnsignedByte(self, value): 13 | self.index += 1 14 | self.buffer.extend(struct.pack("!B", value)) 15 | 16 | def writeInt32(self, value): 17 | self.index += 4 18 | self.buffer.extend(struct.pack("!i", value)) 19 | 20 | def writeUInt32(self, value): 21 | self.index += 4 22 | self.buffer.extend(struct.pack("!I", value)) 23 | 24 | def writeFloat(self, value): 25 | self.index += 4 26 | self.buffer.extend(struct.pack("!f", value)) 27 | 28 | def writeShort(self, value): 29 | self.index += 2 30 | self.buffer.extend(struct.pack("!h", value)) 31 | 32 | def writeUnsignedShort(self, value): 33 | self.index += 2 34 | self.buffer.extend(struct.pack("!H", value)) 35 | 36 | def writeBool(self, value): 37 | self.index += 1 38 | self.buffer.extend(struct.pack("!?", value)) 39 | 40 | def writeStr(self, string): 41 | if isinstance(string, str): 42 | string = string.encode() 43 | self.writeShort(len(string)) 44 | self.index += len(string) 45 | self.buffer.extend(struct.pack("!{}s".format(len(string)), string)) 46 | 47 | def writeStr32(self, string32): 48 | if isinstance(string32, str): 49 | string32 = string32.encode() 50 | self.writeInt32(len(string32)) 51 | self.index += len(string32) 52 | self.buffer.extend(struct.pack("!{}s".format(len(string32)), string32)) 53 | 54 | def writeBytes(self, byteList): 55 | self.writeShort(len(byteList)) 56 | for byte in byteList: 57 | self.writeByte(byte) 58 | 59 | def writeCompressedInt(self, value): 60 | uByte = 0 61 | uByte |= 64*(value<0) 62 | value = abs(value) 63 | uByte |= (value&63) 64 | 65 | value >>= 6 66 | uByte |= 128*(value>0) 67 | 68 | self.writeUnsignedByte(uByte) 69 | 70 | while value > 0: 71 | uByte = value&127 72 | value >>= 7 73 | uByte |= 128*(value>0) 74 | self.writeUnsignedByte(uByte) 75 | 76 | def writeHeader(self, packetId): 77 | sizeBytes = struct.pack("!i", self.index) 78 | for idx, byte in enumerate(sizeBytes): 79 | self.buffer.insert(idx, byte) 80 | self.buffer.insert(4, packetId) 81 | 82 | def reset(self): 83 | self.index = 5 84 | self.buffer = bytearray() 85 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/ShowEffectPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | from Data.WorldPosData import * 3 | 4 | class ShowEffectPacket(Packet): 5 | def __init__(self): 6 | self.type = "SHOWEFFECT" 7 | self.effectType = 0 8 | self.ignore = 0 9 | self.targetObjectId = 0 10 | self.pos1 = WorldPosData() 11 | self.pos2 = WorldPosData() 12 | self.color = 0 13 | self.duration = 0 14 | self.extra = False 15 | self.unknownByte = 0 16 | 17 | def read(self, reader): 18 | self.effectType = reader.readUnsignedByte() 19 | self.ignore = reader.readUnsignedByte()#Better way to do this? 20 | 21 | if self.ignore&64: 22 | self.targetObjectId = reader.readCompressedInt() 23 | else: 24 | self.targetObjectId = 0 25 | 26 | if self.ignore&2: 27 | self.pos1.x = reader.readFloat() 28 | else: 29 | self.pos1.x = 0 30 | 31 | if self.ignore&4: 32 | self.pos1.y = reader.readFloat() 33 | else: 34 | self.pos1.y = 0 35 | 36 | if self.ignore&8: 37 | self.pos2.x = reader.readFloat() 38 | else: 39 | self.pos2.x = 0 40 | 41 | if self.ignore&16: 42 | self.pos2.y = reader.readFloat() 43 | else: 44 | self.pos2.y = 0 45 | 46 | if self.ignore&1: 47 | self.color = reader.readInt32() 48 | else: 49 | self.color = 0 50 | 51 | if self.ignore&32: 52 | self.duration = reader.readFloat() 53 | else: 54 | self.duration = 0 55 | 56 | if reader.bytesAvailable(): 57 | self.extra = True 58 | self.unknownByte = reader.readByte() 59 | 60 | def write(self, writer): 61 | writer.writeUnsignedByte(self.effectType) 62 | writer.writeUnsignedByte(self.ignore) 63 | 64 | if self.ignore&64: 65 | writer.writeCompressedInt(self.targetObjectId) 66 | 67 | if self.ignore&2: 68 | writer.writeFloat(self.pos1.x) 69 | 70 | if self.ignore&4: 71 | writer.writeFloat(self.pos1.y) 72 | 73 | if self.ignore&8: 74 | writer.writeFloat(self.pos2.x) 75 | 76 | if self.ignore&16: 77 | writer.writeFloat(self.pos2.y) 78 | 79 | if self.ignore&1: 80 | writer.writeInt32(self.color) 81 | 82 | if self.ignore&32: 83 | writer.writeFloat(self.duration) 84 | 85 | if self.extra: 86 | writer.writeByte(self.unknownByte) 87 | -------------------------------------------------------------------------------- /Networking/Reader.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | class Reader: 4 | def __init__(self): 5 | self.index = 5#Skip the first 5 header bytes 6 | self.buffer = bytearray() 7 | self._length = 0 8 | 9 | def readByte(self): 10 | value = struct.unpack("!b", self.buffer[self.index:self.index+1])[0] 11 | self.index += 1 12 | return value 13 | 14 | def readUnsignedByte(self): 15 | value = struct.unpack("!B", self.buffer[self.index:self.index+1])[0] 16 | self.index += 1 17 | return value 18 | 19 | def readInt32(self): 20 | value = struct.unpack("!i", self.buffer[self.index:self.index+4])[0] 21 | self.index += 4 22 | return value 23 | 24 | def readUInt32(self): 25 | value = struct.unpack("!I", self.buffer[self.index:self.index+4])[0] 26 | self.index += 4 27 | return value 28 | 29 | def readFloat(self): 30 | value = struct.unpack("!f", self.buffer[self.index:self.index+4])[0] 31 | self.index += 4 32 | return value 33 | 34 | def readShort(self): 35 | value = struct.unpack("!h", self.buffer[self.index:self.index+2])[0] 36 | self.index += 2 37 | return value 38 | 39 | def readUnsignedShort(self): 40 | value = struct.unpack("!H", self.buffer[self.index:self.index+2])[0] 41 | self.index += 2 42 | return value 43 | 44 | def readBool(self): 45 | value = struct.unpack("!?", self.buffer[self.index:self.index+1])[0] 46 | self.index += 1 47 | return value 48 | 49 | def readStr(self): 50 | strLen = self.readShort() 51 | string = struct.unpack("!{}s".format(strLen), self.buffer[self.index:self.index+strLen])[0] 52 | self.index += strLen 53 | if isinstance(string, bytes): 54 | string = string.decode() 55 | return string 56 | 57 | def readStr32(self): 58 | strLen = self.readInt32() 59 | string = struct.unpack("!{}s".format(strLen), self.buffer[self.index:self.index+strLen])[0] 60 | self.index += strLen 61 | if isinstance(string, bytes): 62 | string = string.decode() 63 | return string 64 | 65 | def readBytes(self): 66 | byteList = [] 67 | byteListLen = self.readShort() 68 | for i in range(byteListLen): 69 | byteList.append(self.readByte()) 70 | return byteList 71 | 72 | def readCompressedInt(self): 73 | value = 0 74 | uByte = self.readUnsignedByte() 75 | isNegative = (uByte & 64) != 0 76 | shift = 6 77 | value = uByte & 63; 78 | while uByte & 128: 79 | uByte = self.readUnsignedByte() 80 | value |= (uByte & 127) << shift 81 | shift += 7 82 | 83 | if isNegative: 84 | return -value 85 | 86 | return value 87 | 88 | def bytesAvailable(self): 89 | return self._length - self.index 90 | 91 | def reset(self, buffer): 92 | self.index = 5 93 | self.buffer = buffer 94 | self._length = len(buffer) 95 | 96 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/MapInfoPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class MapInfoPacket(Packet): 4 | def __init__(self): 5 | self.type = "MAPINFO" 6 | self.width = 0 7 | self.height = 0 8 | self.name = "" 9 | self.displayName = "" 10 | self.realmName = "" 11 | self.seed = 0 12 | self.background = 0 13 | self.difficulty = 0 14 | self.allowPlayerTeleport = False 15 | self.showDisplays = False 16 | self.maxPlayers = 0 17 | self.gameOpenedTime = 0 18 | self.newBool = False 19 | self.buildVersion = "" 20 | self.viewRadius = 0 21 | self.newInt = 0 22 | self.dungeonModifiers = [] 23 | self.unknownShort1 = 0 24 | self.unknownBool = False 25 | self.unknownShort2 = 0 26 | self.maxRealmScore = 0 27 | self.curRealmScore = 0 28 | 29 | def read(self, reader): 30 | self.width = reader.readInt32() 31 | self.height = reader.readInt32() 32 | self.name = reader.readStr() 33 | self.displayName = reader.readStr() 34 | self.realmName = reader.readStr() 35 | self.seed = reader.readUInt32() 36 | self.background = reader.readInt32() 37 | self.difficulty = reader.readFloat() 38 | self.allowPlayerTeleport = reader.readBool() 39 | self.showDisplays = reader.readBool() 40 | self.newBool = reader.readBool() 41 | self.maxPlayers = reader.readShort() 42 | self.gameOpenedTime = reader.readUInt32() 43 | self.buildVersion = reader.readStr() 44 | self.viewRadius = reader.readShort() 45 | self.newInt = reader.readInt32() 46 | self.dungeonModifiers = reader.readStr().split(";") 47 | self.unknownShort1 = reader.readShort()#Always 0? 48 | self.unknownBool = reader.readBool() 49 | self.unknownShort2 = reader.readShort() 50 | if reader.bytesAvailable() > 0: 51 | self.maxRealmScore = reader.readInt32() 52 | self.curRealmScore = reader.readInt32() 53 | 54 | def write(self, writer): 55 | writer.writeInt32(self.width) 56 | writer.writeInt32(self.height) 57 | writer.writeStr(self.name) 58 | writer.writeStr(self.displayName) 59 | writer.writeStr(self.realmName) 60 | writer.writeUInt32(self.seed) 61 | writer.writeInt32(self.background) 62 | writer.writeFloat(self.difficulty) 63 | writer.writeBool(self.allowPlayerTeleport) 64 | writer.writeBool(self.showDisplays) 65 | writer.writeBool(self.newBool) 66 | writer.writeShort(self.maxPlayers) 67 | writer.writeUInt32(self.gameOpenedTime) 68 | writer.writeStr(self.buildVersion) 69 | writer.writeShort(self.viewRadius) 70 | writer.writeInt32(self.newInt) 71 | writer.writeStr(";".join(self.dungeonModifiers)) 72 | writer.writeShort(self.unknownShort1) 73 | writer.writeBool(self.unknownBool) 74 | writer.writeShort(self.unknownShort2) 75 | if self.maxRealmScore != 0 and self.curRealmScore != 0: 76 | writer.writeInt32(self.maxRealmScore) 77 | writer.writeInt32(self.curRealmScore) 78 | 79 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/NotificationPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class NotificationPacket(Packet): 4 | def __init__(self): 5 | self.type = "NOTIFICATION" 6 | self.effect = 0 7 | self.extra = 0 8 | self.message = "" 9 | 10 | self.objectId = 0 11 | self.uiExtra = 0 12 | self.queuePos = 0 13 | self.color = 0 14 | self.pictureType = 0 15 | self.emoteId = 0 16 | 17 | def read(self, reader): 18 | self.effect = reader.readByte() 19 | self.extra = reader.readByte() 20 | if self.effect == 0:#StatIncrease 21 | self.message = reader.readStr() 22 | if self.effect == 1:#ServerMessage 23 | self.message = reader.readStr() 24 | if self.effect == 2:#ErrorMessage 25 | self.message = reader.readStr() 26 | if self.effect == 3:#KeepMessage 27 | self.message = reader.readStr() 28 | if self.effect == 4:#UI 29 | self.message = reader.readStr() 30 | self.uiExtra = reader.readShort() 31 | if self.effect == 5:#Queue 32 | self.objectId = reader.readInt32() 33 | self.queuePos = reader.readShort() 34 | if self.effect == 6:#ObjectText/json 35 | self.message = reader.readStr() 36 | self.objectId = reader.readInt32() 37 | self.color = reader.readInt32() 38 | if self.effect == 7:#Death 39 | self.message = reader.readStr() 40 | self.pictureType = reader.readInt32() 41 | if self.effect == 8:#DungeonOpened 42 | self.message = reader.readStr() 43 | self.pictureType = reader.readInt32() 44 | if self.effect == 10:#DungeonCall 45 | self.message = reader.readStr() 46 | self.unknown1 = reader.readInt32() 47 | self.unknown2 = reader.readShort() 48 | if self.effect == 13:#Emote 49 | self.objectId = reader.readInt32() 50 | self.emoteId = reader.readInt32() 51 | 52 | def write(self, writer): 53 | writer.writeByte(self.effect) 54 | writer.writeByte(self.extra) 55 | if self.effect == 0:#StatIncrease 56 | writer.writeStr(self.message) 57 | if self.effect == 1:#ServerMessage 58 | writer.writeStr(self.message) 59 | if self.effect == 2:#ErrorMessage 60 | writer.writeStr(self.message) 61 | if self.effect == 3:#KeepMessage 62 | writer.writeStr(self.message) 63 | if self.effect == 4:#UI 64 | writer.writeStr(self.message) 65 | writer.writeShort(self.uiExtra) 66 | if self.effect == 5:#Queue 67 | writer.writeInt32(self.objectId) 68 | writer.writeShort(self.queuePos) 69 | if self.effect == 6:#ObjectText/json 70 | writer.writeStr(self.message) 71 | writer.writeInt32(self.objectId) 72 | writer.writeInt32(self.color) 73 | if self.effect == 7:#Death 74 | writer.writeStr(self.message) 75 | writer.writeInt32(self.pictureType) 76 | if self.effect == 8:#DungeonOpened 77 | writer.writeStr(self.message) 78 | writer.writeInt32(self.pictureType) 79 | if self.effect == 10:#DungeonCall 80 | writer.writeStr(self.message) 81 | writer.writeInt32(self.unknown1) 82 | writer.writeShort(self.unknown2) 83 | if self.effect == 13:#Emote 84 | writer.writeInt32(self.objectId) 85 | writer.writeInt32(self.emoteId) 86 | 87 | -------------------------------------------------------------------------------- /ClientManager.py: -------------------------------------------------------------------------------- 1 | import random 2 | from hashlib import md5 3 | from Client.Client import Client 4 | 5 | class ClientManager: 6 | def __init__(self): 7 | self.clients = [] 8 | self.updateServers = False 9 | self.baseAccInfo = {"guid": "", "password": "", "secret": ""} 10 | 11 | def addClient(self, accInfo): 12 | accInfo = self.baseAccInfo | accInfo 13 | 14 | if accInfo["guid"] == "" or (accInfo["password"] == "" and accInfo["secret"] == ""): 15 | print("Empty email or password/secret, skipping account") 16 | return None 17 | if not "alias" in accInfo.keys(): 18 | accInfo["alias"] = accInfo["guid"] 19 | if "proxy" in accInfo.keys(): 20 | if not "username" in accInfo["proxy"].keys(): 21 | accInfo["proxy"]["username"] = "" 22 | if not "password" in accInfo["proxy"].keys(): 23 | accInfo["proxy"]["password"] = "" 24 | 25 | for client in self.clients: 26 | if client.guid == accInfo["guid"]: 27 | print("Account already added") 28 | return None 29 | 30 | proxy = accInfo.get("proxy", {}) 31 | proxies = {} 32 | if proxy != {}: 33 | proxies = { 34 | "https": "socks{}://".format(proxy["type"]) + 35 | ("{}:{}@".format(proxy["username"], proxy["password"]) if proxy["username"] != "" else "") + 36 | "{}:{}".format(proxy["host"], proxy["port"]) 37 | } 38 | 39 | client = Client() 40 | 41 | client.clientToken = md5(accInfo["guid"].encode("utf-8") + accInfo["password"].encode("utf-8")).hexdigest() 42 | client.proxies = proxies 43 | 44 | client.getToken(accInfo) 45 | 46 | client.checkInfo(accInfo, self.updateServers) 47 | 48 | import Constants.Servers as Servers 49 | 50 | if not "server" in accInfo.keys(): 51 | accInfo["server"] = random.choice(list(Servers.nameToIp.keys())) 52 | print("Server not in account info using server", accInfo["server"], "instead") 53 | if not accInfo["server"] in list(Servers.nameToIp.keys()): 54 | old = accInfo["server"] 55 | accInfo["server"] = random.choice(list(Servers.nameToIp.keys())) 56 | print("Invalid server", old, "using server", accInfo["server"], "instead") 57 | 58 | client.setup(accInfo) 59 | 60 | client.clientManager = self 61 | client.connect() 62 | 63 | self.clients.append(client) 64 | 65 | return client 66 | 67 | def removeClient(self, guid): 68 | new_clients = [] 69 | for client in self.clients: 70 | if client.guid == guid: 71 | client.stop() 72 | else: 73 | new_clients.append(client) 74 | self.clients = new_clients 75 | 76 | def reconnectIfNeeded(self): 77 | if any(client.active for client in self.clients): 78 | for client in self.clients: 79 | if client.isReady and client.active and not client.isConnected(): 80 | #Has client been disconnected for more than 2.5 secs? 81 | if client.lastPacketTime + 2500 < client.getTime(): 82 | client.connect() 83 | else: 84 | return True 85 | 86 | def stop(self): 87 | print("Disconnecting clients...") 88 | for client in self.clients: 89 | client.stop() 90 | -------------------------------------------------------------------------------- /PluginManager.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import importlib 4 | import inspect 5 | from Networking.PacketHelper import isValidPacket 6 | 7 | def findClass(func): 8 | return getattr(inspect.getmodule(func), func.__qualname__.split(".", 1)[0].rsplit(".", 1)[0]) 9 | 10 | class PacketHooks: 11 | def __init__(self): 12 | self._funcs = {} 13 | self._classes = [] 14 | 15 | def addHook(self, packetType, func): 16 | if isValidPacket(packetType.upper()): 17 | if packetType in self._funcs: 18 | self._funcs[packetType].append(func) 19 | else: 20 | self._funcs[packetType] = [func] 21 | else: 22 | print("WARNING: hooked packet", packetType, "is not a valid packet type") 23 | 24 | def addClass(self, cls): 25 | self._classes.append(cls) 26 | 27 | def callHooks(self, client, packet): 28 | if packet.type in self._funcs: 29 | for func in self._funcs[packet.type]: 30 | for cls in self._classes: 31 | if type(cls) == findClass(func): 32 | if type(cls) == type(client): 33 | func(client, packet) 34 | else: 35 | func(cls, client, packet) 36 | 37 | def resetPlugins(self): 38 | #Remove all plugin hooks and classes, but keep client hooks and classes 39 | new_funcs = {} 40 | for packetType in self._funcs: 41 | hooks = [] 42 | for func in self._funcs[packetType]: 43 | if "Client." in str(func): 44 | hooks.append(func) 45 | if len(hooks) > 0: 46 | new_funcs[packetType] = hooks 47 | self._funcs = new_funcs 48 | 49 | self._classes = [c for c in self._classes 50 | if "Client.Client" in str(type(c))] 51 | 52 | class Plugins: 53 | def __init__(self): 54 | self._plugins = [] 55 | 56 | def addPlugin(self, plugin, args, options): 57 | if options["active"]: 58 | self._plugins.append(plugin) 59 | else: 60 | print("Skipping deactivated plugin", plugin.__module__.replace("Plugins.","",1) + "." + plugin.__name__) 61 | 62 | def reset(self): 63 | for p in self._plugins: 64 | if p.__module__ in sys.modules: 65 | del sys.modules[p.__module__] 66 | del p 67 | self._plugins = [] 68 | 69 | def getPlugins(self): 70 | return self._plugins 71 | 72 | path = "./Plugins" 73 | import_start = "Plugins." 74 | packetHook = PacketHooks() 75 | plugins = Plugins() 76 | 77 | def hook(packetType): 78 | def addFunc(func): 79 | packetHook.addHook(packetType.upper(), func) 80 | return func 81 | return addFunc 82 | 83 | def plugin(*args, **kwargs): 84 | def addPlugin(pluginClass): 85 | plugins.addPlugin(pluginClass, args, kwargs) 86 | return pluginClass 87 | return addPlugin 88 | 89 | def client(): 90 | def addClient(clientClass): 91 | packetHook.addClass(clientClass()) 92 | return clientClass 93 | return addClient 94 | 95 | def loadPlugins(): 96 | for file in os.listdir(path): 97 | if "__" not in file and file.endswith(".py"): 98 | to_import = import_start + file[:-3] 99 | importlib.import_module(to_import) 100 | 101 | for pluginClass in plugins.getPlugins(): 102 | try: 103 | cls = pluginClass()#Init plugin 104 | packetHook.addClass(cls) 105 | print("Loaded plugin", pluginClass.__module__.replace("Plugins.","",1) + "." + pluginClass.__name__) 106 | except Exception as e: 107 | print("Error while loading", pluginClass.__name__) 108 | print(e) 109 | 110 | def reloadPlugins(): 111 | packetHook.resetPlugins() 112 | plugins.reset() 113 | loadPlugins() 114 | 115 | def callHooks(client, packet): 116 | packetHook.callHooks(client, packet) 117 | -------------------------------------------------------------------------------- /Networking/Packets/Incoming/VaultInfoPacket.py: -------------------------------------------------------------------------------- 1 | from Networking.Packets.Packet import Packet 2 | 3 | class VaultInfoPacket(Packet): 4 | def __init__(self): 5 | self.type = "VAULTINFO" 6 | self.unknownBool = False 7 | self.chestObjectId = -1 8 | self.materialObjectId = -1 9 | self.giftObjectId = -1 10 | self.potionObjectId = -1 11 | self.spoilsObjectId = -1 12 | 13 | self.vaultContent = [] 14 | self.materialContent = [] 15 | self.giftContent = [] 16 | self.potionContent = [] 17 | self.spoilsContent = [] 18 | self.vaultUpgrageCost = -1 19 | self.potionUpgradeCose = -1 20 | self.curPotionMax = -1 21 | self.nextPotionMax = -1 22 | self.materialUpgradeCost = -1 23 | 24 | self.unknownBytes = [] 25 | 26 | def read(self, reader): 27 | self.unknownBool = reader.readBool() 28 | self.chestObjectId = reader.readCompressedInt() 29 | self.materialObjectId = reader.readCompressedInt() 30 | self.giftObjectId = reader.readCompressedInt() 31 | self.potionObjectId = reader.readCompressedInt() 32 | self.spoilsObjectId = reader.readCompressedInt() 33 | 34 | vaultCount = reader.readCompressedInt() 35 | for i in range(vaultCount): 36 | self.vaultContent.append(reader.readCompressedInt()) 37 | 38 | materialCount = reader.readCompressedInt() 39 | for i in range(materialCount): 40 | self.materialContent.append(reader.readCompressedInt()) 41 | 42 | giftCount = reader.readCompressedInt() 43 | for i in range(giftCount): 44 | self.giftContent.append(reader.readCompressedInt()) 45 | 46 | potionCount = reader.readCompressedInt() 47 | for i in range(potionCount): 48 | self.potionContent.append(reader.readCompressedInt()) 49 | 50 | spoilsCount = reader.readCompressedInt() 51 | for i in range(spoilsCount): 52 | self.spoilsContent.append(reader.readCompressedInt()) 53 | 54 | self.vaultUpgrageCost = reader.readShort() 55 | self.materialUpgradeCost = reader.readShort() 56 | self.potionUpgradeCose = reader.readShort() 57 | self.curPotionMax = reader.readShort() 58 | self.nextPotionMax = reader.readShort() 59 | 60 | for i in range(reader.bytesAvailable()): 61 | self.unknownBytes.append(reader.readByte()) 62 | 63 | def write(self, writer): 64 | writer.writeBool(self.unknownBool) 65 | writer.writeCompressedInt(self.chestObjectId) 66 | writer.writeCompressedInt(self.materialObjectId) 67 | writer.writeCompressedInt(self.giftObjectId) 68 | writer.writeCompressedInt(self.potionObjectId) 69 | writer.writeCompressedInt(self.spoilsObjectId) 70 | 71 | writer.writeCompressedInt(len(self.vaultContent)) 72 | for i in range(len(self.vaultContent)): 73 | writer.writeCompressedInt(self.vaultContent[i]) 74 | 75 | writer.writeCompressedInt(len(self.materialContent)) 76 | for i in range(len(self.materialContent)): 77 | writer.writeCompressedInt(self.materialContent[i]) 78 | 79 | writer.writeCompressedInt(len(self.giftContent)) 80 | for i in range(len(self.giftContent)): 81 | writer.writeCompressedInt(self.giftContent[i]) 82 | 83 | writer.writeCompressedInt(len(self.potionContent)) 84 | for i in range(len(self.potionContent)): 85 | writer.writeCompressedInt(self.potionContent[i]) 86 | 87 | writer.writeCompressedInt(len(self.spoilsContent)) 88 | for i in range(len(self.spoilsContent)): 89 | writer.writeCompressedInt(self.spoilsContent[i]) 90 | 91 | writer.writeShort(self.vaultUpgrageCost) 92 | writer.writeShort(self.materialUpgradeCost) 93 | writer.writeShort(self.potionUpgradeCose) 94 | writer.writeShort(self.curPotionMax) 95 | writer.writeShort(self.nextPotionMax) 96 | 97 | for byte in self.unknownBytes: 98 | writer.writeByte(byte) 99 | -------------------------------------------------------------------------------- /Constants/StatTypes.py: -------------------------------------------------------------------------------- 1 | 2 | class StatTypes: 3 | MAXHPSTAT = 0 4 | HPSTAT = 1 5 | SIZESTAT = 2 6 | MAXMPSTAT = 3 7 | MPSTAT = 4 8 | NEXTLEVELEXPSTAT = 5 9 | EXPSTAT = 6 10 | LEVELSTAT = 7 11 | INVENTORY0STAT = 8 12 | INVENTORY1STAT = 9 13 | INVENTORY2STAT = 10 14 | INVENTORY3STAT = 11 15 | INVENTORY4STAT = 12 16 | INVENTORY5STAT = 13 17 | INVENTORY6STAT = 14 18 | INVENTORY7STAT = 15 19 | INVENTORY8STAT = 16 20 | INVENTORY9STAT = 17 21 | INVENTORY10STAT = 18 22 | INVENTORY11STAT = 19 23 | ATTACKSTAT = 20 24 | DEFENSESTAT = 21 25 | SPEEDSTAT = 22 26 | TEXTURESTAT = 25 27 | VITALITYSTAT = 26 28 | WISDOMSTAT = 27 29 | DEXTERITYSTAT = 28 30 | CONDITIONSTAT = 29 31 | NUMSTARSSTAT = 30 32 | NAMESTAT = 31 33 | TEX1STAT = 32 34 | TEX2STAT = 33 35 | MERCHANDISETYPESTAT = 34 36 | CREDITSSTAT = 35 37 | MERCHANDISEPRICESTAT = 36 38 | ACTIVESTAT = 37 39 | ACCOUNTIDSTAT = 38 40 | FAMESTAT = 39 41 | MERCHANDISECURRENCYSTAT = 40 42 | CONNECTSTAT = 41 43 | MERCHANDISECOUNTSTAT = 42 44 | MERCHANDISEMINSLEFTSTAT = 43 45 | MERCHANDISEDISCOUNTSTAT = 44 46 | MERCHANDISERANKREQSTAT = 45 47 | MAXHPBOOSTSTAT = 46 48 | MAXMPBOOSTSTAT = 47 49 | ATTACKBOOSTSTAT = 48 50 | DEFENSEBOOSTSTAT = 49 51 | SPEEDBOOSTSTAT = 50 52 | VITALITYBOOSTSTAT = 51 53 | WISDOMBOOSTSTAT = 52 54 | DEXTERITYBOOSTSTAT = 53 55 | OWNERACCOUNTIDSTAT = 54 56 | RANKREQUIREDSTAT = 55 57 | NAMECHOSENSTAT = 56 58 | CURRFAMESTAT = 57 59 | NEXTCLASSQUESTFAMESTAT = 58 60 | LEGENDARYRANKSTAT = 59 61 | SINKLEVELSTAT = 60 62 | ALTTEXTURESTAT = 61 63 | GUILDNAMESTAT = 62 64 | GUILDRANKSTAT = 63 65 | BREATHSTAT = 64 66 | XPBOOSTEDSTAT = 65 67 | XPTIMERSTAT = 66 68 | LDTIMERSTAT = 67 69 | LTTIMERSTAT = 68 70 | HEALTHPOTIONSTACKSTAT = 69 71 | MAGICPOTIONSTACKSTAT = 70 72 | MATERIALAMOUNTSTAT = 71 73 | MATERIALCAPSTAT = 72 74 | HASBACKPACKSTAT = 79 75 | ENCHANTMENTS = 80 76 | PETINSTANCEIDSTAT = 81 77 | PETNAMESTAT = 82 78 | PETTYPESTAT = 83 79 | PETRARITYSTAT = 84 80 | PETMAXABILITYPOWERSTAT = 85 81 | PETFAMILYSTAT = 86 82 | PETFIRSTABILITYPOINTSTAT = 87 83 | PETSECONDABILITYPOINTSTAT = 88 84 | PETTHIRDABILITYPOINTSTAT = 89 85 | PETFIRSTABILITYPOWERSTAT = 90 86 | PETSECONDABILITYPOWERSTAT = 91 87 | PETTHIRDABILITYPOWERSTAT = 92 88 | PETFIRSTABILITYTYPESTAT = 93 89 | PETSECONDABILITYTYPESTAT = 94 90 | PETTHIRDABILITYTYPESTAT = 95 91 | NEWCONSTAT = 96 92 | FORTUNETOKENSTAT = 97 93 | SUPPORTERPOINTSSTAT = 98 94 | SUPPORTERSTAT = 99 95 | CHALLENGERSTARBGSTAT = 100 96 | PROJECTILESPEEDMULT = 102 97 | PROJECTILELIFEMULT = 103 98 | OPENEDATTIMESTAMP = 104 99 | EXALTEDATK = 105 100 | EXALTEDDEFENSE = 106 101 | EXALTEDSPD = 107 102 | EXALTEDVIT = 108 103 | EXALTEDWIS = 109 104 | EXALTEDDEX = 110 105 | EXALTEDHP = 111 106 | EXALTEDMP = 112 107 | EXALTATIONBONUSDMG = 113 108 | EXALTATIONICREDUCTION = 114 109 | GRAVEACCOUNTID = 115 110 | POTIONONETYPE = 116 111 | POTIONTWOTYPE = 117 112 | POTIONTHREETYPE = 118 113 | POTIONBELT = 119 114 | FORGEFIRE = 120 115 | UNKNOWN121 = 121 116 | UNKNOWN123 = 123 117 | DUSTAMOUNTSTAT = 127 118 | CRUCIBLESTAT = 128 119 | BACKPACK0STAT = 131 120 | BACKPACK1STAT = 132 121 | BACKPACK2STAT = 133 122 | BACKPACK3STAT = 134 123 | BACKPACK4STAT = 135 124 | BACKPACK5STAT = 136 125 | BACKPACK6STAT = 137 126 | BACKPACK7STAT = 138 127 | DUSTCAPSTAT = 147 128 | UNKNOWN155 = 155 129 | 130 | 131 | def nameOf(statType): 132 | if statType in StatTypes.__dict__.values(): 133 | for key in StatTypes.__dict__.keys(): 134 | if statType == StatTypes.__dict__[key]: 135 | return key 136 | return "UNKNOWNSTAT" 137 | -------------------------------------------------------------------------------- /Constants/PacketIds.py: -------------------------------------------------------------------------------- 1 | idToType = {0: "FAILURE", 2 | 1: "TELEPORT", 3 | 3: "CLAIMDAILYLOGINREWARD", 4 | 4: "DELETEPET", 5 | 5: "REQUESTTRADE", 6 | 6: "QUESTFETCHRESPONSE", 7 | 7: "JOINGUILD", 8 | 8: "PING", 9 | 9: "PLAYERTEXT", 10 | 10: "NEWTICK", 11 | 11: "SHOWEFFECT", 12 | 12: "SERVERPLAYERSHOOT", 13 | 13: "USEITEM", 14 | 14: "TRADEACCEPTED", 15 | 15: "GUILDREMOVE", 16 | 16: "PETUPGRADEREQUEST", 17 | 17: "ENTERARENA", 18 | 18: "GOTO", 19 | 19: "INVDROP", 20 | 20: "OTHERHIT", 21 | 21: "NAMERESULT", 22 | 22: "BUYRESULT", 23 | 23: "HATCHPET", 24 | 24: "ACTIVEPETUPDATEREQUEST", 25 | 25: "ENEMYHIT", 26 | 26: "GUILDRESULT", 27 | 27: "EDITACCOUNTLIST", 28 | 28: "TRADECHANGED", 29 | 30: "PLAYERSHOOT", 30 | 31: "PONG", 31 | 33: "CHANGEPETSKIN", 32 | 34: "TRADEDONE", 33 | 35: "ENEMYSHOOT", 34 | 36: "ACCEPTTRADE", 35 | 37: "CHANGEGUILDRANK", 36 | 38: "PLAYSOUND", 37 | 39: "VERIFYEMAIL", 38 | 40: "SQUAREHIT", 39 | 41: "NEWABILITY", 40 | 42: "UPDATE", 41 | 44: "TEXT", 42 | 45: "RECONNECT", 43 | 46: "DEATH", 44 | 47: "USEPORTAL", 45 | 48: "GOTOQUESTROOM", 46 | 49: "ALLYSHOOT", 47 | 50: "IMMINENTARENAWAVE", 48 | 51: "RESKIN", 49 | 52: "RESETDAILYQUESTS", 50 | 53: "PETCHANGEFORMMSG", 51 | 55: "INVSWAP", 52 | 56: "CHANGETRADE", 53 | 57: "CREATE", 54 | 58: "QUESTREDEEM", 55 | 59: "CREATEGUILD", 56 | 60: "SETCONDITION", 57 | 61: "LOAD", 58 | 62: "MOVE", 59 | 63: "KEYINFORESPONSE", 60 | 64: "AOE", 61 | 65: "GOTOACK", 62 | 66: "GLOBALNOTIFICATION", 63 | 67: "NOTIFICATION", 64 | 68: "ARENADEATH", 65 | 69: "CLIENTSTAT", 66 | 74: "HELLO", 67 | 75: "DAMAGE", 68 | 76: "ACTIVEPETUPDATE", 69 | 77: "INVITEDTOGUILD", 70 | 78: "PETYARDUPDATE", 71 | 79: "PASSWORDPROMPT", 72 | 80: "ACCEPTARENADEATH", 73 | 81: "UPDATEACK", 74 | 82: "QUESTOBJID", 75 | 83: "PIC", 76 | 84: "REALMHEROESRESPONSE", 77 | 85: "BUY", 78 | 86: "TRADESTART", 79 | 87: "EVOLVEPET", 80 | 88: "TRADEREQUESTED", 81 | 89: "AOEACK", 82 | 90: "PLAYERHIT", 83 | 91: "CANCELTRADE", 84 | 92: "MAPINFO", 85 | 93: "CLAIMDAILYLOGINRESPONSE", 86 | 94: "KEYINFOREQUEST", 87 | 95: "INVRESULT", 88 | 96: "QUESTREDEEMRESPONSE", 89 | 97: "CHOOSENAME", 90 | 98: "QUESTFETCHASK", 91 | 99: "ACCOUNTLIST", 92 | 100: "SHOOTACK", 93 | 101: "CREATESUCCESS", 94 | 102: "CHECKCREDITS", 95 | 103: "GROUNDDAMAGE", 96 | 104: "GUILDINVITE", 97 | 105: "ESCAPE", 98 | 106: "FILE", 99 | 107: "RESKINUNLOCK", 100 | 108: "NEWCHARACTERINFORMATION", 101 | 109: "UNLOCKINFORMATION", 102 | 112: "QUEUEINFORMATION", 103 | 114: "EXALTATIONUPDATE", 104 | 117: "VAULTINFO", 105 | 118: "FORGEREQUEST", 106 | 119: "FORGERESPONSE", 107 | 120: "BLUEPRINTINFO", 108 | 121: "ENEMYSHOOTACK", 109 | 122: "SHOWALLYSHOOT", 110 | 139: "UNKNOWN139", 111 | 149: "CLAIMBATTLEPASS", 112 | 150: "CLAIMBATTLEPASSRESPONSE", 113 | 159: "SENDEMOTE", 114 | 165: "UNKNOWN165", 115 | 169: "REALMSCORE", 116 | 182: "CRUCIBLEREQUEST", 117 | 183: "CRUCIBLERESPONSE", 118 | 200: "CREATEPARTY"} 119 | typeToId = {idToType[i]: i for i in idToType} 120 | -------------------------------------------------------------------------------- /Networking/SocketManager.py: -------------------------------------------------------------------------------- 1 | try: 2 | import socks 3 | except ImportError: 4 | print("Couldn't import PySocks, note that proxies won't work") 5 | import socket 6 | import threading 7 | import struct 8 | import time 9 | 10 | import Crypto.RC4 as RC4 11 | import Constants.PacketIds as PacketId 12 | import Networking.Writer as Writer 13 | import Networking.Reader as Reader 14 | import Networking.PacketHelper as PacketHelper 15 | 16 | HEADERSIZE = 5 17 | PORT = 2050 18 | 19 | class SocketManager: 20 | def __init__(self): 21 | self.hooks = {} 22 | self.ip = None 23 | self.sock = None 24 | self.active = True 25 | self.connected = False 26 | self.queue = [] 27 | self.writer = Writer.Writer() 28 | self.reader = Reader.Reader() 29 | self.incomming_decoder = RC4.RC4(RC4.INCOMING_KEY) 30 | self.outgoing_encoder = RC4.RC4(RC4.OUTGOING_KEY) 31 | self.clientHook = None 32 | self.hooks_thread = None 33 | 34 | def hook(self, packet_type, func): 35 | if not self.active: 36 | print("Socket manager is not active") 37 | return 38 | if packet_type in self.hooks.keys(): 39 | print("Packet type", packet_type, "is already hooked to function", self.hooks[packet_type]) 40 | return 41 | if not PacketHelper.isValidPacket(packet_type) and packet_type != "ANY": 42 | print("Invalid packet_type:", packet_type) 43 | return 44 | self.hooks[packet_type] = func 45 | 46 | def connect(self, ip, proxy): 47 | if not self.active: 48 | print("Socket manager is not active") 49 | return 50 | if self.connected: 51 | print("Socket already connected to", self.ip, "disconnect it first") 52 | return 53 | else: 54 | self.ip = ip 55 | print("Connecting to", self.ip) 56 | self.incomming_decoder.reset() 57 | self.outgoing_encoder.reset() 58 | if proxy: 59 | self.sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM) 60 | proxyVersion = socks.SOCKS5 if proxy["type"] == 5 else socks.SOCKS4 61 | self.sock.set_proxy(proxyVersion, proxy["host"], proxy["port"], True, \ 62 | proxy["username"], proxy["password"]) 63 | else: 64 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 65 | self.sock.connect((self.ip, PORT)) 66 | self.connected = True 67 | self.startListeners() 68 | 69 | def startListeners(self): 70 | if not self.active: 71 | print("Socket manager is not active") 72 | return 73 | if not self.connected: 74 | print("Socket is not connected") 75 | return 76 | self.listen_thread = threading.Thread(target=self._listen) 77 | self.listen_thread.daemon = True 78 | self.listen_thread.start() 79 | if self.hooks_thread is None: 80 | self.hooks_thread = threading.Thread(target=self._callHooks) 81 | self.hooks_thread.daemon = True 82 | self.hooks_thread.start() 83 | 84 | def disconnect(self, join=True): 85 | if not self.active: 86 | print("Socket manager is not active") 87 | return 88 | if not self.connected: 89 | print("Socket already disconnected") 90 | return 91 | self.connected = False 92 | self.sock.shutdown(socket.SHUT_RDWR) 93 | self.sock.close() 94 | self.queue = [] 95 | if join: 96 | self.listen_thread.join() 97 | 98 | def _listen(self): 99 | if not self.active: 100 | print("Socket manager is not active") 101 | return 102 | while self.connected: 103 | recv = None 104 | try: 105 | recv = self.sock.recv(HEADERSIZE) 106 | while len(recv) < HEADERSIZE: 107 | recv += self.sock.recv(HEADERSIZE-len(recv)) 108 | except ConnectionResetError: 109 | print("Client forcefully disconnected from", self.ip) 110 | if self.connected: 111 | self.disconnect(False) 112 | return 113 | except ConnectionAbortedError: 114 | print("Stopped connection to", self.ip) 115 | if self.connected: 116 | self.disconnect(False) 117 | return 118 | except OSError:#Socket is closed 119 | return 120 | 121 | packet_id = recv[4] 122 | size = struct.unpack("!i", recv[:4])[0] 123 | msg = recv 124 | while len(msg) < size: 125 | recv = self.sock.recv(size-len(msg)) 126 | msg += self.incomming_decoder.process(recv) 127 | try: 128 | packet_type = PacketId.idToType[packet_id] 129 | except KeyError: 130 | ## print("Unknown packet id:", packet_id) 131 | ## print(msg) 132 | continue 133 | if not "UNKNOWN" in packet_type: 134 | packet = PacketHelper.createPacket(packet_type) 135 | self.reader.reset(msg) 136 | packet.read(self.reader) 137 | self.queue.append(packet) 138 | 139 | def _callHooks(self): 140 | if not self.active: 141 | print("Socket manager is not active") 142 | return 143 | while self.active: 144 | if len(self.queue) > 0: 145 | packet = self.queue.pop(0) 146 | if not self.clientHook is None: 147 | self.clientHook(packet) 148 | else: 149 | time.sleep(0.5) 150 | 151 | def sendPacket(self, packet): 152 | if not self.active: 153 | print("Socket manager is not active") 154 | return 155 | if self.connected: 156 | self.writer.reset() 157 | packet.write(self.writer) 158 | self.writer.writeHeader(PacketId.typeToId[packet.type]) 159 | self.writer.buffer = self.writer.buffer[:5] + self.outgoing_encoder.process(self.writer.buffer[5:]) 160 | try: 161 | self.sock.sendall(bytes(self.writer.buffer)) 162 | except OSError:#Socket is closed 163 | return 164 | else: 165 | print("Socket is not connected") 166 | -------------------------------------------------------------------------------- /Models/PlayerData.py: -------------------------------------------------------------------------------- 1 | from Constants.StatTypes import StatTypes 2 | 3 | class PlayerData: 4 | def __init__(self): 5 | self.name = "" 6 | self.level = 0 7 | self.xp = 0 8 | self.fame = 0 9 | self.stars = 0 10 | self.accountId = "" 11 | self.accountFame = 0 12 | self.gold = 0 13 | self.characterClass = 0 14 | self.nameChosen = False 15 | self.guildName = "" 16 | self.guildRank = 0 17 | self.maxHp = 0 18 | self.maxHpBoost = 0 19 | self.maxMp = 0 20 | self.maxMpBoost = 0 21 | self.hp = 0 22 | self.mp = 0 23 | self.atk = 0 24 | self.atkBoost = 0 25 | self.defense = 0 26 | self.defenseBoost = 0 27 | self.spd = 0 28 | self.spdBoost = 0 29 | self.dex = 0 30 | self.dexBoost = 0 31 | self.wis = 0 32 | self.wisBoost = 0 33 | self.vit = 0 34 | self.vitBoost = 0 35 | self.condition = [] 36 | self.hasBackpack = False 37 | self.inv = [-1 for i in range(20)] 38 | self.size = 0 39 | self.nextLevelXp = 0 40 | self.clothingDye = 0 41 | self.accessoryDye = 0 42 | self.nextClassQuestFame = 0 43 | self.legendaryRank = 0 44 | self.xpBoosted = False 45 | self.xpBoostTime = 0 46 | self.texture = 0 47 | self.fortuneTokens = 0 48 | self.projSpeedMult = 1 49 | self.projLifeMult = 1 50 | self.opendAtTimestamp = 0 51 | self.exaltedHp = 0 52 | self.exaltedMp = 0 53 | self.exaltedAtk = 0 54 | self.exaltedDefense = 0 55 | self.exaltedSpd = 0 56 | self.exaltedDex = 0 57 | self.exaltedWis = 0 58 | self.exaltedVit = 0 59 | self.exaltationBonusDmg = 0 60 | self.exaltationICReduction = 0 61 | self.graveAccountId = -1 62 | self.potionOneType = -1 63 | self.potionTwoType = -1 64 | self.potionThreeType = -1 65 | self.potionBelt = 0 66 | self.forgeFire = 0 67 | self.characterClass = 0 68 | self.pos = None 69 | self.objectId = 0 70 | 71 | def parse(self, obj): 72 | self.characterClass = obj.objectType 73 | self.pos = obj.status.pos 74 | self.objectId = obj.status.objectId 75 | self.parseStats(obj.status.stats) 76 | 77 | def parseStats(self, stats): 78 | for stat in stats: 79 | if stat.statType == StatTypes.NAMESTAT: 80 | self.name = stat.strStatValue 81 | elif stat.statType == StatTypes.LEVELSTAT: 82 | self.level = stat.statValue 83 | elif stat.statType == StatTypes.EXPSTAT: 84 | self.xp = stat.statValue 85 | elif stat.statType == StatTypes.CURRFAMESTAT: 86 | self.fame = stat.statValue 87 | elif stat.statType == StatTypes.NUMSTARSSTAT: 88 | self.stars = stat.statValue 89 | elif stat.statType == StatTypes.ACCOUNTIDSTAT: 90 | self.accountId = stat.strStatValue 91 | elif stat.statType == StatTypes.FAMESTAT: 92 | self.accountFame = stat.statValue 93 | elif stat.statType == StatTypes.CREDITSSTAT: 94 | self.gold = stat.statValue 95 | elif stat.statType == StatTypes.MAXHPSTAT: 96 | self.maxHp = stat.statValue 97 | elif stat.statType == StatTypes.MAXMPSTAT: 98 | self.maxMp = stat.statValue 99 | elif stat.statType == StatTypes.HPSTAT: 100 | self.hp = stat.statValue 101 | elif stat.statType == StatTypes.MPSTAT: 102 | self.mp = stat.statValue 103 | elif stat.statType == StatTypes.ATTACKSTAT: 104 | self.atk = stat.statValue 105 | elif stat.statType == StatTypes.ATTACKBOOSTSTAT: 106 | self.atkBoost = stat.statValue 107 | elif stat.statType == StatTypes.DEFENSESTAT: 108 | self.defense = stat.statValue 109 | elif stat.statType == StatTypes.DEFENSEBOOSTSTAT: 110 | self.defenseBoost = stat.statValue 111 | elif stat.statType == StatTypes.SPEEDSTAT: 112 | self.spd = stat.statValue 113 | elif stat.statType == StatTypes.SPEEDBOOSTSTAT: 114 | self.spdBoost = stat.statValue 115 | elif stat.statType == StatTypes.DEXTERITYSTAT: 116 | self.dex = stat.statValue 117 | elif stat.statType == StatTypes.DEXTERITYBOOSTSTAT: 118 | self.dexBoost = stat.statValue 119 | elif stat.statType == StatTypes.VITALITYSTAT: 120 | self.vit = stat.statValue 121 | elif stat.statType == StatTypes.VITALITYBOOSTSTAT: 122 | self.vitBoost = stat.statValue 123 | elif stat.statType == StatTypes.CONDITIONSTAT: 124 | self.condition = stat.statValue 125 | elif stat.statType == StatTypes.WISDOMSTAT: 126 | self.wis = stat.statValue 127 | elif stat.statType == StatTypes.WISDOMBOOSTSTAT: 128 | self.wisBoost = stat.statValue 129 | elif stat.statType == StatTypes.HEALTHPOTIONSTACKSTAT: 130 | self.hpPots = stat.statValue 131 | elif stat.statType == StatTypes.MAGICPOTIONSTACKSTAT: 132 | self.mpPots = stat.statValue 133 | elif stat.statType == StatTypes.HASBACKPACKSTAT: 134 | self.hasBackpack = stat.statValue == 1 135 | elif stat.statType == StatTypes.NAMECHOSENSTAT: 136 | self.nameChosen = stat.statValue != 0 137 | elif stat.statType == StatTypes.GUILDNAMESTAT: 138 | self.guildName = stat.strStatValue 139 | elif stat.statType == StatTypes.GUILDRANKSTAT: 140 | self.guildRank = stat.statValue 141 | elif stat.statType == StatTypes.SIZESTAT: 142 | self.size = stat.statValue 143 | elif stat.statType == StatTypes.NEXTLEVELEXPSTAT: 144 | self.nextLevelXp = stat.statValue 145 | elif stat.statType == StatTypes.TEX1STAT: 146 | self.clothingDye = stat.statValue 147 | elif stat.statType == StatTypes.TEX2STAT: 148 | self.accessoryDye = stat.statValue 149 | elif stat.statType == StatTypes.MAXHPBOOSTSTAT: 150 | self.maxHpBoost = stat.statValue 151 | elif stat.statType == StatTypes.MAXMPBOOSTSTAT: 152 | self.maxMpBoost = stat.statValue 153 | elif stat.statType == StatTypes.NEXTCLASSQUESTFAMESTAT: 154 | self.nextClassQuestFame = stat.statValue 155 | elif stat.statType == StatTypes.LEGENDARYRANKSTAT: 156 | self.legendaryRank = stat.statValue 157 | elif stat.statType == StatTypes.XPBOOSTEDSTAT: 158 | self.xpBoosted = stat.statValue == 1 159 | elif stat.statType == StatTypes.XPTIMERSTAT: 160 | self.xpBoostTime = stat.statValue 161 | elif stat.statType == StatTypes.TEXTURESTAT: 162 | self.texture = stat.statValue 163 | elif stat.statType == StatTypes.FORTUNETOKENSTAT: 164 | self.fortuneTokens = stat.statValue 165 | elif stat.statType == StatTypes.PROJECTILESPEEDMULT: 166 | self.projSpeedMult = stat.statValue / 1000 167 | elif stat.statType == StatTypes.PROJECTILELIFEMULT: 168 | self.projLifeMult = stat.statValue / 1000 169 | elif StatTypes.INVENTORY0STAT <= stat.statType <= StatTypes.INVENTORY11STAT: 170 | self.inv[stat.statType-StatTypes.INVENTORY0STAT] = stat.statValue 171 | elif StatTypes.BACKPACK0STAT <= stat.statType <= StatTypes.BACKPACK7STAT: 172 | self.inv[stat.statType-StatTypes.BACKPACK0STAT+12] = stat.statValue 173 | elif stat.statType == StatTypes.OPENEDATTIMESTAMP: 174 | self.opendAtTimestamp = stat.statValue 175 | elif stat.statType == StatTypes.EXALTEDHP: 176 | self.exaltedHp = stat.statValue 177 | elif stat.statType == StatTypes.EXALTEDMP: 178 | self.exaltedMp = stat.statValue 179 | elif stat.statType == StatTypes.EXALTEDATK: 180 | self.exaltedAtk = stat.statValue 181 | elif stat.statType == StatTypes.EXALTEDDEFENSE: 182 | self.exaltedDefense = stat.statValue 183 | elif stat.statType == StatTypes.EXALTEDSPD: 184 | self.exaltedSpd = stat.statValue 185 | elif stat.statType == StatTypes.EXALTEDDEX: 186 | self.exaltedDex = stat.statValue 187 | elif stat.statType == StatTypes.EXALTEDWIS: 188 | self.exaltedWis = stat.statValue 189 | elif stat.statType == StatTypes.EXALTEDVIT: 190 | self.exaltedVit = stat.statValue 191 | elif stat.statType == StatTypes.EXALTATIONBONUSDMG: 192 | self.exaltationBonusDmg = stat.statValue/1000 193 | elif stat.statType == StatTypes.EXALTATIONICREDUCTION: 194 | self.exaltationICReduction = stat.statValue 195 | elif stat.statType == StatTypes.GRAVEACCOUNTID: 196 | self.graveAccountId = stat.statValue 197 | elif stat.statType == StatTypes.POTIONONETYPE: 198 | self.potionOneType = stat.statValue 199 | elif stat.statType == StatTypes.POTIONTWOTYPE: 200 | self.potionTwoType = stat.statValue 201 | elif stat.statType == StatTypes.POTIONTHREETYPE: 202 | self.potionThreeType = stat.statValue 203 | elif stat.statType == StatTypes.POTIONBELT: 204 | self.potionBelt = stat.statValue 205 | elif stat.statType == StatTypes.FORGEFIRE: 206 | self.forgeFire = stat.statValue 207 | 208 | def __str__(self): 209 | out = "" 210 | for key in self.__dict__: 211 | out += "{}: {}\n".format(key, self.__dict__[key]) 212 | return out[:-1] 213 | -------------------------------------------------------------------------------- /Client/Client.py: -------------------------------------------------------------------------------- 1 | import time 2 | import math 3 | import requests 4 | import re 5 | 6 | from Helpers.Random import Random 7 | from Networking.SocketManager import SocketManager 8 | from Models.PlayerData import PlayerData 9 | from Models.CharData import CharData 10 | import Helpers.Servers as ServersHelper 11 | import Data.MoveRecord as MoveRecord 12 | import Models.ConditionEffect as ConditionEffect 13 | import Networking.PacketHelper as PacketHelper 14 | import Constants.GameIds as GameId 15 | import Constants.Servers as Servers 16 | import Constants.ApiPoints as ApiPoints 17 | import Constants.ClassIds as Classes 18 | from PluginManager import callHooks, hook, client 19 | 20 | from threading import Timer 21 | 22 | class RepeatTimer(Timer): 23 | def run(self): 24 | while not self.finished.wait(self.interval): 25 | self.function(*self.args, **self.kwargs) 26 | 27 | MINSPEED = 0.004 28 | MAXSPEED = 0.0096 29 | 30 | MINFREQ = 0.0015 31 | MAXFREQ = 0.008 32 | 33 | @client() 34 | class Client: 35 | def __init__(self): 36 | self.guid = "" 37 | self.password = "" 38 | self.secret = "" 39 | self.alias = "" 40 | self.server = "" 41 | self.proxy = {} 42 | self.proxies = {} 43 | self.internalServer = {"host": "", "name": ""} 44 | self.nexusServer = {"host": "", "name": ""} 45 | self.pos = None 46 | self.sockMan = None 47 | self.clientManager = None 48 | self.nextPos = [] 49 | self.objectId = -1 50 | self.connectedTime = -1 51 | self.random = Random() 52 | self.frameTimeUpdater = None 53 | self.active = True 54 | self.isReady = False 55 | self.key = [] 56 | self.keyTime = -1 57 | self.connectionGuid = "" 58 | self.gameId = GameId.nexus 59 | self.buildVersion = open("gameVersion.txt").read() 60 | self.clientToken = "" 61 | self.accessToken = "" 62 | self.playerData = PlayerData() 63 | self.charData = CharData() 64 | self.needsNewChar = False 65 | self.bulletId = 0 66 | self.lastAttackTime = 0 67 | self.records = [] 68 | self.connectCooldown = 0 69 | self.lastPacketTime = 0 70 | 71 | def getToken(self, accInfo): 72 | self.guid = accInfo["guid"] 73 | self.password = accInfo["password"] 74 | self.secret = accInfo["secret"] 75 | self.alias = accInfo["alias"] 76 | self.proxy = accInfo.get("proxy", {}) 77 | 78 | print("Getting token...") 79 | #Get access token 80 | pwd_key = "password" if self.password != "" else "secret" 81 | pwd_val = self.password if self.password != "" else self.secret 82 | r = requests.post(ApiPoints.VERIFY, data={"guid": self.guid, 83 | pwd_key: pwd_val, 84 | "clientToken": self.clientToken, 85 | "game_net": "Unity", "play_platform": "Unity", "game_net_user_id": ""}, headers=ApiPoints.launcherHeaders, proxies=self.proxies) 86 | pattern = r"AccessToken>(.+)" 87 | try: 88 | self.accessToken = re.findall(pattern, r.text)[0] 89 | except IndexError:#Token not working 90 | print("GETTING TOKEN ERROR:", r.text) 91 | self.active = False 92 | return 93 | #Verify token 94 | r = requests.post(ApiPoints.VERIFYTOKEN, data={"clientToken": self.clientToken, 95 | "accessToken": self.accessToken, 96 | "game_net": "Unity", "play_platform": "Unity", "game_net_user_id": ""}, headers=ApiPoints.launcherHeaders, proxies=self.proxies) 97 | if not "Success" in r.text: 98 | print("VERIFYING TOKEN ERROR:", r.text) 99 | self.active = False 100 | return 101 | 102 | def checkInfo(self, accInfo, updateServers=False): 103 | if not self.active: 104 | return 105 | 106 | self.guid = accInfo["guid"] 107 | self.password = accInfo["password"] 108 | self.secret = accInfo["secret"] 109 | self.alias = accInfo["alias"] 110 | self.proxy = accInfo.get("proxy", {}) 111 | 112 | #Get char data 113 | r = requests.post(ApiPoints.CHAR, data={"do_login": "true", 114 | "accessToken": self.accessToken, 115 | "game_net": "Unity", "play_platform": "Unity", "game_net_user_id": ""}, headers=ApiPoints.launcherHeaders, proxies=self.proxies) 116 | while "Account in use" in r.text: 117 | print(self.guid, "has account in use") 118 | try: 119 | time.sleep(int(re.findall(r"(\d+)", r.text)[0])) 120 | except IndexError: 121 | time.sleep(600) 122 | r = requests.post(ApiPoints.CHAR, data={"do_login": "true", 123 | "accessToken": self.accessToken, 124 | "game_net": "Unity", "play_platform": "Unity", "game_net_user_id": ""}, headers=ApiPoints.launcherHeaders, proxies=self.proxies) 125 | if "Account credentials not valid" in r.text: 126 | print(self.guid, "got invalid credentials") 127 | self.active = False 128 | return 129 | try: 130 | charInfo = re.findall(r'', r.text)[0] 131 | chars = re.findall(r'', r.text) 132 | except IndexError: 133 | print(r.text) 134 | self.active = False 135 | return 136 | 137 | self.charData.nextCharId = int(charInfo[0]) 138 | self.charData.maxNumChars = int(charInfo[1]) 139 | if len(chars) > 0: 140 | self.charData.charIds = [int(i) for i in chars] 141 | self.charData.currentCharId = int(chars[0]) 142 | else: 143 | self.charData.charIds = [self.charData.nextCharId] 144 | self.charData.currentCharId = self.charData.nextCharId 145 | self.charData.nextCharId += 1 146 | self.needsNewChar = True 147 | 148 | if not "TDone" in r.text: 149 | self.gameId = GameId.tutorial 150 | 151 | self.isReady = True 152 | 153 | try: 154 | if updateServers: 155 | ServersHelper.update(self.accessToken, self.proxies) 156 | print("Updated servers") 157 | except AttributeError as e: 158 | print("Failed to update servers") 159 | print(e) 160 | self.active = False 161 | self.isReady = False 162 | 163 | def setup(self, accInfo): 164 | self.server = accInfo["server"] 165 | self.connectedTime = int(time.time()*1000) 166 | 167 | self.internalServer = {"host": Servers.nameToIp[self.server], 168 | "name": self.server} 169 | self.nexusServer = {"host": Servers.nameToIp[self.server], 170 | "name": self.server} 171 | 172 | self.sockMan = SocketManager() 173 | self.sockMan.clientHook = self.onPacket 174 | 175 | def isConnected(self): 176 | return self.sockMan.connected 177 | 178 | def connect(self): 179 | if not self.active: 180 | return 181 | if self.connectCooldown > self.getTime(): 182 | return 183 | if self.sockMan.connected: 184 | self.sockMan.disconnect() 185 | if not self.frameTimeUpdater is None: 186 | self.frameTimeUpdater.cancel() 187 | self.sockMan.connect(self.internalServer["host"], self.proxy) 188 | self.sendHelloPacket() 189 | 190 | def changeServer(self, server): 191 | if not server in Servers.nameToIp.keys(): 192 | print(server, "is not a valid server") 193 | return 194 | self.server = server 195 | self.internalServer = {"host": Servers.nameToIp[self.server], 196 | "name": self.server} 197 | self.nexusServer = {"host": Servers.nameToIp[self.server], 198 | "name": self.server} 199 | self.connect() 200 | 201 | def getSpeed(self, time): 202 | if self.hasEffect(ConditionEffect.SLOWED): 203 | return MINSPEED 204 | speed = MINSPEED + (self.playerData.spd+self.playerData.spdBoost)/75 * (MAXSPEED-MINSPEED) 205 | if self.hasEffect(ConditionEffect.SPEEDY, ConditionEffect.NINJASPEEDY): 206 | speed *= 1.5 207 | return speed * time 208 | 209 | def nexus(self): 210 | packet = PacketHelper.createPacket("ESCAPE") 211 | self.send(packet) 212 | self.gameId = GameId.nexus 213 | self.key = [] 214 | self.keyTime = -1 215 | 216 | def sendHelloPacket(self): 217 | hello_packet = PacketHelper.createPacket("HELLO") 218 | hello_packet.buildVersion = self.buildVersion 219 | hello_packet.gameId = self.gameId 220 | hello_packet.accessToken = self.accessToken 221 | hello_packet.keyTime = self.keyTime 222 | hello_packet.key = self.key 223 | hello_packet.userPlatform = "rotmg" 224 | hello_packet.playPlatform = "rotmg" 225 | hello_packet.userToken = self.clientToken 226 | self.send(hello_packet) 227 | 228 | def send(self, packet): 229 | if self.isConnected(): 230 | self.sockMan.sendPacket(packet) 231 | 232 | def getTime(self): 233 | return int(time.time()*1000) - self.connectedTime 234 | 235 | def disconnect(self): 236 | if self.sockMan.connected: 237 | self.sockMan.disconnect() 238 | if not self.frameTimeUpdater is None: 239 | self.frameTimeUpdater.cancel() 240 | #Wait half a sec to have less chance of getting a failure packet 241 | self.connectCooldown = self.getTime() + 500 242 | 243 | def stop(self): 244 | self.active = False 245 | if self.sockMan.connected: 246 | self.sockMan.disconnect() 247 | self.sockMan.active = False 248 | if not self.frameTimeUpdater is None: 249 | self.frameTimeUpdater.cancel() 250 | 251 | def updateFrameTime(self): 252 | if self.pos is None: 253 | return 254 | time = self.getTime() 255 | if len(self.nextPos) > 0: 256 | diff = min(100, time-self.lastFrameTime) 257 | self.moveTo(self.nextPos[0], diff) 258 | self.records.append(MoveRecord.MoveRecord(time, self.pos.x, self.pos.y)) 259 | self.lastFrameTime = time 260 | 261 | def moveTo(self, target, time): 262 | speed = self.getSpeed(time) 263 | if self.pos.dist(target) > speed: 264 | angle = math.atan2(target.y-self.pos.y, target.x-self.pos.x) 265 | self.walkTo(self.pos + (math.cos(angle) * speed, math.sin(angle) * speed)) 266 | else: 267 | self.walkTo(target) 268 | self.nextPos.pop(0) 269 | 270 | def walkTo(self, target): 271 | if self.hasEffect(ConditionEffect.PARALYZED, ConditionEffect.PAUSED, ConditionEffect.PETRIFIED): 272 | return 273 | self.pos = target.clone() 274 | 275 | def attackFreq(self): 276 | if self.hasEffect(ConditionEffect.DAZED): 277 | return MINFREQ 278 | freq = MINFREQ + (self.playerData.dex+self.playerData.dexBoost)/75 * (MAXFREQ - MINFREQ) 279 | if self.hasEffect(ConditionEffect.BERSERK): 280 | freq *= 1.5 281 | return freq 282 | 283 | def getBulletId(self): 284 | bulletId = self.bulletId 285 | self.bulletId = (self.bulletId + 1) % 128 286 | return bulletId 287 | 288 | def shoot(self, angle): 289 | if self.clientManager.weapons is None: 290 | print("Weapons not loaded") 291 | return False 292 | if self.hasEffect(ConditionEffect.STUNNED, ConditionEffect.PAUSED, ConditionEffect.PETRIFIED): 293 | return False 294 | if not self.playerData.inv[0] in self.clientManager.weapons.keys(): 295 | return False 296 | time = self.getTime() 297 | attackPeriod = 1 / self.attackFreq() * (1/1)#TODO 298 | if time < self.lastAttackTime + attackPeriod: 299 | return False 300 | self.lastAttackTime = time 301 | 302 | shootPacket = PacketHelper.createPacket("PLAYERSHOOT") 303 | shootPacket.time = time 304 | shootPacket.containerType = self.playerData.inv[0] 305 | shootPacket.speedMult = self.playerData.projSpeedMult 306 | shootPacket.lifeMult = self.playerData.projLifeMult 307 | 308 | weapon = self.clientManager.weapons[shootPacket.containerType] 309 | arcRads = weapon.arcGap * math.pi / 180 310 | totalArc = arcRads * (weapon.numProjectiles - 1) 311 | if totalArc < 0: 312 | totalArc = 0 313 | angle -= totalArc/2 314 | 315 | for i in range(weapon.numProjectiles): 316 | shootPacket.bulletId = self.getBulletId() 317 | shootPacket.pos = self.pos.clone() 318 | shootPacket.pos.x += math.cos(angle) * 0.3 319 | shootPacket.pos.y += math.sin(angle) * 0.3 320 | shootPacket.angle = angle 321 | if arcRads > 0: 322 | angle += arcRads 323 | self.send(shootPacket) 324 | 325 | return True 326 | 327 | def hasEffect(self, *effects): 328 | return ConditionEffect.hasEffect(self.playerData.condition, *effects) 329 | 330 | @hook("createSuccess") 331 | def onCreateSuccess(self, packet): 332 | self.objectId = packet.objectId 333 | self.lastAttackTime = 0 334 | self.lastFrameTime = self.getTime() 335 | self.frameTimeUpdater = RepeatTimer(1/10, self.updateFrameTime) 336 | self.frameTimeUpdater.daemon = True 337 | self.frameTimeUpdater.start() 338 | self.records = [] 339 | 340 | show_packet = PacketHelper.createPacket("SHOWALLYSHOOT") 341 | show_packet.toggle = 1 342 | self.send(show_packet) 343 | 344 | @hook("goto") 345 | def onGoto(self, packet): 346 | gotoAck_packet = PacketHelper.createPacket("GOTOACK") 347 | gotoAck_packet.time = self.lastFrameTime 348 | self.send(gotoAck_packet) 349 | if packet.objectId == self.objectId: 350 | self.pos = packet.pos.clone() 351 | 352 | @hook("mapInfo") 353 | def onMapInfo(self, packet): 354 | print("Connected to", self.nexusServer["name"], packet.name) 355 | self.nextPos = [] 356 | if self.needsNewChar: 357 | print("Creating new char") 358 | create_packet = PacketHelper.createPacket("CREATE") 359 | create_packet.classType = Classes.WIZARD 360 | create_packet.skinType = 0 361 | create_packet.isChallenger = 0 362 | self.send(create_packet) 363 | self.needsNewChar = False 364 | else: 365 | load_packet = PacketHelper.createPacket("LOAD") 366 | load_packet.charId = self.charData.currentCharId 367 | self.send(load_packet) 368 | self.random.setSeed(packet.seed) 369 | 370 | @hook("queueInformation") 371 | def onQueueInformation(self, packet): 372 | print("Client", self.alias, f"in queue at position: {packet.curPos}/{packet.maxPos}") 373 | self.connectCooldown = self.getTime() + 10*1000 374 | 375 | @hook("failure") 376 | def onFailure(self, packet): 377 | if packet.errorId == 15: 378 | self.disconnect() 379 | return 380 | print("Error:", packet.errorId) 381 | print(packet.errorDescription) 382 | self.keyTime = -1 383 | self.key = [] 384 | self.gameId = GameId.nexus 385 | if packet.errorDescription == "s.update_client": 386 | self.stop() 387 | elif packet.errorDescription == "Account credentials not valid": 388 | self.stop() 389 | elif packet.errorDescription == "Bad message received": 390 | self.disconnect() 391 | 392 | @hook("ping") 393 | def onPing(self, packet): 394 | pong_packet = PacketHelper.createPacket("PONG") 395 | pong_packet.serial = packet.serial 396 | pong_packet.time = self.getTime() 397 | self.send(pong_packet) 398 | 399 | @hook("newTick") 400 | def onNewTick(self, packet): 401 | move_packet = PacketHelper.createPacket("MOVE") 402 | move_packet.tickId = packet.tickId 403 | move_packet.time = packet.serverRealTimeMS 404 | move_packet.records = self.records 405 | if len(move_packet.records) == 0:#Causes dc otherwise 406 | move_packet.records = [MoveRecord.MoveRecord(self.lastFrameTime, self.pos.x, self.pos.y)] 407 | self.records = [] 408 | self.send(move_packet) 409 | for status in packet.statuses: 410 | if status.objectId == self.objectId: 411 | self.playerData.parseStats(status.stats) 412 | 413 | @hook("update") 414 | def onUpdate(self, packet): 415 | if self.pos is None: 416 | self.pos = packet.pos 417 | updateAck_packet = PacketHelper.createPacket("UPDATEACK") 418 | self.send(updateAck_packet) 419 | for obj in packet.newObjs: 420 | if obj.status.objectId == self.objectId: 421 | self.pos = obj.status.pos 422 | self.playerData.parse(obj) 423 | 424 | @hook("serverPlayerShoot") 425 | def onServerPlayerShoot(self, packet): 426 | if packet.ownerId == self.objectId: 427 | shootAck = PacketHelper.createPacket("SHOOTACK") 428 | shootAck.time = self.lastFrameTime 429 | self.send(shootAck) 430 | 431 | @hook("enemyShoot") 432 | def onEnemyShoot(self, packet): 433 | shootAck = PacketHelper.createPacket("SHOOTACK") 434 | shootAck.time = self.lastFrameTime 435 | self.send(shootAck) 436 | 437 | @hook("reconnect") 438 | def onReconnect(self, packet): 439 | if packet.host != "": 440 | self.internalServer["host"] = packet.host 441 | if packet.name != "": 442 | self.internalServer["name"] = packet.name 443 | self.gameId = packet.gameId 444 | self.key = packet.key 445 | self.keyTime = packet.keyTime 446 | #self.disconnect() 447 | self.connect() 448 | 449 | def onPacket(self, packet): 450 | self.lastPacketTime = self.getTime() 451 | callHooks(self, packet) 452 | --------------------------------------------------------------------------------