diff --git a/common/ruletypes.h b/common/ruletypes.h index a4a199400..60560345e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -340,6 +340,9 @@ RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be u RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame") RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag") RULE_STRING(World, SupportedClients, "", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2") +RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1") +RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files") +RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified") RULE_CATEGORY_END() RULE_CATEGORY(Zone) diff --git a/world/client.cpp b/world/client.cpp index a6f5cfb5f..1c4b61632 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -541,6 +541,17 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) skip_char_info = true; } } + const auto& custom_files_key = RuleS(World, CustomFilesKey); + if (!skip_char_info && !custom_files_key.empty() && cle->Admin() < RuleI(World, CustomFilesAdminLevel)) { + // Modified clients can utilize this unused block in login_info to send custom payloads on login + // which indicates they are using custom client files with the correct version, based on key payload. + const auto client_key = std::string(reinterpret_cast(login_info->unknown064)); + if (custom_files_key != client_key) { + std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) ); + SendUnsupportedClientPacket(message); + skip_char_info = true; + } + } if (!skip_char_info) { SendExpansionInfo();