feat(core): add standalone functions for player settings (#22703)

This commit is contained in:
Yehonal
2025-08-19 03:29:34 +02:00
committed by GitHub
parent f41fb5a169
commit 2ae1dbeab2
2 changed files with 101 additions and 25 deletions

View File

@@ -18,11 +18,95 @@
#include "Player.h"
#include "StringConvert.h"
#include "Tokenize.h"
#include "CharacterDatabase.h"
/*********************************************************/
/*** PLAYER SETTINGS SYSTEM ***/
/*********************************************************/
namespace PlayerSettingsStore
{
// Common helper: parse space-separated data string into PlayerSettingVector
PlayerSettingVector ParseSettingsData(std::string const& data)
{
PlayerSettingVector result;
std::vector<std::string_view> tokens = Acore::Tokenize(data, ' ', false);
result.reserve(tokens.size());
for (auto const& token : tokens)
{
if (token.empty())
continue;
if (auto parsed = Acore::StringTo<uint32>(token))
result.emplace_back(*parsed);
}
return result;
}
// Common helper: serialize PlayerSettingVector to space-separated string
std::string SerializeSettingsData(PlayerSettingVector const& settings)
{
if (settings.empty())
return "";
std::ostringstream data;
data << settings[0].value;
for (size_t i = 1; i < settings.size(); ++i)
data << ' ' << settings[i].value;
return data.str();
}
// helper: load a single source row for a player and parse to vector
static PlayerSettingVector LoadPlayerSettings(uint32 playerLowGuid, std::string const& source)
{
PlayerSettingVector result;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SETTINGS);
stmt->SetData(0, playerLowGuid);
PreparedQueryResult dbRes = CharacterDatabase.Query(stmt);
if (!dbRes)
return result;
do
{
Field* fields = dbRes->Fetch();
std::string rowSource = fields[0].Get<std::string>();
if (rowSource != source)
continue;
std::string data = fields[1].Get<std::string>();
return ParseSettingsData(data);
} while (dbRes->NextRow());
return result;
}
void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint8 index, uint32 value)
{
if (!sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED))
return;
PlayerSettingVector settings = LoadPlayerSettings(playerLowGuid, source);
size_t const requiredSize = static_cast<size_t>(index) + 1;
if (settings.size() < requiredSize)
settings.resize(requiredSize); // zero-initialized PlayerSetting::value
settings[index].value = value;
CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(playerLowGuid, source, settings);
CharacterDatabase.Execute(stmt);
}
}
// Implementation of PrepareReplaceStatement
CharacterDatabasePreparedStatement* PlayerSettingsStore::PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS);
stmt->SetData(0, playerLowGuid);
stmt->SetData(1, source);
stmt->SetData(2, SerializeSettingsData(settings));
return stmt;
}
void Player::_LoadCharacterSettings(PreparedQueryResult result)
{
m_charSettingsMap.clear();
@@ -39,21 +123,7 @@ void Player::_LoadCharacterSettings(PreparedQueryResult result)
std::string source = fields[0].Get<std::string>();
std::string data = fields[1].Get<std::string>();
std::vector<std::string_view> tokens = Acore::Tokenize(data, ' ', false);
PlayerSettingVector settings;
settings.reserve(tokens.size()); // reserve capacity but don't resize
for (auto const& token : tokens)
{
if (token.empty())
continue;
// Try to parse the value safely
if (auto parsed = Acore::StringTo<uint32>(token))
settings.emplace_back(*parsed);
}
PlayerSettingVector settings = PlayerSettingsStore::ParseSettingsData(data);
m_charSettingsMap.emplace(std::move(source), std::move(settings));
} while (result->NextRow());
@@ -81,16 +151,7 @@ void Player::_SavePlayerSettings(CharacterDatabaseTransaction trans)
if (settings.empty())
continue;
std::ostringstream data;
data << settings[0].value;
for (size_t i = 1; i < settings.size(); ++i)
data << ' ' << settings[i].value;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS);
stmt->SetData(0, GetGUID().GetCounter());
stmt->SetData(1, source);
stmt->SetData(2, data.str());
CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(GetGUID().GetCounter(), source, settings);
trans->Append(stmt);
}
}

View File

@@ -17,6 +17,7 @@
#ifndef _PLAYER_SETTINGS_H
#define _PLAYER_SETTINGS_H
#include "DatabaseEnvFwd.h"
class Player;
@@ -51,4 +52,18 @@ struct PlayerSetting
typedef std::vector<PlayerSetting> PlayerSettingVector;
typedef std::map<std::string, PlayerSettingVector> PlayerSettingMap;
// Standalone API: update a player's setting directly on DB by GUID (low part) without requiring a Player instance
namespace PlayerSettingsStore
{
// Update a single setting value for any player by GUID (works for online or offline players).
// This reads the existing "source" row from character_settings, adjusts the index, and REPLACE's it back.
void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint8 index, uint32 value);
// Common helpers for parsing and serializing settings data
PlayerSettingVector ParseSettingsData(std::string const& data);
std::string SerializeSettingsData(PlayerSettingVector const& settings);
// Prepare a REPLACE statement populated with given settings data. Caller may execute or append to a transaction.
CharacterDatabasePreparedStatement* PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings);
}
#endif