From 536c36227c1fd58ebd9f9d75d629e0d01bcb6acb Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Sat, 26 Sep 2026 18:42:13 -0500 Subject: [PATCH] fix: read set_faction the way the client does Level files set factions with set_faction, and most of the string-typed values have a trailing space ("13:6 ", "13:-1 ") or hold several factions separated by spaces ("13:4 6"). The server split only on ';' and TryParse rejects trailing characters, so about 1,500 placed objects (smashable lanterns, fences, candles, treasure chest spawners and so on) silently kept their component faction instead. LWODestroyableComponent::LoadConfigData in the 1.10.64 client reads set_faction as a string, splits it on both ';' and ' ', and replaces its faction list with the result. The server now does the same: split on both, skip empty pieces, and replace the factions (SetFaction for the first, AddFaction for the rest). This also removes the TODO about splitting on spaces, and the faction list from the destructible table no longer uses std::stoi on a token that TryParse already parsed. Values in the shipped maps were counted with a scan of the .lvl files. Needs an in-game check that objects placed with set_faction (for example the AG Survival buff stations and NT treasure chests) are still targeted or ignored as expected; this may also affect #1301. Co-Authored-By: Claude Opus 5.5 --- dGame/Entity.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 5d4da1bcd..ce573824b 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -474,24 +474,26 @@ void Entity::Initialize() { const auto tokenInt = GeneralUtils::TryParse(token); if (tokenInt == destCompData[0].faction) continue; - if (!token.empty()) { - comp->AddFaction(std::stoi(token)); - } + if (tokenInt) comp->AddFaction(tokenInt.value()); } } - // override the factions if needed. + // Level files can replace the factions with set_faction. The client splits the value on + // both ';' and ' ' and replaces its faction list with the result + // (LWODestroyableComponent::LoadConfigData), and many values have a trailing space ("13:6 "). const auto setFaction = GetVarAsString(u"set_faction"); - if (!setFaction.empty()) { - // TODO also split on space here however we do not have a general util for splitting on multiple characters yet. - const auto factionsToAdd = GeneralUtils::SplitString(setFaction, ';'); - for (const auto& faction : factionsToAdd) { + std::vector factionsToSet; + for (const auto& semicolonSplit : GeneralUtils::SplitString(setFaction, ';')) { + for (const auto& faction : GeneralUtils::SplitString(semicolonSplit, ' ')) { const auto factionToAdd = GeneralUtils::TryParse(faction); - if (factionToAdd) { - comp->AddFaction(factionToAdd.value(), true); - } + if (factionToAdd) factionsToSet.push_back(factionToAdd.value()); } } + + if (!factionsToSet.empty()) { + comp->SetFaction(factionsToSet.front(), true); + for (const auto faction : factionsToSet | std::views::drop(1)) comp->AddFaction(faction, true); + } } const auto characterID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER);