diff --git a/Server/mods/deathmatch/logic/CElement.cpp b/Server/mods/deathmatch/logic/CElement.cpp index d8cc25bf7e..793e509a48 100644 --- a/Server/mods/deathmatch/logic/CElement.cpp +++ b/Server/mods/deathmatch/logic/CElement.cpp @@ -438,10 +438,11 @@ bool CElement::CallEvent(const char* szName, const CLuaArguments& Arguments, CPl if (!g_pGame->GetDebugHookManager()->OnPreEvent(szName, Arguments, this, pCaller)) return false; - CEvents* pEvents = g_pGame->GetEvents(); + CEvents* pEvents = g_pGame->GetEvents(); + CEventContext eventContext; // Make sure our event-manager knows we're about to call an event - pEvents->PreEventPulse(); + pEvents->PreEventPulse(&eventContext); // Call the event on our parents/us first CallParentEvent(szName, Arguments, this, pCaller); @@ -450,15 +451,13 @@ bool CElement::CallEvent(const char* szName, const CLuaArguments& Arguments, CPl CallEventNoParent(szName, Arguments, this, pCaller); // Tell the event manager that we're done calling the event - pEvents->PostEventPulse(); + pEvents->PostEventPulse(&eventContext); g_pGame->GetDebugHookManager()->OnPostEvent(szName, Arguments, this, pCaller); // Return whether our event was cancelled or not - return (!pEvents->WasEventCancelled()); -} - -bool CElement::DeleteEvent(CLuaMain* pLuaMain, const char* szName, const CLuaFunctionRef& iLuaFunction) + return !eventContext.IsCancelled(); +}bool CElement::DeleteEvent(CLuaMain* pLuaMain, const char* szName, const CLuaFunctionRef& iLuaFunction) { return m_pEventManager->Delete(pLuaMain, szName, iLuaFunction); } diff --git a/Server/mods/deathmatch/logic/CEvents.cpp b/Server/mods/deathmatch/logic/CEvents.cpp index eb82ea1e09..ed627d78c5 100644 --- a/Server/mods/deathmatch/logic/CEvents.cpp +++ b/Server/mods/deathmatch/logic/CEvents.cpp @@ -123,38 +123,68 @@ void CEvents::RemoveAllEvents() m_EventHashMap.clear(); } -void CEvents::PreEventPulse() +void CEvents::PreEventPulse(CEventContext* pContext) { + assert(pContext); + m_CancelledList.push_back(m_bEventCancelled); + m_ContextStack.push_back(pContext); + + pContext->Reset(); + m_bEventCancelled = false; m_bWasEventCancelled = false; m_strLastError = ""; } -void CEvents::PostEventPulse() +void CEvents::PostEventPulse(CEventContext* pContext) { - m_bWasEventCancelled = m_bEventCancelled; + assert(pContext); + assert(!m_ContextStack.empty()); + assert(m_ContextStack.back() == pContext); + + m_bWasEventCancelled = pContext->IsCancelled(); m_bEventCancelled = m_CancelledList.back() ? true : false; m_CancelledList.pop_back(); + m_ContextStack.pop_back(); } void CEvents::CancelEvent(bool bCancelled) { - m_bEventCancelled = bCancelled; + CancelEvent(bCancelled, nullptr); } void CEvents::CancelEvent(bool bCancelled, const char* szReason) { + // ALWAYS set the old global variable for backward compatibility m_bEventCancelled = bCancelled; - m_strLastError = SStringX(szReason); + + // Also update context if it exists + if (!m_ContextStack.empty()) + { + CEventContext* pCurrentContext = m_ContextStack.back(); + if (bCancelled) + pCurrentContext->Cancel(szReason); + else + pCurrentContext->Reset(); + } + + if (szReason) + m_strLastError = szReason; } bool CEvents::WasEventCancelled() { - return m_bWasEventCancelled; + if (!m_ContextStack.empty()) + return m_ContextStack.back()->IsCancelled(); + + return m_bEventCancelled || m_bWasEventCancelled; } const char* CEvents::GetLastError() { + if (!m_ContextStack.empty()) + return m_ContextStack.back()->GetCancelReason().c_str(); + return m_strLastError; } diff --git a/Server/mods/deathmatch/logic/CEvents.h b/Server/mods/deathmatch/logic/CEvents.h index f15a112eb2..3572c6f557 100644 --- a/Server/mods/deathmatch/logic/CEvents.h +++ b/Server/mods/deathmatch/logic/CEvents.h @@ -14,6 +14,7 @@ #include #include #include +#include "CEventContext.h" struct SEvent { @@ -40,8 +41,8 @@ class CEvents CFastHashMap::const_iterator IterBegin() { return m_EventHashMap.begin(); }; CFastHashMap::const_iterator IterEnd() { return m_EventHashMap.end(); }; - void PreEventPulse(); - void PostEventPulse(); + void PreEventPulse(CEventContext* pContext); + void PostEventPulse(CEventContext* pContext); void CancelEvent(bool bCancelled = true); void CancelEvent(bool bCancelled, const char* szReason); @@ -53,9 +54,10 @@ class CEvents CFastHashMap m_EventHashMap; - std::vector m_CancelledList; - bool m_bEventCancelled; - bool m_bWasEventCancelled; + std::vector m_CancelledList; + bool m_bEventCancelled; + bool m_bWasEventCancelled; + SString m_strLastError; - SString m_strLastError; + std::vector m_ContextStack; }; diff --git a/Shared/mods/deathmatch/logic/CEventContext.h b/Shared/mods/deathmatch/logic/CEventContext.h new file mode 100644 index 0000000000..d60af9b6fc --- /dev/null +++ b/Shared/mods/deathmatch/logic/CEventContext.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: Shared/mods/deathmatch/logic/CEventContext.h + * PURPOSE: Per-event context for event dispatch and cancellation + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include + +class CEventContext +{ +public: + CEventContext() noexcept : m_isCancelled(false) {} + + void Cancel(const char* reason = nullptr) noexcept + { + m_isCancelled = true; + if (reason) + m_cancelReason = reason; + } + + bool IsCancelled() const noexcept { return m_isCancelled; } + const std::string& GetCancelReason() const noexcept { return m_cancelReason; } + + void Reset() noexcept + { + m_isCancelled = false; + m_cancelReason.clear(); + } + +private: + bool m_isCancelled; + std::string m_cancelReason; +};