diff --git a/common/database.cpp b/common/database.cpp index 76673a31b..00ce26b08 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -997,6 +997,18 @@ bool Database::SetVariable(const std::string varname, const std::string &varvalu return true; } +void Database::SetAccountCRCField(uint32 account_id, std::string field_name, uint64 checksum) +{ + QueryDatabase( + fmt::format( + "UPDATE `account` SET `{}` = '{}' WHERE `id` = {}", + field_name, + checksum, + account_id + ) + ); +} + // Get zone starting points from DB bool Database::GetSafePoints(const char* zone_short_name, uint32 instance_version, float* safe_x, float* safe_y, float* safe_z, float* safe_heading, int16* min_status, uint8* min_level, char *flag_needed) { @@ -2285,7 +2297,7 @@ void Database::SetIPExemption(std::string account_ip, int exemption_amount) { auto row = results.begin(); exemption_id = atoi(row[0]); } - + query = fmt::format( "INSERT INTO `ip_exemptions` (`exemption_ip`, `exemption_amount`) VALUES ('{}', {})", account_ip, diff --git a/common/database.h b/common/database.h index 64856ef80..66f7cd1fa 100644 --- a/common/database.h +++ b/common/database.h @@ -190,6 +190,8 @@ public: int16 CheckStatus(uint32 account_id); + void SetAccountCRCField(uint32 account_id, std::string field_name, uint64 checksum); + uint32 CheckLogin(const char* name, const char* password, const char *loginserver, int16* oStatus = 0); uint32 CreateAccount(const char* name, const char* password, int16 status, const char* loginserver, uint32 lsaccount_id); uint32 GetAccountIDFromLSID(const std::string& in_loginserver_id, uint32 in_loginserver_account_id, char* in_account_name = 0, int16* in_status = 0); diff --git a/common/emu_oplist.h b/common/emu_oplist.h index f5386e9b5..da78e4e02 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -558,6 +558,7 @@ N(OP_WhoAllRequest), N(OP_WhoAllResponse), N(OP_World_Client_CRC1), N(OP_World_Client_CRC2), +N(OP_World_Client_CRC3), N(OP_WorldClientReady), N(OP_WorldComplete), N(OP_WorldLogout), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 49d6a7454..21681b262 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -5582,6 +5582,11 @@ struct SayLinkBodyFrame_Struct { /*056*/ }; +struct Checksum_Struct { + uint64 checksum; + uint8 data[2048]; +}; + struct UpdateMovementEntry { /* 00 */ float Y; /* 04 */ float X; diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index e5f0c63c1..2e16e64be 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -116,22 +116,24 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults() /** * Set Defaults */ - log_settings[Logs::WorldServer].log_to_console = static_cast(Logs::General); - log_settings[Logs::ZoneServer].log_to_console = static_cast(Logs::General); - log_settings[Logs::QSServer].log_to_console = static_cast(Logs::General); - log_settings[Logs::UCSServer].log_to_console = static_cast(Logs::General); - log_settings[Logs::Crash].log_to_console = static_cast(Logs::General); - log_settings[Logs::MySQLError].log_to_console = static_cast(Logs::General); - log_settings[Logs::Loginserver].log_to_console = static_cast(Logs::General); - log_settings[Logs::HeadlessClient].log_to_console = static_cast(Logs::General); - log_settings[Logs::NPCScaling].log_to_gmsay = static_cast(Logs::General); - log_settings[Logs::HotReload].log_to_gmsay = static_cast(Logs::General); - log_settings[Logs::HotReload].log_to_console = static_cast(Logs::General); - log_settings[Logs::Loot].log_to_gmsay = static_cast(Logs::General); - log_settings[Logs::Scheduler].log_to_console = static_cast(Logs::General); - log_settings[Logs::Cheat].log_to_console = static_cast(Logs::General); - log_settings[Logs::HTTP].log_to_console = static_cast(Logs::General); - log_settings[Logs::HTTP].log_to_gmsay = static_cast(Logs::General); + log_settings[Logs::WorldServer].log_to_console = static_cast(Logs::General); + log_settings[Logs::ZoneServer].log_to_console = static_cast(Logs::General); + log_settings[Logs::QSServer].log_to_console = static_cast(Logs::General); + log_settings[Logs::UCSServer].log_to_console = static_cast(Logs::General); + log_settings[Logs::Crash].log_to_console = static_cast(Logs::General); + log_settings[Logs::MySQLError].log_to_console = static_cast(Logs::General); + log_settings[Logs::Loginserver].log_to_console = static_cast(Logs::General); + log_settings[Logs::HeadlessClient].log_to_console = static_cast(Logs::General); + log_settings[Logs::NPCScaling].log_to_gmsay = static_cast(Logs::General); + log_settings[Logs::HotReload].log_to_gmsay = static_cast(Logs::General); + log_settings[Logs::HotReload].log_to_console = static_cast(Logs::General); + log_settings[Logs::Loot].log_to_gmsay = static_cast(Logs::General); + log_settings[Logs::Scheduler].log_to_console = static_cast(Logs::General); + log_settings[Logs::Cheat].log_to_console = static_cast(Logs::General); + log_settings[Logs::HTTP].log_to_console = static_cast(Logs::General); + log_settings[Logs::HTTP].log_to_gmsay = static_cast(Logs::General); + log_settings[Logs::ChecksumVerification].log_to_console = static_cast(Logs::General); + log_settings[Logs::ChecksumVerification].log_to_gmsay = static_cast(Logs::General); /** * RFC 5424 @@ -241,7 +243,7 @@ void EQEmuLogSys::ProcessGMSay( */ if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone || EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformWorld - ) { + ) { on_log_gmsay_hook(log_category, message); } } diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 11808d891..5ec265dfa 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -127,6 +127,7 @@ namespace Logs { DiaWind, HTTP, Saylink, + ChecksumVerification, MaxCategoryID /* Don't Remove this */ }; @@ -212,6 +213,7 @@ namespace Logs { "DialogueWindow", "HTTP", "Saylink", + "ChecksumVerification", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 753417d81..6c8988b0c 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -696,6 +696,16 @@ OutF(LogSys, Logs::Detail, Logs::Saylink, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogChecksumVerification(message, ...) do {\ + if (LogSys.log_settings[Logs::ChecksumVerification].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::ChecksumVerification, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogChecksumVerificationDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::ChecksumVerification].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::ChecksumVerification, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -1092,6 +1102,18 @@ #define LogHTTPDetail(message, ...) do {\ } while (0) +#define LogSaylink(message, ...) do {\ +} while (0) + +#define LogSaylinkDetail(message, ...) do {\ +} while (0) + +#define LogChecksumVerification(message, ...) do {\ +} while (0) + +#define LogChecksumVerificationDetail(message, ...) do {\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ } while (0) diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 5fa9a8c5f..2c3445a46 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -5218,6 +5218,11 @@ struct SayLinkBodyFrame_Struct { /*056*/ }; +struct Checksum_Struct { + uint64_t checksum; + uint8_t data[2048]; +}; + }; /*structs*/ }; /*RoF2*/ diff --git a/common/repositories/base/base_account_repository.h b/common/repositories/base/base_account_repository.h index 681b2c1e5..5e8160085 100644 --- a/common/repositories/base/base_account_repository.h +++ b/common/repositories/base/base_account_repository.h @@ -14,6 +14,7 @@ #include "../../database.h" #include "../../string_util.h" +#include class BaseAccountRepository { public: @@ -32,11 +33,14 @@ public: std::string minilogin_ip; int hideme; int rulesflag; - std::string suspendeduntil; + time_t suspendeduntil; int time_creation; int expansion; std::string ban_reason; std::string suspend_reason; + std::string crc_eqgame; + std::string crc_skillcaps; + std::string crc_basedata; }; static std::string PrimaryKey() @@ -66,6 +70,37 @@ public: "expansion", "ban_reason", "suspend_reason", + "crc_eqgame", + "crc_skillcaps", + "crc_basedata", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "name", + "charname", + "sharedplat", + "password", + "status", + "ls_id", + "lsaccount_id", + "gmspeed", + "revoked", + "karma", + "minilogin_ip", + "hideme", + "rulesflag", + "UNIX_TIMESTAMP(suspendeduntil)", + "time_creation", + "expansion", + "ban_reason", + "suspend_reason", + "crc_eqgame", + "crc_skillcaps", + "crc_basedata", }; } @@ -74,6 +109,11 @@ public: return std::string(implode(", ", Columns())); } + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + static std::string TableName() { return std::string("account"); @@ -83,7 +123,7 @@ public: { return fmt::format( "SELECT {} FROM {}", - ColumnsRaw(), + SelectColumnsRaw(), TableName() ); } @@ -115,11 +155,14 @@ public: entry.minilogin_ip = ""; entry.hideme = 0; entry.rulesflag = 0; - entry.suspendeduntil = "0000-00-00 00:00:00"; + entry.suspendeduntil = 0; entry.time_creation = 0; entry.expansion = 0; entry.ban_reason = ""; entry.suspend_reason = ""; + entry.crc_eqgame = ""; + entry.crc_skillcaps = ""; + entry.crc_basedata = ""; return entry; } @@ -169,11 +212,14 @@ public: entry.minilogin_ip = row[11] ? row[11] : ""; entry.hideme = atoi(row[12]); entry.rulesflag = atoi(row[13]); - entry.suspendeduntil = row[14] ? row[14] : ""; + entry.suspendeduntil = strtoll(row[14] ? row[14] : "-1", nullptr, 10); entry.time_creation = atoi(row[15]); entry.expansion = atoi(row[16]); entry.ban_reason = row[17] ? row[17] : ""; entry.suspend_reason = row[18] ? row[18] : ""; + entry.crc_eqgame = row[19] ? row[19] : ""; + entry.crc_skillcaps = row[20] ? row[20] : ""; + entry.crc_basedata = row[21] ? row[21] : ""; return entry; } @@ -220,11 +266,14 @@ public: update_values.push_back(columns[11] + " = '" + EscapeString(account_entry.minilogin_ip) + "'"); update_values.push_back(columns[12] + " = " + std::to_string(account_entry.hideme)); update_values.push_back(columns[13] + " = " + std::to_string(account_entry.rulesflag)); - update_values.push_back(columns[14] + " = '" + EscapeString(account_entry.suspendeduntil) + "'"); + update_values.push_back(columns[14] + " = FROM_UNIXTIME(" + (account_entry.suspendeduntil > 0 ? std::to_string(account_entry.suspendeduntil) : "null") + ")"); update_values.push_back(columns[15] + " = " + std::to_string(account_entry.time_creation)); update_values.push_back(columns[16] + " = " + std::to_string(account_entry.expansion)); update_values.push_back(columns[17] + " = '" + EscapeString(account_entry.ban_reason) + "'"); update_values.push_back(columns[18] + " = '" + EscapeString(account_entry.suspend_reason) + "'"); + update_values.push_back(columns[19] + " = '" + EscapeString(account_entry.crc_eqgame) + "'"); + update_values.push_back(columns[20] + " = '" + EscapeString(account_entry.crc_skillcaps) + "'"); + update_values.push_back(columns[21] + " = '" + EscapeString(account_entry.crc_basedata) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -260,11 +309,14 @@ public: insert_values.push_back("'" + EscapeString(account_entry.minilogin_ip) + "'"); insert_values.push_back(std::to_string(account_entry.hideme)); insert_values.push_back(std::to_string(account_entry.rulesflag)); - insert_values.push_back("'" + EscapeString(account_entry.suspendeduntil) + "'"); + insert_values.push_back("FROM_UNIXTIME(" + (account_entry.suspendeduntil > 0 ? std::to_string(account_entry.suspendeduntil) : "null") + ")"); insert_values.push_back(std::to_string(account_entry.time_creation)); insert_values.push_back(std::to_string(account_entry.expansion)); insert_values.push_back("'" + EscapeString(account_entry.ban_reason) + "'"); insert_values.push_back("'" + EscapeString(account_entry.suspend_reason) + "'"); + insert_values.push_back("'" + EscapeString(account_entry.crc_eqgame) + "'"); + insert_values.push_back("'" + EscapeString(account_entry.crc_skillcaps) + "'"); + insert_values.push_back("'" + EscapeString(account_entry.crc_basedata) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -308,11 +360,14 @@ public: insert_values.push_back("'" + EscapeString(account_entry.minilogin_ip) + "'"); insert_values.push_back(std::to_string(account_entry.hideme)); insert_values.push_back(std::to_string(account_entry.rulesflag)); - insert_values.push_back("'" + EscapeString(account_entry.suspendeduntil) + "'"); + insert_values.push_back("FROM_UNIXTIME(" + (account_entry.suspendeduntil > 0 ? std::to_string(account_entry.suspendeduntil) : "null") + ")"); insert_values.push_back(std::to_string(account_entry.time_creation)); insert_values.push_back(std::to_string(account_entry.expansion)); insert_values.push_back("'" + EscapeString(account_entry.ban_reason) + "'"); insert_values.push_back("'" + EscapeString(account_entry.suspend_reason) + "'"); + insert_values.push_back("'" + EscapeString(account_entry.crc_eqgame) + "'"); + insert_values.push_back("'" + EscapeString(account_entry.crc_skillcaps) + "'"); + insert_values.push_back("'" + EscapeString(account_entry.crc_basedata) + "'"); insert_chunks.push_back("(" + implode(",", insert_values) + ")"); } @@ -360,11 +415,14 @@ public: entry.minilogin_ip = row[11] ? row[11] : ""; entry.hideme = atoi(row[12]); entry.rulesflag = atoi(row[13]); - entry.suspendeduntil = row[14] ? row[14] : ""; + entry.suspendeduntil = strtoll(row[14] ? row[14] : "-1", nullptr, 10); entry.time_creation = atoi(row[15]); entry.expansion = atoi(row[16]); entry.ban_reason = row[17] ? row[17] : ""; entry.suspend_reason = row[18] ? row[18] : ""; + entry.crc_eqgame = row[19] ? row[19] : ""; + entry.crc_skillcaps = row[20] ? row[20] : ""; + entry.crc_basedata = row[21] ? row[21] : ""; all_entries.push_back(entry); } @@ -403,11 +461,14 @@ public: entry.minilogin_ip = row[11] ? row[11] : ""; entry.hideme = atoi(row[12]); entry.rulesflag = atoi(row[13]); - entry.suspendeduntil = row[14] ? row[14] : ""; + entry.suspendeduntil = strtoll(row[14] ? row[14] : "-1", nullptr, 10); entry.time_creation = atoi(row[15]); entry.expansion = atoi(row[16]); entry.ban_reason = row[17] ? row[17] : ""; entry.suspend_reason = row[18] ? row[18] : ""; + entry.crc_eqgame = row[19] ? row[19] : ""; + entry.crc_skillcaps = row[20] ? row[20] : ""; + entry.crc_basedata = row[21] ? row[21] : ""; all_entries.push_back(entry); } diff --git a/common/ruletypes.h b/common/ruletypes.h index 8d4a53daa..d06f9280c 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -242,6 +242,7 @@ RULE_INT(GM, MinStatusToSummonItem, 250, "Minimum required status to summon item RULE_INT(GM, MinStatusToZoneAnywhere, 250, "Minimum required status to zone anywhere") RULE_INT(GM, MinStatusToLevelTarget, 100, "Minimum required status to set the level of a player") RULE_INT(GM, MinStatusToBypassLockedServer, 100, "Players >= this status can log in to the server even when it is locked") +RULE_INT(GM, MinStatusToBypassCheckSumVerification, 100, "Players >= this status can bypass the eqgame.exe and spells_us.txt checksum verification") RULE_CATEGORY_END() RULE_CATEGORY(World) @@ -275,6 +276,7 @@ RULE_INT (World, TellQueueSize, 20, "Maximum tell queue size") RULE_BOOL(World, StartZoneSameAsBindOnCreation, true, "Should the start zone always be the same location as your bind?") RULE_BOOL(World, EnforceCharacterLimitAtLogin, false, "Enforce the limit for characters that are online at login") RULE_BOOL(World, EnableDevTools, true, "Enable or Disable the Developer Tools globally (Most of the time you want this enabled)") +RULE_BOOL(World, EnableChecksumVerification, false, "Enable or Disable the Checksum Verification for eqgame.exe and spells_us.txt") RULE_CATEGORY_END() RULE_CATEGORY(Zone) diff --git a/common/servertalk.h b/common/servertalk.h index 37c96d844..258cee546 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -227,6 +227,7 @@ #define ServerOP_HotReloadQuests 0x4011 #define ServerOP_UpdateSchedulerEvents 0x4012 #define ServerOP_ReloadContentFlags 0x4013 +#define ServerOP_ReloadVariablesWorld 0x4014 #define ServerOP_CZDialogueWindow 0x4500 #define ServerOP_CZLDoNUpdate 0x4501 diff --git a/common/version.h b/common/version.h index 568513041..2039c637e 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9175 +#define CURRENT_BINARY_DATABASE_VERSION 9176 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 3ecb0df68..8bcbfd2ca 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -22,8 +22,9 @@ OP_ExpansionInfo=0x590d OP_GuildsList=0x507a OP_EnterWorld=0x578f OP_PostEnterWorld=0x6259 -OP_World_Client_CRC1=0x12cc -OP_World_Client_CRC2=0x0f13 +OP_World_Client_CRC1=0x0f13 +OP_World_Client_CRC2=0x4b8d +OP_World_Client_CRC3=0x298d OP_SendSpellChecksum=0x0000 OP_SendSkillCapsChecksum=0x0000 diff --git a/utils/scripts/generators/repository-generator.pl b/utils/scripts/generators/repository-generator.pl index bd7514a93..975f4a227 100644 --- a/utils/scripts/generators/repository-generator.pl +++ b/utils/scripts/generators/repository-generator.pl @@ -249,6 +249,11 @@ foreach my $table_to_generate (@tables) { $default_value = '""'; } + # for datetime values that set default value all zeroed out + if ($default_value =~ /0000-00-00 00:00:00/i) { + $default_value = 0; + } + my $struct_data_type = translate_mysql_data_type_to_c($data_type); # struct diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 3c210a287..f5fd1f654 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -429,6 +429,7 @@ 9173|2021_09_14_zone_lava_damage.sql|SHOW COLUMNS FROM `zone` LIKE 'lava_damage'|empty| 9174|2021_10_09_not_null_door_columns.sql|SELECT * FROM db_version WHERE version >= 9174|empty| 9175|2022_01_02_expansion_default_value_all.sql|SHOW COLUMNS FROM `forage` LIKE 'min_expansion'|contains|unsigned +9176|2022_01_10_checksum_verification.sql|SHOW COLUMNS FROM `account` LIKE 'checksum_crc1_eqgame'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2022_01_10_checksum_verification.sql b/utils/sql/git/required/2022_01_10_checksum_verification.sql new file mode 100644 index 000000000..7a18ef843 --- /dev/null +++ b/utils/sql/git/required/2022_01_10_checksum_verification.sql @@ -0,0 +1,10 @@ +INSERT INTO `variables` (`varname`, `value`, `information`, `ts`) VALUES ('crc_eqgame', '0', 'Client CRC64 Checksum on: eqgame.exe', '2021-09-23 14:16:27'); +INSERT INTO `variables` (`varname`, `value`, `information`, `ts`) VALUES ('crc_skillcaps', '0', 'Client CRC64 Checksum on: SkillCaps.txt', '2021-09-23 14:16:21'); +INSERT INTO `variables` (`varname`, `value`, `information`, `ts`) VALUES ('crc_basedata', '0', 'Client CRC64 Checksum on: BaseData.txt','2021-09-23 14:16:21'); + +ALTER TABLE `account` + ADD COLUMN `crc_eqgame` TEXT NULL AFTER `suspend_reason`, + ADD COLUMN `crc_skillcaps` TEXT NULL AFTER `crc_eqgame`, + ADD COLUMN `crc_basedata` TEXT NULL AFTER `crc_skillcaps`; + +ALTER TABLE account` CHANGE `suspendeduntil` `suspendeduntil` datetime NULL COMMENT ''; diff --git a/world/client.cpp b/world/client.cpp index d5305eb67..7013b790b 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -47,6 +47,7 @@ #include "wguild_mgr.h" #include "sof_char_create_data.h" #include "world_store.h" +#include "../common/repositories/account_repository.h" #include #include @@ -1013,15 +1014,18 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { switch(opcode) { - case OP_World_Client_CRC1: - case OP_World_Client_CRC2: + case OP_World_Client_CRC1: // eqgame.exe + case OP_World_Client_CRC2: // SkillCaps.txt + case OP_World_Client_CRC3: // BaseData.txt { // There is no obvious entry in the CC struct to indicate that the 'Start Tutorial button // is selected when a character is created. I have observed that in this case, OP_EnterWorld is sent // before OP_World_Client_CRC1. Therefore, if we receive OP_World_Client_CRC1 before OP_EnterWorld, // then 'Start Tutorial' was not chosen. StartInTutorial = false; - return true; + + return HandleChecksumPacket(app); + } case OP_SendLoginInfo: { @@ -1142,6 +1146,146 @@ bool Client::Process() { return ret; } +bool Client::HandleChecksumPacket(const EQApplicationPacket *app) +{ + // Is checksum verification turned on + if (!RuleB(World, EnableChecksumVerification)) { + return true; + } + + // Get packet structure + auto *cs = (Checksum_Struct *)app->pBuffer; + + // Determine which checksum to process + switch (app->GetOpcode()) { + case OP_World_Client_CRC1: // eqgame.exe + { + bool passes_checksum_validation = ( + ChecksumVerificationCRCEQGame(cs->checksum) || + (GetAdmin() >= RuleI(GM, MinStatusToBypassCheckSumVerification)) + ); + + LogChecksumVerification( + "eqgame.exe validation [{}] client [{}] ({}) has [{}] status [{}]", + passes_checksum_validation ? "Passed" : "Failed", + GetAccountName(), + GetAccountID(), + cs->checksum, + GetAdmin() + ); + + return passes_checksum_validation; + } + case OP_World_Client_CRC2: // SkillCaps.txt + { + bool passes_checksum_validation = ( + ChecksumVerificationCRCSkillCaps(cs->checksum) || + (GetAdmin() >= RuleI(GM, MinStatusToBypassCheckSumVerification)) + ); + + LogChecksumVerification( + "SkillCaps.txt validation [{}] client [{}] ({}) has [{}] status [{}]", + passes_checksum_validation ? "Passed" : "Failed", + GetAccountName(), + GetAccountID(), + cs->checksum, + GetAdmin() + ); + + return passes_checksum_validation; + } + case OP_World_Client_CRC3: // BaseData.txt + { + bool passes_checksum_validation = ( + ChecksumVerificationCRCBaseData(cs->checksum) || + (GetAdmin() >= RuleI(GM, MinStatusToBypassCheckSumVerification)) + ); + + LogChecksumVerification( + "BaseData.txt validation [{}] client [{}] ({}) has [{}] status [{}]", + passes_checksum_validation ? "Passed" : "Failed", + GetAccountName(), + GetAccountID(), + cs->checksum, + GetAdmin() + ); + + return passes_checksum_validation; + } + } + + return false; +} + +bool Client::ChecksumVerificationCRCEQGame(uint64 checksum) +{ + database.SetAccountCRCField(GetAccountID(), "crc_eqgame", checksum); + + // Get checksum variable for eqgame.exe + std::string checksumvar; + uint64_t checksumint; + if (database.GetVariable("crc_eqgame", checksumvar)) { + checksumint = atoll(checksumvar.c_str()); + } + else { + LogChecksumVerification("[checksum_crc1_eqgame] variable not set in variables table."); + return true; + } + + // Verify checksums match + if (checksumint == checksum) { + return true; + } + + return false; +} + +bool Client::ChecksumVerificationCRCSkillCaps(uint64 checksum) +{ + database.SetAccountCRCField(GetAccountID(), "crc_skillcaps", checksum); + + // Get checksum variable for eqgame.exe + std::string checksumvar; + uint64_t checksumint; + if (database.GetVariable("crc_skillcaps", checksumvar)) { + checksumint = atoll(checksumvar.c_str()); + } + else { + LogChecksumVerification("[checksum_crc2_skillcaps] variable not set in variables table."); + return true; + } + + // Verify checksums match + if (checksumint == checksum) { + return true; + } + + return false; +} + +bool Client::ChecksumVerificationCRCBaseData(uint64 checksum) +{ + database.SetAccountCRCField(GetAccountID(), "crc_basedata", checksum); + + // Get checksum variable for skill_caps.txt + std::string checksumvar; + uint64_t checksumint; + if (database.GetVariable("crc_basedata", checksumvar)) { + checksumint = atoll(checksumvar.c_str()); + } + else { + LogChecksumVerification("[checksum_crc3_basedata] variable not set in variables table."); + return true; + } + + // Verify checksums match + if (checksumint == checksum) { + return true; + } + + return false; +} + void Client::EnterWorld(bool TryBootup) { if (zone_id == 0) return; diff --git a/world/client.h b/world/client.h index 3631cf129..d94f5a598 100644 --- a/world/client.h +++ b/world/client.h @@ -116,6 +116,10 @@ private: bool HandleEnterWorldPacket(const EQApplicationPacket *app); bool HandleDeleteCharacterPacket(const EQApplicationPacket *app); bool HandleZoneChangePacket(const EQApplicationPacket *app); + bool HandleChecksumPacket(const EQApplicationPacket *app); + bool ChecksumVerificationCRCEQGame(uint64 checksum); + bool ChecksumVerificationCRCSkillCaps(uint64 checksum); + bool ChecksumVerificationCRCBaseData(uint64 checksum); EQStreamInterface* eqs; }; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 8326c0657..c79190b9e 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -879,6 +879,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { RuleManager::Instance()->LoadRules(&database, "default", true); break; } + case ServerOP_ReloadVariablesWorld: + { + database.LoadVariables(); + break; + } case ServerOP_ReloadPerlExportSettings: { zoneserver_list.SendPacket(pack); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index e8e113a8a..ea135f679 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -548,6 +548,7 @@ SET(gm_commands gm_commands/unmemspells.cpp gm_commands/unscribespell.cpp gm_commands/unscribespells.cpp + gm_commands/updatechecksum.cpp gm_commands/untraindisc.cpp gm_commands/untraindiscs.cpp gm_commands/uptime.cpp diff --git a/zone/command.cpp b/zone/command.cpp index e1c81bfce..53061d371 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -388,6 +388,7 @@ int command_init(void) command_add("unscribespells", "- Clear out your or your player target's spell book.", AccountStatus::GMCoder, command_unscribespells) || command_add("untraindisc", "[Spell ID] - Untrain your or your target's discipline by Spell ID", AccountStatus::GMCoder, command_untraindisc) || command_add("untraindiscs", "- Untrains all disciplines from your target.", AccountStatus::GMCoder, command_untraindiscs) || + command_add("updatechecksum", "update client checksum", AccountStatus::GMImpossible, command_updatechecksum) || command_add("uptime", "[zone server id] - Get uptime of worldserver, or zone server if argument provided", AccountStatus::Steward, command_uptime) || command_add("version", "- Display current version of EQEmu server", AccountStatus::Player, command_version) || command_add("viewcurrencies", "- View your or your target's currencies", AccountStatus::GMAdmin, command_viewcurrencies) || diff --git a/zone/command.h b/zone/command.h index dc64da7b2..d65f8ad94 100644 --- a/zone/command.h +++ b/zone/command.h @@ -310,6 +310,7 @@ void command_unscribespell(Client *c, const Seperator *sep); void command_unscribespells(Client *c, const Seperator *sep); void command_untraindisc(Client *c, const Seperator *sep); void command_untraindiscs(Client *c, const Seperator *sep); +void command_updatechecksum(Client* c, const Seperator* sep); void command_uptime(Client *c, const Seperator *sep); void command_version(Client *c, const Seperator *sep); void command_viewcurrencies(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/updatechecksum.cpp b/zone/gm_commands/updatechecksum.cpp new file mode 100755 index 000000000..3550eb7e2 --- /dev/null +++ b/zone/gm_commands/updatechecksum.cpp @@ -0,0 +1,35 @@ +#include "../client.h" +#include "../worldserver.h" +#include "../../common/repositories/account_repository.h" + +extern WorldServer worldserver; + +void command_updatechecksum(Client *c, const Seperator *sep) +{ + if (c) { + // if account found + auto account = AccountRepository::FindOne(database, c->AccountID()); + if (account.id > 0) { + database.SetVariable("crc_eqgame", account.crc_eqgame); + database.SetVariable("crc_skillcaps", account.crc_skillcaps); + database.SetVariable("crc_basedata", account.crc_basedata); + + // reload rules (world) + auto pack = new ServerPacket(ServerOP_ReloadRulesWorld, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload rules. (only world)"); + safe_delete(pack); + + // reload variables (world) + pack = new ServerPacket(ServerOP_ReloadVariablesWorld, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload variables. (only world)"); + safe_delete(pack); + + return; + } + + // we should never see this + c->Message(Chat::Red, "Error: Your account was not found!"); + } +}