mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-04-09 17:26:58 +00:00
feat: implement character and property reputation system
- Added ICharacterReputation and IPropertyReputationContribution interfaces for managing character and property reputations. - Implemented MySQL and SQLite database methods for getting and setting character reputations. - Created migration scripts for character and property reputation tables in both MySQL and SQLite. - Updated CharacterComponent to retrieve and set character reputation. - Enhanced PropertyManagementComponent to manage property reputation and contributions. - Added methods for handling reputation contributions and decay. - Introduced CharacterReputationMigration to migrate existing character reputations from XML to the database.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(CDClientDatabase)
|
||||
add_subdirectory(GameDatabase)
|
||||
|
||||
add_library(dDatabase STATIC "MigrationRunner.cpp" "ModelNormalizeMigration.cpp")
|
||||
add_library(dDatabase STATIC "MigrationRunner.cpp" "ModelNormalizeMigration.cpp" "CharacterReputationMigration.cpp")
|
||||
|
||||
add_custom_target(conncpp_dylib
|
||||
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR})
|
||||
|
||||
41
dDatabase/CharacterReputationMigration.cpp
Normal file
41
dDatabase/CharacterReputationMigration.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "CharacterReputationMigration.h"
|
||||
|
||||
#include "Database.h"
|
||||
#include "Logger.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
uint32_t CharacterReputationMigration::Run() {
|
||||
uint32_t charactersMigrated = 0;
|
||||
|
||||
const auto allCharIds = Database::Get()->GetAllCharacterIds();
|
||||
const bool previousCommitValue = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
|
||||
for (const auto charId : allCharIds) {
|
||||
const auto xmlStr = Database::Get()->GetCharacterXml(charId);
|
||||
if (xmlStr.empty()) continue;
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
if (doc.Parse(xmlStr.c_str(), xmlStr.size()) != tinyxml2::XML_SUCCESS) {
|
||||
LOG("Failed to parse XML for character %llu during reputation migration", charId);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* obj = doc.FirstChildElement("obj");
|
||||
if (!obj) continue;
|
||||
|
||||
auto* character = obj->FirstChildElement("char");
|
||||
if (!character) continue;
|
||||
|
||||
int64_t reputation = 0;
|
||||
if (character->QueryInt64Attribute("rpt", &reputation) == tinyxml2::XML_SUCCESS && reputation != 0) {
|
||||
Database::Get()->SetCharacterReputation(charId, reputation);
|
||||
charactersMigrated++;
|
||||
}
|
||||
}
|
||||
|
||||
Database::Get()->Commit();
|
||||
Database::Get()->SetAutoCommit(previousCommitValue);
|
||||
|
||||
return charactersMigrated;
|
||||
}
|
||||
7
dDatabase/CharacterReputationMigration.h
Normal file
7
dDatabase/CharacterReputationMigration.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace CharacterReputationMigration {
|
||||
uint32_t Run();
|
||||
};
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "IAccountsRewardCodes.h"
|
||||
#include "IBehaviors.h"
|
||||
#include "IUgcModularBuild.h"
|
||||
#include "ICharacterReputation.h"
|
||||
#include "IPropertyReputationContribution.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
# define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (std::exception& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0)
|
||||
@@ -38,7 +40,8 @@ class GameDatabase :
|
||||
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
|
||||
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList,
|
||||
public IBehaviors, public IUgcModularBuild {
|
||||
public IBehaviors, public IUgcModularBuild,
|
||||
public ICharacterReputation, public IPropertyReputationContribution {
|
||||
public:
|
||||
virtual ~GameDatabase() = default;
|
||||
// TODO: These should be made private.
|
||||
|
||||
@@ -33,6 +33,9 @@ public:
|
||||
// Get the character ids for the given account.
|
||||
virtual std::vector<LWOOBJID> GetAccountCharacterIds(const LWOOBJID accountId) = 0;
|
||||
|
||||
// Get all character ids.
|
||||
virtual std::vector<LWOOBJID> GetAllCharacterIds() = 0;
|
||||
|
||||
// Insert a new character into the database.
|
||||
virtual void InsertNewCharacter(const ICharInfo::Info info) = 0;
|
||||
|
||||
|
||||
14
dDatabase/GameDatabase/ITables/ICharacterReputation.h
Normal file
14
dDatabase/GameDatabase/ITables/ICharacterReputation.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __ICHARACTERREPUTATION__H__
|
||||
#define __ICHARACTERREPUTATION__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
class ICharacterReputation {
|
||||
public:
|
||||
virtual int64_t GetCharacterReputation(const LWOOBJID charId) = 0;
|
||||
virtual void SetCharacterReputation(const LWOOBJID charId, const int64_t reputation) = 0;
|
||||
};
|
||||
|
||||
#endif //!__ICHARACTERREPUTATION__H__
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef __IPROPERTYREPUTATIONCONTRIBUTION__H__
|
||||
#define __IPROPERTYREPUTATIONCONTRIBUTION__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
class IPropertyReputationContribution {
|
||||
public:
|
||||
struct ContributionInfo {
|
||||
LWOOBJID playerId{};
|
||||
uint32_t reputationGained{};
|
||||
};
|
||||
|
||||
// Get today's reputation contributions for a property.
|
||||
virtual std::vector<ContributionInfo> GetPropertyReputationContributions(
|
||||
const LWOOBJID propertyId, const std::string& date) = 0;
|
||||
|
||||
// Upsert a player's reputation contribution for a property on a given date.
|
||||
virtual void UpdatePropertyReputationContribution(
|
||||
const LWOOBJID propertyId, const LWOOBJID playerId,
|
||||
const std::string& date, const uint32_t reputationGained) = 0;
|
||||
|
||||
// Update the total reputation on a property.
|
||||
virtual void UpdatePropertyReputation(const LWOOBJID propertyId, const uint32_t reputation) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IPROPERTYREPUTATIONCONTRIBUTION__H__
|
||||
@@ -139,6 +139,15 @@ public:
|
||||
void IncrementTimesPlayed(const LWOOBJID playerId, const uint32_t gameId) override;
|
||||
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<LWOOBJID> characterId) override;
|
||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||
std::vector<LWOOBJID> GetAllCharacterIds() override;
|
||||
int64_t GetCharacterReputation(const LWOOBJID charId) override;
|
||||
void SetCharacterReputation(const LWOOBJID charId, const int64_t reputation) override;
|
||||
std::vector<IPropertyReputationContribution::ContributionInfo> GetPropertyReputationContributions(
|
||||
const LWOOBJID propertyId, const std::string& date) override;
|
||||
void UpdatePropertyReputationContribution(
|
||||
const LWOOBJID propertyId, const LWOOBJID playerId,
|
||||
const std::string& date, const uint32_t reputationGained) override;
|
||||
void UpdatePropertyReputation(const LWOOBJID propertyId, const uint32_t reputation) override;
|
||||
uint32_t GetAccountCount() override;
|
||||
bool IsNameInUse(const std::string_view name) override;
|
||||
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override;
|
||||
|
||||
@@ -21,6 +21,8 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"Servers.cpp"
|
||||
"Ugc.cpp"
|
||||
"UgcModularBuild.cpp"
|
||||
"CharacterReputation.cpp"
|
||||
"PropertyReputationContribution.cpp"
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
@@ -52,6 +52,18 @@ std::vector<LWOOBJID> MySQLDatabase::GetAccountCharacterIds(const LWOOBJID accou
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> MySQLDatabase::GetAllCharacterIds() {
|
||||
auto result = ExecuteSelect("SELECT id FROM charinfo;");
|
||||
|
||||
std::vector<LWOOBJID> toReturn;
|
||||
toReturn.reserve(result->rowsCount());
|
||||
while (result->next()) {
|
||||
toReturn.push_back(result->getInt64("id"));
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
void MySQLDatabase::InsertNewCharacter(const ICharInfo::Info info) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)",
|
||||
|
||||
17
dDatabase/GameDatabase/MySQL/Tables/CharacterReputation.cpp
Normal file
17
dDatabase/GameDatabase/MySQL/Tables/CharacterReputation.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
int64_t MySQLDatabase::GetCharacterReputation(const LWOOBJID charId) {
|
||||
auto result = ExecuteSelect("SELECT reputation FROM character_reputation WHERE character_id = ? LIMIT 1;", charId);
|
||||
|
||||
if (!result->next()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result->getInt64("reputation");
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetCharacterReputation(const LWOOBJID charId, const int64_t reputation) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO character_reputation (character_id, reputation) VALUES (?, ?) ON DUPLICATE KEY UPDATE reputation = ?;",
|
||||
charId, reputation, reputation);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<IPropertyReputationContribution::ContributionInfo> MySQLDatabase::GetPropertyReputationContributions(
|
||||
const LWOOBJID propertyId, const std::string& date) {
|
||||
auto result = ExecuteSelect(
|
||||
"SELECT player_id, reputation_gained FROM property_reputation_contribution WHERE property_id = ? AND contribution_date = ?;",
|
||||
propertyId, date);
|
||||
|
||||
std::vector<IPropertyReputationContribution::ContributionInfo> contributions;
|
||||
while (result->next()) {
|
||||
IPropertyReputationContribution::ContributionInfo info;
|
||||
info.playerId = result->getUInt64("player_id");
|
||||
info.reputationGained = static_cast<uint32_t>(result->getUInt("reputation_gained"));
|
||||
contributions.push_back(info);
|
||||
}
|
||||
return contributions;
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdatePropertyReputationContribution(
|
||||
const LWOOBJID propertyId, const LWOOBJID playerId,
|
||||
const std::string& date, const uint32_t reputationGained) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO property_reputation_contribution (property_id, player_id, contribution_date, reputation_gained) "
|
||||
"VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE reputation_gained = ?;",
|
||||
propertyId, playerId, date, reputationGained, reputationGained);
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdatePropertyReputation(const LWOOBJID propertyId, const uint32_t reputation) {
|
||||
ExecuteUpdate("UPDATE properties SET reputation = ? WHERE id = ?;", reputation, propertyId);
|
||||
}
|
||||
@@ -123,6 +123,15 @@ public:
|
||||
void IncrementTimesPlayed(const LWOOBJID playerId, const uint32_t gameId) override;
|
||||
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<LWOOBJID> characterId) override;
|
||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||
std::vector<LWOOBJID> GetAllCharacterIds() override;
|
||||
int64_t GetCharacterReputation(const LWOOBJID charId) override;
|
||||
void SetCharacterReputation(const LWOOBJID charId, const int64_t reputation) override;
|
||||
std::vector<IPropertyReputationContribution::ContributionInfo> GetPropertyReputationContributions(
|
||||
const LWOOBJID propertyId, const std::string& date) override;
|
||||
void UpdatePropertyReputationContribution(
|
||||
const LWOOBJID propertyId, const LWOOBJID playerId,
|
||||
const std::string& date, const uint32_t reputationGained) override;
|
||||
void UpdatePropertyReputation(const LWOOBJID propertyId, const uint32_t reputation) override;
|
||||
uint32_t GetAccountCount() override;
|
||||
bool IsNameInUse(const std::string_view name) override;
|
||||
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override;
|
||||
|
||||
@@ -21,6 +21,8 @@ set(DDATABASES_DATABASES_SQLITE_TABLES_SOURCES
|
||||
"Servers.cpp"
|
||||
"Ugc.cpp"
|
||||
"UgcModularBuild.cpp"
|
||||
"CharacterReputation.cpp"
|
||||
"PropertyReputationContribution.cpp"
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
@@ -55,6 +55,18 @@ std::vector<LWOOBJID> SQLiteDatabase::GetAccountCharacterIds(const LWOOBJID acco
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> SQLiteDatabase::GetAllCharacterIds() {
|
||||
auto [_, result] = ExecuteSelect("SELECT id FROM charinfo;");
|
||||
|
||||
std::vector<LWOOBJID> toReturn;
|
||||
while (!result.eof()) {
|
||||
toReturn.push_back(result.getInt64Field("id"));
|
||||
result.nextRow();
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::InsertNewCharacter(const ICharInfo::Info info) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`, `prop_clone_id`) VALUES (?,?,?,?,?,?,(SELECT IFNULL(MAX(`prop_clone_id`), 0) + 1 FROM `charinfo`))",
|
||||
|
||||
17
dDatabase/GameDatabase/SQLite/Tables/CharacterReputation.cpp
Normal file
17
dDatabase/GameDatabase/SQLite/Tables/CharacterReputation.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "SQLiteDatabase.h"
|
||||
|
||||
int64_t SQLiteDatabase::GetCharacterReputation(const LWOOBJID charId) {
|
||||
auto [_, result] = ExecuteSelect("SELECT reputation FROM character_reputation WHERE character_id = ? LIMIT 1;", charId);
|
||||
|
||||
if (result.eof()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result.getInt64Field("reputation");
|
||||
}
|
||||
|
||||
void SQLiteDatabase::SetCharacterReputation(const LWOOBJID charId, const int64_t reputation) {
|
||||
ExecuteInsert(
|
||||
"INSERT OR REPLACE INTO character_reputation (character_id, reputation) VALUES (?, ?);",
|
||||
charId, reputation);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "SQLiteDatabase.h"
|
||||
|
||||
std::vector<IPropertyReputationContribution::ContributionInfo> SQLiteDatabase::GetPropertyReputationContributions(
|
||||
const LWOOBJID propertyId, const std::string& date) {
|
||||
auto [_, result] = ExecuteSelect(
|
||||
"SELECT player_id, reputation_gained FROM property_reputation_contribution WHERE property_id = ? AND contribution_date = ?;",
|
||||
propertyId, date);
|
||||
|
||||
std::vector<IPropertyReputationContribution::ContributionInfo> contributions;
|
||||
while (!result.eof()) {
|
||||
IPropertyReputationContribution::ContributionInfo info;
|
||||
info.playerId = result.getInt64Field("player_id");
|
||||
info.reputationGained = static_cast<uint32_t>(result.getIntField("reputation_gained"));
|
||||
contributions.push_back(info);
|
||||
result.nextRow();
|
||||
}
|
||||
return contributions;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdatePropertyReputationContribution(
|
||||
const LWOOBJID propertyId, const LWOOBJID playerId,
|
||||
const std::string& date, const uint32_t reputationGained) {
|
||||
ExecuteInsert(
|
||||
"INSERT OR REPLACE INTO property_reputation_contribution (property_id, player_id, contribution_date, reputation_gained) "
|
||||
"VALUES (?, ?, ?, ?);",
|
||||
propertyId, playerId, date, reputationGained);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdatePropertyReputation(const LWOOBJID propertyId, const uint32_t reputation) {
|
||||
ExecuteUpdate("UPDATE properties SET reputation = ? WHERE id = ?;", reputation, propertyId);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef TESTSQLDATABASE_H
|
||||
#define TESTSQLDATABASE_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include "GameDatabase.h"
|
||||
|
||||
class TestSQLDatabase : public GameDatabase {
|
||||
@@ -103,11 +104,27 @@ class TestSQLDatabase : public GameDatabase {
|
||||
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<LWOOBJID> characterId) override {};
|
||||
void DeleteUgcBuild(const LWOOBJID bigId) override {};
|
||||
uint32_t GetAccountCount() override { return 0; };
|
||||
std::vector<LWOOBJID> GetAllCharacterIds() override { return {}; };
|
||||
int64_t GetCharacterReputation(const LWOOBJID charId) override {
|
||||
auto it = m_CharacterReputation.find(charId);
|
||||
return it != m_CharacterReputation.end() ? it->second : 0;
|
||||
};
|
||||
void SetCharacterReputation(const LWOOBJID charId, const int64_t reputation) override {
|
||||
m_CharacterReputation[charId] = reputation;
|
||||
};
|
||||
std::vector<IPropertyReputationContribution::ContributionInfo> GetPropertyReputationContributions(
|
||||
const LWOOBJID propertyId, const std::string& date) override { return {}; };
|
||||
void UpdatePropertyReputationContribution(
|
||||
const LWOOBJID propertyId, const LWOOBJID playerId,
|
||||
const std::string& date, const uint32_t reputationGained) override {};
|
||||
void UpdatePropertyReputation(const LWOOBJID propertyId, const uint32_t reputation) override {};
|
||||
|
||||
bool IsNameInUse(const std::string_view name) override { return false; };
|
||||
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override { return {}; }
|
||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOOBJID id) override { return {}; }
|
||||
std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) override { return {}; }
|
||||
private:
|
||||
std::unordered_map<LWOOBJID, int64_t> m_CharacterReputation;
|
||||
};
|
||||
|
||||
#endif //!TESTSQLDATABASE_H
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Logger.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "ModelNormalizeMigration.h"
|
||||
#include "CharacterReputationMigration.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
@@ -49,6 +50,7 @@ void MigrationRunner::RunMigrations() {
|
||||
bool runNormalizeMigrations = false;
|
||||
bool runNormalizeAfterFirstPartMigrations = false;
|
||||
bool runBrickBuildsNotOnGrid = false;
|
||||
bool runCharacterReputationMigration = false;
|
||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
|
||||
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
|
||||
|
||||
@@ -67,6 +69,9 @@ void MigrationRunner::RunMigrations() {
|
||||
runNormalizeAfterFirstPartMigrations = true;
|
||||
} else if (migration.name.ends_with("_brickbuilds_not_on_grid.sql")) {
|
||||
runBrickBuildsNotOnGrid = true;
|
||||
} else if (migration.name.ends_with("_character_reputation.sql")) {
|
||||
runCharacterReputationMigration = true;
|
||||
finalSQL.append(migration.data.c_str());
|
||||
} else {
|
||||
finalSQL.append(migration.data.c_str());
|
||||
}
|
||||
@@ -74,7 +79,7 @@ void MigrationRunner::RunMigrations() {
|
||||
Database::Get()->InsertMigration(migration.name);
|
||||
}
|
||||
|
||||
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations && !runBrickBuildsNotOnGrid) {
|
||||
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations && !runBrickBuildsNotOnGrid && !runCharacterReputationMigration) {
|
||||
LOG("Server database is up to date.");
|
||||
return;
|
||||
}
|
||||
@@ -110,6 +115,11 @@ void MigrationRunner::RunMigrations() {
|
||||
if (runBrickBuildsNotOnGrid) {
|
||||
ModelNormalizeMigration::RunBrickBuildGrid();
|
||||
}
|
||||
|
||||
if (runCharacterReputationMigration) {
|
||||
uint32_t charactersMigrated = CharacterReputationMigration::Run();
|
||||
LOG("%u characters had their reputation migrated from XML to the database.", charactersMigrated);
|
||||
}
|
||||
}
|
||||
|
||||
void MigrationRunner::RunSQLiteMigrations() {
|
||||
|
||||
Reference in New Issue
Block a user