From 432f4f6ff9233aec9e9c5cef43a7aee234af1cb0 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Thu, 1 Oct 2026 16:38:56 -0500 Subject: [PATCH] fix(component): send a script component only when the client makes one The client makes a script component for a registry script with a non-zero id, or for a custom script in the config it is sent (LOT 176 never): ApplyScriptComponentIfHasScript and LWOScriptComponent's allocator. A character's script and a server-only custom_script_server stay on the server and send nothing. The 5 LOTs whose registry script id is 0 (e.g. the AG concert fan, LOT 3002: live sent no config and no script) lose the bit the client never read. Co-Authored-By: Claude Opus 5.5 --- dGame/Entity.cpp | 6 ++++++ dGame/Entity.h | 3 +++ dGame/dComponents/ScriptComponent.cpp | 9 +++++++++ 3 files changed, 18 insertions(+) diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 04f42550a..959b180ad 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -1133,6 +1133,12 @@ void Entity::SetGMLevel(eGameMasterLevel value) { } } +bool Entity::IsVarSentToClient(const std::u16string& name) const { + if (GetComponent() && !GetComponent()) return m_Settings.values.contains(name); + const auto& syncLDF = GetVar>(u"syncLDF"); + return std::ranges::find(syncLDF, name) != syncLDF.end(); +} + void Entity::WriteLDFData(const LwoNameValue& ldf, RakNet::BitStream& outBitStream) const { RakNet::BitStream settingStream; int32_t numberOfValidKeys = ldf.values.size(); diff --git a/dGame/Entity.h b/dGame/Entity.h index e9618867c..2d28368ec 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -353,6 +353,9 @@ public: */ std::string GetVarAsString(const std::u16string& name) const; + // Whether the construction sends this level config key to the client (a model's settings, or syncLDF) + bool IsVarSentToClient(const std::u16string& name) const; + /* * Collision */ diff --git a/dGame/dComponents/ScriptComponent.cpp b/dGame/dComponents/ScriptComponent.cpp index 287b4cba0..d13922e41 100644 --- a/dGame/dComponents/ScriptComponent.cpp +++ b/dGame/dComponents/ScriptComponent.cpp @@ -51,6 +51,15 @@ void ScriptComponent::LoadConfigData() { m_Client = client && m_ScriptName.empty(); SetScript(m_ScriptName); + + // Sent only when the client makes a script component too: its registry script (not one with id 0), or a custom + // script in the config the client gets (ApplyScriptComponentIfHasScript 0x01031460, LOT 176 never; the + // allocator 0x0104d710). A character's and a server-only custom script stay on the server. + const auto clientCustomScript = [this](const std::u16string& key) { + return m_Parent->IsVarSentToClient(key) && !m_Parent->GetVarAsString(key).empty(); + }; + m_Serialized = (m_ComponentID > 0 && !isCharacter) + || (m_Parent->GetLOT() != 176 && (clientCustomScript(u"custom_script_server") || clientCustomScript(u"custom_script_client"))); } void ScriptComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {