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
autoStackCumulatives = false
moneyRate = 1
clientVersion = 780
-- Deaths
-- 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_LEVEL_THRESHOLD] = getGlobalNumber(L, "newbieLevelThreshold", 5);
integer[MONEY_RATE] = getGlobalNumber(L, "moneyRate", 1);
integer[CLIENT_VERSION] = getGlobalNumber(L, "clientVersion");
loaded = true;
lua_close(L);

View File

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

View File

@ -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;

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_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;

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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");

View File

@ -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;

View File

@ -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");

View File

@ -1775,6 +1775,7 @@ void ProtocolGame::sendOutfitWindow()
Outfit_t currentOutfit = player->getDefaultOutfit();
AddOutfit(msg, currentOutfit);
const ClientVersion_t clientVersion = g_game.getClientVersion();
std::vector<ProtocolOutfit> 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<uint16_t>(outfit.lookType);
if (clientVersion > CLIENT_VERSION_781) {
msg.addString(outfit.name);
}
msg.addByte(outfit.addons);
}

View File

@ -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;

View File

@ -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<ProtocolLogin>(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;
}

View File

@ -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);

View File

@ -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();

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);
std::string getClientVersionString(uint32_t version);
std::string getClientVersionString(ClientVersion_t version);
inline int64_t OTSYS_TIME()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();