introduce multiple client version support in config.lua and NPCs

This commit is contained in:
ErikasKontenis 2020-02-08 19:22:56 +02:00
parent ff7636b945
commit 0cd1af211e
18 changed files with 118 additions and 14 deletions

View File

@ -37,6 +37,7 @@ replaceKickOnLogin = true
maxPacketsPerSecond = -1 maxPacketsPerSecond = -1
autoStackCumulatives = false autoStackCumulatives = false
moneyRate = 1 moneyRate = 1
clientVersion = 780
-- Deaths -- Deaths
-- NOTE: Leave deathLosePercent as -1 if you want to use the default -- NOTE: Leave deathLosePercent as -1 if you want to use the default

View File

@ -123,6 +123,7 @@ bool ConfigManager::load()
integer[NEWBIE_TOWN] = getGlobalNumber(L, "newbieTownId", 1); integer[NEWBIE_TOWN] = getGlobalNumber(L, "newbieTownId", 1);
integer[NEWBIE_LEVEL_THRESHOLD] = getGlobalNumber(L, "newbieLevelThreshold", 5); integer[NEWBIE_LEVEL_THRESHOLD] = getGlobalNumber(L, "newbieLevelThreshold", 5);
integer[MONEY_RATE] = getGlobalNumber(L, "moneyRate", 1); integer[MONEY_RATE] = getGlobalNumber(L, "moneyRate", 1);
integer[CLIENT_VERSION] = getGlobalNumber(L, "clientVersion");
loaded = true; loaded = true;
lua_close(L); lua_close(L);

View File

@ -111,6 +111,7 @@ class ConfigManager
NEWBIE_TOWN, NEWBIE_TOWN,
NEWBIE_LEVEL_THRESHOLD, NEWBIE_LEVEL_THRESHOLD,
MONEY_RATE, MONEY_RATE,
CLIENT_VERSION,
LAST_INTEGER_CONFIG /* this must be the last one */ LAST_INTEGER_CONFIG /* this must be the last one */
}; };

View File

@ -351,6 +351,13 @@ enum ReloadTypes_t : uint8_t {
RELOAD_TYPE_WEAPONS, 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_GUILD = 0x00;
static constexpr int32_t CHANNEL_PARTY = 0x01; static constexpr int32_t CHANNEL_PARTY = 0x01;
static constexpr int32_t CHANNEL_RULE_REP = 0x02; static constexpr int32_t CHANNEL_RULE_REP = 0x02;

View File

@ -24,10 +24,6 @@ static constexpr auto STATUS_SERVER_NAME = "Sabrehaven";
static constexpr auto STATUS_SERVER_VERSION = "1.0"; static constexpr auto STATUS_SERVER_VERSION = "1.0";
static constexpr auto STATUS_SERVER_DEVELOPERS = "OTLand community & Sabrehaven Developers Team"; 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_DIGITS = 6U;
static constexpr auto AUTHENTICATOR_PERIOD = 30U; static constexpr auto AUTHENTICATOR_PERIOD = 30U;

View File

@ -76,6 +76,11 @@ void Game::setWorldType(WorldType_t type)
worldType = type; worldType = type;
} }
void Game::setClientVersion(ClientVersion_t version)
{
clientVersion = version;
}
void Game::setGameState(GameState_t newState) void Game::setGameState(GameState_t newState)
{ {
if (gameState == GAME_STATE_SHUTDOWN) { if (gameState == GAME_STATE_SHUTDOWN) {

View File

@ -127,6 +127,11 @@ class Game
return worldType; return worldType;
} }
void setClientVersion(ClientVersion_t version);
ClientVersion_t getClientVersion() const {
return clientVersion;
}
Cylinder* internalGetCylinder(Player* player, const Position& pos) const; Cylinder* internalGetCylinder(Player* player, const Position& pos) const;
Thing* internalGetThing(Player* player, const Position& pos, int32_t index, Thing* internalGetThing(Player* player, const Position& pos, int32_t index,
uint32_t spriteId, stackPosType_t type) const; uint32_t spriteId, stackPosType_t type) const;
@ -533,6 +538,7 @@ class Game
GameState_t gameState = GAME_STATE_NORMAL; GameState_t gameState = GAME_STATE_NORMAL;
WorldType_t worldType = WORLD_TYPE_PVP; WorldType_t worldType = WORLD_TYPE_PVP;
ClientVersion_t clientVersion;
LightState_t lightState = LIGHT_STATE_DAY; LightState_t lightState = LIGHT_STATE_DAY;
uint8_t lightLevel = LIGHT_LEVEL_DAY; uint8_t lightLevel = LIGHT_LEVEL_DAY;

View File

@ -1676,6 +1676,7 @@ void LuaScriptInterface::registerFunctions()
registerEnumIn("configKeys", ConfigManager::NEWBIE_LEVEL_THRESHOLD) registerEnumIn("configKeys", ConfigManager::NEWBIE_LEVEL_THRESHOLD)
registerEnumIn("configKeys", ConfigManager::BLOCK_HEIGHT) registerEnumIn("configKeys", ConfigManager::BLOCK_HEIGHT)
registerEnumIn("configKeys", ConfigManager::DROP_ITEMS) registerEnumIn("configKeys", ConfigManager::DROP_ITEMS)
registerEnumIn("configKeys", ConfigManager::CLIENT_VERSION)
// os // os
registerMethod("os", "mtime", LuaScriptInterface::luaSystemTime); registerMethod("os", "mtime", LuaScriptInterface::luaSystemTime);

View File

@ -51,7 +51,9 @@ void Npcs::loadNpcs()
return; 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); script.readCoordinate(masterPos.x, masterPos.y, masterPos.z);
} else if (ident == "radius") { } else if (ident == "radius") {
masterRadius = script.readNumber(); masterRadius = script.readNumber();
} else if (ident == "clientversion") {
clientVersion = script.readNumber();
} else if (ident == "behaviour") { } else if (ident == "behaviour") {
if (behaviourDatabase) { if (behaviourDatabase) {
script.error("behaviour database already defined"); script.error("behaviour database already defined");

View File

@ -85,6 +85,9 @@ class Npc final : public Creature
int32_t getMasterRadius() const { int32_t getMasterRadius() const {
return masterRadius; return masterRadius;
} }
int32_t getClientVersion() const {
return clientVersion;
}
const Position& getMasterPos() const { const Position& getMasterPos() const {
return masterPos; return masterPos;
} }
@ -141,6 +144,7 @@ class Npc final : public Creature
uint32_t lastTalkCreature; uint32_t lastTalkCreature;
uint32_t focusCreature; uint32_t focusCreature;
uint32_t masterRadius; uint32_t masterRadius;
uint16_t clientVersion = 0;
int64_t conversationStartTime; int64_t conversationStartTime;
int64_t conversationEndTime; int64_t conversationEndTime;

View File

@ -245,6 +245,27 @@ void mainLoader(int, char*[], ServiceManager* services)
} }
std::cout << asUpperCaseString(worldType) << std::endl; 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; std::cout << ">> Loading map" << std::endl;
if (!g_game.loadMainMap(g_config.getString(ConfigManager::MAP_NAME))) { if (!g_game.loadMainMap(g_config.getString(ConfigManager::MAP_NAME))) {
startupErrorMessage("Failed to load map"); startupErrorMessage("Failed to load map");

View File

@ -1775,6 +1775,7 @@ void ProtocolGame::sendOutfitWindow()
Outfit_t currentOutfit = player->getDefaultOutfit(); Outfit_t currentOutfit = player->getDefaultOutfit();
AddOutfit(msg, currentOutfit); AddOutfit(msg, currentOutfit);
const ClientVersion_t clientVersion = g_game.getClientVersion();
std::vector<ProtocolOutfit> protocolOutfits; std::vector<ProtocolOutfit> protocolOutfits;
if (player->isAccessPlayer()) { if (player->isAccessPlayer()) {
static const std::string gamemasterOutfitName = "Gamemaster"; static const std::string gamemasterOutfitName = "Gamemaster";
@ -1790,14 +1791,19 @@ void ProtocolGame::sendOutfitWindow()
} }
protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons); protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons);
if (protocolOutfits.size() == 15) { // Game client doesn't allow more than 15 outfits if (CLIENT_VERSION_780 <= clientVersion && clientVersion <= CLIENT_VERSION_792) {
break; if (protocolOutfits.size() == 15) { // Game client doesn't allow more than 15 outfits in 780-792
break;
}
} }
} }
msg.addByte(protocolOutfits.size()); msg.addByte(protocolOutfits.size());
for (const ProtocolOutfit& outfit : protocolOutfits) { for (const ProtocolOutfit& outfit : protocolOutfits) {
msg.add<uint16_t>(outfit.lookType); msg.add<uint16_t>(outfit.lookType);
if (clientVersion > CLIENT_VERSION_781) {
msg.addString(outfit.name);
}
msg.addByte(outfit.addons); msg.addByte(outfit.addons);
} }
@ -2053,4 +2059,4 @@ void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg)
// process additional opcodes via lua script event // process additional opcodes via lua script event
addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer); addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer);
} }

View File

@ -262,7 +262,7 @@ class ProtocolGame final : public Protocol
uint32_t eventConnect = 0; uint32_t eventConnect = 0;
uint32_t challengeTimestamp = 0; uint32_t challengeTimestamp = 0;
uint16_t version = CLIENT_VERSION_MIN; uint16_t version;
uint8_t challengeRandom = 0; uint8_t challengeRandom = 0;

View File

@ -118,7 +118,7 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
if (version <= 760) { if (version <= 760) {
std::ostringstream ss; 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); disconnectClient(ss.str(), version);
return; return;
} }
@ -136,9 +136,9 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
enableXTEAEncryption(); enableXTEAEncryption();
setXTEAKey(key); setXTEAKey(key);
if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { if (!isProtocolAllowed(version)) {
std::ostringstream ss; 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); disconnectClient(ss.str(), version);
return; return;
} }
@ -185,3 +185,19 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
auto thisPtr = std::static_pointer_cast<ProtocolLogin>(shared_from_this()); auto thisPtr = std::static_pointer_cast<ProtocolLogin>(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, 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;
}

View File

@ -21,6 +21,7 @@
#define FS_PROTOCOLLOGIN_H_1238F4B473074DF2ABC595C29E81C46D #define FS_PROTOCOLLOGIN_H_1238F4B473074DF2ABC595C29E81C46D
#include "protocol.h" #include "protocol.h"
#include "tools.h"
class NetworkMessage; class NetworkMessage;
class OutputMessage; class OutputMessage;
@ -38,6 +39,7 @@ class ProtocolLogin : public Protocol
explicit ProtocolLogin(Connection_ptr connection) : Protocol(connection) {} explicit ProtocolLogin(Connection_ptr connection) : Protocol(connection) {}
void onRecvFirstMessage(NetworkMessage& msg) override; void onRecvFirstMessage(NetworkMessage& msg) override;
bool isProtocolAllowed(uint16_t version);
private: private:
void disconnectClient(const std::string& message, uint16_t version); void disconnectClient(const std::string& message, uint16_t version);

View File

@ -110,7 +110,7 @@ void ProtocolStatus::sendStatusString()
serverinfo.append_attribute("url") = g_config.getString(ConfigManager::URL).c_str(); serverinfo.append_attribute("url") = g_config.getString(ConfigManager::URL).c_str();
serverinfo.append_attribute("server") = STATUS_SERVER_NAME; serverinfo.append_attribute("server") = STATUS_SERVER_NAME;
serverinfo.append_attribute("version") = STATUS_SERVER_VERSION; 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"); pugi::xml_node owner = tsqp.append_child("owner");
owner.append_attribute("name") = g_config.getString(ConfigManager::OWNER_NAME).c_str(); 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->addByte(0x23); // server software info
output->addString(STATUS_SERVER_NAME); output->addString(STATUS_SERVER_NAME);
output->addString(STATUS_SERVER_VERSION); output->addString(STATUS_SERVER_VERSION);
output->addString(CLIENT_VERSION_STR); output->addString(getClientVersionString(g_game.getClientVersion()));
} }
send(output); send(output);
disconnect(); disconnect();

View File

@ -1230,3 +1230,33 @@ void getFilesInDirectory(const boost::filesystem::path& root, const std::string&
} }
} }
} }
std::string getClientVersionString(uint32_t version)
{
return getClientVersionString(static_cast<ClientVersion_t>(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;
}

View File

@ -100,6 +100,9 @@ const char* getReturnMessage(ReturnValue value);
void getFilesInDirectory(const boost::filesystem::path& root, const std::string& ext, std::vector<boost::filesystem::path>& ret); void getFilesInDirectory(const boost::filesystem::path& root, const std::string& ext, std::vector<boost::filesystem::path>& ret);
std::string getClientVersionString(uint32_t version);
std::string getClientVersionString(ClientVersion_t version);
inline int64_t OTSYS_TIME() inline int64_t OTSYS_TIME()
{ {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();