From ad4cf36193dedeb060eb62b7903b05e253810202 Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Sun, 29 Sep 2019 21:56:37 +0300 Subject: [PATCH 1/8] first changes --- CMakeLists.txt | 1 + data/XML/outfits.xml | 110 +++++++++++++++++++++++++ src/CMakeLists.txt | 2 + src/actions.cpp | 74 ++++++++++++----- src/actions.h | 49 +++++++++-- src/configmanager.cpp | 1 + src/configmanager.h | 1 + src/enums.h | 1 + src/game.cpp | 29 +++++-- src/outfit.cpp | 77 ++++++++++++++++++ src/outfit.h | 63 ++++++++++++++ src/player.cpp | 109 +++++++++++++++++++++---- src/player.h | 19 ++++- src/protocol.cpp | 66 +++------------ src/protocol.h | 116 ++++++++++++++------------ src/protocolgame.cpp | 97 ++++++++++++++++------ src/protocolgame.h | 10 ++- src/protocollogin.cpp | 105 ++++++++++++++++-------- src/protocollogin.h | 9 +- src/rsa.cpp | 116 ++++++++++++-------------- src/rsa.h | 26 +++--- src/server.cpp | 6 +- src/server.h | 14 ++-- src/tools.cpp | 72 ++++++++++++++++ src/tools.h | 3 + src/xtea.cpp | 140 ++++++++++++++++++++++++++++++++ src/xtea.h | 32 ++++++++ vc14/theforgottenserver.vcxproj | 4 + 28 files changed, 1042 insertions(+), 310 deletions(-) create mode 100644 data/XML/outfits.xml create mode 100644 src/outfit.cpp create mode 100644 src/outfit.h create mode 100644 src/xtea.cpp create mode 100644 src/xtea.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3150690..268b7f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(cotire) add_compile_options(-Wall -pipe -fvisibility=hidden) +add_definitions(-DBOOST_ALL_NO_LIB) if (CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-fno-strict-aliasing) diff --git a/data/XML/outfits.xml b/data/XML/outfits.xml new file mode 100644 index 0000000..fca7ddc --- /dev/null +++ b/data/XML/outfits.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76c0cad..5beaa34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,6 +41,7 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/networkmessage.cpp ${CMAKE_CURRENT_LIST_DIR}/npc.cpp ${CMAKE_CURRENT_LIST_DIR}/otserv.cpp + ${CMAKE_CURRENT_LIST_DIR}/outfit.cpp ${CMAKE_CURRENT_LIST_DIR}/outputmessage.cpp ${CMAKE_CURRENT_LIST_DIR}/party.cpp ${CMAKE_CURRENT_LIST_DIR}/player.cpp @@ -66,5 +67,6 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/vocation.cpp ${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp ${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp + ${CMAKE_CURRENT_LIST_DIR}/xtea.cpp ) diff --git a/src/actions.cpp b/src/actions.cpp index 439748d..c23df0d 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -224,7 +224,7 @@ Action* Actions::getAction(const Item* item) return g_spells->getRuneSpell(item->getID()); } -ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item) +ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey) { if (Door* door = item->getDoor()) { if (!door->canUse(player)) { @@ -235,13 +235,17 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_ Action* action = getAction(item); if (action) { if (action->isScripted()) { - if (action->executeUse(player, item, pos, nullptr, pos)) { + if (action->executeUse(player, item, pos, nullptr, pos, isHotkey)) { return RETURNVALUE_NOERROR; } if (item->isRemoved()) { return RETURNVALUE_CANNOTUSETHISOBJECT; } + } else if (action->function) { + if (action->function(player, item, pos, nullptr, pos, isHotkey)) { + return RETURNVALUE_NOERROR; + } } } @@ -273,6 +277,11 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_ openContainer = container; } + uint32_t corpseOwner = container->getCorpseOwner(); + if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) { + return RETURNVALUE_YOUARENOTTHEOWNER; + } + //open/close container int32_t oldContainerId = player->getContainerID(openContainer); if (oldContainerId != -1) { @@ -309,12 +318,16 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_ return RETURNVALUE_CANNOTUSETHISOBJECT; } -bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item) +bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey) { player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL)); player->stopWalk(); - ReturnValue ret = internalUseItem(player, pos, index, item); + if (isHotkey) { + showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1)); + } + + ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); return false; @@ -323,7 +336,7 @@ bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* } bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& toPos, - uint8_t toStackPos, Item* item, Creature* creature/* = nullptr*/) + uint8_t toStackPos, Item* item, bool isHotkey, Creature* creature/* = nullptr*/) { player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL)); player->stopWalk(); @@ -340,7 +353,11 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& return false; } - if (!action->executeUse(player, item, fromPos, action->getTarget(player, creature, toPos, toStackPos), toPos)) { + if (isHotkey) { + showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1)); + } + + if (!action->executeUse(player, item, fromPos, action->getTarget(player, creature, toPos, toStackPos), toPos, isHotkey)) { if (!action->hasOwnErrorHandler()) { player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT); } @@ -349,8 +366,25 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& return true; } +void Actions::showUseHotkeyMessage(Player* player, const Item* item, uint32_t count) +{ + std::ostringstream ss; + + const ItemType& it = Item::items[item->getID()]; + if (!it.showCount) { + ss << "Using one of " << item->getName() << "..."; + } + else if (count == 1) { + ss << "Using the last " << item->getName() << "..."; + } + else { + ss << "Using one of " << count << ' ' << item->getPluralName() << "..."; + } + player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); +} + Action::Action(LuaScriptInterface* interface) : - Event(interface), allowFarUse(false), checkFloor(true), checkLineOfSight(true) {} + Event(interface), function(nullptr), allowFarUse(false), checkFloor(true), checkLineOfSight(true) {} Action::Action(const Action* copy) : Event(copy), allowFarUse(copy->allowFarUse), checkFloor(copy->checkFloor), checkLineOfSight(copy->checkLineOfSight) {} @@ -359,17 +393,17 @@ bool Action::configureEvent(const pugi::xml_node& node) { pugi::xml_attribute allowFarUseAttr = node.attribute("allowfaruse"); if (allowFarUseAttr) { - setAllowFarUse(allowFarUseAttr.as_bool()); + allowFarUse = allowFarUseAttr.as_bool(); } pugi::xml_attribute blockWallsAttr = node.attribute("blockwalls"); if (blockWallsAttr) { - setCheckLineOfSight(blockWallsAttr.as_bool()); + checkLineOfSight = blockWallsAttr.as_bool(); } pugi::xml_attribute checkFloorAttr = node.attribute("checkfloor"); if (checkFloorAttr) { - setCheckFloor(checkFloorAttr.as_bool()); + checkFloor = checkFloorAttr.as_bool(); } return true; @@ -382,10 +416,11 @@ std::string Action::getScriptEventName() const ReturnValue Action::canExecuteAction(const Player* player, const Position& toPos) { - if (!getAllowFarUse()) { + if (!allowFarUse) { return g_actions->canUse(player, toPos); - } else { - return g_actions->canUseFar(player, toPos, getCheckLineOfSight(), getCheckFloor()); + } + else { + return g_actions->canUseFar(player, toPos, checkLineOfSight, checkFloor); } } @@ -397,9 +432,9 @@ Thing* Action::getTarget(Player* player, Creature* targetCreature, const Positio return g_game.internalGetThing(player, toPosition, toStackPos, 0, STACKPOS_USETARGET); } -bool Action::executeUse(Player* player, Item* item, const Position& fromPos, Thing* target, const Position& toPos) +bool Action::executeUse(Player* player, Item* item, const Position& fromPosition, Thing* target, const Position& toPosition, bool isHotkey) { - //onUse(player, item, fromPosition, target, toPosition) + //onUse(player, item, fromPosition, target, toPosition, isHotkey) if (!scriptInterface->reserveScriptEnv()) { std::cout << "[Error - Action::executeUse] Call stack overflow" << std::endl; return false; @@ -416,10 +451,11 @@ bool Action::executeUse(Player* player, Item* item, const Position& fromPos, Thi LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushThing(L, item); - LuaScriptInterface::pushPosition(L, fromPos); + LuaScriptInterface::pushPosition(L, fromPosition); LuaScriptInterface::pushThing(L, target); - LuaScriptInterface::pushPosition(L, toPos); + LuaScriptInterface::pushPosition(L, toPosition); - return scriptInterface->callFunction(5); -} + LuaScriptInterface::pushBoolean(L, isHotkey); + return scriptInterface->callFunction(6); +} \ No newline at end of file diff --git a/src/actions.h b/src/actions.h index f4ae6d9..6f03677 100644 --- a/src/actions.h +++ b/src/actions.h @@ -24,6 +24,10 @@ #include "enums.h" #include "luascript.h" +class Action; +using Action_ptr = std::unique_ptr; +using ActionFunction = std::function; + class Action : public Event { public: @@ -34,7 +38,7 @@ class Action : public Event //scripting virtual bool executeUse(Player* player, Item* item, const Position& fromPosition, - Thing* target, const Position& toPosition); + Thing* target, const Position& toPosition, bool isHotkey); // bool getAllowFarUse() const { @@ -58,18 +62,44 @@ class Action : public Event checkFloor = v; } + std::vector getItemIdRange() { + return ids; + } + void addItemId(uint16_t id) { + ids.emplace_back(id); + } + + std::vector getUniqueIdRange() { + return uids; + } + void addUniqueId(uint16_t id) { + uids.emplace_back(id); + } + + std::vector getActionIdRange() { + return aids; + } + void addActionId(uint16_t id) { + aids.emplace_back(id); + } + virtual ReturnValue canExecuteAction(const Player* player, const Position& toPos); virtual bool hasOwnErrorHandler() { return false; } virtual Thing* getTarget(Player* player, Creature* targetCreature, const Position& toPosition, uint8_t toStackPos) const; - protected: + ActionFunction function; + + private: std::string getScriptEventName() const override; - bool allowFarUse; - bool checkFloor; - bool checkLineOfSight; + bool allowFarUse = false; + bool checkFloor = true; + bool checkLineOfSight = true; + std::vector ids; + std::vector uids; + std::vector aids; }; class Actions final : public BaseEvents @@ -82,15 +112,16 @@ class Actions final : public BaseEvents Actions(const Actions&) = delete; Actions& operator=(const Actions&) = delete; - bool useItem(Player* player, const Position& pos, uint8_t index, Item* item); - bool useItemEx(Player* player, const Position& fromPos, const Position& toPos, uint8_t toStackPos, Item* item, Creature* creature = nullptr); + bool useItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey); + bool useItemEx(Player* player, const Position& fromPos, const Position& toPos, uint8_t toStackPos, Item* item, bool isHotkey, Creature* creature = nullptr); ReturnValue canUse(const Player* player, const Position& pos); ReturnValue canUse(const Player* player, const Position& pos, const Item* item); ReturnValue canUseFar(const Creature* creature, const Position& toPos, bool checkLineOfSight, bool checkFloor); - protected: - ReturnValue internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item); + private: + ReturnValue internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey); + static void showUseHotkeyMessage(Player* player, const Item* item, uint32_t count); void clear() final; LuaScriptInterface& getScriptInterface() final; diff --git a/src/configmanager.cpp b/src/configmanager.cpp index 2993702..5b2b7a0 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -68,6 +68,7 @@ bool ConfigManager::load() boolean[SHOW_MONSTER_LOOT] = getGlobalBoolean(L, "showMonsterLoot", true); boolean[ALLOW_CHANGEOUTFIT] = getGlobalBoolean(L, "allowChangeOutfit", true); boolean[ONE_PLAYER_ON_ACCOUNT] = getGlobalBoolean(L, "onePlayerOnlinePerAccount", true); + boolean[AIMBOT_HOTKEY_ENABLED] = getGlobalBoolean(L, "hotkeyAimbotEnabled", true); boolean[REMOVE_RUNE_CHARGES] = getGlobalBoolean(L, "removeChargesFromRunes", true); boolean[EXPERIENCE_FROM_PLAYERS] = getGlobalBoolean(L, "experienceByKillingPlayers", false); boolean[FREE_PREMIUM] = getGlobalBoolean(L, "freePremium", false); diff --git a/src/configmanager.h b/src/configmanager.h index 6c14b68..1461eb0 100644 --- a/src/configmanager.h +++ b/src/configmanager.h @@ -29,6 +29,7 @@ class ConfigManager SHOW_MONSTER_LOOT, ALLOW_CHANGEOUTFIT, ONE_PLAYER_ON_ACCOUNT, + AIMBOT_HOTKEY_ENABLED, REMOVE_RUNE_CHARGES, EXPERIENCE_FROM_PLAYERS, FREE_PREMIUM, diff --git a/src/enums.h b/src/enums.h index 1ae3739..a403145 100644 --- a/src/enums.h +++ b/src/enums.h @@ -359,6 +359,7 @@ struct Outfit_t { uint8_t lookBody = 0; uint8_t lookLegs = 0; uint8_t lookFeet = 0; + uint8_t lookAddons = 0; }; struct LightInfo { diff --git a/src/game.cpp b/src/game.cpp index 1d587b6..905d7fe 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1866,6 +1866,11 @@ void Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t f return; } + bool isHotkey = (fromPos.x == 0xFFFF && fromPos.y == 0 && fromPos.z == 0); + if (isHotkey && !g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) { + return; + } + Thing* thing = internalGetThing(player, fromPos, fromStackPos, fromSpriteId, STACKPOS_USEITEM); if (!thing) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); @@ -1934,7 +1939,7 @@ void Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t f player->resetIdleTime(); player->setNextActionTask(nullptr); - g_actions->useItemEx(player, fromPos, toPos, toStackPos, item); + g_actions->useItemEx(player, fromPos, toPos, toStackPos, item, isHotkey); } void Game::playerUseItem(uint32_t playerId, const Position& pos, uint8_t stackPos, @@ -1945,6 +1950,11 @@ void Game::playerUseItem(uint32_t playerId, const Position& pos, uint8_t stackPo return; } + bool isHotkey = (pos.x == 0xFFFF && pos.y == 0 && pos.z == 0); + if (isHotkey && !g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) { + return; + } + Thing* thing = internalGetThing(player, pos, stackPos, spriteId, STACKPOS_USEITEM); if (!thing) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); @@ -1989,7 +1999,7 @@ void Game::playerUseItem(uint32_t playerId, const Position& pos, uint8_t stackPo player->resetIdleTime(); player->setNextActionTask(nullptr); - g_actions->useItem(player, pos, index, item); + g_actions->useItem(player, pos, index, item, isHotkey); } void Game::playerUseWithCreature(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, uint32_t creatureId, uint16_t spriteId) @@ -2004,13 +2014,16 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position& fromPos, uin return; } - if (creature->getPlayer()) { - player->sendCancelMessage(RETURNVALUE_DIRECTPLAYERSHOOT); + if (!Position::areInRange<7, 5, 0>(creature->getPosition(), player->getPosition())) { return; } - if (!Position::areInRange<7, 5, 0>(creature->getPosition(), player->getPosition())) { - return; + bool isHotkey = (fromPos.x == 0xFFFF && fromPos.y == 0 && fromPos.z == 0); + if (!g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) { + if (creature->getPlayer() || isHotkey) { + player->sendCancelMessage(RETURNVALUE_DIRECTPLAYERSHOOT); + return; + } } Thing* thing = internalGetThing(player, fromPos, fromStackPos, spriteId, STACKPOS_USEITEM); @@ -2081,7 +2094,7 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position& fromPos, uin player->resetIdleTime(); player->setNextActionTask(nullptr); - g_actions->useItemEx(player, fromPos, creature->getPosition(), creature->getParent()->getThingIndex(creature), item, creature); + g_actions->useItemEx(player, fromPos, creature->getPosition(), creature->getParent()->getThingIndex(creature), item, isHotkey, creature); } void Game::playerCloseContainer(uint32_t playerId, uint8_t cid) @@ -2874,7 +2887,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit) return; } - if (player->canWear(outfit.lookType)) { + if (player->canWear(outfit.lookType, outfit.lookAddons)) { player->defaultOutfit = outfit; if (player->hasCondition(CONDITION_OUTFIT)) { diff --git a/src/outfit.cpp b/src/outfit.cpp new file mode 100644 index 0000000..9984e9a --- /dev/null +++ b/src/outfit.cpp @@ -0,0 +1,77 @@ +/** + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Mark Samman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "otpch.h" + +#include "outfit.h" + +#include "pugicast.h" +#include "tools.h" + +bool Outfits::loadFromXml() +{ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file("data/XML/outfits.xml"); + if (!result) { + printXMLError("Error - Outfits::loadFromXml", "data/XML/outfits.xml", result); + return false; + } + + for (auto outfitNode : doc.child("outfits").children()) { + pugi::xml_attribute attr; + if ((attr = outfitNode.attribute("enabled")) && !attr.as_bool()) { + continue; + } + + if (!(attr = outfitNode.attribute("type"))) { + std::cout << "[Warning - Outfits::loadFromXml] Missing outfit type." << std::endl; + continue; + } + + uint16_t type = pugi::cast(attr.value()); + if (type > PLAYERSEX_LAST) { + std::cout << "[Warning - Outfits::loadFromXml] Invalid outfit type " << type << "." << std::endl; + continue; + } + + pugi::xml_attribute lookTypeAttribute = outfitNode.attribute("looktype"); + if (!lookTypeAttribute) { + std::cout << "[Warning - Outfits::loadFromXml] Missing looktype on outfit." << std::endl; + continue; + } + + outfits[type].emplace_back( + outfitNode.attribute("name").as_string(), + pugi::cast(lookTypeAttribute.value()), + outfitNode.attribute("premium").as_bool(), + outfitNode.attribute("unlocked").as_bool(true) + ); + } + return true; +} + +const Outfit* Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const +{ + for (const Outfit& outfit : outfits[sex]) { + if (outfit.lookType == lookType) { + return &outfit; + } + } + return nullptr; +} diff --git a/src/outfit.h b/src/outfit.h new file mode 100644 index 0000000..50aefa6 --- /dev/null +++ b/src/outfit.h @@ -0,0 +1,63 @@ +/** + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Mark Samman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef FS_OUTFIT_H_C56E7A707E3F422C8C93D9BE09916AA3 +#define FS_OUTFIT_H_C56E7A707E3F422C8C93D9BE09916AA3 + +#include "enums.h" + +struct Outfit { + Outfit(std::string name, uint16_t lookType, bool premium, bool unlocked) : + name(std::move(name)), lookType(lookType), premium(premium), unlocked(unlocked) {} + + std::string name; + uint16_t lookType; + bool premium; + bool unlocked; +}; + +struct ProtocolOutfit { + ProtocolOutfit(const std::string& name, uint16_t lookType, uint8_t addons) : + name(name), lookType(lookType), addons(addons) {} + + const std::string& name; + uint16_t lookType; + uint8_t addons; +}; + +class Outfits +{ + public: + static Outfits& getInstance() { + static Outfits instance; + return instance; + } + + bool loadFromXml(); + + const Outfit* getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const; + const std::vector& getOutfits(PlayerSex_t sex) const { + return outfits[sex]; + } + + private: + std::vector outfits[PLAYERSEX_LAST + 1]; +}; + +#endif diff --git a/src/player.cpp b/src/player.cpp index 57b15bf..7d17518 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -493,6 +493,11 @@ uint16_t Player::getContainerIndex(uint8_t cid) const return it->second.index; } +bool Player::canOpenCorpse(uint32_t ownerId) const +{ + return getID() == ownerId || (party && party->canOpenCorpse(ownerId)); +} + uint16_t Player::getLookCorpse() const { if (sex == PLAYERSEX_FEMALE) { @@ -3129,7 +3134,7 @@ bool Player::onKilledCreature(Creature* target, bool lastHit/* = true*/) void Player::gainExperience(uint64_t gainExp, Creature* source) { - if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0) { + if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0 || staminaMinutes == 0) { return; } @@ -3223,26 +3228,31 @@ void Player::changeSoul(int32_t soulChange) sendStats(); } -bool Player::canWear(uint32_t lookType) const +bool Player::canWear(uint32_t lookType, uint8_t addons) const { if (group->access) { return true; } - if (getSex() == PLAYERSEX_MALE) { - if (lookType >= 132 && lookType <= 134 && isPremium()) { - return true; - } else if (lookType >= 128 && lookType <= 131) { - return true; - } - } else if (getSex() == PLAYERSEX_FEMALE) { - if (lookType >= 140 && lookType <= 142 && isPremium()) { - return true; - } else if (lookType >= 136 && lookType <= 139) { - return true; - } + const Outfit* outfit = Outfits::getInstance().getOutfitByLookType(sex, lookType); + if (!outfit) { + return false; } + if (outfit->premium && !isPremium()) { + return false; + } + + if (outfit->unlocked && addons == 0) { + return true; + } + + for (const OutfitEntry& outfitEntry : outfits) { + if (outfitEntry.lookType != lookType) { + continue; + } + return (outfitEntry.addons & addons) == addons; + } return false; } @@ -3263,6 +3273,77 @@ bool Player::canLogout() return !isPzLocked() && !hasCondition(CONDITION_INFIGHT); } +void Player::genReservedStorageRange() +{ + //generate outfits range + uint32_t base_key = PSTRG_OUTFITS_RANGE_START; + for (const OutfitEntry& entry : outfits) { + storageMap[++base_key] = (entry.lookType << 16) | entry.addons; + } +} + +void Player::addOutfit(uint16_t lookType, uint8_t addons) +{ + for (OutfitEntry& outfitEntry : outfits) { + if (outfitEntry.lookType == lookType) { + outfitEntry.addons |= addons; + return; + } + } + outfits.emplace_back(lookType, addons); +} + +bool Player::removeOutfit(uint16_t lookType) +{ + for (auto it = outfits.begin(), end = outfits.end(); it != end; ++it) { + OutfitEntry& entry = *it; + if (entry.lookType == lookType) { + outfits.erase(it); + return true; + } + } + return false; +} + +bool Player::removeOutfitAddon(uint16_t lookType, uint8_t addons) +{ + for (OutfitEntry& outfitEntry : outfits) { + if (outfitEntry.lookType == lookType) { + outfitEntry.addons &= ~addons; + return true; + } + } + return false; +} + +bool Player::getOutfitAddons(const Outfit& outfit, uint8_t& addons) const +{ + if (group->access) { + addons = 3; + return true; + } + + if (outfit.premium && !isPremium()) { + return false; + } + + for (const OutfitEntry& outfitEntry : outfits) { + if (outfitEntry.lookType != outfit.lookType) { + continue; + } + + addons = outfitEntry.addons; + return true; + } + + if (!outfit.unlocked) { + return false; + } + + addons = 0; + return true; +} + void Player::setSex(PlayerSex_t newSex) { sex = newSex; diff --git a/src/player.h b/src/player.h index 16e0f3b..743e2ac 100644 --- a/src/player.h +++ b/src/player.h @@ -23,6 +23,7 @@ #include "creature.h" #include "container.h" #include "cylinder.h" +#include "outfit.h" #include "enums.h" #include "vocation.h" #include "protocolgame.h" @@ -77,9 +78,10 @@ struct OpenContainer { }; struct OutfitEntry { - constexpr OutfitEntry(uint16_t lookType) : lookType(lookType) {} + constexpr OutfitEntry(uint16_t lookType, uint8_t addons) : lookType(lookType), addons(addons) {} uint16_t lookType; + uint8_t addons; }; struct Skill { @@ -148,6 +150,10 @@ class Player final : public Creature, public Cylinder return ((50ULL * lv * lv * lv) - (150ULL * lv * lv) + (400ULL * lv)) / 3ULL; } + uint16_t getStaminaMinutes() const { + return staminaMinutes; + } + uint64_t getBankBalance() const { return bankBalance; } @@ -265,8 +271,11 @@ class Player final : public Creature, public Cylinder int8_t getContainerID(const Container* container) const; uint16_t getContainerIndex(uint8_t cid) const; + bool canOpenCorpse(uint32_t ownerId) const; + void addStorageValue(const uint32_t key, const int32_t value); bool getStorageValue(const uint32_t key, int32_t& value) const; + void genReservedStorageRange(); void setGroup(Group* newGroup) { group = newGroup; @@ -556,7 +565,12 @@ class Player final : public Creature, public Cylinder } void checkSkullTicks(); - bool canWear(uint32_t lookType) const; + bool canWear(uint32_t lookType, uint8_t addons) const; + void addOutfit(uint16_t lookType, uint8_t addons); + bool removeOutfit(uint16_t lookType); + bool removeOutfitAddon(uint16_t lookType, uint8_t addons); + bool getOutfitAddons(const Outfit& outfit, uint8_t& addons) const; + bool canLogout(); @@ -1032,6 +1046,7 @@ class Player final : public Creature, public Cylinder int32_t shieldBlockCount = 0; int32_t idleTime = 0; + uint16_t staminaMinutes = 2520; uint16_t maxWriteLen = 0; uint8_t soul = 0; diff --git a/src/protocol.cpp b/src/protocol.cpp index 34f027c..00c7ebe 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -22,6 +22,7 @@ #include "protocol.h" #include "outputmessage.h" #include "rsa.h" +#include "xtea.h" extern RSA g_RSA; @@ -32,7 +33,7 @@ void Protocol::onSendMessage(const OutputMessage_ptr& msg) const if (encryptionEnabled) { XTEA_encrypt(*msg); - msg->addCryptoHeader(); + msg->addCryptoHeader(checksumEnabled); } } } @@ -51,7 +52,8 @@ OutputMessage_ptr Protocol::getOutputBuffer(int32_t size) //dispatcher thread if (!outputBuffer) { outputBuffer = OutputMessagePool::getOutputMessage(); - } else if ((outputBuffer->getLength() + size) > NetworkMessage::MAX_PROTOCOL_BODY_LENGTH) { + } + else if ((outputBuffer->getLength() + size) > NetworkMessage::MAX_PROTOCOL_BODY_LENGTH) { send(outputBuffer); outputBuffer = OutputMessagePool::getOutputMessage(); } @@ -60,73 +62,27 @@ OutputMessage_ptr Protocol::getOutputBuffer(int32_t size) void Protocol::XTEA_encrypt(OutputMessage& msg) const { - const uint32_t delta = 0x61C88647; - // The message must be a multiple of 8 - size_t paddingBytes = msg.getLength() % 8; + size_t paddingBytes = msg.getLength() % 8u; if (paddingBytes != 0) { msg.addPaddingBytes(8 - paddingBytes); } uint8_t* buffer = msg.getOutputBuffer(); - const size_t messageLength = msg.getLength(); - size_t readPos = 0; - const uint32_t k[] = {key[0], key[1], key[2], key[3]}; - while (readPos < messageLength) { - uint32_t v0; - memcpy(&v0, buffer + readPos, 4); - uint32_t v1; - memcpy(&v1, buffer + readPos + 4, 4); - - uint32_t sum = 0; - - for (int32_t i = 32; --i >= 0;) { - v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); - sum -= delta; - v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]); - } - - memcpy(buffer + readPos, &v0, 4); - readPos += 4; - memcpy(buffer + readPos, &v1, 4); - readPos += 4; - } + xtea::encrypt(buffer, msg.getLength(), key); } bool Protocol::XTEA_decrypt(NetworkMessage& msg) const { - if (((msg.getLength() - 2) % 8) != 0) { + if (((msg.getLength() - 6) & 7) != 0) { return false; } - const uint32_t delta = 0x61C88647; - uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition(); - const size_t messageLength = (msg.getLength() - 6); - size_t readPos = 0; - const uint32_t k[] = {key[0], key[1], key[2], key[3]}; - while (readPos < messageLength) { - uint32_t v0; - memcpy(&v0, buffer + readPos, 4); - uint32_t v1; - memcpy(&v1, buffer + readPos + 4, 4); + xtea::decrypt(buffer, msg.getLength() - 6, key); - uint32_t sum = 0xC6EF3720; - - for (int32_t i = 32; --i >= 0;) { - v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]); - sum += delta; - v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); - } - - memcpy(buffer + readPos, &v0, 4); - readPos += 4; - memcpy(buffer + readPos, &v1, 4); - readPos += 4; - } - - int innerLength = msg.get(); - if (innerLength > msg.getLength() - 4) { + uint16_t innerLength = msg.get(); + if (innerLength + 8 > msg.getLength()) { return false; } @@ -151,4 +107,4 @@ uint32_t Protocol::getIP() const } return 0; -} +} \ No newline at end of file diff --git a/src/protocol.h b/src/protocol.h index d2a46df..76bf5d1 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -21,77 +21,85 @@ #define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1 #include "connection.h" +#include "xtea.h" class Protocol : public std::enable_shared_from_this { - public: - explicit Protocol(Connection_ptr connection) : connection(connection) {} - virtual ~Protocol() = default; +public: + explicit Protocol(Connection_ptr connection) : connection(connection) {} + virtual ~Protocol() = default; - // non-copyable - Protocol(const Protocol&) = delete; - Protocol& operator=(const Protocol&) = delete; + // non-copyable + Protocol(const Protocol&) = delete; + Protocol& operator=(const Protocol&) = delete; - virtual void parsePacket(NetworkMessage&) {} + virtual void parsePacket(NetworkMessage&) {} - virtual void onSendMessage(const OutputMessage_ptr& msg) const; - void onRecvMessage(NetworkMessage& msg); - virtual void onRecvFirstMessage(NetworkMessage& msg) = 0; - virtual void onConnect() {} + virtual void onSendMessage(const OutputMessage_ptr& msg) const; + void onRecvMessage(NetworkMessage& msg); + virtual void onRecvFirstMessage(NetworkMessage& msg) = 0; + virtual void onConnect() {} - bool isConnectionExpired() const { - return connection.expired(); + bool isConnectionExpired() const { + return connection.expired(); + } + + Connection_ptr getConnection() const { + return connection.lock(); + } + + uint32_t getIP() const; + + //Use this function for autosend messages only + OutputMessage_ptr getOutputBuffer(int32_t size); + + OutputMessage_ptr& getCurrentBuffer() { + return outputBuffer; + } + + void send(OutputMessage_ptr msg) const { + if (auto connection = getConnection()) { + connection->send(msg); } + } - Connection_ptr getConnection() const { - return connection.lock(); +protected: + void disconnect() const { + if (auto connection = getConnection()) { + connection->close(); } + } + void enableXTEAEncryption() { + encryptionEnabled = true; + } + void setXTEAKey(xtea::key key) { + this->key = std::move(key); + } + void disableChecksum() { + checksumEnabled = false; + } - uint32_t getIP() const; + static bool RSA_decrypt(NetworkMessage& msg); - //Use this function for autosend messages only - OutputMessage_ptr getOutputBuffer(int32_t size); + void setRawMessages(bool value) { + rawMessages = value; + } - OutputMessage_ptr& getCurrentBuffer() { - return outputBuffer; - } + virtual void release() {} - void send(OutputMessage_ptr msg) const { - if (auto connection = getConnection()) { - connection->send(msg); - } - } +private: + void XTEA_encrypt(OutputMessage& msg) const; + bool XTEA_decrypt(NetworkMessage& msg) const; - protected: - void disconnect() const { - if (auto connection = getConnection()) { - connection->close(); - } - } - void enableXTEAEncryption() { - encryptionEnabled = true; - } - void setXTEAKey(const uint32_t* key) { - memcpy(this->key, key, sizeof(*key) * 4); - } + friend class Connection; - void XTEA_encrypt(OutputMessage& msg) const; - bool XTEA_decrypt(NetworkMessage& msg) const; - static bool RSA_decrypt(NetworkMessage& msg); + OutputMessage_ptr outputBuffer; - void setRawMessages(bool value) { - rawMessages = value; - } - - virtual void release() {} - friend class Connection; - - OutputMessage_ptr outputBuffer; - private: - const ConnectionWeak_ptr connection; - uint32_t key[4] = {}; - bool encryptionEnabled = false; - bool rawMessages = false; + const ConnectionWeak_ptr connection; + xtea::key key; + bool encryptionEnabled = false; + bool checksumEnabled = true; + bool rawMessages = false; }; #endif diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index 6142df6..f2536f0 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -242,18 +242,20 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) OperatingSystem_t operatingSystem = static_cast(msg.get()); version = msg.get(); + msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision + if (!Protocol::RSA_decrypt(msg)) { disconnect(); return; } - uint32_t key[4]; + xtea::key key; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(key); + setXTEAKey(std::move(key)); if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { NetworkMessage opcodeMessage; @@ -315,12 +317,32 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem))); } -void ProtocolGame::sendUpdateRequest() +void ProtocolGame::onConnect() { auto output = OutputMessagePool::getOutputMessage(); - output->addByte(0x11); + static std::random_device rd; + static std::ranlux24 generator(rd()); + static std::uniform_int_distribution randNumber(0x00, 0xFF); + + // Skip checksum + output->skipBytes(sizeof(uint32_t)); + + // Packet length & type + output->add(0x0006); + output->addByte(0x1F); + + // Add timestamp & random number + challengeTimestamp = static_cast(time(nullptr)); + output->add(challengeTimestamp); + + challengeRandom = randNumber(generator); + output->addByte(challengeRandom); + + // Go back and write checksum + output->skipBytes(-12); + output->add(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8)); + send(output); - disconnect(); } void ProtocolGame::disconnectClient(const std::string& message) const @@ -693,6 +715,7 @@ void ProtocolGame::parseSetOutfit(NetworkMessage& msg) newOutfit.lookBody = msg.getByte(); newOutfit.lookLegs = msg.getByte(); newOutfit.lookFeet = msg.getByte(); + newOutfit.lookAddons = msg.getByte(); addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit); } @@ -1715,20 +1738,31 @@ void ProtocolGame::sendOutfitWindow() Outfit_t currentOutfit = player->getDefaultOutfit(); AddOutfit(msg, currentOutfit); - if (player->getSex() == PLAYERSEX_MALE) { - msg.add(128); - if (player->isPremium()) { - msg.add(134); - } else { - msg.add(131); - } - } else { - msg.add(136); - if (player->isPremium()) { - msg.add(142); - } else { - msg.add(139); + std::vector protocolOutfits; + if (player->isAccessPlayer()) { + static const std::string gamemasterOutfitName = "Gamemaster"; + protocolOutfits.emplace_back(gamemasterOutfitName, 75, 0); } + + const auto& outfits = Outfits::getInstance().getOutfits(player->getSex()); + protocolOutfits.reserve(outfits.size()); + for (const Outfit& outfit : outfits) { + uint8_t addons; + if (!player->getOutfitAddons(outfit, addons)) { + continue; + } + + protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons); + if (protocolOutfits.size() == 100) { // Game client doesn't allow more than 100 outfits + break; + } + } + + msg.addByte(protocolOutfits.size()); + for (const ProtocolOutfit& outfit : protocolOutfits) { + msg.add(outfit.lookType); + msg.addString(outfit.name); + msg.addByte(outfit.addons); } writeToOutputBuffer(msg); @@ -1810,14 +1844,22 @@ void ProtocolGame::AddPlayerStats(NetworkMessage& msg) msg.add(std::min(player->getHealth(), std::numeric_limits::max())); msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); - msg.add(static_cast(player->getFreeCapacity() / 100.)); - if (player->getExperience() >= std::numeric_limits::max()) { - msg.add(0); - } else { - msg.add(static_cast(player->getExperience())); - } - msg.add(static_cast(player->getLevel())); + msg.add(player->getFreeCapacity()); + msg.add(player->getCapacity()); + + msg.add(player->getExperience()); + + msg.add(player->getLevel()); + msg.addByte(player->getLevelPercent()); + + msg.add(100); // base xp gain rate + msg.add(0); // xp voucher + msg.add(0); // low level bonus + msg.add(0); // xp boost + msg.add(100); // stamina multiplier (100 = x1.0) + + msg.add(std::min(player->getMana(), std::numeric_limits::max())); msg.add(std::min(player->getMaxMana(), std::numeric_limits::max())); @@ -1825,6 +1867,10 @@ void ProtocolGame::AddPlayerStats(NetworkMessage& msg) msg.addByte(player->getMagicLevelPercent()); msg.addByte(player->getSoul()); + + msg.add(player->getStaminaMinutes()); + + msg.add(0); // xp boost time (seconds) } void ProtocolGame::AddPlayerSkills(NetworkMessage& msg) @@ -1846,6 +1892,7 @@ void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit) msg.addByte(outfit.lookBody); msg.addByte(outfit.lookLegs); msg.addByte(outfit.lookFeet); + msg.addByte(outfit.lookAddons); } else { msg.addItemId(outfit.lookTypeEx); } diff --git a/src/protocolgame.h b/src/protocolgame.h index b5e7a8d..42c919d 100644 --- a/src/protocolgame.h +++ b/src/protocolgame.h @@ -50,8 +50,9 @@ class ProtocolGame final : public Protocol { public: // static protocol information - enum {server_sends_first = false}; - enum {protocol_identifier = 0x0A}; // Not required as we send first + enum { server_sends_first = true }; + enum { protocol_identifier = 0 }; // Not required as we send first + enum { use_checksum = true }; static const char* protocol_name() { return "gameworld protocol"; @@ -71,7 +72,6 @@ class ProtocolGame final : public Protocol return std::static_pointer_cast(shared_from_this()); } void connect(uint32_t playerId, OperatingSystem_t operatingSystem); - void sendUpdateRequest(); void disconnectClient(const std::string& message) const; void writeToOutputBuffer(const NetworkMessage& msg); @@ -86,6 +86,7 @@ class ProtocolGame final : public Protocol // we have all the parse methods void parsePacket(NetworkMessage& msg) final; void onRecvFirstMessage(NetworkMessage& msg) final; + void onConnect() override; //Parse methods void parseAutoWalk(NetworkMessage& msg); @@ -262,8 +263,11 @@ class ProtocolGame final : public Protocol Player* player = nullptr; uint32_t eventConnect = 0; + uint32_t challengeTimestamp = 0; uint16_t version = CLIENT_VERSION_MIN; + uint8_t challengeRandom = 0; + bool debugAssertSent = false; bool acceptPackets = false; }; diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index be9a125..fb2602b 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -32,36 +32,39 @@ extern ConfigManager g_config; extern Game g_game; -void ProtocolLogin::sendUpdateRequest() +void ProtocolLogin::disconnectClient(const std::string& message, uint16_t version) { auto output = OutputMessagePool::getOutputMessage(); - output->addByte(0x1E); - send(output); - - disconnect(); -} - -void ProtocolLogin::disconnectClient(const std::string& message) -{ - auto output = OutputMessagePool::getOutputMessage(); - - output->addByte(0x0A); + output->addByte(version >= 1076 ? 0x0B : 0x0A); output->addString(message); send(output); disconnect(); } -void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& password) +void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& password, const std::string& token, uint16_t version) { Account account; if (!IOLoginData::loginserverAuthentication(accountNumber, password, account)) { - disconnectClient("Accountnumber or password is not correct."); + disconnectClient("Accountnumber or password is not correct.", version); return; } + uint32_t ticks = time(nullptr) / AUTHENTICATOR_PERIOD; + auto output = OutputMessagePool::getOutputMessage(); + if (!std::to_string(accountNumber).empty()) { + if (token.empty() || !(token == generateToken(std::to_string(accountNumber), ticks) || token == generateToken(std::to_string(accountNumber), ticks - 1) || token == generateToken(std::to_string(accountNumber), ticks + 1))) { + output->addByte(0x0D); + output->addByte(0); + send(output); + disconnect(); + return; + } + output->addByte(0x0C); + output->addByte(0); + } //Update premium days Game::updatePremium(account); @@ -76,23 +79,38 @@ void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& output->addString(ss.str()); } + //Add session key + output->addByte(0x28); + output->addString(std::to_string(accountNumber) + "\n" + password + "\n" + token + "\n" + std::to_string(ticks)); + + //Add char list output->addByte(0x64); + output->addByte(1); // number of worlds + + output->addByte(0); // world id + output->addString(g_config.getString(ConfigManager::SERVER_NAME)); + output->addString(g_config.getString(ConfigManager::IP)); + output->add(g_config.getNumber(ConfigManager::GAME_PORT)); + output->addByte(0); + uint8_t size = std::min(std::numeric_limits::max(), account.characters.size()); output->addByte(size); for (uint8_t i = 0; i < size; i++) { + output->addByte(0); output->addString(account.characters[i]); - output->addString(g_config.getString(ConfigManager::SERVER_NAME)); - output->add(inet_addr(g_config.getString(ConfigManager::IP).c_str())); - output->add(g_config.getNumber(ConfigManager::GAME_PORT)); } //Add premium days + output->addByte(0); if (g_config.getBoolean(ConfigManager::FREE_PREMIUM)) { - output->add(0xFFFF); - } else { - output->add(account.premiumDays); + output->addByte(1); + output->add(0); + } + else { + output->addByte(account.premiumDays > 0 ? 1 : 0); + output->add(time(nullptr) + (account.premiumDays * 86400)); } send(output); @@ -109,41 +127,55 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) msg.skipBytes(2); // client OS - /*uint16_t version =*/ msg.get(); - msg.skipBytes(12); + uint16_t version = msg.get(); + if (version >= 971) { + msg.skipBytes(17); + } + else { + msg.skipBytes(12); + } /* * Skipped bytes: * 4 bytes: protocolVersion * 12 bytes: dat, spr, pic signatures (4 bytes each) + * 1 byte: 0 */ + if (version <= 760) { + std::ostringstream ss; + ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; + disconnectClient(ss.str(), version); + return; + } + if (!Protocol::RSA_decrypt(msg)) { disconnect(); return; } - uint32_t key[4]; + xtea::key key; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(key); + setXTEAKey(std::move(key)); - /*if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { - //sendUpdateRequest(); - disconnectClient("Use Tibia 7.72 to login!"); + if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { + std::ostringstream ss; + ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; + disconnectClient(ss.str(), version); return; - }*/ + } if (g_game.getGameState() == GAME_STATE_STARTUP) { - disconnectClient("Gameworld is starting up. Please wait."); + disconnectClient("Gameworld is starting up. Please wait.", version); return; } if (g_game.getGameState() == GAME_STATE_MAINTAIN) { - disconnectClient("Gameworld is under maintenance.\nPlease re-connect in a while."); + disconnectClient("Gameworld is under maintenance.\nPlease re-connect in a while.", version); return; } @@ -160,7 +192,7 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) std::ostringstream ss; ss << "Your IP has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason; - disconnectClient(ss.str()); + disconnectClient(ss.str(), version); return; } @@ -176,6 +208,15 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) return; } + // read authenticator token and stay logged in flag from last 128 bytes + msg.skipBytes((msg.getLength() - 128) - msg.getBufferPosition()); + if (!Protocol::RSA_decrypt(msg)) { + disconnectClient("Invalid authentification token.", version); + return; + } + + std::string authToken = msg.getString(); + auto thisPtr = std::static_pointer_cast(shared_from_this()); - g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password))); + g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version))); } diff --git a/src/protocollogin.h b/src/protocollogin.h index 17004f6..4651b50 100644 --- a/src/protocollogin.h +++ b/src/protocollogin.h @@ -38,13 +38,12 @@ class ProtocolLogin : public Protocol explicit ProtocolLogin(Connection_ptr connection) : Protocol(connection) {} - void onRecvFirstMessage(NetworkMessage& msg); + void onRecvFirstMessage(NetworkMessage& msg) override; - protected: - void sendUpdateRequest(); - void disconnectClient(const std::string& message); + private: + void disconnectClient(const std::string& message, uint16_t version); - void getCharacterList(uint32_t accountNumber, const std::string& password); + void getCharacterList(uint32_t accountNumber, const std::string& password, const std::string& token, uint16_t version); }; #endif diff --git a/src/rsa.cpp b/src/rsa.cpp index affcad4..ba557ac 100644 --- a/src/rsa.cpp +++ b/src/rsa.cpp @@ -21,72 +21,60 @@ #include "rsa.h" -RSA::RSA() +#include +#include + +#include +#include + +static CryptoPP::AutoSeededRandomPool prng; + +void RSA::decrypt(char* msg) const { - mpz_init(n); - mpz_init2(d, 1024); + CryptoPP::Integer m{ reinterpret_cast(msg), 128 }; + auto c = pk.CalculateInverse(prng, m); + c.Encode(reinterpret_cast(msg), 128); } -RSA::~RSA() +static const std::string header = "-----BEGIN RSA PRIVATE KEY-----"; +static const std::string footer = "-----END RSA PRIVATE KEY-----"; + +void RSA::loadPEM(const std::string& filename) { - mpz_clear(n); - mpz_clear(d); -} - -void RSA::setKey(const char* pString, const char* qString) -{ - mpz_t p, q, e; - mpz_init2(p, 1024); - mpz_init2(q, 1024); - mpz_init(e); - - mpz_set_str(p, pString, 10); - mpz_set_str(q, qString, 10); - - // e = 65537 - mpz_set_ui(e, 65537); - - // n = p * q - mpz_mul(n, p, q); - - mpz_t p_1, q_1, pq_1; - mpz_init2(p_1, 1024); - mpz_init2(q_1, 1024); - mpz_init2(pq_1, 1024); - - mpz_sub_ui(p_1, p, 1); - mpz_sub_ui(q_1, q, 1); - - // pq_1 = (p -1)(q - 1) - mpz_mul(pq_1, p_1, q_1); - - // d = e^-1 mod (p - 1)(q - 1) - mpz_invert(d, e, pq_1); - - mpz_clear(p_1); - mpz_clear(q_1); - mpz_clear(pq_1); - - mpz_clear(p); - mpz_clear(q); - mpz_clear(e); -} - -void RSA::decrypt(char* msg) const -{ - mpz_t c, m; - mpz_init2(c, 1024); - mpz_init2(m, 1024); - - mpz_import(c, 128, 1, 1, 0, 0, msg); - - // m = c^d mod n - mpz_powm(m, c, d, n); - - size_t count = (mpz_sizeinbase(m, 2) + 7) / 8; - memset(msg, 0, 128 - count); - mpz_export(msg + (128 - count), nullptr, 1, 1, 0, 0, m); - - mpz_clear(c); - mpz_clear(m); + std::ifstream file{ filename }; + + if (!file.is_open()) { + throw std::runtime_error("Missing file " + filename + "."); + } + + std::ostringstream oss; + for (std::string line; std::getline(file, line); oss << line); + std::string key = oss.str(); + + if (key.substr(0, header.size()) != header) { + throw std::runtime_error("Missing RSA private key header."); + } + + if (key.substr(key.size() - footer.size(), footer.size()) != footer) { + throw std::runtime_error("Missing RSA private key footer."); + } + + key = key.substr(header.size(), key.size() - footer.size()); + + CryptoPP::ByteQueue queue; + CryptoPP::Base64Decoder decoder; + decoder.Attach(new CryptoPP::Redirector(queue)); + decoder.Put(reinterpret_cast(key.c_str()), key.size()); + decoder.MessageEnd(); + + try { + pk.BERDecodePrivateKey(queue, false, queue.MaxRetrievable()); + + if (!pk.Validate(prng, 3)) { + throw std::runtime_error("RSA private key is not valid."); + } + } + catch (const CryptoPP::Exception& e) { + std::cout << e.what() << '\n'; + } } diff --git a/src/rsa.h b/src/rsa.h index e75335b..0783e7c 100644 --- a/src/rsa.h +++ b/src/rsa.h @@ -20,24 +20,24 @@ #ifndef FS_RSA_H_C4E277DA8E884B578DDBF0566F504E91 #define FS_RSA_H_C4E277DA8E884B578DDBF0566F504E91 -#include +#include + +#include class RSA { - public: - RSA(); - ~RSA(); +public: + RSA() = default; - // non-copyable - RSA(const RSA&) = delete; - RSA& operator=(const RSA&) = delete; + // non-copyable + RSA(const RSA&) = delete; + RSA& operator=(const RSA&) = delete; - void setKey(const char* pString, const char* qString); - void decrypt(char* msg) const; + void loadPEM(const std::string& filename); + void decrypt(char* msg) const; - private: - //use only GMP - mpz_t n, d; +private: + CryptoPP::RSA::PrivateKey pk; }; -#endif +#endif \ No newline at end of file diff --git a/src/server.cpp b/src/server.cpp index 1923c74..1b09123 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -132,7 +132,7 @@ void ServicePort::onAccept(Connection_ptr connection, const boost::system::error } } -Protocol_ptr ServicePort::make_protocol(NetworkMessage& msg, const Connection_ptr& connection) const +Protocol_ptr ServicePort::make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const { uint8_t protocolID = msg.getByte(); for (auto& service : services) { @@ -140,7 +140,9 @@ Protocol_ptr ServicePort::make_protocol(NetworkMessage& msg, const Connection_pt continue; } - return service->make_protocol(connection); + if ((checksummed && service->is_checksummed()) || !service->is_checksummed()) { + return service->make_protocol(connection); + } } return nullptr; } diff --git a/src/server.h b/src/server.h index f6ff611..02e5278 100644 --- a/src/server.h +++ b/src/server.h @@ -29,6 +29,7 @@ class ServiceBase { public: virtual bool is_single_socket() const = 0; + virtual bool is_checksummed() const = 0; virtual uint8_t get_protocol_identifier() const = 0; virtual const char* get_protocol_name() const = 0; @@ -39,17 +40,20 @@ template class Service final : public ServiceBase { public: - bool is_single_socket() const final { + bool is_single_socket() const override { return ProtocolType::server_sends_first; } - uint8_t get_protocol_identifier() const final { + bool is_checksummed() const override { + return ProtocolType::use_checksum; + } + uint8_t get_protocol_identifier() const override { return ProtocolType::protocol_identifier; } - const char* get_protocol_name() const final { + const char* get_protocol_name() const override { return ProtocolType::protocol_name(); } - Protocol_ptr make_protocol(const Connection_ptr& c) const final { + Protocol_ptr make_protocol(const Connection_ptr& c) const override { return std::make_shared(c); } }; @@ -71,7 +75,7 @@ class ServicePort : public std::enable_shared_from_this std::string get_protocol_names() const; bool add_service(const Service_ptr& new_svc); - Protocol_ptr make_protocol(NetworkMessage& msg, const Connection_ptr& connection) const; + Protocol_ptr make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const; void onStopServer(); void onAccept(Connection_ptr connection, const boost::system::error_code& error); diff --git a/src/tools.cpp b/src/tools.cpp index 14e0320..05be137 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -272,6 +272,51 @@ std::string pluralizeString(std::string str) return str2; } +std::string generateToken(const std::string& key, uint32_t ticks) +{ + // generate message from ticks + std::string message(8, 0); + for (uint8_t i = 8; --i; ticks >>= 8) { + message[i] = static_cast(ticks & 0xFF); + } + + // hmac key pad generation + std::string iKeyPad(64, 0x36), oKeyPad(64, 0x5C); + for (uint8_t i = 0; i < key.length(); ++i) { + iKeyPad[i] ^= key[i]; + oKeyPad[i] ^= key[i]; + } + + oKeyPad.reserve(84); + + // hmac concat inner pad with message + iKeyPad.append(message); + + // hmac first pass + message.assign(transformToSHA1(iKeyPad)); + + // hmac concat outer pad with message, conversion from hex to int needed + for (uint8_t i = 0; i < message.length(); i += 2) { + oKeyPad.push_back(static_cast(std::strtoul(message.substr(i, 2).c_str(), nullptr, 16))); + } + + // hmac second pass + message.assign(transformToSHA1(oKeyPad)); + + // calculate hmac offset + uint32_t offset = static_cast(std::strtoul(message.substr(39, 1).c_str(), nullptr, 16) & 0xF); + + // get truncated hash + uint32_t truncHash = static_cast(std::strtoul(message.substr(2 * offset, 8).c_str(), nullptr, 16)) & 0x7FFFFFFF; + message.assign(std::to_string(truncHash)); + + // return only last AUTHENTICATOR_DIGITS (default 6) digits, also asserts exactly 6 digits + uint32_t hashLen = message.length(); + message.assign(message.substr(hashLen - std::min(hashLen, AUTHENTICATOR_DIGITS))); + message.insert(0, AUTHENTICATOR_DIGITS - std::min(hashLen, AUTHENTICATOR_DIGITS), '0'); + return message; +} + void replaceString(std::string& str, const std::string& sought, const std::string& replacement) { size_t pos = 0; @@ -790,6 +835,33 @@ std::string getSkillName(uint8_t skillid) } } +uint32_t adlerChecksum(const uint8_t* data, size_t length) +{ + if (length > NETWORKMESSAGE_MAXSIZE) { + return 0; + } + + const uint16_t adler = 65521; + + uint32_t a = 1, b = 0; + + while (length > 0) { + size_t tmp = length > 5552 ? 5552 : length; + length -= tmp; + + do { + a += *data++; + b += a; + } while (--tmp); + + a %= adler; + b %= adler; + } + + return (b << 16) | a; +} + + std::string ucfirst(std::string str) { for (char& i : str) { diff --git a/src/tools.h b/src/tools.h index 1edc868..b615af3 100644 --- a/src/tools.h +++ b/src/tools.h @@ -30,6 +30,7 @@ void printXMLError(const std::string& where, const std::string& fileName, const pugi::xml_parse_result& result); std::string transformToSHA1(const std::string& input); +std::string generateToken(const std::string& key, uint32_t ticks); uint8_t getLiquidColor(uint8_t type); void extractArticleAndName(std::string& data, std::string& article, std::string& name); @@ -83,6 +84,8 @@ std::string getCombatName(CombatType_t combatType); std::string getSkillName(uint8_t skillid); +uint32_t adlerChecksum(const uint8_t* data, size_t length); + std::string ucfirst(std::string str); std::string ucwords(std::string str); bool booleanString(const std::string& str); diff --git a/src/xtea.cpp b/src/xtea.cpp new file mode 100644 index 0000000..1144ba6 --- /dev/null +++ b/src/xtea.cpp @@ -0,0 +1,140 @@ +/** + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Mark Samman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "otpch.h" + +#include "xtea.h" + +#include +#include + +namespace xtea { + +namespace { + +constexpr uint32_t delta = 0x9E3779B9; + +template +void XTEA_encrypt(uint8_t data[BLOCK_SIZE * 8], const key& k) +{ + alignas(16) uint32_t left[BLOCK_SIZE], right[BLOCK_SIZE]; + for (auto i = 0u, j = 0u; i < BLOCK_SIZE; i += 1u, j += 8u) { + left[i] = data[j] | data[j+1] << 8u | data[j+2] << 16u | data[j+3] << 24u; + right[i] = data[j+4] | data[j+5] << 8u | data[j+6] << 16u | data[j+7] << 24u; + } + + uint32_t sum = 0u; + for (auto i = 0u; i < 32; ++i) { + for (auto j = 0u; j < BLOCK_SIZE; ++j) { + left[j] += (((right[j] << 4) ^ (right[j] >> 5)) + right[j]) ^ (sum + k[sum & 3]); + } + sum += delta; + for (auto j = 0u; j < BLOCK_SIZE; ++j) { + right[j] += (((left[j] << 4) ^ (left[j] >> 5)) + left[j]) ^ (sum + k[(sum >> 11) & 3]); + } + } + + for (auto i = 0u, j = 0u; i < BLOCK_SIZE; i += 1u, j += 8u) { + data[j] = static_cast(left[i]); + data[j+1] = static_cast(left[i] >> 8u); + data[j+2] = static_cast(left[i] >> 16u); + data[j+3] = static_cast(left[i] >> 24u); + data[j+4] = static_cast(right[i]); + data[j+5] = static_cast(right[i] >> 8u); + data[j+6] = static_cast(right[i] >> 16u); + data[j+7] = static_cast(right[i] >> 24u); + } +} + +template +void XTEA_decrypt(uint8_t data[BLOCK_SIZE * 8], const key& k) +{ + alignas(16) uint32_t left[BLOCK_SIZE], right[BLOCK_SIZE]; + for (auto i = 0u, j = 0u; i < BLOCK_SIZE; i += 1u, j += 8u) { + left[i] = data[j] | data[j+1] << 8u | data[j+2] << 16u | data[j+3] << 24u; + right[i] = data[j+4] | data[j+5] << 8u | data[j+6] << 16u | data[j+7] << 24u; + } + + uint32_t sum = delta << 5; + for (auto i = 0u; i < 32; ++i) { + for (auto j = 0u; j < BLOCK_SIZE; ++j) { + right[j] -= (((left[j] << 4) ^ (left[j] >> 5)) + left[j]) ^ (sum + k[(sum >> 11) & 3]); + } + sum -= delta; + for (auto j = 0u; j < BLOCK_SIZE; ++j) { + left[j] -= (((right[j] << 4) ^ (right[j] >> 5)) + right[j]) ^ (sum + k[(sum) & 3]); + } + } + + for (auto i = 0u, j = 0u; i < BLOCK_SIZE; i += 1u, j += 8u) { + data[j] = static_cast(left[i]); + data[j+1] = static_cast(left[i] >> 8u); + data[j+2] = static_cast(left[i] >> 16u); + data[j+3] = static_cast(left[i] >> 24u); + data[j+4] = static_cast(right[i]); + data[j+5] = static_cast(right[i] >> 8u); + data[j+6] = static_cast(right[i] >> 16u); + data[j+7] = static_cast(right[i] >> 24u); + } +} + +constexpr auto InitialBlockSize = +#if defined(__AVX512F__) + 128u; +#elif defined(__AVX__) + 32u; +#elif defined(__SSE__) || defined(__ARM_FEATURE_SIMD32) + 8u; +#elif defined(__x86_64__) + 2u; +#else + 1u; +#endif + +template +struct XTEA { + static constexpr auto step = BlockSize * 8u; + + void operator()(uint8_t* input, size_t length, const key& k) const { + const auto blocks = (length & ~(step - 1)); + for (auto i = 0u; i < blocks; i += step) { + if (Encrypt) { + XTEA_encrypt(input + i, k); + } else { + XTEA_decrypt(input + i, k); + } + } + input += blocks; + length -= blocks; + + if (BlockSize != 1) { + XTEA()(input, length, k); + } + } +}; + +constexpr auto encrypt_v = XTEA(); +constexpr auto decrypt_v = XTEA(); + +} // anonymous namespace + +void encrypt(uint8_t* data, size_t length, const key& k) { encrypt_v(data, length, k); } +void decrypt(uint8_t* data, size_t length, const key& k) { decrypt_v(data, length, k); } + +} // namespace xtea diff --git a/src/xtea.h b/src/xtea.h new file mode 100644 index 0000000..6f8f555 --- /dev/null +++ b/src/xtea.h @@ -0,0 +1,32 @@ +/** + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Mark Samman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TFS_XTEA_H +#define TFS_XTEA_H + +namespace xtea { + +using key = std::array; + +void encrypt(uint8_t* data, size_t length, const key& k); +void decrypt(uint8_t* data, size_t length, const key& k); + +} // namespace xtea + +#endif // TFS_XTEA_H diff --git a/vc14/theforgottenserver.vcxproj b/vc14/theforgottenserver.vcxproj index 8d24fba..afb71d6 100644 --- a/vc14/theforgottenserver.vcxproj +++ b/vc14/theforgottenserver.vcxproj @@ -190,6 +190,7 @@ otpch.h + @@ -215,6 +216,7 @@ + @@ -263,6 +265,7 @@ + @@ -291,6 +294,7 @@ + From 45c787b241dbcd319a5dd838b54c70c52d889a10 Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Sun, 29 Sep 2019 22:59:23 +0300 Subject: [PATCH 2/8] its compiling now --- key.pem | 13 ++++++++ src/connection.cpp | 37 ++++++++++++++++----- src/const.h | 8 +++++ src/definitions.h | 5 ++- src/networkmessage.h | 1 + src/otserv.cpp | 10 ++++-- src/outputmessage.h | 6 +++- src/party.cpp | 8 +++++ src/party.h | 1 + src/protocollogin.cpp | 6 ++-- src/spells.cpp | 76 ++++++++++++++++++++++--------------------- src/spells.h | 18 +++++----- 12 files changed, 127 insertions(+), 62 deletions(-) create mode 100644 key.pem diff --git a/key.pem b/key.pem new file mode 100644 index 0000000..ec19bb4 --- /dev/null +++ b/key.pem @@ -0,0 +1,13 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCbZGkDtFsHrJVlaNhzU71xZROd15QHA7A+bdB5OZZhtKg3qmBWHXzLlFL6AIBZ +SQmIKrW8pYoaGzX4sQWbcrEhJhHGFSrT27PPvuetwUKnXT11lxUJwyHFwkpb1R/UYPAbThW+sN4Z +MFKKXT8VwePL9cQB1nd+EKyqsz2+jVt/9QIDAQABAoGAQovTtTRtr3GnYRBvcaQxAvjIV9ZUnFRm +C7Y3i1KwJhOZ3ozmSLrEEOLqTgoc7R+sJ1YzEiDKbbete11EC3gohlhW56ptj0WDf+7ptKOgqiEy +Kh4qt1sYJeeGz4GiiooJoeKFGdtk/5uvMR6FDCv6H7ewigVswzf330Q3Ya7+jYECQQERBxsga6+5 +x6IofXyNF6QuMqvuiN/pUgaStUOdlnWBf/T4yUpKvNS1+I4iDzqGWOOSR6RsaYPYVhj9iRABoKyx +AkEAkbNzB6vhLAWht4dUdGzaREF3p4SwNcu5bJRa/9wCLSHaS9JaTq4lljgVPp1zyXyJCSCWpFnl +0WvK3Qf6nVBIhQJBANS7rK8+ONWQbxENdZaZ7Rrx8HUTwSOS/fwhsGWBbl1Qzhdq/6/sIfEHkfeH +1hoH+IlpuPuf21MdAqvJt+cMwoECQF1LyBOYduYGcSgg6u5mKVldhm3pJCA+ZGxnjuGZEnet3qeA +eb05++112fyvO85ABUun524z9lokKNFh45NKLjUCQGshzV43P+RioiBhtEpB/QFzijiS4L2HKNu1 +tdhudnUjWkaf6jJmQS/ppln0hhRMHlk9Vus/bPx7LtuDuo6VQDo= +-----END RSA PRIVATE KEY----- diff --git a/src/connection.cpp b/src/connection.cpp index 859066c..56a3861 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -182,45 +182,66 @@ void Connection::parsePacket(const boost::system::error_code& error) if (error) { close(FORCE_CLOSE); return; - } else if (connectionState != CONNECTION_STATE_OPEN) { + } + else if (connectionState != CONNECTION_STATE_OPEN) { return; } + //Check packet checksum + uint32_t checksum; + int32_t len = msg.getLength() - msg.getBufferPosition() - NetworkMessage::CHECKSUM_LENGTH; + if (len > 0) { + checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition() + NetworkMessage::CHECKSUM_LENGTH, len); + } + else { + checksum = 0; + } + + uint32_t recvChecksum = msg.get(); + if (recvChecksum != checksum) { + // it might not have been the checksum, step back + msg.skipBytes(-NetworkMessage::CHECKSUM_LENGTH); + } + if (!receivedFirst) { // First message received receivedFirst = true; if (!protocol) { // Game protocol has already been created at this point - protocol = service_port->make_protocol(msg, shared_from_this()); + protocol = service_port->make_protocol(recvChecksum == checksum, msg, shared_from_this()); if (!protocol) { close(FORCE_CLOSE); return; } - } else { + } + else { msg.skipBytes(1); // Skip protocol ID } protocol->onRecvFirstMessage(msg); - } else { + } + else { protocol->onRecvMessage(msg); // Send the packet to the current protocol } try { readTimer.expires_from_now(boost::posix_time::seconds(CONNECTION_READ_TIMEOUT)); readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr(shared_from_this()), - std::placeholders::_1)); + std::placeholders::_1)); // Wait to the next packet boost::asio::async_read(socket, - boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH), - std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); - } catch (boost::system::system_error& e) { + boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH), + std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); + } + catch (boost::system::system_error& e) { std::cout << "[Network error - Connection::parsePacket] " << e.what() << std::endl; close(FORCE_CLOSE); } } + void Connection::send(const OutputMessage_ptr& msg) { std::lock_guard lockClass(connectionLock); diff --git a/src/const.h b/src/const.h index 8490b1f..f596521 100644 --- a/src/const.h +++ b/src/const.h @@ -335,4 +335,12 @@ static constexpr int32_t CHANNEL_PARTY = 0x01; static constexpr int32_t CHANNEL_RULE_REP = 0x02; static constexpr int32_t CHANNEL_PRIVATE = 0xFFFF; +//Reserved player storage key ranges; +//[10000000 - 20000000]; +static constexpr int32_t PSTRG_RESERVED_RANGE_START = 10000000; +static constexpr int32_t PSTRG_RESERVED_RANGE_SIZE = 10000000; +//[1000 - 1500]; +static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000); +static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500; + #endif diff --git a/src/definitions.h b/src/definitions.h index 4ab6c38..cec245e 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -22,12 +22,15 @@ static constexpr auto STATUS_SERVER_NAME = "Sabrehaven"; static constexpr auto STATUS_SERVER_VERSION = "1.0"; -static constexpr auto STATUS_SERVER_DEVELOPERS = "Sabrehaven Developers Team"; +static constexpr auto STATUS_SERVER_DEVELOPERS = "OTLand community & Sabrehaven Developers Team"; static constexpr auto CLIENT_VERSION_MIN = 772; static constexpr auto CLIENT_VERSION_MAX = 772; static constexpr auto CLIENT_VERSION_STR = "7.72"; +static constexpr auto AUTHENTICATOR_DIGITS = 6U; +static constexpr auto AUTHENTICATOR_PERIOD = 30U; + #ifndef __FUNCTION__ #define __FUNCTION__ __func__ #endif diff --git a/src/networkmessage.h b/src/networkmessage.h index 7d11153..b588f5a 100644 --- a/src/networkmessage.h +++ b/src/networkmessage.h @@ -37,6 +37,7 @@ class NetworkMessage // 2 bytes for encrypted message size static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 4; enum { HEADER_LENGTH = 2 }; + enum { CHECKSUM_LENGTH = 4 }; enum { XTEA_MULTIPLE = 8 }; enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - XTEA_MULTIPLE }; enum { MAX_PROTOCOL_BODY_LENGTH = MAX_BODY_LENGTH - 10 }; diff --git a/src/otserv.cpp b/src/otserv.cpp index c6ee693..e6762a4 100644 --- a/src/otserv.cpp +++ b/src/otserv.cpp @@ -163,9 +163,13 @@ void mainLoader(int, char*[], ServiceManager* services) #endif //set RSA key - const char* p("14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113"); - const char* q("7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101"); - g_RSA.setKey(p, q); + try { + g_RSA.loadPEM("key.pem"); + } + catch (const std::exception& e) { + startupErrorMessage(e.what()); + return; + } std::cout << ">> Establishing database connection..." << std::flush; diff --git a/src/outputmessage.h b/src/outputmessage.h index 01897ff..92c690d 100644 --- a/src/outputmessage.h +++ b/src/outputmessage.h @@ -43,7 +43,11 @@ class OutputMessage : public NetworkMessage add_header(info.length); } - void addCryptoHeader() { + void addCryptoHeader(bool addChecksum) { + if (addChecksum) { + add_header(adlerChecksum(buffer + outputBufferStart, info.length)); + } + writeMessageLength(); } diff --git a/src/party.cpp b/src/party.cpp index 9ac0bed..e0d80a4 100644 --- a/src/party.cpp +++ b/src/party.cpp @@ -473,3 +473,11 @@ void Party::clearPlayerPoints(Player* player) updateSharedExperience(); } } + +bool Party::canOpenCorpse(uint32_t ownerId) const +{ + if (Player* player = g_game.getPlayerByID(ownerId)) { + return leader->getID() == ownerId || player->getParty() == this; + } + return false; +} diff --git a/src/party.h b/src/party.h index 58c72ec..96fce94 100644 --- a/src/party.h +++ b/src/party.h @@ -65,6 +65,7 @@ class Party bool empty() const { return memberList.empty() && inviteList.empty(); } + bool canOpenCorpse(uint32_t ownerId) const; void shareExperience(uint64_t experience, Creature* source/* = nullptr*/); bool setSharedExperience(Player* player, bool sharedExpActive); diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index fb2602b..fad7a98 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -198,13 +198,13 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) uint32_t accountNumber = msg.get(); if (!accountNumber) { - disconnectClient("Invalid account number."); + disconnectClient("Invalid account number.", version); return; } std::string password = msg.getString(); if (password.empty()) { - disconnectClient("Invalid password."); + disconnectClient("Invalid password.", version); return; } @@ -218,5 +218,5 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) std::string authToken = msg.getString(); auto thisPtr = std::static_pointer_cast(shared_from_this()); - g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version))); + g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, authToken, version))); } diff --git a/src/spells.cpp b/src/spells.cpp index a79f0b8..b08a048 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -1801,47 +1801,46 @@ ReturnValue RuneSpell::canExecuteAction(const Player* player, const Position& to return RETURNVALUE_NOERROR; } -bool RuneSpell::executeUse(Player* player, Item* item, const Position&, Thing* target, const Position& toPosition) +bool RuneSpell::executeUse(Player* player, Item* item, const Position&, Thing* target, const Position& toPosition, bool isHotkey) { if (!playerRuneSpellCheck(player, toPosition)) { return false; } - bool result = false; - if (scripted) { - LuaVariant var; - - if (needTarget) { - var.type = VARIANT_NUMBER; - - if (target == nullptr) { - Tile* toTile = g_game.map.getTile(toPosition); - if (toTile) { - const Creature* visibleCreature = toTile->getTopCreature(); - if (visibleCreature) { - var.number = visibleCreature->getID(); - } - } - } else { - var.number = target->getCreature()->getID(); - } - } else { - var.type = VARIANT_POSITION; - var.pos = toPosition; - } - - result = internalCastSpell(player, var); - } else if (runeFunction) { - result = runeFunction(this, player, toPosition); + if (!scripted) { + return false; } - if (!result) { + LuaVariant var; + + if (needTarget) { + var.type = VARIANT_NUMBER; + + if (target == nullptr) { + Tile* toTile = g_game.map.getTile(toPosition); + if (toTile) { + const Creature* visibleCreature = toTile->getBottomVisibleCreature(player); + if (visibleCreature) { + var.number = visibleCreature->getID(); + } + } + } + else { + var.number = target->getCreature()->getID(); + } + } + else { + var.type = VARIANT_POSITION; + var.pos = toPosition; + } + + if (!internalCastSpell(player, var, isHotkey)) { return false; } postCastSpell(player); if (hasCharges && item && g_config.getBoolean(ConfigManager::REMOVE_RUNE_CHARGES)) { - int32_t newCount = std::max(0, item->getCharges() - 1); + int32_t newCount = std::max(0, item->getItemCount() - 1); g_game.transformItem(item, item->getID(), newCount); } return true; @@ -1852,7 +1851,7 @@ bool RuneSpell::castSpell(Creature* creature) LuaVariant var; var.type = VARIANT_NUMBER; var.number = creature->getID(); - return internalCastSpell(creature, var); + return internalCastSpell(creature, var, false); } bool RuneSpell::castSpell(Creature* creature, Creature* target) @@ -1860,23 +1859,24 @@ bool RuneSpell::castSpell(Creature* creature, Creature* target) LuaVariant var; var.type = VARIANT_NUMBER; var.number = target->getID(); - return internalCastSpell(creature, var); + return internalCastSpell(creature, var, false); } -bool RuneSpell::internalCastSpell(Creature* creature, const LuaVariant& var) +bool RuneSpell::internalCastSpell(Creature* creature, const LuaVariant& var, bool isHotkey) { bool result; if (scripted) { - result = executeCastSpell(creature, var); - } else { + result = executeCastSpell(creature, var, isHotkey); + } + else { result = false; } return result; } -bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant& var) +bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant& var, bool isHotkey) { - //onCastSpell(creature, var) + //onCastSpell(creature, var, isHotkey) if (!scriptInterface->reserveScriptEnv()) { std::cout << "[Error - RuneSpell::executeCastSpell] Call stack overflow" << std::endl; return false; @@ -1894,5 +1894,7 @@ bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant& var) LuaScriptInterface::pushVariant(L, var); - return scriptInterface->callFunction(2); + LuaScriptInterface::pushBoolean(L, isHotkey); + + return scriptInterface->callFunction(3); } diff --git a/src/spells.h b/src/spells.h index 24ddab3..d0907fe 100644 --- a/src/spells.h +++ b/src/spells.h @@ -56,7 +56,7 @@ class Spells final : public BaseEvents TalkActionResult_t playerSaySpell(Player* player, std::string& words); static Position getCasterPosition(Creature* creature, Direction dir); - std::string getScriptBaseName() const final; + std::string getScriptBaseName() const override; protected: void clear() final; @@ -94,9 +94,9 @@ class CombatSpell final : public Event, public BaseSpell CombatSpell(const CombatSpell&) = delete; CombatSpell& operator=(const CombatSpell&) = delete; - bool castSpell(Creature* creature) final; - bool castSpell(Creature* creature, Creature* target) final; - bool configureEvent(const pugi::xml_node&) final { + bool castSpell(Creature* creature) override; + bool castSpell(Creature* creature, Creature* target) override; + bool configureEvent(const pugi::xml_node&) override { return true; } @@ -109,7 +109,7 @@ class CombatSpell final : public Event, public BaseSpell } protected: - std::string getScriptEventName() const final { + std::string getScriptEventName() const override { return "onCastSpell"; } @@ -209,7 +209,7 @@ class InstantSpell : public TalkAction, public Spell //scripting bool executeCastSpell(Creature* creature, const LuaVariant& var); - bool isInstant() const final { + bool isInstant() const override { return true; } bool getHasParam() const { @@ -291,13 +291,13 @@ class RuneSpell final : public Action, public Spell return targetCreature; } - bool executeUse(Player* player, Item* item, const Position& fromPosition, Thing* target, const Position& toPosition) final; + bool executeUse(Player* player, Item* item, const Position& fromPosition, Thing* target, const Position& toPosition, bool isHotkey) override; bool castSpell(Creature* creature) final; bool castSpell(Creature* creature, Creature* target) final; //scripting - bool executeCastSpell(Creature* creature, const LuaVariant& var); + bool executeCastSpell(Creature* creature, const LuaVariant& var, bool isHotkey); bool isInstant() const final { return false; @@ -312,7 +312,7 @@ class RuneSpell final : public Action, public Spell static RuneSpellFunction Illusion; static RuneSpellFunction Convince; - bool internalCastSpell(Creature* creature, const LuaVariant& var); + bool internalCastSpell(Creature* creature, const LuaVariant& var, bool isHotkey); RuneSpellFunction* runeFunction = nullptr; uint16_t runeId = 0; From f0e103a47575e1e22d408336b0d632806d8376f0 Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Sat, 2 Nov 2019 18:01:04 +0200 Subject: [PATCH 3/8] definitions has to be 781 lmao --- src/definitions.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/definitions.h b/src/definitions.h index cec245e..63c9e08 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -24,9 +24,9 @@ static constexpr auto STATUS_SERVER_NAME = "Sabrehaven"; static constexpr auto STATUS_SERVER_VERSION = "1.0"; static constexpr auto STATUS_SERVER_DEVELOPERS = "OTLand community & Sabrehaven Developers Team"; -static constexpr auto CLIENT_VERSION_MIN = 772; -static constexpr auto CLIENT_VERSION_MAX = 772; -static constexpr auto CLIENT_VERSION_STR = "7.72"; +static constexpr auto CLIENT_VERSION_MIN = 781; +static constexpr auto CLIENT_VERSION_MAX = 781; +static constexpr auto CLIENT_VERSION_STR = "7.81"; static constexpr auto AUTHENTICATOR_DIGITS = 6U; static constexpr auto AUTHENTICATOR_PERIOD = 30U; From 3d055667592239188b3bcd7c4ce0a8c197549da8 Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Sun, 10 Nov 2019 19:45:03 +0200 Subject: [PATCH 4/8] issiauskinau kad checksum yra nahuj reikalingas ir kad onConnect turejo buti uzkomentuotas. Reikia issiaiskinti kodel luzta onLogin clientas. Kitus failus reikia perziureti ir isvalyti siuksles --- config.lua | 2 +- data/world/mymap-house.xml | 2 + data/world/mymap-spawn.xml | 2 + data/world/mymap.otbm | Bin 0 -> 10923 bytes src/configmanager.cpp | 1 + src/configmanager.h | 1 + src/connection.cpp | 19 +----- src/iologindata.cpp | 9 ++- src/luascript.cpp | 39 ++++++++++- src/luascript.h | 3 + src/monsters.cpp | 4 ++ src/outputmessage.h | 112 ++++++++++++++++---------------- src/protocolgame.cpp | 55 ++++++++-------- src/protocollogin.cpp | 51 +++------------ src/protocollogin.h | 2 +- src/server.cpp | 34 +++++----- src/server.h | 129 ++++++++++++++++++------------------- 17 files changed, 233 insertions(+), 232 deletions(-) create mode 100644 data/world/mymap-house.xml create mode 100644 data/world/mymap-spawn.xml create mode 100644 data/world/mymap.otbm diff --git a/config.lua b/config.lua index e354f60..0383f6a 100644 --- a/config.lua +++ b/config.lua @@ -52,7 +52,7 @@ timeBetweenExActions = 1000 -- Map -- NOTE: set mapName WITHOUT .otbm at the end -mapName = "map" +mapName = "mymap" mapAuthor = "CipSoft" -- MySQL diff --git a/data/world/mymap-house.xml b/data/world/mymap-house.xml new file mode 100644 index 0000000..9d137e4 --- /dev/null +++ b/data/world/mymap-house.xml @@ -0,0 +1,2 @@ + + diff --git a/data/world/mymap-spawn.xml b/data/world/mymap-spawn.xml new file mode 100644 index 0000000..2bde032 --- /dev/null +++ b/data/world/mymap-spawn.xml @@ -0,0 +1,2 @@ + + diff --git a/data/world/mymap.otbm b/data/world/mymap.otbm new file mode 100644 index 0000000000000000000000000000000000000000..b0ed9ec5f5dfe57b6037d2c7e14d434a6aa29908 GIT binary patch literal 10923 zcmX|{$&Td45r(<%tmOd=G<@;NI_MpIv=3g+c1vnN188OhjcnQP+g*Ec-^nD+uKaRz z5S#h^e?%mg2$mpC)3jOB*MDoC<{9h3eP1#3-)nz+{?E$~PrrTm^~a~bzI=T7<>e1w zp8ov&^V6Sx`0(rJU!K1G=3D)D+Fw3DeO%Xmc=_`EFCTvX^~2{+PtX5({_xZDcR#&+ z6K=JS|6B1Nzx@3C+ox~-`|+n(t^D};-(OzdS7u8yHPfB#x77cqzRdQ4<1zKWdp|#= z{=eRjXU;G1YwCahzJBBU&M`rka-MO_SugOF94mOuv0=SU{pa`i9r=6K2XrHRBCZes zF{_n@mDfye?KPKMdM#w=*HW&(Wpe#3m+Nl@>!mzD;6LPY%Ln+Ol)hsoFVQiVz9ald z{8J|5oZu(;34SL28GeSJiGP7#sQ&`L;J?DJ_^<5mO8qzZ4SvIa!+(e0iGPRR$)CUz z{0Th4pJwv9q+Gugp2A-%u7Az3k=N_B3IC~;b)PGl?+ib~&+rTU0>8j7#J^JC6@I1uYb)d4;5YaU|Bd+fO4f0Q-{E)S zC-{?E?lXZW@D!fbGG7W$;jf)sf9<*cgN*Y!a{W7w@V7zg-$r>nzz^^P{DA-1$@-7* z;~?W5;V1H+dYShGKaE^};-B%KiGLoY{sOKY=Ik6rRGGJ0UnjL4Y*c_DX|moj3(3@Gpy_={4OTjDS2u_gYJ_!T|0 z!e15ASJ6Xj>Z{>3@oRc~18?9B{sw;wZ{aO7q9uO^?{c}Hj`}+MJ-nyK_vG*41AHL< zK>h(f!bjqd_(!hK1fSp&e1Z$X1r~xkAOsR2*n^lEv|^?TF|$oEQ-@ecBo;D>MQIi^ z8T~Y)A7}K_jDDIkPjYxpzt8FS1^v8WUKRKY{3Y?rnr8!E!Ykre^!o~4wesH7%%ht8 zHS?~<-w?mS-!w8$!@OzXE%mk3-{S8oo&oCX$lt+x{5|=5cu#!;e1H$+AMlUx5kBG{ z@lWsxKEWsaLLd+Vg%C)DAcmNgW@ajiS!re!XHlAkDaL%%+C=8)jFF zzs29eTV_|sZ0p!xNB)ldJ-nyBp8P$0fDg>pf%pS_gpb4@`8FBh6ZKEG&9#pEEFOZrCF3_Ax<{Q_b5}tC{tr3Q)47&(&S2#D@m>-1yKqm zDU_sGC6C!wA}LqNW2BWzQm&H6G_8>uW-70w$*R7JrMmiLm##Qn# zk1EQjqD*XhQce`c>gwAY{68ox<(jvC`gV;X4;HH}%M!*!;f4%guZBhO&s84NQ6ZsIrLCVmrc zF;*?O1-IZf-zzrX12(hPLGLi<9rP~iZXxfTi{9hhL+9i3;Q{L*M~&93(cv{3vqtaM z;W}Jr2I_DFZqWS={01}7WJa6#O}L5Qf?IG4ZZQ{ZX0#2r;WmDU8R{@&9k>H`nc*&e z7w*D6>i6It+~YT|5BHhTKHSG2zyl^*01w!I2oH%L!b84cB6tLk;1N8A$K;RUG5I&- z-LSr4eT#m}{o1mw@f%HJ4rzmYel&WoP7BwWGdj&#=QpIm95d+N26M*1ZxY{xoA^!o zzeRkD{1)6|PTKfw{5ISszk}a_JIpbM{4U&OO1b!5xCi&h?=fdRxX*a<@%#9F=5&BR zAb-IA1NgNem@1l*B}0Dv7Bi<|=v28w-i0B-SdCL`AWY*h*rnD7KO~ zNE{_`l*CaI7m2${9)r_eCH%n@@+P_9sVLqmdCXQ%Me!NGzLNN>0( zA>*D+S=t4DfnN;PO{u$T@{V5NSNvD_je2gntm_89!S9-kcgKH+-{A@Q6Y5RKpO`XU zO8!)raZ>y#{A{rwo1-IrXICCCmaP9`b9AKc;<7*dSNvD_6@G)?;5SEJ_Z$3f$vk(P zed529Kf#~iPvD8me&DIie&H$n?6EJOW56+FKc1}T;&TkxcgX(mU-4h@Uqki*zrk;T ztm_89!|(8WAoJddpLjA~0#AsagzU$Y`V^j0UyA<{$vUnPc_Uf(Jt9vm&l7YB=P5kp zJmWdcc+N7OlZ@vq=Q+#aIXs6KnyjOM7w`gJ!b@G&RT>;k>8pre;ji#l_-l9#ui>>R z<22OY@E$dWjN9OE@wfO}{4Knrz7F2OJ9y7~*b~3!J?p7|z(2qT`~&`x{3G#4>Kl0v zC*n`=3IBv&2m%O!LI^IQ5K1#E&8##tVZ^L7i_$Dgvnb6%EgA1r#ygksPG!84Iqz)F zJD2lL=e*N}#j`-qDDW5XlK3U@OLz&d;1%&JdQt_iE%~h1_-luActd>+yushVTjICy z7Juu?x;uJaXLC+{9sVAF&;ELN4IKp#yPJb%!7x04q zSinno2`}-N@CshRD|ki!uIZmOyoT5G-v-|Jvd#wHP=8DO7Jo~9E%`fm=gWK0;qUPG z@E(6pfA8S~^JRb!_y_oie}s?ZAIU$#C-{Vaf(wCQ{WOvjLkLO`LTP4h()!gTr-7Im zBVtyXMa5Z^W+9bW&}7V(ESB#@&Uqfo%tgd8me;z3m*~rl%w92jDrQf`dd&>0S+6%T zPeYt0rq`oy(YNSZ);r>Ntaq&U=zHqzSs#c$5P#UnJR|EPaYoiB;!mtk)F}i8As|91 zG~*F7NyNO7S4k|&wJ6uJdB2eIKV`jt)N1r>jh3y^`89^04%hhz)EO>1++a8waD$F- zFceJuCh<+U3Af-D!`p&eaGUrxej9F+-@)&|9k_$vf!g`9^Aw4!+q-W z;XXWo2lxX+KBxgaWcY^g5Pt}d@JC#~2p+*>{4v8nCVmXxz&H3e+@B3UBDU}?`M1R1 z;@9Zq8m(NTlWPnN9j?Q5xXv&z;06Q9pvxOHd6R)h$8@FO9FhxkK27((icn3EAa z!XLq7{4qSnA5-53zJYJ>Z}4yVVA?W}`W{u9Q(U^6HoX(FJ9j?O-{04r5 z?r*?NxCuA$n{W$m!7aE2w;6Ia+=kn52fxD*b>I%%F7c1dkZvF+7IH)F1OBZUf)IH~2Tyx266qe9QgW z@)4skAv6Yr#^BQVQKz%6vu?0%ux>DFOa`FIy2-jle2aDKF@I*;X5D7pCcZ-)hr#Zm zbJ2O6dz|~|ea-`Tz$^@@HzZyJkI=`QZy2Im&Uf_99es00-`vqR_w?;O|M6oF-!oTs zyq7!P%N_6Cj`wcQd%1`2;d}Uw@2VZo<&OKh 0) { - checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition() + NetworkMessage::CHECKSUM_LENGTH, len); - } - else { - checksum = 0; - } - - uint32_t recvChecksum = msg.get(); - if (recvChecksum != checksum) { - // it might not have been the checksum, step back - msg.skipBytes(-NetworkMessage::CHECKSUM_LENGTH); - } - if (!receivedFirst) { // First message received receivedFirst = true; if (!protocol) { // Game protocol has already been created at this point - protocol = service_port->make_protocol(recvChecksum == checksum, msg, shared_from_this()); + protocol = service_port->make_protocol(msg, shared_from_this()); if (!protocol) { close(FORCE_CLOSE); return; @@ -242,6 +226,7 @@ void Connection::parsePacket(const boost::system::error_code& error) } + void Connection::send(const OutputMessage_ptr& msg) { std::lock_guard lockClass(connectionLock); diff --git a/src/iologindata.cpp b/src/iologindata.cpp index 4fb9fbc..3a52c64 100644 --- a/src/iologindata.cpp +++ b/src/iologindata.cpp @@ -188,7 +188,7 @@ bool IOLoginData::preloadPlayer(Player* player, const std::string& name) bool IOLoginData::loadPlayerById(Player* player, uint32_t id) { std::ostringstream query; - query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `id` = " << id; + query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `id` = " << id; return loadPlayer(player, Database::getInstance()->storeQuery(query.str())); } @@ -196,7 +196,7 @@ bool IOLoginData::loadPlayerByName(Player* player, const std::string& name) { Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `name` = " << db->escapeString(name); + query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `name` = " << db->escapeString(name); return loadPlayer(player, db->storeQuery(query.str())); } @@ -296,6 +296,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) player->defaultOutfit.lookBody = result->getNumber("lookbody"); player->defaultOutfit.lookLegs = result->getNumber("looklegs"); player->defaultOutfit.lookFeet = result->getNumber("lookfeet"); + player->defaultOutfit.lookAddons = result->getNumber("lookaddons"); player->currentOutfit = player->defaultOutfit; if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) { @@ -331,6 +332,8 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) player->loginPosition = player->getTemplePosition(); } + player->staminaMinutes = result->getNumber("stamina"); + static const std::string skillNames[] = {"skill_fist", "skill_club", "skill_sword", "skill_axe", "skill_dist", "skill_shielding", "skill_fishing"}; static const std::string skillNameTries[] = {"skill_fist_tries", "skill_club_tries", "skill_sword_tries", "skill_axe_tries", "skill_dist_tries", "skill_shielding_tries", "skill_fishing_tries"}; static constexpr size_t size = sizeof(skillNames) / sizeof(std::string); @@ -616,6 +619,7 @@ bool IOLoginData::savePlayer(Player* player) query << "`lookhead` = " << static_cast(player->defaultOutfit.lookHead) << ','; query << "`looklegs` = " << static_cast(player->defaultOutfit.lookLegs) << ','; query << "`looktype` = " << player->defaultOutfit.lookType << ','; + query << "`lookaddons` = " << static_cast(player->defaultOutfit.lookAddons) << ','; query << "`maglevel` = " << player->magLevel << ','; query << "`mana` = " << player->mana << ','; query << "`manamax` = " << player->manaMax << ','; @@ -654,6 +658,7 @@ bool IOLoginData::savePlayer(Player* player) query << "`lastlogout` = " << player->getLastLogout() << ','; query << "`balance` = " << player->bankBalance << ','; + query << "`stamina` = " << player->getStaminaMinutes() << ','; query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ','; query << "`skill_fist_tries` = " << player->skills[SKILL_FIST].tries << ','; diff --git a/src/luascript.cpp b/src/luascript.cpp index 8099d24..3401679 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -730,6 +730,8 @@ Position LuaScriptInterface::getPosition(lua_State* L, int32_t arg) Outfit_t LuaScriptInterface::getOutfit(lua_State* L, int32_t arg) { Outfit_t outfit; + outfit.lookAddons = getField(L, arg, "lookAddons"); + outfit.lookFeet = getField(L, arg, "lookFeet"); outfit.lookLegs = getField(L, arg, "lookLegs"); outfit.lookBody = getField(L, arg, "lookBody"); @@ -882,6 +884,7 @@ void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit) setField(L, "lookBody", outfit.lookBody); setField(L, "lookLegs", outfit.lookLegs); setField(L, "lookFeet", outfit.lookFeet); + setField(L, "lookAddons", outfit.lookAddons); } #define registerEnum(value) { std::string enumName = #value; registerGlobalVariable(enumName.substr(enumName.find_last_of(':') + 1), value); } @@ -1607,6 +1610,7 @@ void LuaScriptInterface::registerFunctions() registerEnumIn("configKeys", ConfigManager::ALLOW_CLONES) registerEnumIn("configKeys", ConfigManager::BIND_ONLY_GLOBAL_ADDRESS) registerEnumIn("configKeys", ConfigManager::OPTIMIZE_DATABASE) + registerEnumIn("configKeys", ConfigManager::STAMINA_SYSTEM) registerEnumIn("configKeys", ConfigManager::WARN_UNSAFE_SCRIPTS) registerEnumIn("configKeys", ConfigManager::CONVERT_UNSAFE_SCRIPTS) registerEnumIn("configKeys", ConfigManager::TELEPORT_NEWBIES) @@ -2008,6 +2012,9 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "getGroup", LuaScriptInterface::luaPlayerGetGroup); registerMethod("Player", "setGroup", LuaScriptInterface::luaPlayerSetGroup); + registerMethod("Player", "getStamina", LuaScriptInterface::luaPlayerGetStamina); + registerMethod("Player", "setStamina", LuaScriptInterface::luaPlayerSetStamina); + registerMethod("Player", "getSoul", LuaScriptInterface::luaPlayerGetSoul); registerMethod("Player", "addSoul", LuaScriptInterface::luaPlayerAddSoul); registerMethod("Player", "getMaxSoul", LuaScriptInterface::luaPlayerGetMaxSoul); @@ -7757,6 +7764,35 @@ int LuaScriptInterface::luaPlayerSetGroup(lua_State* L) return 1; } +int LuaScriptInterface::luaPlayerGetStamina(lua_State* L) +{ + // player:getStamina() + Player* player = getUserdata(L, 1); + if (player) { + lua_pushnumber(L, player->getStaminaMinutes()); + } + else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaPlayerSetStamina(lua_State* L) +{ + // player:setStamina(stamina) + uint16_t stamina = getNumber(L, 2); + Player* player = getUserdata(L, 1); + if (player) { + player->staminaMinutes = std::min(2520, stamina); + player->sendStats(); + pushBoolean(L, true); + } + else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaPlayerGetSoul(lua_State* L) { // player:getSoul() @@ -10616,11 +10652,12 @@ int LuaScriptInterface::luaConditionSetSpeedDelta(lua_State* L) int LuaScriptInterface::luaConditionSetOutfit(lua_State* L) { // condition:setOutfit(outfit) - // condition:setOutfit(lookTypeEx, lookType, lookHead, lookBody, lookLegs, lookFeet) + // condition:setOutfit(lookTypeEx, lookType, lookHead, lookBody, lookLegs, lookFeet[, lookAddons]) Outfit_t outfit; if (isTable(L, 2)) { outfit = getOutfit(L, 2); } else { + outfit.lookAddons = getNumber(L, 8, outfit.lookAddons); outfit.lookFeet = getNumber(L, 7); outfit.lookLegs = getNumber(L, 6); outfit.lookBody = getNumber(L, 5); diff --git a/src/luascript.h b/src/luascript.h index 8b90a48..90c2dd0 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -849,6 +849,9 @@ class LuaScriptInterface static int luaPlayerGetGroup(lua_State* L); static int luaPlayerSetGroup(lua_State* L); + static int luaPlayerGetStamina(lua_State* L); + static int luaPlayerSetStamina(lua_State* L); + static int luaPlayerGetSoul(lua_State* L); static int luaPlayerAddSoul(lua_State* L); static int luaPlayerGetMaxSoul(lua_State* L); diff --git a/src/monsters.cpp b/src/monsters.cpp index bf46bd1..9b5c467 100644 --- a/src/monsters.cpp +++ b/src/monsters.cpp @@ -759,6 +759,10 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa if ((attr = node.attribute("feet"))) { mType->info.outfit.lookFeet = pugi::cast(attr.value()); } + + if ((attr = node.attribute("addons"))) { + mType->info.outfit.lookAddons = pugi::cast(attr.value()); + } } else if ((attr = node.attribute("typeex"))) { mType->info.outfit.lookTypeEx = pugi::cast(attr.value()); } else { diff --git a/src/outputmessage.h b/src/outputmessage.h index 92c690d..09f5435 100644 --- a/src/outputmessage.h +++ b/src/outputmessage.h @@ -28,80 +28,76 @@ class Protocol; class OutputMessage : public NetworkMessage { - public: - OutputMessage() = default; +public: + OutputMessage() = default; - // non-copyable - OutputMessage(const OutputMessage&) = delete; - OutputMessage& operator=(const OutputMessage&) = delete; + // non-copyable + OutputMessage(const OutputMessage&) = delete; + OutputMessage& operator=(const OutputMessage&) = delete; - uint8_t* getOutputBuffer() { - return buffer + outputBufferStart; - } + uint8_t* getOutputBuffer() { + return buffer + outputBufferStart; + } - void writeMessageLength() { - add_header(info.length); - } + void writeMessageLength() { + add_header(info.length); + } - void addCryptoHeader(bool addChecksum) { - if (addChecksum) { - add_header(adlerChecksum(buffer + outputBufferStart, info.length)); - } + void addCryptoHeader(bool addChecksum) { + writeMessageLength(); + } - writeMessageLength(); - } + inline void append(const NetworkMessage& msg) { + auto msgLen = msg.getLength(); + memcpy(buffer + info.position, msg.getBuffer() + 4, msgLen); + info.length += msgLen; + info.position += msgLen; + } - inline void append(const NetworkMessage& msg) { - auto msgLen = msg.getLength(); - memcpy(buffer + info.position, msg.getBuffer() + 4, msgLen); - info.length += msgLen; - info.position += msgLen; - } + inline void append(const OutputMessage_ptr& msg) { + auto msgLen = msg->getLength(); + memcpy(buffer + info.position, msg->getBuffer() + 4, msgLen); + info.length += msgLen; + info.position += msgLen; + } - inline void append(const OutputMessage_ptr& msg) { - auto msgLen = msg->getLength(); - memcpy(buffer + info.position, msg->getBuffer() + 4, msgLen); - info.length += msgLen; - info.position += msgLen; - } +protected: + template + inline void add_header(T add) { + assert(outputBufferStart >= sizeof(T)); + outputBufferStart -= sizeof(T); + memcpy(buffer + outputBufferStart, &add, sizeof(T)); + //added header size to the message size + info.length += sizeof(T); + } - protected: - template - inline void add_header(T add) { - assert(outputBufferStart >= sizeof(T)); - outputBufferStart -= sizeof(T); - memcpy(buffer + outputBufferStart, &add, sizeof(T)); - //added header size to the message size - info.length += sizeof(T); - } - - MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION; + MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION; }; class OutputMessagePool { - public: - // non-copyable - OutputMessagePool(const OutputMessagePool&) = delete; - OutputMessagePool& operator=(const OutputMessagePool&) = delete; +public: + // non-copyable + OutputMessagePool(const OutputMessagePool&) = delete; + OutputMessagePool& operator=(const OutputMessagePool&) = delete; - static OutputMessagePool& getInstance() { - static OutputMessagePool instance; - return instance; - } + static OutputMessagePool& getInstance() { + static OutputMessagePool instance; + return instance; + } - void sendAll(); - void scheduleSendAll(); + void sendAll(); + void scheduleSendAll(); - static OutputMessage_ptr getOutputMessage(); + static OutputMessage_ptr getOutputMessage(); - void addProtocolToAutosend(Protocol_ptr protocol); - void removeProtocolFromAutosend(const Protocol_ptr& protocol); - private: - OutputMessagePool() = default; - //NOTE: A vector is used here because this container is mostly read - //and relatively rarely modified (only when a client connects/disconnects) - std::vector bufferedProtocols; + void addProtocolToAutosend(Protocol_ptr protocol); + void removeProtocolFromAutosend(const Protocol_ptr& protocol); +private: + OutputMessagePool() = default; + //NOTE: A vector is used here because this container is mostly read + //and relatively rarely modified (only when a client connects/disconnects) + std::vector bufferedProtocols; }; diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index f2536f0..3714984 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -242,8 +242,6 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) OperatingSystem_t operatingSystem = static_cast(msg.get()); version = msg.get(); - msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision - if (!Protocol::RSA_decrypt(msg)) { disconnect(); return; @@ -304,45 +302,24 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) disconnectClient("Account number or password is not correct."); return; } - + Account account; if (!IOLoginData::loginserverAuthentication(accountNumber, password, account)) { disconnectClient("Account number or password is not correct."); return; } - + //Update premium days Game::updatePremium(account); g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem))); + } void ProtocolGame::onConnect() { - auto output = OutputMessagePool::getOutputMessage(); - static std::random_device rd; - static std::ranlux24 generator(rd()); - static std::uniform_int_distribution randNumber(0x00, 0xFF); - // Skip checksum - output->skipBytes(sizeof(uint32_t)); - // Packet length & type - output->add(0x0006); - output->addByte(0x1F); - - // Add timestamp & random number - challengeTimestamp = static_cast(time(nullptr)); - output->add(challengeTimestamp); - - challengeRandom = randNumber(generator); - output->addByte(challengeRandom); - - // Go back and write checksum - output->skipBytes(-12); - output->add(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8)); - - send(output); } void ProtocolGame::disconnectClient(const std::string& message) const @@ -464,12 +441,15 @@ void ProtocolGame::parsePacket(NetworkMessage& msg) void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg) { + msg.add(0x00); //environmental effects + int32_t count; Item* ground = tile->getGround(); if (ground) { msg.addItem(ground); count = 1; - } else { + } + else { count = 0; } @@ -478,7 +458,11 @@ void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg) for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) { msg.addItem(*it); - if (++count == 10) { + count++; + if (count == 9 && tile->getPosition() == player->getPosition()) { + break; + } + else if (count == 10) { return; } } @@ -486,11 +470,20 @@ void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg) const CreatureVector* creatures = tile->getCreatures(); if (creatures) { + bool playerAdded = false; for (const Creature* creature : boost::adaptors::reverse(*creatures)) { if (!player->canSeeCreature(creature)) { continue; } + if (tile->getPosition() == player->getPosition() && count == 9 && !playerAdded) { + creature = player; + } + + if (creature->getID() == player->getID()) { + playerAdded = true; + } + bool known; uint32_t removedKnown; checkCreatureAsKnown(creature->getID(), known, removedKnown); @@ -1864,12 +1857,18 @@ void ProtocolGame::AddPlayerStats(NetworkMessage& msg) msg.add(std::min(player->getMaxMana(), std::numeric_limits::max())); msg.addByte(std::min(player->getMagicLevel(), std::numeric_limits::max())); + msg.addByte(std::min(player->getBaseMagicLevel(), std::numeric_limits::max())); msg.addByte(player->getMagicLevelPercent()); msg.addByte(player->getSoul()); msg.add(player->getStaminaMinutes()); + msg.add(player->getBaseSpeed() / 2); + + Condition* condition = player->getCondition(CONDITION_REGENERATION); + msg.add(condition ? condition->getTicks() / 1000 : 0x00); + msg.add(0); // xp boost time (seconds) } diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index fad7a98..f32c17e 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -43,7 +43,7 @@ void ProtocolLogin::disconnectClient(const std::string& message, uint16_t versio disconnect(); } -void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& password, const std::string& token, uint16_t version) +void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& password, uint16_t version) { Account account; if (!IOLoginData::loginserverAuthentication(accountNumber, password, account)) { @@ -51,20 +51,7 @@ void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& return; } - uint32_t ticks = time(nullptr) / AUTHENTICATOR_PERIOD; - auto output = OutputMessagePool::getOutputMessage(); - if (!std::to_string(accountNumber).empty()) { - if (token.empty() || !(token == generateToken(std::to_string(accountNumber), ticks) || token == generateToken(std::to_string(accountNumber), ticks - 1) || token == generateToken(std::to_string(accountNumber), ticks + 1))) { - output->addByte(0x0D); - output->addByte(0); - send(output); - disconnect(); - return; - } - output->addByte(0x0C); - output->addByte(0); - } //Update premium days Game::updatePremium(account); @@ -79,38 +66,24 @@ void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& output->addString(ss.str()); } - //Add session key - output->addByte(0x28); - output->addString(std::to_string(accountNumber) + "\n" + password + "\n" + token + "\n" + std::to_string(ticks)); - - //Add char list output->addByte(0x64); - output->addByte(1); // number of worlds - - output->addByte(0); // world id - output->addString(g_config.getString(ConfigManager::SERVER_NAME)); - output->addString(g_config.getString(ConfigManager::IP)); - output->add(g_config.getNumber(ConfigManager::GAME_PORT)); - output->addByte(0); - uint8_t size = std::min(std::numeric_limits::max(), account.characters.size()); output->addByte(size); for (uint8_t i = 0; i < size; i++) { - output->addByte(0); output->addString(account.characters[i]); + output->addString(g_config.getString(ConfigManager::SERVER_NAME)); + output->add(inet_addr(g_config.getString(ConfigManager::IP).c_str())); + output->add(g_config.getNumber(ConfigManager::GAME_PORT)); } //Add premium days - output->addByte(0); if (g_config.getBoolean(ConfigManager::FREE_PREMIUM)) { - output->addByte(1); - output->add(0); + output->add(0xFFFF); } else { - output->addByte(account.premiumDays > 0 ? 1 : 0); - output->add(time(nullptr) + (account.premiumDays * 86400)); + output->add(account.premiumDays); } send(output); @@ -118,6 +91,7 @@ void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& disconnect(); } + void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) { if (g_game.getGameState() == GAME_STATE_SHUTDOWN) { @@ -208,15 +182,6 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) return; } - // read authenticator token and stay logged in flag from last 128 bytes - msg.skipBytes((msg.getLength() - 128) - msg.getBufferPosition()); - if (!Protocol::RSA_decrypt(msg)) { - disconnectClient("Invalid authentification token.", version); - return; - } - - std::string authToken = msg.getString(); - auto thisPtr = std::static_pointer_cast(shared_from_this()); - g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, authToken, version))); + g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version))); } diff --git a/src/protocollogin.h b/src/protocollogin.h index 4651b50..3b75851 100644 --- a/src/protocollogin.h +++ b/src/protocollogin.h @@ -43,7 +43,7 @@ class ProtocolLogin : public Protocol private: void disconnectClient(const std::string& message, uint16_t version); - void getCharacterList(uint32_t accountNumber, const std::string& password, const std::string& token, uint16_t version); + void getCharacterList(uint32_t accountNumber, const std::string& password, uint16_t version); }; #endif diff --git a/src/server.cpp b/src/server.cpp index 1b09123..d681456 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -56,7 +56,8 @@ void ServiceManager::stop() for (auto& servicePortIt : acceptors) { try { io_service.post(std::bind(&ServicePort::onStopServer, servicePortIt.second)); - } catch (boost::system::system_error& e) { + } + catch (boost::system::system_error& e) { std::cout << "[ServiceManager::stop] Network Error: " << e.what() << std::endl; } } @@ -114,25 +115,28 @@ void ServicePort::onAccept(Connection_ptr connection, const boost::system::error Service_ptr service = services.front(); if (service->is_single_socket()) { connection->accept(service->make_protocol(connection)); - } else { + } + else { connection->accept(); } - } else { + } + else { connection->close(Connection::FORCE_CLOSE); } accept(); - } else if (error != boost::asio::error::operation_aborted) { + } + else if (error != boost::asio::error::operation_aborted) { if (!pendingStart) { close(); pendingStart = true; g_scheduler.addEvent(createSchedulerTask(15000, - std::bind(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort))); + std::bind(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort))); } } } -Protocol_ptr ServicePort::make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const +Protocol_ptr ServicePort::make_protocol(NetworkMessage& msg, const Connection_ptr& connection) const { uint8_t protocolID = msg.getByte(); for (auto& service : services) { @@ -140,9 +144,7 @@ Protocol_ptr ServicePort::make_protocol(bool checksummed, NetworkMessage& msg, c continue; } - if ((checksummed && service->is_checksummed()) || !service->is_checksummed()) { - return service->make_protocol(connection); - } + return service->make_protocol(connection); } return nullptr; } @@ -169,21 +171,23 @@ void ServicePort::open(uint16_t port) try { if (g_config.getBoolean(ConfigManager::BIND_ONLY_GLOBAL_ADDRESS)) { acceptor.reset(new boost::asio::ip::tcp::acceptor(io_service, boost::asio::ip::tcp::endpoint( - boost::asio::ip::address(boost::asio::ip::address_v4::from_string(g_config.getString(ConfigManager::IP))), serverPort))); - } else { + boost::asio::ip::address(boost::asio::ip::address_v4::from_string(g_config.getString(ConfigManager::IP))), serverPort))); + } + else { acceptor.reset(new boost::asio::ip::tcp::acceptor(io_service, boost::asio::ip::tcp::endpoint( - boost::asio::ip::address(boost::asio::ip::address_v4(INADDR_ANY)), serverPort))); + boost::asio::ip::address(boost::asio::ip::address_v4(INADDR_ANY)), serverPort))); } acceptor->set_option(boost::asio::ip::tcp::no_delay(true)); accept(); - } catch (boost::system::system_error& e) { + } + catch (boost::system::system_error& e) { std::cout << "[ServicePort::open] Error: " << e.what() << std::endl; pendingStart = true; g_scheduler.addEvent(createSchedulerTask(15000, - std::bind(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port))); + std::bind(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port))); } } @@ -197,7 +201,7 @@ void ServicePort::close() bool ServicePort::add_service(const Service_ptr& new_svc) { - if (std::any_of(services.begin(), services.end(), [](const Service_ptr& svc) {return svc->is_single_socket();})) { + if (std::any_of(services.begin(), services.end(), [](const Service_ptr& svc) {return svc->is_single_socket(); })) { return false; } diff --git a/src/server.h b/src/server.h index 02e5278..e1d9aa2 100644 --- a/src/server.h +++ b/src/server.h @@ -27,98 +27,94 @@ class Protocol; class ServiceBase { - public: - virtual bool is_single_socket() const = 0; - virtual bool is_checksummed() const = 0; - virtual uint8_t get_protocol_identifier() const = 0; - virtual const char* get_protocol_name() const = 0; +public: + virtual bool is_single_socket() const = 0; + virtual uint8_t get_protocol_identifier() const = 0; + virtual const char* get_protocol_name() const = 0; - virtual Protocol_ptr make_protocol(const Connection_ptr& c) const = 0; + virtual Protocol_ptr make_protocol(const Connection_ptr& c) const = 0; }; template class Service final : public ServiceBase { - public: - bool is_single_socket() const override { - return ProtocolType::server_sends_first; - } - bool is_checksummed() const override { - return ProtocolType::use_checksum; - } - uint8_t get_protocol_identifier() const override { - return ProtocolType::protocol_identifier; - } - const char* get_protocol_name() const override { - return ProtocolType::protocol_name(); - } +public: + bool is_single_socket() const final { + return ProtocolType::server_sends_first; + } + uint8_t get_protocol_identifier() const final { + return ProtocolType::protocol_identifier; + } + const char* get_protocol_name() const final { + return ProtocolType::protocol_name(); + } - Protocol_ptr make_protocol(const Connection_ptr& c) const override { - return std::make_shared(c); - } + Protocol_ptr make_protocol(const Connection_ptr& c) const final { + return std::make_shared(c); + } }; class ServicePort : public std::enable_shared_from_this { - public: - explicit ServicePort(boost::asio::io_service& io_service) : io_service(io_service) {} - ~ServicePort(); +public: + explicit ServicePort(boost::asio::io_service& io_service) : io_service(io_service) {} + ~ServicePort(); - // non-copyable - ServicePort(const ServicePort&) = delete; - ServicePort& operator=(const ServicePort&) = delete; + // non-copyable + ServicePort(const ServicePort&) = delete; + ServicePort& operator=(const ServicePort&) = delete; - static void openAcceptor(std::weak_ptr weak_service, uint16_t port); - void open(uint16_t port); - void close(); - bool is_single_socket() const; - std::string get_protocol_names() const; + static void openAcceptor(std::weak_ptr weak_service, uint16_t port); + void open(uint16_t port); + void close(); + bool is_single_socket() const; + std::string get_protocol_names() const; - bool add_service(const Service_ptr& new_svc); - Protocol_ptr make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const; + bool add_service(const Service_ptr& new_svc); + Protocol_ptr make_protocol(NetworkMessage& msg, const Connection_ptr& connection) const; - void onStopServer(); - void onAccept(Connection_ptr connection, const boost::system::error_code& error); + void onStopServer(); + void onAccept(Connection_ptr connection, const boost::system::error_code& error); - protected: - void accept(); +protected: + void accept(); - boost::asio::io_service& io_service; - std::unique_ptr acceptor; - std::vector services; + boost::asio::io_service& io_service; + std::unique_ptr acceptor; + std::vector services; - uint16_t serverPort = 0; - bool pendingStart = false; + uint16_t serverPort = 0; + bool pendingStart = false; }; class ServiceManager { - public: - ServiceManager() = default; - ~ServiceManager(); +public: + ServiceManager() = default; + ~ServiceManager(); - // non-copyable - ServiceManager(const ServiceManager&) = delete; - ServiceManager& operator=(const ServiceManager&) = delete; + // non-copyable + ServiceManager(const ServiceManager&) = delete; + ServiceManager& operator=(const ServiceManager&) = delete; - void run(); - void stop(); + void run(); + void stop(); - template - bool add(uint16_t port); + template + bool add(uint16_t port); - bool is_running() const { - return acceptors.empty() == false; - } + bool is_running() const { + return acceptors.empty() == false; + } - protected: - void die(); +protected: + void die(); - std::unordered_map acceptors; + std::unordered_map acceptors; - boost::asio::io_service io_service; - boost::asio::deadline_timer death_timer { io_service }; - bool running = false; + boost::asio::io_service io_service; + boost::asio::deadline_timer death_timer{ io_service }; + bool running = false; }; template @@ -137,13 +133,14 @@ bool ServiceManager::add(uint16_t port) service_port = std::make_shared(io_service); service_port->open(port); acceptors[port] = service_port; - } else { + } + else { service_port = foundServicePort->second; if (service_port->is_single_socket() || ProtocolType::server_sends_first) { std::cout << "ERROR: " << ProtocolType::protocol_name() << - " and " << service_port->get_protocol_names() << - " cannot use the same port " << port << '.' << std::endl; + " and " << service_port->get_protocol_names() << + " cannot use the same port " << port << '.' << std::endl; return false; } } From ba1409dbe669c9bf891e0444540d4de1af33069f Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Sun, 10 Nov 2019 23:18:18 +0200 Subject: [PATCH 5/8] geri pataisymai. Ieskoti bugu reikia per otlcient --- src/networkmessage.h | 1 - src/outputmessage.h | 2 +- src/protocol.cpp | 2 +- src/protocol.h | 4 ---- src/protocolgame.cpp | 40 ++++++++-------------------------------- src/protocolgame.h | 1 - src/protocollogin.h | 1 - src/protocolstatus.h | 1 - src/tools.cpp | 26 -------------------------- src/tools.h | 2 -- 10 files changed, 10 insertions(+), 70 deletions(-) diff --git a/src/networkmessage.h b/src/networkmessage.h index b588f5a..7d11153 100644 --- a/src/networkmessage.h +++ b/src/networkmessage.h @@ -37,7 +37,6 @@ class NetworkMessage // 2 bytes for encrypted message size static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 4; enum { HEADER_LENGTH = 2 }; - enum { CHECKSUM_LENGTH = 4 }; enum { XTEA_MULTIPLE = 8 }; enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - XTEA_MULTIPLE }; enum { MAX_PROTOCOL_BODY_LENGTH = MAX_BODY_LENGTH - 10 }; diff --git a/src/outputmessage.h b/src/outputmessage.h index 09f5435..3dde508 100644 --- a/src/outputmessage.h +++ b/src/outputmessage.h @@ -43,7 +43,7 @@ public: add_header(info.length); } - void addCryptoHeader(bool addChecksum) { + void addCryptoHeader() { writeMessageLength(); } diff --git a/src/protocol.cpp b/src/protocol.cpp index 00c7ebe..e443ef0 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -33,7 +33,7 @@ void Protocol::onSendMessage(const OutputMessage_ptr& msg) const if (encryptionEnabled) { XTEA_encrypt(*msg); - msg->addCryptoHeader(checksumEnabled); + msg->addCryptoHeader(); } } } diff --git a/src/protocol.h b/src/protocol.h index 76bf5d1..89039c9 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -75,9 +75,6 @@ protected: void setXTEAKey(xtea::key key) { this->key = std::move(key); } - void disableChecksum() { - checksumEnabled = false; - } static bool RSA_decrypt(NetworkMessage& msg); @@ -98,7 +95,6 @@ private: const ConnectionWeak_ptr connection; xtea::key key; bool encryptionEnabled = false; - bool checksumEnabled = true; bool rawMessages = false; }; diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index 3714984..b690a9c 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -441,8 +441,6 @@ void ProtocolGame::parsePacket(NetworkMessage& msg) void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg) { - msg.add(0x00); //environmental effects - int32_t count; Item* ground = tile->getGround(); if (ground) { @@ -1509,7 +1507,8 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos msg.addByte(0x0A); msg.add(player->getID()); - msg.add(0x32); // beat duration (50) + msg.addByte(0x32); // beat duration (50) + msg.addByte(0x00); // can report bugs? if (player->getAccountType() >= ACCOUNT_TYPE_TUTOR) { @@ -1533,16 +1532,9 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos sendMagicEffect(pos, CONST_ME_TELEPORT); } - sendInventoryItem(CONST_SLOT_HEAD, player->getInventoryItem(CONST_SLOT_HEAD)); - sendInventoryItem(CONST_SLOT_NECKLACE, player->getInventoryItem(CONST_SLOT_NECKLACE)); - sendInventoryItem(CONST_SLOT_BACKPACK, player->getInventoryItem(CONST_SLOT_BACKPACK)); - sendInventoryItem(CONST_SLOT_ARMOR, player->getInventoryItem(CONST_SLOT_ARMOR)); - sendInventoryItem(CONST_SLOT_RIGHT, player->getInventoryItem(CONST_SLOT_RIGHT)); - sendInventoryItem(CONST_SLOT_LEFT, player->getInventoryItem(CONST_SLOT_LEFT)); - sendInventoryItem(CONST_SLOT_LEGS, player->getInventoryItem(CONST_SLOT_LEGS)); - sendInventoryItem(CONST_SLOT_FEET, player->getInventoryItem(CONST_SLOT_FEET)); - sendInventoryItem(CONST_SLOT_RING, player->getInventoryItem(CONST_SLOT_RING)); - sendInventoryItem(CONST_SLOT_AMMO, player->getInventoryItem(CONST_SLOT_AMMO)); + for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { + sendInventoryItem(static_cast(i), player->getInventoryItem(static_cast(i))); + } sendStats(); sendSkills(); @@ -1837,39 +1829,23 @@ void ProtocolGame::AddPlayerStats(NetworkMessage& msg) msg.add(std::min(player->getHealth(), std::numeric_limits::max())); msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); - msg.add(player->getFreeCapacity()); - msg.add(player->getCapacity()); + + msg.add(player->getFreeCapacity() / 100); - msg.add(player->getExperience()); + msg.add(std::min(player->getExperience(), 0x7FFFFFFF)); msg.add(player->getLevel()); - msg.addByte(player->getLevelPercent()); - msg.add(100); // base xp gain rate - msg.add(0); // xp voucher - msg.add(0); // low level bonus - msg.add(0); // xp boost - msg.add(100); // stamina multiplier (100 = x1.0) - - msg.add(std::min(player->getMana(), std::numeric_limits::max())); msg.add(std::min(player->getMaxMana(), std::numeric_limits::max())); msg.addByte(std::min(player->getMagicLevel(), std::numeric_limits::max())); - msg.addByte(std::min(player->getBaseMagicLevel(), std::numeric_limits::max())); msg.addByte(player->getMagicLevelPercent()); msg.addByte(player->getSoul()); msg.add(player->getStaminaMinutes()); - - msg.add(player->getBaseSpeed() / 2); - - Condition* condition = player->getCondition(CONDITION_REGENERATION); - msg.add(condition ? condition->getTicks() / 1000 : 0x00); - - msg.add(0); // xp boost time (seconds) } void ProtocolGame::AddPlayerSkills(NetworkMessage& msg) diff --git a/src/protocolgame.h b/src/protocolgame.h index 42c919d..aca3e38 100644 --- a/src/protocolgame.h +++ b/src/protocolgame.h @@ -52,7 +52,6 @@ class ProtocolGame final : public Protocol // static protocol information enum { server_sends_first = true }; enum { protocol_identifier = 0 }; // Not required as we send first - enum { use_checksum = true }; static const char* protocol_name() { return "gameworld protocol"; diff --git a/src/protocollogin.h b/src/protocollogin.h index 3b75851..a45898f 100644 --- a/src/protocollogin.h +++ b/src/protocollogin.h @@ -31,7 +31,6 @@ class ProtocolLogin : public Protocol // static protocol information enum {server_sends_first = false}; enum {protocol_identifier = 0x01}; - enum {use_checksum = true}; static const char* protocol_name() { return "login protocol"; } diff --git a/src/protocolstatus.h b/src/protocolstatus.h index c9fef5a..2e6cae2 100644 --- a/src/protocolstatus.h +++ b/src/protocolstatus.h @@ -29,7 +29,6 @@ class ProtocolStatus final : public Protocol // static protocol information enum {server_sends_first = false}; enum {protocol_identifier = 0xFF}; - enum {use_checksum = false}; static const char* protocol_name() { return "status protocol"; } diff --git a/src/tools.cpp b/src/tools.cpp index 05be137..c6f68b7 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -835,32 +835,6 @@ std::string getSkillName(uint8_t skillid) } } -uint32_t adlerChecksum(const uint8_t* data, size_t length) -{ - if (length > NETWORKMESSAGE_MAXSIZE) { - return 0; - } - - const uint16_t adler = 65521; - - uint32_t a = 1, b = 0; - - while (length > 0) { - size_t tmp = length > 5552 ? 5552 : length; - length -= tmp; - - do { - a += *data++; - b += a; - } while (--tmp); - - a %= adler; - b %= adler; - } - - return (b << 16) | a; -} - std::string ucfirst(std::string str) { diff --git a/src/tools.h b/src/tools.h index b615af3..8f05a93 100644 --- a/src/tools.h +++ b/src/tools.h @@ -84,8 +84,6 @@ std::string getCombatName(CombatType_t combatType); std::string getSkillName(uint8_t skillid); -uint32_t adlerChecksum(const uint8_t* data, size_t length); - std::string ucfirst(std::string str); std::string ucwords(std::string str); bool booleanString(const std::string& str); From 5535e5056292acfafaf4de454d25e34e54c3d3ba Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Mon, 11 Nov 2019 19:17:21 +0200 Subject: [PATCH 6/8] fix walking issue in 781 protocol --- src/protocol.cpp | 65 ++++++++++++++++++++++++++++++++++++------- src/protocol.h | 26 ++++++++--------- src/protocolgame.cpp | 4 +-- src/protocollogin.cpp | 4 +-- 4 files changed, 70 insertions(+), 29 deletions(-) diff --git a/src/protocol.cpp b/src/protocol.cpp index e443ef0..2686f1a 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -22,7 +22,6 @@ #include "protocol.h" #include "outputmessage.h" #include "rsa.h" -#include "xtea.h" extern RSA g_RSA; @@ -62,27 +61,73 @@ OutputMessage_ptr Protocol::getOutputBuffer(int32_t size) void Protocol::XTEA_encrypt(OutputMessage& msg) const { + const uint32_t delta = 0x61C88647; + // The message must be a multiple of 8 - size_t paddingBytes = msg.getLength() % 8u; + size_t paddingBytes = msg.getLength() % 8; if (paddingBytes != 0) { msg.addPaddingBytes(8 - paddingBytes); } uint8_t* buffer = msg.getOutputBuffer(); - xtea::encrypt(buffer, msg.getLength(), key); + const size_t messageLength = msg.getLength(); + size_t readPos = 0; + const uint32_t k[] = { key[0], key[1], key[2], key[3] }; + while (readPos < messageLength) { + uint32_t v0; + memcpy(&v0, buffer + readPos, 4); + uint32_t v1; + memcpy(&v1, buffer + readPos + 4, 4); + + uint32_t sum = 0; + + for (int32_t i = 32; --i >= 0;) { + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); + sum -= delta; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]); + } + + memcpy(buffer + readPos, &v0, 4); + readPos += 4; + memcpy(buffer + readPos, &v1, 4); + readPos += 4; + } } bool Protocol::XTEA_decrypt(NetworkMessage& msg) const { - if (((msg.getLength() - 6) & 7) != 0) { + if (((msg.getLength() - 2) & 7) != 0) { return false; } - uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition(); - xtea::decrypt(buffer, msg.getLength() - 6, key); + const uint32_t delta = 0x61C88647; - uint16_t innerLength = msg.get(); - if (innerLength + 8 > msg.getLength()) { + uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition(); + const size_t messageLength = (msg.getLength() - 2); + size_t readPos = 0; + const uint32_t k[] = { key[0], key[1], key[2], key[3] }; + while (readPos < messageLength) { + uint32_t v0; + memcpy(&v0, buffer + readPos, 4); + uint32_t v1; + memcpy(&v1, buffer + readPos + 4, 4); + + uint32_t sum = 0xC6EF3720; + + for (int32_t i = 32; --i >= 0;) { + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]); + sum += delta; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); + } + + memcpy(buffer + readPos, &v0, 4); + readPos += 4; + memcpy(buffer + readPos, &v1, 4); + readPos += 4; + } + + int innerLength = msg.get(); + if (innerLength > msg.getLength() - 4) { return false; } @@ -92,7 +137,7 @@ bool Protocol::XTEA_decrypt(NetworkMessage& msg) const bool Protocol::RSA_decrypt(NetworkMessage& msg) { - if ((msg.getLength() - msg.getBufferPosition()) < 128) { + if ((msg.getLength() - msg.getBufferPosition()) != 128) { return false; } @@ -107,4 +152,4 @@ uint32_t Protocol::getIP() const } return 0; -} \ No newline at end of file +} diff --git a/src/protocol.h b/src/protocol.h index 89039c9..43ad54d 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -1,6 +1,6 @@ /** - * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Sabrehaven and Mark Samman + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2016 Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,12 +21,11 @@ #define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1 #include "connection.h" -#include "xtea.h" class Protocol : public std::enable_shared_from_this { public: - explicit Protocol(Connection_ptr connection) : connection(connection) {} + explicit Protocol(Connection_ptr connection) : connection(connection), key(), encryptionEnabled(false), rawMessages(false) {} virtual ~Protocol() = default; // non-copyable @@ -72,10 +71,12 @@ protected: void enableXTEAEncryption() { encryptionEnabled = true; } - void setXTEAKey(xtea::key key) { - this->key = std::move(key); + void setXTEAKey(const uint32_t* key) { + memcpy(this->key, key, sizeof(*key) * 4); } + void XTEA_encrypt(OutputMessage& msg) const; + bool XTEA_decrypt(NetworkMessage& msg) const; static bool RSA_decrypt(NetworkMessage& msg); void setRawMessages(bool value) { @@ -83,19 +84,14 @@ protected: } virtual void release() {} - -private: - void XTEA_encrypt(OutputMessage& msg) const; - bool XTEA_decrypt(NetworkMessage& msg) const; - friend class Connection; OutputMessage_ptr outputBuffer; - +private: const ConnectionWeak_ptr connection; - xtea::key key; - bool encryptionEnabled = false; - bool rawMessages = false; + uint32_t key[4]; + bool encryptionEnabled; + bool rawMessages; }; #endif diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index b690a9c..d128c4f 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -247,13 +247,13 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) return; } - xtea::key key; + uint32_t key[4]; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(std::move(key)); + setXTEAKey(key); if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { NetworkMessage opcodeMessage; diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index f32c17e..27e2ea3 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -128,13 +128,13 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) return; } - xtea::key key; + uint32_t key[4]; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(std::move(key)); + setXTEAKey(key); if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { std::ostringstream ss; From 527debe319fdf0c2cfab33c63af58da687a4636d Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Mon, 11 Nov 2019 20:21:51 +0200 Subject: [PATCH 7/8] fix set outfit functionality --- data/XML/outfits.xml | 75 -------------------------------------------- src/const.h | 2 ++ src/otserv.cpp | 7 +++++ src/player.cpp | 14 +++++++++ src/protocolgame.cpp | 3 +- 5 files changed, 24 insertions(+), 77 deletions(-) diff --git a/data/XML/outfits.xml b/data/XML/outfits.xml index fca7ddc..6fbd4f7 100644 --- a/data/XML/outfits.xml +++ b/data/XML/outfits.xml @@ -16,44 +16,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -70,41 +32,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/const.h b/src/const.h index f596521..4a8caf6 100644 --- a/src/const.h +++ b/src/const.h @@ -343,4 +343,6 @@ static constexpr int32_t PSTRG_RESERVED_RANGE_SIZE = 10000000; static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000); static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500; +#define IS_IN_KEYRANGE(key, range) (key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) <= PSTRG_##range##_SIZE)) + #endif diff --git a/src/otserv.cpp b/src/otserv.cpp index e6762a4..ce6e996 100644 --- a/src/otserv.cpp +++ b/src/otserv.cpp @@ -220,6 +220,13 @@ void mainLoader(int, char*[], ServiceManager* services) return; } + std::cout << ">> Loading outfits" << std::endl; + auto& outfits = Outfits::getInstance(); + if (!outfits.loadFromXml()) { + startupErrorMessage("Unable to load outfits!"); + return; + } + std::cout << ">> Checking world type... " << std::flush; std::string worldType = asLowerCaseString(g_config.getString(ConfigManager::WORLD_TYPE)); if (worldType == "pvp") { diff --git a/src/player.cpp b/src/player.cpp index 7d17518..29e66c7 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -509,6 +509,20 @@ uint16_t Player::getLookCorpse() const void Player::addStorageValue(const uint32_t key, const int32_t value) { + if (IS_IN_KEYRANGE(key, RESERVED_RANGE)) { + if (IS_IN_KEYRANGE(key, OUTFITS_RANGE)) { + outfits.emplace_back( + value >> 16, + value & 0xFF + ); + return; + } + else { + std::cout << "Warning: unknown reserved key: " << key << " player: " << getName() << std::endl; + return; + } + } + if (value != -1) { storageMap[key] = value; } else { diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index d128c4f..b004596 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -1738,7 +1738,7 @@ void ProtocolGame::sendOutfitWindow() } protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons); - if (protocolOutfits.size() == 100) { // Game client doesn't allow more than 100 outfits + if (protocolOutfits.size() == 15) { // Game client doesn't allow more than 15 outfits break; } } @@ -1746,7 +1746,6 @@ void ProtocolGame::sendOutfitWindow() msg.addByte(protocolOutfits.size()); for (const ProtocolOutfit& outfit : protocolOutfits) { msg.add(outfit.lookType); - msg.addString(outfit.name); msg.addByte(outfit.addons); } From 05af593814d83347078b7f99eb382d49429af89c Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Wed, 13 Nov 2019 17:06:13 +0200 Subject: [PATCH 8/8] fix debug caused by SendIcons. Fix max stamina. Fix character say. Fully finish 7.81 basic implementation. --- config.lua | 2 +- nostalrius.sql | 2 + src/luascript.cpp | 2 +- src/player.h | 2 +- src/protocolgame.cpp | 118 +++++++++++++++++++++---------------------- src/protocolgame.h | 1 - 6 files changed, 64 insertions(+), 63 deletions(-) diff --git a/config.lua b/config.lua index 0383f6a..e354f60 100644 --- a/config.lua +++ b/config.lua @@ -52,7 +52,7 @@ timeBetweenExActions = 1000 -- Map -- NOTE: set mapName WITHOUT .otbm at the end -mapName = "mymap" +mapName = "map" mapAuthor = "CipSoft" -- MySQL diff --git a/nostalrius.sql b/nostalrius.sql index 4f21d2b..4564d28 100644 --- a/nostalrius.sql +++ b/nostalrius.sql @@ -1125,6 +1125,7 @@ CREATE TABLE `players` ( `lookhead` int(11) NOT NULL DEFAULT '0', `looklegs` int(11) NOT NULL DEFAULT '0', `looktype` int(11) NOT NULL DEFAULT '136', + `lookaddons` int(11) NOT NULL DEFAULT '0', `maglevel` int(11) NOT NULL DEFAULT '0', `mana` int(11) NOT NULL DEFAULT '0', `manamax` int(11) NOT NULL DEFAULT '0', @@ -1147,6 +1148,7 @@ CREATE TABLE `players` ( `onlinetime` int(11) NOT NULL DEFAULT '0', `deletion` bigint(15) NOT NULL DEFAULT '0', `balance` bigint(20) UNSIGNED NOT NULL DEFAULT '0', + `stamina` smallint(5) NOT NULL DEFAULT '3360', `skill_fist` int(10) UNSIGNED NOT NULL DEFAULT '10', `skill_fist_tries` bigint(20) UNSIGNED NOT NULL DEFAULT '0', `skill_club` int(10) UNSIGNED NOT NULL DEFAULT '10', diff --git a/src/luascript.cpp b/src/luascript.cpp index 3401679..7519da5 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -7783,7 +7783,7 @@ int LuaScriptInterface::luaPlayerSetStamina(lua_State* L) uint16_t stamina = getNumber(L, 2); Player* player = getUserdata(L, 1); if (player) { - player->staminaMinutes = std::min(2520, stamina); + player->staminaMinutes = std::min(3360, stamina); player->sendStats(); pushBoolean(L, true); } diff --git a/src/player.h b/src/player.h index 743e2ac..5050903 100644 --- a/src/player.h +++ b/src/player.h @@ -1046,7 +1046,7 @@ class Player final : public Creature, public Cylinder int32_t shieldBlockCount = 0; int32_t idleTime = 0; - uint16_t staminaMinutes = 2520; + uint16_t staminaMinutes = 3360; uint16_t maxWriteLen = 0; uint8_t soul = 0; diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index b004596..0b51ab1 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -1168,11 +1168,12 @@ void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelNam writeToOutputBuffer(msg); } + void ProtocolGame::sendIcons(uint16_t icons) { NetworkMessage msg; msg.addByte(0xA2); - msg.addByte(static_cast(icons)); + msg.add(icons); writeToOutputBuffer(msg); } @@ -1204,6 +1205,8 @@ void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool h writeToOutputBuffer(msg); } + + void ProtocolGame::sendTradeItemRequest(const std::string& traderName, const Item* item, bool ack) { NetworkMessage msg; @@ -1277,14 +1280,61 @@ void ProtocolGame::sendCreatureTurn(const Creature* creature, uint32_t stackPos) void ProtocolGame::sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, const Position* pos/* = nullptr*/) { NetworkMessage msg; - AddCreatureSpeak(msg, creature, type, text, 0, pos); + msg.addByte(0xAA); + + static uint32_t statementId = 0; + msg.add(++statementId); + + msg.addString(creature->getName()); + + //Add level only for players + if (const Player* speaker = creature->getPlayer()) { + msg.add(speaker->getLevel()); + } + else { + msg.add(0x00); + } + + msg.addByte(type); + if (pos) { + msg.addPosition(*pos); + } + else { + msg.addPosition(creature->getPosition()); + } + + msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendToChannel(const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId) { NetworkMessage msg; - AddCreatureSpeak(msg, creature, type, text, channelId); + msg.addByte(0xAA); + + static uint32_t statementId = 0; + msg.add(++statementId); + if (!creature) { + msg.add(0x00); + } + else if (type == TALKTYPE_CHANNEL_R2) { + msg.add(0x00); + type = TALKTYPE_CHANNEL_R1; + } + else { + msg.addString(creature->getName()); + //Add level only for players + if (const Player* speaker = creature->getPlayer()) { + msg.add(speaker->getLevel()); + } + else { + msg.add(0x00); + } + } + + msg.addByte(type); + msg.add(channelId); + msg.addString(text); writeToOutputBuffer(msg); } @@ -1294,16 +1344,13 @@ void ProtocolGame::sendPrivateMessage(const Player* speaker, SpeakClasses type, msg.addByte(0xAA); static uint32_t statementId = 0; msg.add(++statementId); - if (type == TALKTYPE_RVR_ANSWER) { - msg.addString("Gamemaster"); - } else { - if (speaker) { - msg.addString(speaker->getName()); - } else { - msg.add(0x00); - } + if (speaker) { + msg.addString(speaker->getName()); + msg.add(speaker->getLevel()); + } + else { + msg.add(0x00); } - msg.addByte(type); msg.addString(text); writeToOutputBuffer(msg); @@ -1890,53 +1937,6 @@ void ProtocolGame::AddCreatureLight(NetworkMessage& msg, const Creature* creatur msg.addByte(lightInfo.color); } -void ProtocolGame::AddCreatureSpeak(NetworkMessage& msg, const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId, const Position* pos /*= nullptr*/) -{ - msg.addByte(0xAA); - static uint32_t statementId = 0; - msg.add(++statementId); - - if (type != TALKTYPE_RVR_ANSWER) { - if (type != TALKTYPE_CHANNEL_R2) { - if (creature) { - msg.addString(creature->getName()); - } else { - msg.add(0); - } - } else { - msg.add(0); - } - } - - msg.addByte(type); - switch (type) { - case TALKTYPE_SAY: - case TALKTYPE_WHISPER: - case TALKTYPE_YELL: - case TALKTYPE_MONSTER_SAY: - case TALKTYPE_MONSTER_YELL: - if (!pos) { - msg.addPosition(creature->getPosition()); - } else { - msg.addPosition(*pos); - } - break; - case TALKTYPE_CHANNEL_Y: - case TALKTYPE_CHANNEL_R1: - case TALKTYPE_CHANNEL_R2: - case TALKTYPE_CHANNEL_O: - msg.add(channelId); - break; - case TALKTYPE_RVR_CHANNEL: - msg.add(0); - break; - default: - break; - } - - msg.addString(text); -} - //tile void ProtocolGame::RemoveTileThing(NetworkMessage& msg, const Position& pos, uint32_t stackpos) { diff --git a/src/protocolgame.h b/src/protocolgame.h index aca3e38..9d0cbe3 100644 --- a/src/protocolgame.h +++ b/src/protocolgame.h @@ -234,7 +234,6 @@ class ProtocolGame final : public Protocol void AddPlayerSkills(NetworkMessage& msg); void AddWorldLight(NetworkMessage& msg, const LightInfo& lightInfo); void AddCreatureLight(NetworkMessage& msg, const Creature* creature); - void AddCreatureSpeak(NetworkMessage& msg, const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId, const Position* pos = nullptr); //tiles static void RemoveTileThing(NetworkMessage& msg, const Position& pos, uint32_t stackpos);