Skip to content

Commit 8d41e19

Browse files
authored
Resolve Event Context Loss in Nested Event Cacnellation (#4529)
* Implement event context management in CEvents and CElement classes * Refactor CEvents class to use a context stack instead of a single current context pointer
1 parent 3530317 commit 8d41e19

File tree

4 files changed

+89
-16
lines changed

4 files changed

+89
-16
lines changed

Server/mods/deathmatch/logic/CElement.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -438,10 +438,11 @@ bool CElement::CallEvent(const char* szName, const CLuaArguments& Arguments, CPl
438438
if (!g_pGame->GetDebugHookManager()->OnPreEvent(szName, Arguments, this, pCaller))
439439
return false;
440440

441-
CEvents* pEvents = g_pGame->GetEvents();
441+
CEvents* pEvents = g_pGame->GetEvents();
442+
CEventContext eventContext;
442443

443444
// Make sure our event-manager knows we're about to call an event
444-
pEvents->PreEventPulse();
445+
pEvents->PreEventPulse(&eventContext);
445446

446447
// Call the event on our parents/us first
447448
CallParentEvent(szName, Arguments, this, pCaller);
@@ -450,12 +451,12 @@ bool CElement::CallEvent(const char* szName, const CLuaArguments& Arguments, CPl
450451
CallEventNoParent(szName, Arguments, this, pCaller);
451452

452453
// Tell the event manager that we're done calling the event
453-
pEvents->PostEventPulse();
454+
pEvents->PostEventPulse(&eventContext);
454455

455456
g_pGame->GetDebugHookManager()->OnPostEvent(szName, Arguments, this, pCaller);
456457

457458
// Return whether our event was cancelled or not
458-
return (!pEvents->WasEventCancelled());
459+
return !eventContext.IsCancelled();
459460
}
460461

461462
bool CElement::DeleteEvent(CLuaMain* pLuaMain, const char* szName, const CLuaFunctionRef& iLuaFunction)

Server/mods/deathmatch/logic/CEvents.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,38 +123,68 @@ void CEvents::RemoveAllEvents()
123123
m_EventHashMap.clear();
124124
}
125125

126-
void CEvents::PreEventPulse()
126+
void CEvents::PreEventPulse(CEventContext* pContext)
127127
{
128+
assert(pContext);
129+
128130
m_CancelledList.push_back(m_bEventCancelled);
131+
m_ContextStack.push_back(pContext);
132+
133+
pContext->Reset();
134+
129135
m_bEventCancelled = false;
130136
m_bWasEventCancelled = false;
131137
m_strLastError = "";
132138
}
133139

134-
void CEvents::PostEventPulse()
140+
void CEvents::PostEventPulse(CEventContext* pContext)
135141
{
136-
m_bWasEventCancelled = m_bEventCancelled;
142+
assert(pContext);
143+
assert(!m_ContextStack.empty());
144+
assert(m_ContextStack.back() == pContext);
145+
146+
m_bWasEventCancelled = pContext->IsCancelled();
137147
m_bEventCancelled = m_CancelledList.back() ? true : false;
138148
m_CancelledList.pop_back();
149+
m_ContextStack.pop_back();
139150
}
140151

141152
void CEvents::CancelEvent(bool bCancelled)
142153
{
143-
m_bEventCancelled = bCancelled;
154+
CancelEvent(bCancelled, nullptr);
144155
}
145156

146157
void CEvents::CancelEvent(bool bCancelled, const char* szReason)
147158
{
159+
// ALWAYS set the old global variable for backward compatibility
148160
m_bEventCancelled = bCancelled;
149-
m_strLastError = SStringX(szReason);
161+
162+
// Also update context if it exists
163+
if (!m_ContextStack.empty())
164+
{
165+
CEventContext* pCurrentContext = m_ContextStack.back();
166+
if (bCancelled)
167+
pCurrentContext->Cancel(szReason);
168+
else
169+
pCurrentContext->Reset();
170+
}
171+
172+
if (szReason)
173+
m_strLastError = szReason;
150174
}
151175

152176
bool CEvents::WasEventCancelled()
153177
{
154-
return m_bWasEventCancelled;
178+
if (!m_ContextStack.empty())
179+
return m_ContextStack.back()->IsCancelled();
180+
181+
return m_bEventCancelled || m_bWasEventCancelled;
155182
}
156183

157184
const char* CEvents::GetLastError()
158185
{
186+
if (!m_ContextStack.empty())
187+
return m_ContextStack.back()->GetCancelReason().c_str();
188+
159189
return m_strLastError;
160190
}

Server/mods/deathmatch/logic/CEvents.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <string>
1515
#include <list>
1616
#include <vector>
17+
#include "CEventContext.h"
1718

1819
struct SEvent
1920
{
@@ -40,8 +41,8 @@ class CEvents
4041
CFastHashMap<SString, SEvent*>::const_iterator IterBegin() { return m_EventHashMap.begin(); };
4142
CFastHashMap<SString, SEvent*>::const_iterator IterEnd() { return m_EventHashMap.end(); };
4243

43-
void PreEventPulse();
44-
void PostEventPulse();
44+
void PreEventPulse(CEventContext* pContext);
45+
void PostEventPulse(CEventContext* pContext);
4546

4647
void CancelEvent(bool bCancelled = true);
4748
void CancelEvent(bool bCancelled, const char* szReason);
@@ -53,9 +54,10 @@ class CEvents
5354

5455
CFastHashMap<SString, SEvent*> m_EventHashMap;
5556

56-
std::vector<int> m_CancelledList;
57-
bool m_bEventCancelled;
58-
bool m_bWasEventCancelled;
57+
std::vector<int> m_CancelledList;
58+
bool m_bEventCancelled;
59+
bool m_bWasEventCancelled;
60+
SString m_strLastError;
5961

60-
SString m_strLastError;
62+
std::vector<CEventContext*> m_ContextStack;
6163
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: Shared/mods/deathmatch/logic/CEventContext.h
6+
* PURPOSE: Per-event context for event dispatch and cancellation
7+
*
8+
* Multi Theft Auto is available from https://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#pragma once
13+
14+
#include <string>
15+
16+
class CEventContext
17+
{
18+
public:
19+
CEventContext() noexcept : m_isCancelled(false) {}
20+
21+
void Cancel(const char* reason = nullptr) noexcept
22+
{
23+
m_isCancelled = true;
24+
if (reason)
25+
m_cancelReason = reason;
26+
}
27+
28+
bool IsCancelled() const noexcept { return m_isCancelled; }
29+
const std::string& GetCancelReason() const noexcept { return m_cancelReason; }
30+
31+
void Reset() noexcept
32+
{
33+
m_isCancelled = false;
34+
m_cancelReason.clear();
35+
}
36+
37+
private:
38+
bool m_isCancelled;
39+
std::string m_cancelReason;
40+
};

0 commit comments

Comments
 (0)