From fae7cf7773dee77a40b65495dd66eea69035f032 Mon Sep 17 00:00:00 2001 From: Emagi Date: Mon, 18 Aug 2025 13:56:53 -0400 Subject: [PATCH] character properties not hard coded are now dynamically fed into the info struct string, we can now SetInfoStruct(Player, "somevar", "avalue") and it will save to the DB to persist cross zone with GetInfoStruct(Player, "somevar") --- source/WorldServer/Entity.cpp | 417 ++++++++++++++++----------- source/WorldServer/Entity.h | 12 +- source/WorldServer/WorldDatabase.cpp | 246 ++++++++++------ source/WorldServer/WorldDatabase.h | 3 +- source/WorldServer/client.cpp | 1 + 5 files changed, 416 insertions(+), 263 deletions(-) diff --git a/source/WorldServer/Entity.cpp b/source/WorldServer/Entity.cpp index d5e0c49..f3d152b 100644 --- a/source/WorldServer/Entity.cpp +++ b/source/WorldServer/Entity.cpp @@ -32,6 +32,7 @@ #include "Skills.h" #include "Rules/Rules.h" #include "LuaInterface.h" +#include "WorldDatabase.h" extern World world; extern MasterItemList master_item_list; @@ -40,6 +41,7 @@ extern MasterSkillList master_skill_list; extern RuleManager rule_manager; extern Classes classes; extern LuaInterface* lua_interface; +extern WorldDatabase database; Entity::Entity(){ MapInfoStruct(); @@ -616,6 +618,65 @@ void Entity::MapInfoStruct() set_float_funcs["max_chase_distance"] = l::bind(&InfoStruct::set_max_chase_distance, &info_struct, l::_1); } +void Entity::RegisterProperty(const std::string& name) { + int32 charID = 0; + + if(IsPlayer()) + charID = ((Player*)this)->GetCharacterID(); + + { + std::shared_lock rlock(propertiesMutex); + if (set_string_funcs.find(name) != set_string_funcs.end() || + get_string_funcs.find(name) != get_string_funcs.end()) + return; + } + + { + std::unique_lock wlock(propertiesMutex); + if (set_string_funcs.find(name) != set_string_funcs.end() || + get_string_funcs.find(name) != get_string_funcs.end()) { + return; + } + + set_string_funcs.emplace(name, [this, name, database, charID](std::string v) { + { + std::lock_guard lk(GetInfoStruct()->classMutex); + GetInfoStruct()->props[name] = v; + } + if(charID) + database.insertCharacterProperty(charID, const_cast(name.c_str()), const_cast(v.c_str())); + }); + + get_string_funcs.emplace(name, [this, name]() -> std::string { + std::lock_guard lk(GetInfoStruct()->classMutex); + auto it = GetInfoStruct()->props.find(name); + return (it == GetInfoStruct()->props.end()) ? std::string{} : it->second; + }); + } +} + +void Entity::SetProperty(const std::string& name, const std::string& value) { + auto it = set_string_funcs.find(name); + if (it == set_string_funcs.end()) + return; + + int32 charID = 0; + + if(IsPlayer()) + charID = ((Player*)this)->GetCharacterID(); + + if(charID) + database.insertCharacterProperty(charID, const_cast(name.c_str()), const_cast(value.c_str())); + + return it->second(value); +} + +std::optional Entity::GetProperty(const std::string& name) const { + auto it = get_string_funcs.find(name); + if (it == get_string_funcs.end()) return std::nullopt; + return it->second(); +} + bool Entity::HasMoved(bool include_heading){ if(GetX() == last_x && GetY() == last_y && GetZ() == last_z && ((!include_heading) || (include_heading && GetHeading() == last_heading))) return false; @@ -3780,251 +3841,271 @@ void Entity::HaltMovement() std::string Entity::GetInfoStructString(std::string field) { - map>::const_iterator itr = get_string_funcs.find(field); - if(itr != get_string_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_string_funcs.find(field); + if(itr != get_string_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return std::string(""); } int8 Entity::GetInfoStructInt8(std::string field) { - map>::const_iterator itr = get_int8_funcs.find(field); - if(itr != get_int8_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int8_funcs.find(field); + if(itr != get_int8_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } int16 Entity::GetInfoStructInt16(std::string field) { - map>::const_iterator itr = get_int16_funcs.find(field); - if(itr != get_int16_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int16_funcs.find(field); + if(itr != get_int16_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } int32 Entity::GetInfoStructInt32(std::string field) { - map>::const_iterator itr = get_int32_funcs.find(field); - if(itr != get_int32_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int32_funcs.find(field); + if(itr != get_int32_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } int64 Entity::GetInfoStructInt64(std::string field) { - map>::const_iterator itr = get_int64_funcs.find(field); - if(itr != get_int64_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int64_funcs.find(field); + if(itr != get_int64_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } sint8 Entity::GetInfoStructSInt8(std::string field) { - map>::const_iterator itr = get_sint8_funcs.find(field); - if(itr != get_sint8_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint8_funcs.find(field); + if(itr != get_sint8_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } sint16 Entity::GetInfoStructSInt16(std::string field) { - map>::const_iterator itr = get_sint16_funcs.find(field); - if(itr != get_sint16_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint16_funcs.find(field); + if(itr != get_sint16_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } sint32 Entity::GetInfoStructSInt32(std::string field) { - map>::const_iterator itr = get_sint32_funcs.find(field); - if(itr != get_sint32_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint32_funcs.find(field); + if(itr != get_sint32_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } sint64 Entity::GetInfoStructSInt64(std::string field) { - map>::const_iterator itr = get_sint64_funcs.find(field); - if(itr != get_sint64_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint64_funcs.find(field); + if(itr != get_sint64_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } float Entity::GetInfoStructFloat(std::string field) { - map>::const_iterator itr = get_float_funcs.find(field); - if(itr != get_float_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_float_funcs.find(field); + if(itr != get_float_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0.0f; } int64 Entity::GetInfoStructUInt(std::string field) { - map>::const_iterator itr = get_int8_funcs.find(field); - if(itr != get_int8_funcs.end()) - { - auto func = (itr->second)(); - return func; - } - map>::const_iterator itr2 = get_int16_funcs.find(field); - if(itr2 != get_int16_funcs.end()) - { - auto func = (itr2->second)(); - return func; - } - map>::const_iterator itr3 = get_int32_funcs.find(field); - if(itr3 != get_int32_funcs.end()) - { - auto func = (itr3->second)(); - return func; - } - map>::const_iterator itr4 = get_int64_funcs.find(field); - if(itr4 != get_int64_funcs.end()) - { - auto func = (itr4->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int8_funcs.find(field); + if(itr != get_int8_funcs.end()) + { + auto func = (itr->second)(); + return func; + } + map>::const_iterator itr2 = get_int16_funcs.find(field); + if(itr2 != get_int16_funcs.end()) + { + auto func = (itr2->second)(); + return func; + } + map>::const_iterator itr3 = get_int32_funcs.find(field); + if(itr3 != get_int32_funcs.end()) + { + auto func = (itr3->second)(); + return func; + } + map>::const_iterator itr4 = get_int64_funcs.find(field); + if(itr4 != get_int64_funcs.end()) + { + auto func = (itr4->second)(); + return func; + } return 0; } sint64 Entity::GetInfoStructSInt(std::string field) { - map>::const_iterator itr = get_sint8_funcs.find(field); - if(itr != get_sint8_funcs.end()) - { - auto func = (itr->second)(); - return func; - } - map>::const_iterator itr2 = get_sint16_funcs.find(field); - if(itr2 != get_sint16_funcs.end()) - { - auto func = (itr2->second)(); - return func; - } - map>::const_iterator itr3 = get_sint32_funcs.find(field); - if(itr3 != get_sint32_funcs.end()) - { - auto func = (itr3->second)(); - return func; - } - map>::const_iterator itr4 = get_sint64_funcs.find(field); - if(itr4 != get_sint64_funcs.end()) - { - auto func = (itr4->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint8_funcs.find(field); + if(itr != get_sint8_funcs.end()) + { + auto func = (itr->second)(); + return func; + } + map>::const_iterator itr2 = get_sint16_funcs.find(field); + if(itr2 != get_sint16_funcs.end()) + { + auto func = (itr2->second)(); + return func; + } + map>::const_iterator itr3 = get_sint32_funcs.find(field); + if(itr3 != get_sint32_funcs.end()) + { + auto func = (itr3->second)(); + return func; + } + map>::const_iterator itr4 = get_sint64_funcs.find(field); + if(itr4 != get_sint64_funcs.end()) + { + auto func = (itr4->second)(); + return func; + } return 0; } bool Entity::SetInfoStructString(std::string field, std::string value) { - map>::const_iterator itr = set_string_funcs.find(field); - if(itr != set_string_funcs.end()) - { - (itr->second)(value); - return true; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = set_string_funcs.find(field); + if(itr != set_string_funcs.end()) + { + (itr->second)(value); + return true; + } + else { + RegisterProperty(field); + SetProperty(field, value); + } return false; } bool Entity::SetInfoStructUInt(std::string field, int64 value) { - map>::const_iterator itr = set_int8_funcs.find(field); - if(itr != set_int8_funcs.end()) - { - (itr->second)((int8)value); - return true; - } - map>::const_iterator itr2 = set_int16_funcs.find(field); - if(itr2 != set_int16_funcs.end()) - { - (itr2->second)((int16)value); - return true; - } - map>::const_iterator itr3 = set_int32_funcs.find(field); - if(itr3 != set_int32_funcs.end()) - { - (itr3->second)((int32)value); - return true; - } - map>::const_iterator itr4 = set_int64_funcs.find(field); - if(itr4 != set_int64_funcs.end()) - { - (itr4->second)(value); - return true; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = set_int8_funcs.find(field); + if(itr != set_int8_funcs.end()) + { + (itr->second)((int8)value); + return true; + } + map>::const_iterator itr2 = set_int16_funcs.find(field); + if(itr2 != set_int16_funcs.end()) + { + (itr2->second)((int16)value); + return true; + } + map>::const_iterator itr3 = set_int32_funcs.find(field); + if(itr3 != set_int32_funcs.end()) + { + (itr3->second)((int32)value); + return true; + } + map>::const_iterator itr4 = set_int64_funcs.find(field); + if(itr4 != set_int64_funcs.end()) + { + (itr4->second)(value); + return true; + } return false; } bool Entity::SetInfoStructSInt(std::string field, sint64 value) { - map>::const_iterator itr = set_sint8_funcs.find(field); - if(itr != set_sint8_funcs.end()) - { - (itr->second)((sint8)value); - return true; - } - map>::const_iterator itr2 = set_sint16_funcs.find(field); - if(itr2 != set_sint16_funcs.end()) - { - (itr2->second)((sint16)value); - return true; - } - map>::const_iterator itr3 = set_sint32_funcs.find(field); - if(itr3 != set_sint32_funcs.end()) - { - (itr3->second)((sint32)value); - return true; - } - map>::const_iterator itr4 = set_sint64_funcs.find(field); - if(itr4 != set_sint64_funcs.end()) - { - (itr4->second)(value); - return true; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = set_sint8_funcs.find(field); + if(itr != set_sint8_funcs.end()) + { + (itr->second)((sint8)value); + return true; + } + map>::const_iterator itr2 = set_sint16_funcs.find(field); + if(itr2 != set_sint16_funcs.end()) + { + (itr2->second)((sint16)value); + return true; + } + map>::const_iterator itr3 = set_sint32_funcs.find(field); + if(itr3 != set_sint32_funcs.end()) + { + (itr3->second)((sint32)value); + return true; + } + map>::const_iterator itr4 = set_sint64_funcs.find(field); + if(itr4 != set_sint64_funcs.end()) + { + (itr4->second)(value); + return true; + } return false; } bool Entity::SetInfoStructFloat(std::string field, float value) { - map>::const_iterator itr = set_float_funcs.find(field); - if(itr != set_float_funcs.end()) - { - (itr->second)(value); - return true; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = set_float_funcs.find(field); + if(itr != set_float_funcs.end()) + { + (itr->second)(value); + return true; + } return false; } diff --git a/source/WorldServer/Entity.h b/source/WorldServer/Entity.h index fd16b6d..c0dccf9 100644 --- a/source/WorldServer/Entity.h +++ b/source/WorldServer/Entity.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -1110,6 +1112,9 @@ struct InfoStruct{ // maintained via their own mutex SpellEffects spell_effects[45]; MaintainedEffects maintained_effects[30]; + // when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock + std::mutex classMutex; + std::unordered_map props; private: std::string name_; int8 class1_; @@ -1328,8 +1333,6 @@ private: int8 max_spell_reduction_override_; float max_chase_distance_; - // when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock - std::mutex classMutex; }; struct WardInfo { @@ -1449,6 +1452,9 @@ public: void DeleteSpellEffects(bool removeClient = false); void RemoveSpells(bool unfriendlyOnly = false); void MapInfoStruct(); + void RegisterProperty(const std::string& name); + void SetProperty(const std::string& name, const std::string& value); + std::optional GetProperty(const std::string& name) const; virtual float GetDodgeChance(); virtual void AddMaintainedSpell(LuaSpell* spell); virtual void AddSpellEffect(LuaSpell* spell, int32 override_expire_time = 0); @@ -2205,6 +2211,8 @@ private: map > set_sint8_funcs; map > set_string_funcs; + + mutable std::shared_mutex propertiesMutex; }; #endif diff --git a/source/WorldServer/WorldDatabase.cpp b/source/WorldServer/WorldDatabase.cpp index 4a7a550..7061bc2 100644 --- a/source/WorldServer/WorldDatabase.cpp +++ b/source/WorldServer/WorldDatabase.cpp @@ -2146,125 +2146,187 @@ bool WorldDatabase::insertCharacterProperty(Client* client, char* propName, char return true; } -bool WorldDatabase::loadCharacterProperties(Client* client) { +bool WorldDatabase::insertCharacterProperty(int32 charID, char* propName, char* propValue) { + Query query, query2; + + string update_status = string("update character_properties set propvalue='%s' where charid=%i and propname='%s'"); + query.RunQuery2(Q_UPDATE, update_status.c_str(), propValue, charID, propName); + if (!query.GetAffectedRows()) + { + query2.RunQuery2(Q_UPDATE, "insert into character_properties (charid, propname, propvalue) values(%i, '%s', '%s')", charID, propName, propValue); + if (query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF) { + LogWrite(WORLD__ERROR, 0, "World", "Error in insertCharacterProperty query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + } + return true; +} + +bool WorldDatabase::loadCharacterProperties(Client* client, bool preload) { Query query; MYSQL_ROW row; int32 id = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT propname, propvalue FROM character_properties where charid = %i", client->GetCharacterID()); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, + "SELECT propname, propvalue FROM character_properties WHERE charid = %i", + client->GetCharacterID()); + // no character found if (result == NULL) { - LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character properties for '%s'", client->GetPlayer()->GetName()); + LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character properties for '%s'", + client->GetPlayer()->GetName()); return false; } while (result && (row = mysql_fetch_row(result))) { - char* prop_name = row[0]; + char* prop_name = row[0]; char* prop_value = row[1]; if (!prop_name || !prop_value) continue; - if (!stricmp(prop_name, CHAR_PROPERTY_SPEED)) - { - float new_speed = atof(prop_value); - client->GetPlayer()->SetSpeed(new_speed, true); - client->GetPlayer()->SetCharSheetChanged(true); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE)) - { - int8 flymode = atoul(prop_value); - if (flymode) // avoid fly mode notification unless enabled - ClientPacketFunctions::SendFlyMode(client, flymode, false); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_INVUL)) - { - int8 invul = atoul(prop_value); - client->GetPlayer()->SetInvulnerable(invul == 1); - if (client->GetPlayer()->GetInvulnerable()) - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!"); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_GMVISION)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->SetGMVision(val == 1); - client->GetCurrentZone()->SendAllSpawnsForVisChange(client, false); - if (val) - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "GM Vision Enabled!"); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_REGIONDEBUG)) - { - int8 val = atoul(prop_value); + bool matched = false; - client->SetRegionDebug(val == 1); - if (val) - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Region Debug Enabled!"); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_LUADEBUG)) - { - int8 val = atoul(prop_value); - if (val) - { - client->SetLuaDebugClient(true); - if (lua_interface) - lua_interface->UpdateDebugClients(client); - - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You will now receive LUA error messages."); + if (!stricmp(prop_name, CHAR_PROPERTY_SPEED)) { + matched = true; + if (!preload) { + float new_speed = (float)atof(prop_value); + client->GetPlayer()->SetSpeed(new_speed, true); + client->GetPlayer()->SetCharSheetChanged(true); } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTMETHOD)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_loot_method(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE)) { + matched = true; + if (!preload) { + int8 flymode = (int8)atoul(prop_value); + if (flymode) // avoid fly mode notification unless enabled + ClientPacketFunctions::SendFlyMode(client, flymode, false); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTITEMRARITY)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_loot_items_rarity(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_INVUL)) { + matched = true; + if (!preload) { + int8 invul = (int8)atoul(prop_value); + client->GetPlayer()->SetInvulnerable(invul == 1); + if (client->GetPlayer()->GetInvulnerable()) + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!"); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOSPLIT)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_auto_split(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GMVISION)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->SetGMVision(val == 1); + client->GetCurrentZone()->SendAllSpawnsForVisChange(client, false); + if (val) + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "GM Vision Enabled!"); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPDEFAULTYELL)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_default_yell(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_REGIONDEBUG)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->SetRegionDebug(val == 1); + if (val) + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Region Debug Enabled!"); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOLOCK)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_autolock(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_LUADEBUG)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + if (val) { + client->SetLuaDebugClient(true); + if (lua_interface) + lua_interface->UpdateDebugClients(client); + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You will now receive LUA error messages."); + } + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPSOLOAUTOLOCK)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_solo_autolock(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTMETHOD)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_loot_method(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_AUTOLOOTMETHOD)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_auto_loot_method(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTITEMRARITY)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_loot_items_rarity(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOCKMETHOD)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_lock_method(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOSPLIT)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_auto_split(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_ASSISTAUTOATTACK)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_assist_auto_attack(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPDEFAULTYELL)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_default_yell(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_SETACTIVEFOOD)) - { - int32 val = atoul(prop_value); - client->GetPlayer()->SetActiveFoodUniqueID(val, false); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOLOCK)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_autolock(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_SETACTIVEDRINK)) - { - int32 val = atoul(prop_value); - client->GetPlayer()->SetActiveDrinkUniqueID(val, false); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPSOLOAUTOLOCK)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_solo_autolock(val); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_AUTOLOOTMETHOD)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_auto_loot_method(val); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOCKMETHOD)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_lock_method(val); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_ASSISTAUTOATTACK)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_assist_auto_attack(val); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_SETACTIVEFOOD)) { + matched = true; + if (!preload) { + int32 val = (int32)atoul(prop_value); + client->GetPlayer()->SetActiveFoodUniqueID(val, false); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_SETACTIVEDRINK)) { + matched = true; + if (!preload) { + int32 val = (int32)atoul(prop_value); + client->GetPlayer()->SetActiveDrinkUniqueID(val, false); + } + } + else { + // Unknown property: only act during preload AND only if no stricmp matched + // (we're in the 'else', so matched == false by definition) + if (preload) { + client->GetPlayer()->RegisterProperty(std::string(prop_name)); + client->GetPlayer()->SetProperty(std::string(prop_name), std::string(prop_value)); + } + // When preload == false, we intentionally ignore unknown properties. } } diff --git a/source/WorldServer/WorldDatabase.h b/source/WorldServer/WorldDatabase.h index 6c61261..0ef5067 100644 --- a/source/WorldServer/WorldDatabase.h +++ b/source/WorldServer/WorldDatabase.h @@ -333,7 +333,8 @@ public: bool InsertCharacterStats(int32 character_id, int8 class_id, int8 race_id); bool UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp); bool insertCharacterProperty(Client* client, char* propName, char* propValue); - bool loadCharacterProperties(Client* client); + bool insertCharacterProperty(int32 charID, char* propName, char* propValue); + bool loadCharacterProperties(Client* client, bool preload = false); string GetPlayerName(char* name); int32 GetCharacterTimeStamp(int32 character_id, int32 account_id,bool* char_exists); int32 GetCharacterTimeStamp(int32 character_id); diff --git a/source/WorldServer/client.cpp b/source/WorldServer/client.cpp index 4766c66..186af6a 100644 --- a/source/WorldServer/client.cpp +++ b/source/WorldServer/client.cpp @@ -12249,6 +12249,7 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code) new_client_login = NewLoginState::LOGIN_ALLOWED; } + database.loadCharacterProperties(this, true); // vault slots RefreshVaultSlotCount();