feat(core): add standalone functions for player settings (#22703)
This commit is contained in:
@@ -18,11 +18,95 @@
|
|||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "StringConvert.h"
|
#include "StringConvert.h"
|
||||||
#include "Tokenize.h"
|
#include "Tokenize.h"
|
||||||
|
#include "CharacterDatabase.h"
|
||||||
|
|
||||||
/*********************************************************/
|
/*********************************************************/
|
||||||
/*** PLAYER SETTINGS SYSTEM ***/
|
/*** 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)
|
void Player::_LoadCharacterSettings(PreparedQueryResult result)
|
||||||
{
|
{
|
||||||
m_charSettingsMap.clear();
|
m_charSettingsMap.clear();
|
||||||
@@ -39,21 +123,7 @@ void Player::_LoadCharacterSettings(PreparedQueryResult result)
|
|||||||
std::string source = fields[0].Get<std::string>();
|
std::string source = fields[0].Get<std::string>();
|
||||||
std::string data = fields[1].Get<std::string>();
|
std::string data = fields[1].Get<std::string>();
|
||||||
|
|
||||||
std::vector<std::string_view> tokens = Acore::Tokenize(data, ' ', false);
|
PlayerSettingVector settings = PlayerSettingsStore::ParseSettingsData(data);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_charSettingsMap.emplace(std::move(source), std::move(settings));
|
m_charSettingsMap.emplace(std::move(source), std::move(settings));
|
||||||
|
|
||||||
} while (result->NextRow());
|
} while (result->NextRow());
|
||||||
@@ -81,16 +151,7 @@ void Player::_SavePlayerSettings(CharacterDatabaseTransaction trans)
|
|||||||
if (settings.empty())
|
if (settings.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::ostringstream data;
|
CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(GetGUID().GetCounter(), source, settings);
|
||||||
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());
|
|
||||||
trans->Append(stmt);
|
trans->Append(stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#ifndef _PLAYER_SETTINGS_H
|
#ifndef _PLAYER_SETTINGS_H
|
||||||
#define _PLAYER_SETTINGS_H
|
#define _PLAYER_SETTINGS_H
|
||||||
|
#include "DatabaseEnvFwd.h"
|
||||||
|
|
||||||
class Player;
|
class Player;
|
||||||
|
|
||||||
@@ -51,4 +52,18 @@ struct PlayerSetting
|
|||||||
typedef std::vector<PlayerSetting> PlayerSettingVector;
|
typedef std::vector<PlayerSetting> PlayerSettingVector;
|
||||||
typedef std::map<std::string, PlayerSettingVector> PlayerSettingMap;
|
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
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user