diff --git a/config.lua b/config.lua index 5e3fe2e..1f5b5f9 100644 --- a/config.lua +++ b/config.lua @@ -37,6 +37,7 @@ replaceKickOnLogin = true maxPacketsPerSecond = -1 autoStackCumulatives = false moneyRate = 1 +clientVersion = 780 -- Deaths -- NOTE: Leave deathLosePercent as -1 if you want to use the default diff --git a/src/configmanager.cpp b/src/configmanager.cpp index 6610b74..5136cbf 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -123,6 +123,7 @@ bool ConfigManager::load() integer[NEWBIE_TOWN] = getGlobalNumber(L, "newbieTownId", 1); integer[NEWBIE_LEVEL_THRESHOLD] = getGlobalNumber(L, "newbieLevelThreshold", 5); integer[MONEY_RATE] = getGlobalNumber(L, "moneyRate", 1); + integer[CLIENT_VERSION] = getGlobalNumber(L, "clientVersion"); loaded = true; lua_close(L); diff --git a/src/configmanager.h b/src/configmanager.h index c81e761..e1c20f1 100644 --- a/src/configmanager.h +++ b/src/configmanager.h @@ -111,6 +111,7 @@ class ConfigManager NEWBIE_TOWN, NEWBIE_LEVEL_THRESHOLD, MONEY_RATE, + CLIENT_VERSION, LAST_INTEGER_CONFIG /* this must be the last one */ }; diff --git a/src/const.h b/src/const.h index 29609fd..5cc9f41 100644 --- a/src/const.h +++ b/src/const.h @@ -351,6 +351,13 @@ enum ReloadTypes_t : uint8_t { RELOAD_TYPE_WEAPONS, }; +enum ClientVersion_t : uint16_t { + CLIENT_VERSION_780 = 780, + CLIENT_VERSION_781 = 781, + CLIENT_VERSION_790 = 790, + CLIENT_VERSION_792 = 792, +}; + static constexpr int32_t CHANNEL_GUILD = 0x00; static constexpr int32_t CHANNEL_PARTY = 0x01; static constexpr int32_t CHANNEL_RULE_REP = 0x02; diff --git a/src/definitions.h b/src/definitions.h index 5aa6634..bab0b0e 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -24,10 +24,6 @@ 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 = 780; -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; diff --git a/src/game.cpp b/src/game.cpp index c460377..c35f83a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -76,6 +76,11 @@ void Game::setWorldType(WorldType_t type) worldType = type; } +void Game::setClientVersion(ClientVersion_t version) +{ + clientVersion = version; +} + void Game::setGameState(GameState_t newState) { if (gameState == GAME_STATE_SHUTDOWN) { diff --git a/src/game.h b/src/game.h index 0582a23..e7132c4 100644 --- a/src/game.h +++ b/src/game.h @@ -127,6 +127,11 @@ class Game return worldType; } + void setClientVersion(ClientVersion_t version); + ClientVersion_t getClientVersion() const { + return clientVersion; + } + Cylinder* internalGetCylinder(Player* player, const Position& pos) const; Thing* internalGetThing(Player* player, const Position& pos, int32_t index, uint32_t spriteId, stackPosType_t type) const; @@ -533,6 +538,7 @@ class Game GameState_t gameState = GAME_STATE_NORMAL; WorldType_t worldType = WORLD_TYPE_PVP; + ClientVersion_t clientVersion; LightState_t lightState = LIGHT_STATE_DAY; uint8_t lightLevel = LIGHT_LEVEL_DAY; diff --git a/src/luascript.cpp b/src/luascript.cpp index 95a540e..d7cf15b 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -1676,6 +1676,7 @@ void LuaScriptInterface::registerFunctions() registerEnumIn("configKeys", ConfigManager::NEWBIE_LEVEL_THRESHOLD) registerEnumIn("configKeys", ConfigManager::BLOCK_HEIGHT) registerEnumIn("configKeys", ConfigManager::DROP_ITEMS) + registerEnumIn("configKeys", ConfigManager::CLIENT_VERSION) // os registerMethod("os", "mtime", LuaScriptInterface::luaSystemTime); diff --git a/src/npc.cpp b/src/npc.cpp index 4d811e7..f058b92 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -51,7 +51,9 @@ void Npcs::loadNpcs() return; } - g_game.placeCreature(npc, npc->getMasterPos(), false, true); + if (npc->getClientVersion() <= g_game.getClientVersion()) { + g_game.placeCreature(npc, npc->getMasterPos(), false, true); + } } } @@ -152,6 +154,8 @@ bool Npc::load() script.readCoordinate(masterPos.x, masterPos.y, masterPos.z); } else if (ident == "radius") { masterRadius = script.readNumber(); + } else if (ident == "clientversion") { + clientVersion = script.readNumber(); } else if (ident == "behaviour") { if (behaviourDatabase) { script.error("behaviour database already defined"); diff --git a/src/npc.h b/src/npc.h index acf95ec..d3e91c9 100644 --- a/src/npc.h +++ b/src/npc.h @@ -85,6 +85,9 @@ class Npc final : public Creature int32_t getMasterRadius() const { return masterRadius; } + int32_t getClientVersion() const { + return clientVersion; + } const Position& getMasterPos() const { return masterPos; } @@ -141,6 +144,7 @@ class Npc final : public Creature uint32_t lastTalkCreature; uint32_t focusCreature; uint32_t masterRadius; + uint16_t clientVersion = 0; int64_t conversationStartTime; int64_t conversationEndTime; diff --git a/src/otserv.cpp b/src/otserv.cpp index ce6e996..8457637 100644 --- a/src/otserv.cpp +++ b/src/otserv.cpp @@ -245,6 +245,27 @@ void mainLoader(int, char*[], ServiceManager* services) } std::cout << asUpperCaseString(worldType) << std::endl; + std::cout << ">> Checking client version... " << std::flush; + int32_t clientVersion = g_config.getNumber(ConfigManager::CLIENT_VERSION); + if (clientVersion == 780) { + g_game.setClientVersion(CLIENT_VERSION_780); + } else if (clientVersion == 781) { + g_game.setClientVersion(CLIENT_VERSION_781); + } else if (clientVersion == 790) { + g_game.setClientVersion(CLIENT_VERSION_790); + } else if (clientVersion == 792) { + g_game.setClientVersion(CLIENT_VERSION_792); + } + else { + std::cout << std::endl; + + std::ostringstream ss; + ss << "> ERROR: Unknown client version: " << g_config.getNumber(ConfigManager::CLIENT_VERSION) << ", valid client versions are: 780, 781, 790, 792."; + startupErrorMessage(ss.str()); + return; + } + std::cout << clientVersion << std::endl; + std::cout << ">> Loading map" << std::endl; if (!g_game.loadMainMap(g_config.getString(ConfigManager::MAP_NAME))) { startupErrorMessage("Failed to load map"); diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index a371bc1..b93956e 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -1775,6 +1775,7 @@ void ProtocolGame::sendOutfitWindow() Outfit_t currentOutfit = player->getDefaultOutfit(); AddOutfit(msg, currentOutfit); + const ClientVersion_t clientVersion = g_game.getClientVersion(); std::vector protocolOutfits; if (player->isAccessPlayer()) { static const std::string gamemasterOutfitName = "Gamemaster"; @@ -1790,14 +1791,19 @@ void ProtocolGame::sendOutfitWindow() } protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons); - if (protocolOutfits.size() == 15) { // Game client doesn't allow more than 15 outfits - break; + if (CLIENT_VERSION_780 <= clientVersion && clientVersion <= CLIENT_VERSION_792) { + if (protocolOutfits.size() == 15) { // Game client doesn't allow more than 15 outfits in 780-792 + break; + } } } msg.addByte(protocolOutfits.size()); for (const ProtocolOutfit& outfit : protocolOutfits) { msg.add(outfit.lookType); + if (clientVersion > CLIENT_VERSION_781) { + msg.addString(outfit.name); + } msg.addByte(outfit.addons); } @@ -2053,4 +2059,4 @@ void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg) // process additional opcodes via lua script event addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer); -} +} \ No newline at end of file diff --git a/src/protocolgame.h b/src/protocolgame.h index 9d0cbe3..773c633 100644 --- a/src/protocolgame.h +++ b/src/protocolgame.h @@ -262,7 +262,7 @@ class ProtocolGame final : public Protocol uint32_t eventConnect = 0; uint32_t challengeTimestamp = 0; - uint16_t version = CLIENT_VERSION_MIN; + uint16_t version; uint8_t challengeRandom = 0; diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index 27e2ea3..02903d3 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -118,7 +118,7 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) if (version <= 760) { std::ostringstream ss; - ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; + ss << "Only clients with protocol " << getClientVersionString(g_game.getClientVersion()) << " allowed!"; disconnectClient(ss.str(), version); return; } @@ -136,9 +136,9 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) enableXTEAEncryption(); setXTEAKey(key); - if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { + if (!isProtocolAllowed(version)) { std::ostringstream ss; - ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; + ss << "Only clients with protocol " << getClientVersionString(g_game.getClientVersion()) << " allowed!"; disconnectClient(ss.str(), version); return; } @@ -185,3 +185,19 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) auto thisPtr = std::static_pointer_cast(shared_from_this()); g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version))); } + + +bool ProtocolLogin::isProtocolAllowed(uint16_t version) +{ + ClientVersion_t allowedClientVersion = g_game.getClientVersion(); + + if (allowedClientVersion == version) + { + return true; + } + else if (version == 781 && allowedClientVersion == CLIENT_VERSION_780) { + return true; + } + + return false; +} \ No newline at end of file diff --git a/src/protocollogin.h b/src/protocollogin.h index a45898f..a95b1ec 100644 --- a/src/protocollogin.h +++ b/src/protocollogin.h @@ -21,6 +21,7 @@ #define FS_PROTOCOLLOGIN_H_1238F4B473074DF2ABC595C29E81C46D #include "protocol.h" +#include "tools.h" class NetworkMessage; class OutputMessage; @@ -38,6 +39,7 @@ class ProtocolLogin : public Protocol explicit ProtocolLogin(Connection_ptr connection) : Protocol(connection) {} void onRecvFirstMessage(NetworkMessage& msg) override; + bool isProtocolAllowed(uint16_t version); private: void disconnectClient(const std::string& message, uint16_t version); diff --git a/src/protocolstatus.cpp b/src/protocolstatus.cpp index d29b19e..1b7740e 100644 --- a/src/protocolstatus.cpp +++ b/src/protocolstatus.cpp @@ -110,7 +110,7 @@ void ProtocolStatus::sendStatusString() serverinfo.append_attribute("url") = g_config.getString(ConfigManager::URL).c_str(); serverinfo.append_attribute("server") = STATUS_SERVER_NAME; serverinfo.append_attribute("version") = STATUS_SERVER_VERSION; - serverinfo.append_attribute("client") = CLIENT_VERSION_STR; + serverinfo.append_attribute("client") = getClientVersionString(g_game.getClientVersion()).c_str(); pugi::xml_node owner = tsqp.append_child("owner"); owner.append_attribute("name") = g_config.getString(ConfigManager::OWNER_NAME).c_str(); @@ -240,7 +240,7 @@ void ProtocolStatus::sendInfo(uint16_t requestedInfo, const std::string& charact output->addByte(0x23); // server software info output->addString(STATUS_SERVER_NAME); output->addString(STATUS_SERVER_VERSION); - output->addString(CLIENT_VERSION_STR); + output->addString(getClientVersionString(g_game.getClientVersion())); } send(output); disconnect(); diff --git a/src/tools.cpp b/src/tools.cpp index dc18231..f970fe7 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -1230,3 +1230,33 @@ void getFilesInDirectory(const boost::filesystem::path& root, const std::string& } } } + +std::string getClientVersionString(uint32_t version) +{ + return getClientVersionString(static_cast(version)); +} + +std::string getClientVersionString(ClientVersion_t version) +{ + std::string result; + switch (version) + { + case CLIENT_VERSION_780: + result = "7.80"; + break; + case CLIENT_VERSION_781: + result = "7.81"; + break; + case CLIENT_VERSION_790: + result = "7.90"; + break; + case CLIENT_VERSION_792: + result = "7.92"; + break; + default: + result = "Unknown"; + break; + } + + return result; +} diff --git a/src/tools.h b/src/tools.h index e507430..2e1716f 100644 --- a/src/tools.h +++ b/src/tools.h @@ -100,6 +100,9 @@ const char* getReturnMessage(ReturnValue value); void getFilesInDirectory(const boost::filesystem::path& root, const std::string& ext, std::vector& ret); +std::string getClientVersionString(uint32_t version); +std::string getClientVersionString(ClientVersion_t version); + inline int64_t OTSYS_TIME() { return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count();