From fa9c942471d0b276981668bd17c92dbd937e7243 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Sat, 12 May 2012 01:52:16 -0300 Subject: [PATCH] basic protocol 953 support, logging in real tibia! --- BUGS | 4 +- modules/game_containers/containers.lua | 2 +- src/framework/core/filestream.cpp | 26 ++++++++++ src/framework/core/filestream.h | 1 + src/framework/net/protocol.cpp | 14 ++++-- src/otclient/CMakeLists.txt | 5 ++ src/otclient/core/container.cpp | 1 - src/otclient/core/container.h | 6 +-- src/otclient/core/game.cpp | 4 +- src/otclient/core/game.h | 2 +- src/otclient/core/thingstype.cpp | 13 ++++- src/otclient/core/thingtype.h | 2 +- src/otclient/luafunctions.cpp | 4 +- src/otclient/net/protocolcodes.h | 34 ++++++++++--- src/otclient/net/protocolgame.cpp | 25 ++-------- src/otclient/net/protocolgame.h | 9 ++-- src/otclient/net/protocolgameparse.cpp | 68 ++++++++++++++++++++------ src/otclient/net/protocolgamesend.cpp | 11 +++-- 18 files changed, 163 insertions(+), 68 deletions(-) diff --git a/BUGS b/BUGS index 40249d59..5f6fa26b 100644 --- a/BUGS +++ b/BUGS @@ -2,6 +2,7 @@ modules recursivity makes client crash, it should generate a warning == P1 BUGS (affects game play) +in some situations creatures may disappears while walking sometimes minimap desync Z pos follow and autowalk doesn't cancel when walking via hotkeys when reading invalid spr/dat the client crashs @@ -27,5 +28,4 @@ hotkeys works while windows are locked, it shouldn't skulls is rendering outside map bounds party options does not work when re-logging inside a party sometimes we can still view hits from above/bottom floors from a fight that is not visible -must removeThing in protocol parseTileAdd when stackpos is greater than 11 - +scroll does not follow characerlist selection when changing focus with up/down arrows diff --git a/modules/game_containers/containers.lua b/modules/game_containers/containers.lua index a5d7c14c..5a43e6f1 100644 --- a/modules/game_containers/containers.lua +++ b/modules/game_containers/containers.lua @@ -38,7 +38,7 @@ local function onContainerOpen(container, previousContainer) name = name:sub(1,1):upper() .. name:sub(2) containerWindow:setText(name) - containerItemWidget:setItemId(container:getItemId()) + containerItemWidget:setItem(container:getContainerItem()) containerPanel:destroyChildren() for slot=0,container:getCapacity()-1 do diff --git a/src/framework/core/filestream.cpp b/src/framework/core/filestream.cpp index dcc70a9c..edd4b096 100644 --- a/src/framework/core/filestream.cpp +++ b/src/framework/core/filestream.cpp @@ -223,6 +223,32 @@ uint64 FileStream::getU64() return v; } +std::string FileStream::getString() +{ + std::string str; + int len = getU16(); + if(len > 0 && len < 8192) { + char buffer[8192]; + if(m_fileHandle) { + if(PHYSFS_read(m_fileHandle, buffer, 1, len) == 0) + logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + else + str = std::string(buffer, len); + } else { + if(m_cacheReadPos+len > m_cacheBuffer.size()) { + logTraceError("operation failed on '", m_name, "': reached file eof"); + return 0; + } + + str = std::string((char*)&m_cacheBuffer[m_cacheReadPos], len); + m_cacheReadPos += len; + } + } else { + logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + } + return str; +} + void FileStream::addU8(uint8 v) { if(PHYSFS_write(m_fileHandle, &v, 1, 1) != 1) diff --git a/src/framework/core/filestream.h b/src/framework/core/filestream.h index 2510f1c2..02be7444 100644 --- a/src/framework/core/filestream.h +++ b/src/framework/core/filestream.h @@ -53,6 +53,7 @@ public: uint16 getU16(); uint32 getU32(); uint64 getU64(); + std::string getString(); void addU8(uint8 v); void addU16(uint8 v); void addU32(uint8 v); diff --git a/src/framework/net/protocol.cpp b/src/framework/net/protocol.cpp index d84545a9..723b8023 100644 --- a/src/framework/net/protocol.cpp +++ b/src/framework/net/protocol.cpp @@ -124,9 +124,17 @@ void Protocol::internalRecvData(uint8* buffer, uint16 size) return; } - if(m_xteaEncryptionEnabled && !xteaDecrypt(m_inputMessage)) { - logTraceError("failed to decrypt message"); - return; + if(m_xteaEncryptionEnabled) { + if(!xteaDecrypt(m_inputMessage)) { + logTraceError("failed to decrypt message"); + return; + } + } else { + int size = m_inputMessage.getU16(); + if(size != m_inputMessage.getUnreadSize()) { + logTraceError("invalid message size"); + return; + } } onRecv(m_inputMessage); diff --git a/src/otclient/CMakeLists.txt b/src/otclient/CMakeLists.txt index 548ce5be..981bcc13 100644 --- a/src/otclient/CMakeLists.txt +++ b/src/otclient/CMakeLists.txt @@ -6,9 +6,14 @@ ENDIF(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 6) # otclient options OPTION(BOT_PROTECTION "Enable bot protection" ON) SET(PROTOCOL 860 CACHE "Protocol version" STRING) +SET(CIPSOFT_RSA "Use cipsoft RSA to login into original tibia" OFF) ADD_DEFINITIONS(-DPROTOCOL=${PROTOCOL}) MESSAGE(STATUS "Protocol: " ${PROTOCOL}) +IF(CIPSOFT_RSA) + ADD_DEFINITIONS(-DCIPSOFT_RSA) +ENDIF() + IF(BOT_PROTECTION) ADD_DEFINITIONS(-DBOT_PROTECTION) MESSAGE(STATUS "Bot protection: ON") diff --git a/src/otclient/core/container.cpp b/src/otclient/core/container.cpp index 0df11273..f21080c3 100644 --- a/src/otclient/core/container.cpp +++ b/src/otclient/core/container.cpp @@ -26,7 +26,6 @@ Container::Container() { m_id = -1; - m_itemId = 0; m_capacity = 20; m_name = "Container"; m_hasParent = false; diff --git a/src/otclient/core/container.h b/src/otclient/core/container.h index 6381d579..77bb655b 100644 --- a/src/otclient/core/container.h +++ b/src/otclient/core/container.h @@ -46,14 +46,14 @@ public: void setId(int id) { m_id = id; } void setCapacity(int capacity) { m_capacity = capacity; } void setName(std::string name) { m_name = name; } - void setItemId(uint16 itemId) { m_itemId = itemId; } + void setContainerItem(const ItemPtr& containerItem) { m_containerItem = containerItem; } void setHasParent(bool hasParent) { m_hasParent = hasParent; } std::string getName() { return m_name; } int getId() { return m_id; } int getCapacity() { return m_capacity; } int getItemsCount() { return m_items.size(); } - uint16 getItemId() { return m_itemId; } + ItemPtr getContainerItem() { return m_containerItem; } bool hasParent() { return m_hasParent; } private: @@ -61,7 +61,7 @@ private: int m_id; int m_capacity; - uint16 m_itemId; + ItemPtr m_containerItem; std::string m_name; bool m_hasParent; std::deque m_items; diff --git a/src/otclient/core/game.cpp b/src/otclient/core/game.cpp index dda0bdd5..18b05694 100644 --- a/src/otclient/core/game.cpp +++ b/src/otclient/core/game.cpp @@ -163,14 +163,14 @@ void Game::processCreatureSpeak(const std::string& name, int level, Otc::SpeakTy g_lua.callGlobalField("g_game", "onCreatureSpeak", name, level, type, message, channelId, creaturePos); } -void Game::processOpenContainer(int containerId, int itemId, const std::string& name, int capacity, bool hasParent, const std::vector& items) +void Game::processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector& items) { ContainerPtr previousContainer = getContainer(containerId); ContainerPtr container = ContainerPtr(new Container()); container->setId(containerId); container->setCapacity(capacity); container->setName(name); - container->setItemId(itemId); + container->setContainerItem(containerItem); container->setHasParent(hasParent); m_containers[containerId] = container; container->addItems(items); diff --git a/src/otclient/core/game.h b/src/otclient/core/game.h index efdb3b49..13cebb4c 100644 --- a/src/otclient/core/game.h +++ b/src/otclient/core/game.h @@ -64,7 +64,7 @@ protected: void processCreatureSpeak(const std::string& name, int level, Otc::SpeakType type, const std::string& message, int channelId, const Position& creaturePos); // container related - void processOpenContainer(int containerId, int itemId, const std::string& name, int capacity, bool hasParent, const std::vector& items); + void processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector& items); void processCloseContainer(int containerId); void processContainerAddItem(int containerId, const ItemPtr& item); void processContainerUpdateItem(int containerId, int slot, const ItemPtr& item); diff --git a/src/otclient/core/thingstype.cpp b/src/otclient/core/thingstype.cpp index dfc1f95f..75a2bacb 100644 --- a/src/otclient/core/thingstype.cpp +++ b/src/otclient/core/thingstype.cpp @@ -65,8 +65,9 @@ void ThingsType::parseThingType(const FileStreamPtr& fin, ThingType& thingType) { while(true) { int property = fin->getU8(); - if(property == ThingType::LastPropertyValue) + if(property == ThingType::LastPropertyValue) { break; + } thingType.m_properties[property] = true; @@ -93,6 +94,16 @@ void ThingsType::parseThingType(const FileStreamPtr& fin, ThingType& thingType) #if PROTOCOL<=810 else if(property == ThingType::IsRune) thingType.m_properties[ThingType::IsStackable] = true; +#endif +#if PROTOCOL>=944 + else if(property == ThingType::Market) { + fin->getU16(); // category + fin->getU16(); // trade as + fin->getU16(); // show as + fin->getString(); // name + fin->getU16(); // restrict profession + fin->getU16(); // level + } #endif } diff --git a/src/otclient/core/thingtype.h b/src/otclient/core/thingtype.h index f4561dcc..9947c895 100644 --- a/src/otclient/core/thingtype.h +++ b/src/otclient/core/thingtype.h @@ -78,7 +78,7 @@ struct ThingType IsFullGround, IgnoreLook, Cloth, - Animation, + Market, LastProperty, LastPropertyValue = 255 }; diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp index 265d4742..cfd7edc0 100644 --- a/src/otclient/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -187,13 +187,13 @@ void OTClient::registerLuaFunctions() g_lua.bindClassMemberFunction("setId", &Container::setId); g_lua.bindClassMemberFunction("setCapacity", &Container::setCapacity); g_lua.bindClassMemberFunction("setName", &Container::setName); - g_lua.bindClassMemberFunction("setItemId", &Container::setItemId); + g_lua.bindClassMemberFunction("setContainerItem", &Container::setContainerItem); g_lua.bindClassMemberFunction("setHasParent", &Container::setHasParent); g_lua.bindClassMemberFunction("getName", &Container::getName); g_lua.bindClassMemberFunction("getId", &Container::getId); g_lua.bindClassMemberFunction("getCapacity", &Container::getCapacity); g_lua.bindClassMemberFunction("getItemsCount", &Container::getItemsCount); - g_lua.bindClassMemberFunction("getItemId", &Container::getItemId); + g_lua.bindClassMemberFunction("getContainerItem", &Container::getContainerItem); g_lua.bindClassMemberFunction("hasParent", &Container::hasParent); g_lua.registerClass(); diff --git a/src/otclient/net/protocolcodes.h b/src/otclient/net/protocolcodes.h index 8232ac15..148e3b4c 100644 --- a/src/otclient/net/protocolcodes.h +++ b/src/otclient/net/protocolcodes.h @@ -25,13 +25,11 @@ #include -#if PROTOCOL != 810 && \ - PROTOCOL != 854 && \ - PROTOCOL != 860 && \ - PROTOCOL != 861 && \ - PROTOCOL != 862 && \ - PROTOCOL != 870 && \ - PROTOCOL != 910 +#if !(PROTOCOL == 810) && \ + !(PROTOCOL == 854) && \ + !(PROTOCOL >= 860 && PROTOCOL <= 862) && \ + !(PROTOCOL >= 870 && PROTOCOL <= 871) && \ + !(PROTOCOL >= 910 && PROTOCOL <= 953) #error "the supplied protocol version is not supported" #endif @@ -50,8 +48,8 @@ namespace Proto { "07119674283982419152118103759076030616683978566631413"; #endif + constexpr int PicSignature = 0x4F8C231A; // 953 pic signature constexpr int ClientVersion = PROTOCOL; - constexpr int PicSignature = 0x4E119CBF; enum OsTypes { OsLinux = 1, @@ -89,7 +87,13 @@ namespace Proto { GameServerLoginError = 20, GameServerLoginAdvice = 21, GameServerLoginWait = 22, +#if PROTOCOL>=953 + GameServerPing = 29, + GameServerPingBack = 30, +#else + GameServerPingBack = 29, GameServerPing = 30, +#endif GameServerChallange = 31, GameServerDeath = 40, GameServerFullMap = 100, @@ -130,6 +134,7 @@ namespace Proto { GameServerCreatureUnpass = 146, GameServerEditText = 150, GameServerEditList = 151, + GameServerPlayerDataBasic = 159, GameServerPlayerData = 160, GameServerPlayerSkills = 161, GameServerPlayerState = 162, @@ -162,6 +167,14 @@ namespace Proto { GameServerChannelEvent = 243, GameServerObjectInfo = 244, // 910 GameServerPlayerInventory = 245, // 910 + GameServerMarketEnter = 246, // 944 + GameServerMarketLeave = 247, // 944 + GameServerMarketBrowseItem = 248, // 944 + GameServerMarketAcceptOffer = 249, // 944 + GameServerMarketOwnOffers = 250, // 944 + GameServerMarketCancelOffer = 251, // 944 + GameServerMarketBrowseOwnHistory = 252, // 944 + GameServerMarketMarketDetail = 253, // 944 GameServerExtendedOpcode = 254 // otclient only }; @@ -236,6 +249,11 @@ namespace Proto { ClientRequestQuestLine = 241, ClientRuleViolationReport = 242, // 910 ClientGetObjectInfo = 243, // 910 + ClientMarketLeave = 244, // 944 + ClientMarketBrowse = 245, // 944 + ClientMarketCreateOffer = 246, // 944 + ClientMarketCancelOffer = 247, // 944 + ClientMarketAcceptOffer = 248, // 944 ClientExtendedOpcode = 254 // otclient only }; diff --git a/src/otclient/net/protocolgame.cpp b/src/otclient/net/protocolgame.cpp index 07107af4..5de0cac0 100644 --- a/src/otclient/net/protocolgame.cpp +++ b/src/otclient/net/protocolgame.cpp @@ -42,37 +42,22 @@ void ProtocolGame::login(const std::string& accountName, const std::string& acco void ProtocolGame::onConnect() { - recv(); - // must create local player before parsing anything m_localPlayer = LocalPlayerPtr(new LocalPlayer); #if PROTOCOL>=854 - m_waitingLoginPacket = true; + enableChecksum(); #else sendLoginPacket(0, 0); #endif + + recv(); } void ProtocolGame::onRecv(InputMessage& inputMessage) { - // only for protocol >= 860 - if(m_waitingLoginPacket) { - inputMessage.skipBytes(3); - uint32 timestamp = inputMessage.getU32(); - uint8 unknown = inputMessage.getU8(); - - m_waitingLoginPacket = false; - - enableChecksum(); - - sendLoginPacket(timestamp, unknown); - recv(); - } - else { - parseMessage(inputMessage); - recv(); - } + parseMessage(inputMessage); + recv(); } void ProtocolGame::onError(const boost::system::error_code& error) diff --git a/src/otclient/net/protocolgame.h b/src/otclient/net/protocolgame.h index f95a013e..293baffa 100644 --- a/src/otclient/net/protocolgame.h +++ b/src/otclient/net/protocolgame.h @@ -103,7 +103,7 @@ public: void sendExtendedOpcode(uint8 opcode, const std::string& buffer); private: - void sendLoginPacket(uint timestamp, uint8 unknown); + void sendLoginPacket(uint challangeTimestamp, uint8 challangeRandom); // Parse Messages void parseMessage(InputMessage& msg); @@ -113,7 +113,8 @@ private: void parseLoginError(InputMessage& msg); void parseLoginAdvice(InputMessage& msg); void parseLoginWait(InputMessage& msg); - void parsePing(InputMessage&); + void parsePing(InputMessage& msg); + void parseChallange(InputMessage& msg); void parseDeath(InputMessage& msg); void parseMapDescription(InputMessage& msg); void parseMapMoveNorth(InputMessage& msg); @@ -150,6 +151,7 @@ private: void parseCreatureTurn(InputMessage& msg); void parseEditText(InputMessage& msg); void parseEditList(InputMessage& msg); + void parsePlayerInfo(InputMessage& msg); void parsePlayerStats(InputMessage& msg); void parsePlayerSkills(InputMessage& msg); void parsePlayerState(InputMessage& msg); @@ -198,8 +200,7 @@ private: Position parsePosition(InputMessage& msg); private: - Boolean m_waitingLoginPacket; - Boolean m_firstPacket; + Boolean m_gameInitialized; std::string m_accountName; std::string m_accountPassword; std::string m_characterName; diff --git a/src/otclient/net/protocolgameparse.cpp b/src/otclient/net/protocolgameparse.cpp index 08dd500f..21f28bcd 100644 --- a/src/otclient/net/protocolgameparse.cpp +++ b/src/otclient/net/protocolgameparse.cpp @@ -41,13 +41,9 @@ void ProtocolGame::parseMessage(InputMessage& msg) try { while(!msg.eof()) { opcode = msg.getU8(); - //dump << opcode; - if(m_firstPacket) { - if(opcode != Proto::GameServerInitGame) - logWarning("first server network opcode is not GameServerInitGame"); - m_firstPacket = false; - } + if(!m_gameInitialized && opcode >= Proto::GameServerFullMap) + logWarning("first game network opcode is not GameServerInitGame"); switch(opcode) { case Proto::GameServerInitGame: @@ -68,6 +64,12 @@ void ProtocolGame::parseMessage(InputMessage& msg) case Proto::GameServerPing: parsePing(msg); break; + case Proto::GameServerPingBack: + // nothing todo + break; + case Proto::GameServerChallange: + parseChallange(msg); + break; case Proto::GameServerDeath: parseDeath(msg); break; @@ -261,30 +263,44 @@ void ProtocolGame::parseMessage(InputMessage& msg) parseQuestLine(msg); break; #if PROTOCOL>=870 - case Proto::GameServerSpellDelay: // 870 only + case Proto::GameServerSpellDelay: parseSpellDelay(msg); break; - case Proto::GameServerSpellGroupDelay: // 870 only + case Proto::GameServerSpellGroupDelay: parseSpellGroupDelay(msg); break; #endif #if PROTOCOL>=910 - case Proto::GameServerChannelEvent: // 910 only + case Proto::GameServerPlayerDataBasic: + parsePlayerInfo(msg); + break; + case Proto::GameServerChannelEvent: parseChannelEvent(msg); break; - case Proto::GameServerObjectInfo: // 910 only + case Proto::GameServerObjectInfo: parseObjectInfo(msg); break; - case Proto::GameServerPlayerInventory: // 910 only + case Proto::GameServerPlayerInventory: parsePlayerInventory(msg); break; +#endif +#if PROTOCOL>=944 + case Proto::GameServerMarketEnter: + case Proto::GameServerMarketLeave: + case Proto::GameServerMarketBrowseItem: + case Proto::GameServerMarketAcceptOffer: + case Proto::GameServerMarketOwnOffers: + case Proto::GameServerMarketCancelOffer: + case Proto::GameServerMarketBrowseOwnHistory: + case Proto::GameServerMarketMarketDetail: + //TODO + break; #endif // additional opcode used by otclient only case Proto::GameServerExtendedOpcode: parseExtendedOpcode(msg); break; // not handled yet - //case Proto::GameServerChallange: //case Proto::GameServerTrappers //case Proto::GameServerWait: default: @@ -300,6 +316,7 @@ void ProtocolGame::parseMessage(InputMessage& msg) void ProtocolGame::parseInitGame(InputMessage& msg) { + m_gameInitialized = true; uint playerId = msg.getU32(); int serverBeat = msg.getU16(); bool canReportBugs = msg.getU8(); @@ -338,12 +355,19 @@ void ProtocolGame::parseLoginWait(InputMessage& msg) g_game.processLoginWait(message, time); } -void ProtocolGame::parsePing(InputMessage&) +void ProtocolGame::parsePing(InputMessage& msg) { g_game.processPing(); sendPingResponse(); } +void ProtocolGame::parseChallange(InputMessage& msg) +{ + uint32 timestamp = msg.getU32(); + uint8 random = msg.getU8(); + sendLoginPacket(timestamp, random); +} + void ProtocolGame::parseDeath(InputMessage& msg) { int penality = 100; @@ -474,7 +498,11 @@ void ProtocolGame::parseCreatureMove(InputMessage& msg) void ProtocolGame::parseOpenContainer(InputMessage& msg) { int containerId = msg.getU8(); - int itemId = msg.getU16(); +#if PROTOCOL>=920 + ItemPtr containerItem = internalGetItem(msg); +#else + ItemPtr containerItem = Item::create(msg.getU16()); +#endif std::string name = msg.getString(); int capacity = msg.getU8(); bool hasParent = (msg.getU8() != 0); @@ -484,7 +512,7 @@ void ProtocolGame::parseOpenContainer(InputMessage& msg) for(int i = 0; i < itemCount; i++) items[i] = internalGetItem(msg); - g_game.processOpenContainer(containerId, itemId, name, capacity, hasParent, items); + g_game.processOpenContainer(containerId, containerItem, name, capacity, hasParent, items); } void ProtocolGame::parseCloseContainer(InputMessage& msg) @@ -780,6 +808,16 @@ void ProtocolGame::parseEditList(InputMessage& msg) g_game.processEditList(id, doorId, text); } +void ProtocolGame::parsePlayerInfo(InputMessage& msg) +{ + msg.getU8(); // is premium? + msg.getU8(); // profession + int numSpells = msg.getU16(); + for(int i=0;i -void ProtocolGame::sendLoginPacket(uint timestamp, uint8 unknown) +void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRandom) { OutputMessage msg; @@ -48,12 +48,15 @@ void ProtocolGame::sendLoginPacket(uint timestamp, uint8 unknown) enableChecksum(); msg.addString(m_accountName); + paddingBytes -= 2 + m_accountName.length(); msg.addString(m_characterName); + paddingBytes -= 2 + m_characterName.length(); msg.addString(m_accountPassword); + paddingBytes -= 2 + m_accountPassword.length(); - msg.addU32(timestamp); - msg.addU8(unknown); - paddingBytes -= 11 + m_accountName.length() + m_characterName.length() + m_accountPassword.length(); + msg.addU32(challangeTimestamp); + msg.addU8(challangeRandom); + paddingBytes -= 5; #else // PROTOCOL>=810 msg.addU32(Fw::fromstring(m_accountName)); msg.addString(m_characterName);