From 9aa06f349c50a1ec8a070797607d8a01645b5a9d Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Fri, 2 Oct 2026 08:19:18 -0500 Subject: [PATCH] fix(entity): message handlers run in the order they registered, as the client Handlers sat in an unordered_multimap, whose order for one message is up to the standard library: GCC ran them newest first, so the components loaded last answered a RequestUse first, and Windows builds could differ. They are now kept per message in registration order: the object's own handlers, then its components' in load order (LWOGameObject::PassMessageToComponents). Co-Authored-By: Claude Opus 5.5 --- dGame/Entity.cpp | 13 +++++++++---- dGame/Entity.h | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index b689de8bf..95175d9f4 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -1731,18 +1731,23 @@ int32_t Entity::GetCollisionGroup() const { return 0; } +// LWOGameObject::PassMessageToComponents (0x00f62500): every handler registered for the message, in registration +// order; only the ones there when the call starts (the client copies the list first; GetPosition is too hot for a copy) bool Entity::HandleMsg(GameMessages::GameMsg& msg) const { + const auto it = m_MsgHandlers.find(msg.msgId); + if (it == m_MsgHandlers.end()) return false; + bool handled = false; - auto [beg, end] = m_MsgHandlers.equal_range(msg.msgId); - for (auto& it = beg; it != end; ++it) { - if (it->second) handled |= it->second(msg); + const auto& handlers = it->second; + for (size_t i = 0, count = handlers.size(); i < count; i++) { + if (handlers[i]) handled |= handlers[i](msg); } return handled; } void Entity::RegisterMsg(const MessageType::Game msgId, std::function handler) { - m_MsgHandlers.emplace(msgId, handler); + m_MsgHandlers[msgId].push_back(std::move(handler)); } bool Entity::MsgRequestServerObjectInfo(GameMessages::RequestServerObjectInfoEvent& event) { diff --git a/dGame/Entity.h b/dGame/Entity.h index 5906d6448..8a2b93c5b 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -480,7 +480,9 @@ private: // objectID of receiver and map of notification name to script std::map> m_Subscriptions; - std::unordered_multimap> m_MsgHandlers; + // Each message's handlers in the order they registered: the object's own, then its components' in load order + // (LWOGameObject::registeredGameMessages) + std::unordered_map>> m_MsgHandlers; }; /**