From 5843b78e87ab8456510866ba049b688e45915e05 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 4 Oct 2013 04:09:54 +0200 Subject: [PATCH] Support for client version 7.6 Tell me if there are parts which I should change. - Not everything is tested yet, feel free to report bugs in 7.6 - the data send in between login opcode and account data might not be correct, but I could not find any documents showing the right protocol yet - Hotkeys working --- modules/game_hotkeys/hotkeys_manager.lua | 32 +++++- modules/gamelib/game.lua | 10 +- modules/gamelib/protocol.lua | 6 +- modules/gamelib/protocollogin.lua | 38 ++++--- src/client/const.h | 9 ++ src/client/container.cpp | 8 ++ src/client/container.h | 1 + src/client/game.cpp | 34 ++++++- src/client/game.h | 1 + src/client/item.cpp | 121 ++++++++++++----------- src/client/luafunctions.cpp | 1 + src/client/protocolcodes.cpp | 2 +- src/client/protocolgameparse.cpp | 53 +++++++--- src/client/protocolgamesend.cpp | 35 ++++--- 14 files changed, 240 insertions(+), 111 deletions(-) diff --git a/modules/game_hotkeys/hotkeys_manager.lua b/modules/game_hotkeys/hotkeys_manager.lua index ed332a61..e40d8d99 100644 --- a/modules/game_hotkeys/hotkeys_manager.lua +++ b/modules/game_hotkeys/hotkeys_manager.lua @@ -375,15 +375,41 @@ function doKeyCombo(keyCombo) modules.game_console.setTextEditText(hotKey.value) end elseif hotKey.useType == HOTKEY_MANAGER_USE then - g_game.useInventoryItem(hotKey.itemId) + if g_game.getProtocolVersion() < 780 then + local item = g_game.findItemInContainers(hotKey.itemId, -1) + if item then + g_game.use(item) + end + else + g_game.useInventoryItem(hotKey.itemId) + end elseif hotKey.useType == HOTKEY_MANAGER_USEONSELF then - g_game.useInventoryItemWith(hotKey.itemId, g_game.getLocalPlayer()) + if g_game.getProtocolVersion() < 780 then + local item = g_game.findItemInContainers(hotKey.itemId, -1) + if item then + g_game.useWith(item, g_game.getLocalPlayer()) + end + else + g_game.useInventoryItemWith(hotKey.itemId, g_game.getLocalPlayer()) + end elseif hotKey.useType == HOTKEY_MANAGER_USEONTARGET then local attackingCreature = g_game.getAttackingCreature() if not attackingCreature then return end - g_game.useInventoryItemWith(hotKey.itemId, attackingCreature) + if g_game.getProtocolVersion() < 780 then + local item = g_game.findItemInContainers(hotKey.itemId, -1) + if item then + g_game.useWith(item, attackingCreature) + end + else + g_game.useInventoryItemWith(hotKey.itemId, attackingCreature) + end elseif hotKey.useType == HOTKEY_MANAGER_USEWITH then local item = Item.create(hotKey.itemId) + if g_game.getProtocolVersion() < 780 then + local tmpItem = g_game.findItemInContainers(hotKey.itemId, -1) + if not tmpItem then return true end + item = tmpItem + end modules.game_interface.startUseWith(item) end end diff --git a/modules/gamelib/game.lua b/modules/gamelib/game.lua index d53ed076..4593e877 100644 --- a/modules/gamelib/game.lua +++ b/modules/gamelib/game.lua @@ -34,11 +34,11 @@ end function g_game.getSupportedClients() return { - 810, 811, 840, 842, 850, 853, 854, - 860, 861, 862, 870, 910, 940, 944, - 953, 954, 960, 961, 963, 970, 980, - 981, 982, 983, 984, 985, 986, 1001, - 1002, 1010 + 760, 810, 811, 840, 842, 850, 853, + 854, 860, 861, 862, 870, 910, 940, + 944, 953, 954, 960, 961, 963, 970, + 980, 981, 982, 983, 984, 985, 986, + 1001, 1002, 1010 } end diff --git a/modules/gamelib/protocol.lua b/modules/gamelib/protocol.lua index 05a769f3..f3b69cd4 100644 --- a/modules/gamelib/protocol.lua +++ b/modules/gamelib/protocol.lua @@ -188,5 +188,9 @@ ClientOpcodes = { ClientMarketCreate = 246, -- 944 ClientMarketCancel = 247, -- 944 ClientMarketAccept = 248, -- 944 - ClientAnswerModalDialog = 249 -- 960 + ClientAnswerModalDialog = 249, -- 960 + + -- 760 + ClientEnterAccount760 = 513, + ClientEnterGame760 = 522 } diff --git a/modules/gamelib/protocollogin.lua b/modules/gamelib/protocollogin.lua index bb19bb7f..68ff5b09 100644 --- a/modules/gamelib/protocollogin.lua +++ b/modules/gamelib/protocollogin.lua @@ -27,9 +27,13 @@ end function ProtocolLogin:sendLoginPacket() local msg = OutputMessage.create() - - msg:addU8(ClientOpcodes.ClientEnterAccount) - msg:addU16(g_game.getOs()) + if g_game.getProtocolVersion() == 760 then + msg:addU16(ClientOpcodes.ClientEnterAccount760) + else + msg:addU8(ClientOpcodes.ClientEnterAccount) + msg:addU16(g_game.getOs()) + end + msg:addU16(g_game.getProtocolVersion()) if g_game.getProtocolVersion() >= 971 then @@ -49,14 +53,16 @@ function ProtocolLogin:sendLoginPacket() -- first RSA byte must be 0 msg:addU8(0) - -- xtea key - self:generateXteaKey() - local xteaKey = self:getXteaKey() - msg:addU32(xteaKey[1]) - msg:addU32(xteaKey[2]) - msg:addU32(xteaKey[3]) - msg:addU32(xteaKey[4]) - + if g_game.getProtocolVersion() >= 800 then + -- xtea key + self:generateXteaKey() + local xteaKey = self:getXteaKey() + msg:addU32(xteaKey[1]) + msg:addU32(xteaKey[2]) + msg:addU32(xteaKey[3]) + msg:addU32(xteaKey[4]) + end + if g_game.getFeature(GameAccountNames) then msg:addString(self.accountName) else @@ -73,14 +79,18 @@ function ProtocolLogin:sendLoginPacket() local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset) assert(paddingBytes >= 0) msg:addPaddingBytes(paddingBytes, 0) - msg:encryptRsa() - + if g_game.getProtocolVersion() >= 800 then + msg:encryptRsa() + end + if g_game.getFeature(GameProtocolChecksum) then self:enableChecksum() end self:send(msg) - self:enableXteaEncryption() + if g_game.getProtocolVersion() >= 800 then + self:enableXteaEncryption() + end self:recv() end diff --git a/src/client/const.h b/src/client/const.h index 7e243085..6e83c851 100644 --- a/src/client/const.h +++ b/src/client/const.h @@ -355,6 +355,15 @@ namespace Otc GameDoubleShopSellAmount = 39, GameContainerPagination = 40, GameThingMarks = 41, + + GameLooktypeU16 = 42, + GamePlayerStamina = 43, + GamePlayerAddons = 44, + GameMessageStatments = 45, + GameMessageLevel = 46, + GameNewFluids = 47, + GamePlayerStateU16 = 48, + // 51-100 reserved to be defined in lua LastGameFeature = 101 }; diff --git a/src/client/container.cpp b/src/client/container.cpp index d9f7a0f3..e8abe0a7 100644 --- a/src/client/container.cpp +++ b/src/client/container.cpp @@ -59,6 +59,14 @@ void Container::onAddItem(const ItemPtr& item) callLuaField("onAddItem", 0, item); } +ItemPtr Container::findItemById(uint itemId, int subType) +{ + for(const ItemPtr item : m_items) + if(item->getId() == itemId && (subType == -1 || item->getSubType() == subType)) + return item; + return nullptr; +} + void Container::onAddItems(const std::vector& items) { for(const ItemPtr& item : items) diff --git a/src/client/container.h b/src/client/container.h index 350bd3a9..fa67c950 100644 --- a/src/client/container.h +++ b/src/client/container.h @@ -45,6 +45,7 @@ public: std::string getName() { return m_name; } bool hasParent() { return m_hasParent; } bool isClosed() { return m_closed; } + ItemPtr findItemById(uint itemId, int subType); protected: void onOpen(const ContainerPtr& previousContainer); diff --git a/src/client/game.cpp b/src/client/game.cpp index 53b7779e..b39a0f29 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -884,7 +884,7 @@ void Game::useWith(const ItemPtr& item, const ThingPtr& toThing) if(!pos.isValid()) // virtual item pos = Position(0xFFFF, 0, 0); // means that is a item in inventory - if(toThing->isCreature()) + if(toThing->isCreature() && g_game.getProtocolVersion() >= 780) m_protocolGame->sendUseOnCreature(pos, item->getId(), item->getStackPos(), toThing->getId()); else m_protocolGame->sendUseItemWith(pos, item->getId(), item->getStackPos(), toThing->getPosition(), toThing->getId(), toThing->getStackPos()); @@ -903,6 +903,20 @@ void Game::useInventoryItemWith(int itemId, const ThingPtr& toThing) m_protocolGame->sendUseItemWith(pos, itemId, 0, toThing->getPosition(), toThing->getId(), toThing->getStackPos()); } +ItemPtr Game::findItemInContainers(uint itemId, int subType) +{ + for(auto& it : m_containers) { + const ContainerPtr& container = it.second; + + if(container) { + ItemPtr item = container->findItemById(itemId, subType); + if(item != nullptr) + return item; + } + } + return nullptr; +} + int Game::open(const ItemPtr& item, const ContainerPtr& previousContainer) { if(!canPerformGameAction() || !item) @@ -1400,19 +1414,31 @@ void Game::setProtocolVersion(int version) if(isOnline()) stdext::throw_exception("Unable to change protocol version while online"); - if(version != 0 && (version < 810 || version > 1010)) + if(version != 0 && version != 760 && (version < 810 || version > 1010)) stdext::throw_exception(stdext::format("Protocol version %d not supported", version)); m_features.reset(); enableFeature(Otc::GameFormatCreatureName); + + if(version >= 780) + { + enableFeature(Otc::GamePlayerAddons); + enableFeature(Otc::GamePlayerStamina); + enableFeature(Otc::GameNewFluids); + enableFeature(Otc::GameMessageLevel); + enableFeature(Otc::GameMessageStatments); + enableFeature(Otc::GamePlayerStateU16); + enableFeature(Otc::GameLooktypeU16); + } + if(version >= 840) { enableFeature(Otc::GameProtocolChecksum); enableFeature(Otc::GameChallengeOnLogin); enableFeature(Otc::GameAccountNames); } - if(version <= 854) { + if(version >= 780 && version <= 854) { // 780 might not be accurate enableFeature(Otc::GameChargeableItems); } @@ -1487,7 +1513,7 @@ void Game::setClientVersion(int version) if(isOnline()) stdext::throw_exception("Unable to change client version while online"); - if(version != 0 && (version < 810 || version > 1010)) + if(version != 0 && version != 760 && (version < 810 || version > 1010)) stdext::throw_exception(stdext::format("Client version %d not supported", version)); m_clientVersion = version; diff --git a/src/client/game.h b/src/client/game.h index c2834af9..e76d6c4d 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -158,6 +158,7 @@ public: void useWith(const ItemPtr& fromThing, const ThingPtr& toThing); void useInventoryItem(int itemId); void useInventoryItemWith(int itemId, const ThingPtr& toThing); + ItemPtr findItemInContainers(uint itemId, int subType); // container related int open(const ItemPtr& item, const ContainerPtr& previousContainer); diff --git a/src/client/item.cpp b/src/client/item.cpp index 69fa994f..83dc3599 100644 --- a/src/client/item.cpp +++ b/src/client/item.cpp @@ -288,65 +288,68 @@ void Item::calculatePatterns(int& xPattern, int& yPattern, int& zPattern) } } else if(isSplash() || isFluidContainer()) { int color = Otc::FluidTransparent; - switch(m_countOrSubType) { - case Otc::FluidNone: - color = Otc::FluidTransparent; - break; - case Otc::FluidWater: - color = Otc::FluidBlue; - break; - case Otc::FluidMana: - color = Otc::FluidPurple; - break; - case Otc::FluidBeer: - color = Otc::FluidBrown; - break; - case Otc::FluidOil: - color = Otc::FluidBrown; - break; - case Otc::FluidBlood: - color = Otc::FluidRed; - break; - case Otc::FluidSlime: - color = Otc::FluidGreen; - break; - case Otc::FluidMud: - color = Otc::FluidBrown; - break; - case Otc::FluidLemonade: - color = Otc::FluidYellow; - break; - case Otc::FluidMilk: - color = Otc::FluidWhite; - break; - case Otc::FluidWine: - color = Otc::FluidPurple; - break; - case Otc::FluidHealth: - color = Otc::FluidRed; - break; - case Otc::FluidUrine: - color = Otc::FluidYellow; - break; - case Otc::FluidRum: - color = Otc::FluidBrown; - break; - case Otc::FluidFruidJuice: - color = Otc::FluidYellow; - break; - case Otc::FluidCoconutMilk: - color = Otc::FluidWhite; - break; - case Otc::FluidTea: - color = Otc::FluidBrown; - break; - case Otc::FluidMead: - color = Otc::FluidBrown; - break; - default: - color = Otc::FluidTransparent; - break; - } + if(g_game.getFeature(Otc::GameNewFluids)) { + switch(m_countOrSubType) { + case Otc::FluidNone: + color = Otc::FluidTransparent; + break; + case Otc::FluidWater: + color = Otc::FluidBlue; + break; + case Otc::FluidMana: + color = Otc::FluidPurple; + break; + case Otc::FluidBeer: + color = Otc::FluidBrown; + break; + case Otc::FluidOil: + color = Otc::FluidBrown; + break; + case Otc::FluidBlood: + color = Otc::FluidRed; + break; + case Otc::FluidSlime: + color = Otc::FluidGreen; + break; + case Otc::FluidMud: + color = Otc::FluidBrown; + break; + case Otc::FluidLemonade: + color = Otc::FluidYellow; + break; + case Otc::FluidMilk: + color = Otc::FluidWhite; + break; + case Otc::FluidWine: + color = Otc::FluidPurple; + break; + case Otc::FluidHealth: + color = Otc::FluidRed; + break; + case Otc::FluidUrine: + color = Otc::FluidYellow; + break; + case Otc::FluidRum: + color = Otc::FluidBrown; + break; + case Otc::FluidFruidJuice: + color = Otc::FluidYellow; + break; + case Otc::FluidCoconutMilk: + color = Otc::FluidWhite; + break; + case Otc::FluidTea: + color = Otc::FluidBrown; + break; + case Otc::FluidMead: + color = Otc::FluidBrown; + break; + default: + color = Otc::FluidTransparent; + break; + } + } else + color = m_countOrSubType; xPattern = (color % 4) % getNumPatternX(); yPattern = (color / 4) % getNumPatternY(); diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 7b4fec4b..9f4d060d 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -184,6 +184,7 @@ void Client::registerLuaFunctions() g_lua.bindSingletonFunction("g_game", "useWith", &Game::useWith, &g_game); g_lua.bindSingletonFunction("g_game", "useInventoryItem", &Game::useInventoryItem, &g_game); g_lua.bindSingletonFunction("g_game", "useInventoryItemWith", &Game::useInventoryItemWith, &g_game); + g_lua.bindSingletonFunction("g_game", "findItemInContainers", &Game::findItemInContainers, &g_game); g_lua.bindSingletonFunction("g_game", "open", &Game::open, &g_game); g_lua.bindSingletonFunction("g_game", "openParent", &Game::openParent, &g_game); g_lua.bindSingletonFunction("g_game", "close", &Game::close, &g_game); diff --git a/src/client/protocolcodes.cpp b/src/client/protocolcodes.cpp index 29eff5e9..4a30be61 100644 --- a/src/client/protocolcodes.cpp +++ b/src/client/protocolcodes.cpp @@ -87,7 +87,7 @@ void buildMessageModesMap(int version) { messageModesMap[Otc::MessageLook] = 25; messageModesMap[Otc::MessageFailure] = 26; messageModesMap[Otc::MessageBlue] = 27; - } else if(version >= 810) { + } else if(version >= 760) { messageModesMap[Otc::MessageNone] = 0; messageModesMap[Otc::MessageSay] = 1; messageModesMap[Otc::MessageWhisper] = 2; diff --git a/src/client/protocolgameparse.cpp b/src/client/protocolgameparse.cpp index 647a81d6..42255e43 100644 --- a/src/client/protocolgameparse.cpp +++ b/src/client/protocolgameparse.cpp @@ -1038,7 +1038,9 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg) double magicLevelPercent = msg->getU8(); double soul = msg->getU8(); - double stamina = msg->getU16(); + double stamina = 0; + if(g_game.getFeature(Otc::GamePlayerStamina)) + stamina = msg->getU16(); double baseSpeed = 0; if(g_game.getFeature(Otc::GameSkillsBase)) @@ -1092,7 +1094,12 @@ void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg) void ProtocolGame::parsePlayerState(const InputMessagePtr& msg) { - int states = msg->getU16(); + int states; + if(g_game.getFeature(Otc::GamePlayerStateU16)) + states = msg->getU16(); + else + states = msg->getU8(); + m_localPlayer->setStates(states); } @@ -1130,10 +1137,15 @@ void ProtocolGame::parseMultiUseCooldown(const InputMessagePtr& msg) void ProtocolGame::parseTalk(const InputMessagePtr& msg) { - msg->getU32(); // channel statement guid + if(g_game.getFeature(Otc::GameMessageStatments)) + msg->getU32(); // channel statement guid std::string name = g_game.formatCreatureName(msg->getString()); - int level = msg->getU16(); + + int level = 0; + if(g_game.getFeature(Otc::GameMessageLevel)) + level = msg->getU16(); + Otc::MessageMode mode = Proto::translateMessageModeFromServer(msg->getU8()); int channelId = 0; Position pos; @@ -1380,15 +1392,23 @@ void ProtocolGame::parseFloorChangeDown(const InputMessagePtr& msg) void ProtocolGame::parseOpenOutfitWindow(const InputMessagePtr& msg) { Outfit currentOutfit = getOutfit(msg); - std::vector > outfitList; - int outfitCount = msg->getU8(); - for(int i = 0; i < outfitCount; i++) { - int outfitId = msg->getU16(); - std::string outfitName = msg->getString(); - int outfitAddons = msg->getU8(); - outfitList.push_back(std::make_tuple(outfitId, outfitName, outfitAddons)); + if(g_game.getProtocolVersion() < 780) { + int outfitStart = msg->getU8(); + int outfitEnd = msg->getU8(); + for(int i = outfitStart; i <= outfitEnd; i++) + outfitList.push_back(std::make_tuple(i, "", 0)); + } + else { + int outfitCount = msg->getU8(); + for(int i = 0; i < outfitCount; i++) { + int outfitId = msg->getU16(); + std::string outfitName = msg->getString(); + int outfitAddons = msg->getU8(); + + outfitList.push_back(std::make_tuple(outfitId, outfitName, outfitAddons)); + } } std::vector > mountList; @@ -1665,14 +1685,21 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg) { Outfit outfit; - int lookType = msg->getU16(); + int lookType; + if(g_game.getFeature(Otc::GameLooktypeU16)) + lookType = msg->getU16(); + else + lookType = msg->getU8(); + if(lookType != 0) { outfit.setCategory(ThingCategoryCreature); int head = msg->getU8(); int body = msg->getU8(); int legs = msg->getU8(); int feet = msg->getU8(); - int addons = msg->getU8(); + int addons = 0; + if(g_game.getFeature(Otc::GamePlayerAddons)) + addons = msg->getU8(); if(!g_things.isValidDatId(lookType, ThingCategoryCreature)) { g_logger.traceError(stdext::format("invalid outfit looktype %d", lookType)); diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index 5ae33942..6a2da0d5 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -52,8 +52,16 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando { OutputMessagePtr msg(new OutputMessage); - msg->addU8(Proto::ClientPendingGame); - msg->addU16(g_game.getOs()); + if(g_game.getProtocolVersion() == 760) + { + msg->addU16(0x20A); + msg->addU8(g_game.getOs()); + } + else + { + msg->addU8(Proto::ClientPendingGame); + msg->addU16(g_game.getOs()); + } msg->addU16(g_game.getProtocolVersion()); if(g_game.getProtocolVersion() >= 971) { @@ -65,13 +73,16 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando msg->addU8(0); // first RSA byte must be 0 - // xtea key - generateXteaKey(); - msg->addU32(m_xteaKey[0]); - msg->addU32(m_xteaKey[1]); - msg->addU32(m_xteaKey[2]); - msg->addU32(m_xteaKey[3]); - msg->addU8(0); // is gm set? + if(g_game.getProtocolVersion() >= 800) + { + // xtea key + generateXteaKey(); + msg->addU32(m_xteaKey[0]); + msg->addU32(m_xteaKey[1]); + msg->addU32(m_xteaKey[2]); + msg->addU32(m_xteaKey[3]); + msg->addU8(0); // is gm set? + } if(g_game.getFeature(Otc::GameAccountNames)) msg->addString(m_accountName); @@ -96,14 +107,16 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando msg->addPaddingBytes(paddingBytes); // encrypt with RSA - msg->encryptRsa(); + if(g_game.getProtocolVersion() >= 800) + msg->encryptRsa(); if(g_game.getFeature(Otc::GameProtocolChecksum)) enableChecksum(); send(msg); - enableXteaEncryption(); + if(g_game.getProtocolVersion() >= 800) + enableXteaEncryption(); } void ProtocolGame::sendEnterGame()