[Feature] Client Checksum Verification (Resubmit old 1678) (#1922)

* [Feature] Client Checksum Verification (Resubmit old 1678)

* Updated db version

* Add new updatechecksum to CmakeLists.txt

* Removed magic number and used constant

* Fix new command to have access to worldserver

* spacing, more venbose desc and remove unneeded check

* Cleanup, refactoring

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Paul Coene 2022-03-06 21:26:29 -05:00 committed by GitHub
parent eca2ed0321
commit 2a5ddde78a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 356 additions and 33 deletions

View File

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

View File

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

View File

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

View File

@ -5582,6 +5582,11 @@ struct SayLinkBodyFrame_Struct {
/*056*/
};
struct Checksum_Struct {
uint64 checksum;
uint8 data[2048];
};
struct UpdateMovementEntry {
/* 00 */ float Y;
/* 04 */ float X;

View File

@ -116,22 +116,24 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
/**
* Set Defaults
*/
log_settings[Logs::WorldServer].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::ZoneServer].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::QSServer].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::UCSServer].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Crash].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::MySQLError].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Loginserver].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::HeadlessClient].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::NPCScaling].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::HotReload].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::HotReload].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Loot].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::Scheduler].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Cheat].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::HTTP].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::HTTP].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::WorldServer].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::ZoneServer].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::QSServer].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::UCSServer].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Crash].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::MySQLError].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Loginserver].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::HeadlessClient].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::NPCScaling].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::HotReload].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::HotReload].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Loot].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::Scheduler].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Cheat].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::HTTP].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::HTTP].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::ChecksumVerification].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::ChecksumVerification].log_to_gmsay = static_cast<uint8>(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);
}
}

View File

@ -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",
};
}

View File

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

View File

@ -5218,6 +5218,11 @@ struct SayLinkBodyFrame_Struct {
/*056*/
};
struct Checksum_Struct {
uint64_t checksum;
uint8_t data[2048];
};
}; /*structs*/
}; /*RoF2*/

View File

@ -14,6 +14,7 @@
#include "../../database.h"
#include "../../string_util.h"
#include <ctime>
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<std::string> 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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <iostream>
#include <iomanip>
@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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