mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
feat(db): client system info as reported by the client
client_sysinfo (migrations mysql 102, sqlite 85) keeps the system description each account's client sent at login, exactly as sent, plus the physical memory read from it. While nothing but the memory in use changes, the account's newest row gets the new time and one more login; otherwise a new row starts. Log pruning deletes rows not seen for a while (eLog::CLIENT_SYSINFO) and deleting an account deletes its rows. Tests on SQLite alone (dDatabaseSqliteTests) and in the MySQL parity tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -201,7 +201,7 @@ void MySQLDatabase::DeleteAccount(const uint32_t accountId) {
|
||||
ExecuteDelete("DELETE FROM character_snapshots WHERE character_id = ?;", characterId);
|
||||
}
|
||||
// Rows about the account itself. Audit log, chat log and reports stay as the record of what happened.
|
||||
for (const auto* table : { "account_tokens", "account_recovery_codes", "account_notes", "account_strikes", "account_login_addresses",
|
||||
for (const auto* table : { "account_tokens", "account_recovery_codes", "account_notes", "account_strikes", "account_login_addresses", "client_sysinfo",
|
||||
"dashboard_preferences", "accounts_rewardcodes", "player_cheat_detections" }) {
|
||||
ExecuteDelete(std::string("DELETE FROM ") + table + " WHERE account_id = ?;", accountId);
|
||||
}
|
||||
|
||||
@@ -297,6 +297,7 @@ uint32_t MySQLDatabase::PruneLog(eLog log, int64_t beforeTime) {
|
||||
case eLog::CHEAT_DETECTION: return static_cast<uint32_t>(ExecuteUpdate("DELETE FROM player_cheat_detections WHERE violation_time < FROM_UNIXTIME(?);", beforeTime));
|
||||
case eLog::CHAT: return static_cast<uint32_t>(ExecuteUpdate("DELETE FROM chat_log WHERE time > 0 AND time < ?;", beforeTime));
|
||||
case eLog::LOGIN_ADDRESS: return static_cast<uint32_t>(ExecuteUpdate("DELETE FROM account_login_addresses WHERE last_seen < ?;", beforeTime));
|
||||
case eLog::CLIENT_SYSINFO: return static_cast<uint32_t>(ExecuteUpdate("DELETE FROM client_sysinfo WHERE last_seen < ?;", beforeTime));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"MessageCaptures.cpp"
|
||||
"ChatLog.cpp"
|
||||
"ChatFlags.cpp"
|
||||
"ClientSysInfo.cpp"
|
||||
"RelatedData.cpp"
|
||||
"ActivityLog.cpp"
|
||||
"Behaviors.cpp"
|
||||
|
||||
58
dDatabase/GameDatabase/MySQL/Tables/ClientSysInfo.cpp
Normal file
58
dDatabase/GameDatabase/MySQL/Tables/ClientSysInfo.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
namespace {
|
||||
IClientSysInfo::SysInfoRow Row(sql::ResultSet& r) {
|
||||
IClientSysInfo::SysInfoRow s;
|
||||
s.id = r.getUInt64("id");
|
||||
s.accountId = r.getUInt("account_id");
|
||||
s.firstSeen = r.getInt64("first_seen");
|
||||
s.lastSeen = r.getInt64("last_seen");
|
||||
s.logins = r.getUInt("logins");
|
||||
s.ip = r.getString("ip").c_str();
|
||||
s.clientOs = r.getUInt("client_os");
|
||||
s.memoryStats = r.getString("memory_stats").c_str();
|
||||
s.memoryTotalKb = r.getUInt64("memory_total_kb");
|
||||
s.videoCard = r.getString("video_card").c_str();
|
||||
s.numberOfProcessors = r.getUInt("number_of_processors");
|
||||
s.processorType = r.getUInt("processor_type");
|
||||
s.processorLevel = static_cast<uint16_t>(r.getUInt("processor_level"));
|
||||
s.processorRevision = static_cast<uint16_t>(r.getUInt("processor_revision"));
|
||||
s.osVersionInfoSize = r.getUInt("os_version_info_size");
|
||||
s.majorVersion = r.getUInt("os_major_version");
|
||||
s.minorVersion = r.getUInt("os_minor_version");
|
||||
s.buildNumber = r.getUInt("os_build_number");
|
||||
s.platformId = r.getUInt("os_platform_id");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
void MySQLDatabase::RecordClientSysInfo(const SysInfoRow& s) {
|
||||
auto newest = ExecuteSelect("SELECT * FROM client_sysinfo WHERE account_id = ? ORDER BY id DESC LIMIT 1;", s.accountId);
|
||||
if (newest->next()) {
|
||||
const auto previous = Row(*newest.m_resultSet);
|
||||
if (previous.SameAs(s)) {
|
||||
ExecuteUpdate("UPDATE client_sysinfo SET last_seen = ?, logins = logins + 1, memory_stats = ? WHERE id = ?;", s.lastSeen, s.memoryStats, previous.id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ExecuteInsert("INSERT INTO client_sysinfo (account_id, first_seen, last_seen, logins, ip, client_os, memory_stats, memory_total_kb, video_card, "
|
||||
"number_of_processors, processor_type, processor_level, processor_revision, os_version_info_size, os_major_version, os_minor_version, "
|
||||
"os_build_number, os_platform_id) VALUES (?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
|
||||
s.accountId, s.firstSeen, s.lastSeen, s.ip, s.clientOs, s.memoryStats, s.memoryTotalKb, s.videoCard, s.numberOfProcessors, s.processorType,
|
||||
s.processorLevel, s.processorRevision, s.osVersionInfoSize, s.majorVersion, s.minorVersion, s.buildNumber, s.platformId);
|
||||
}
|
||||
|
||||
std::vector<IClientSysInfo::SysInfoRow> MySQLDatabase::GetClientSysInfo(const uint32_t accountId, const uint32_t limit) {
|
||||
std::vector<SysInfoRow> rows;
|
||||
auto result = ExecuteSelect("SELECT * FROM client_sysinfo WHERE account_id = ? ORDER BY id DESC LIMIT ?;", accountId, limit);
|
||||
while (result->next()) rows.push_back(Row(*result.m_resultSet));
|
||||
return rows;
|
||||
}
|
||||
|
||||
std::vector<IClientSysInfo::SysInfoRow> MySQLDatabase::GetLatestClientSysInfo(const uint32_t limit) {
|
||||
std::vector<SysInfoRow> rows;
|
||||
auto result = ExecuteSelect("SELECT c.* FROM client_sysinfo c JOIN (SELECT MAX(id) AS id FROM client_sysinfo GROUP BY account_id) n ON n.id = c.id "
|
||||
"ORDER BY c.id DESC LIMIT ?;", limit);
|
||||
while (result->next()) rows.push_back(Row(*result.m_resultSet));
|
||||
return rows;
|
||||
}
|
||||
Reference in New Issue
Block a user