diff --git a/Client/mods/deathmatch/logic/CMapEventManager.cpp b/Client/mods/deathmatch/logic/CMapEventManager.cpp index 4054b1a0afb..7fdd0573bf6 100644 --- a/Client/mods/deathmatch/logic/CMapEventManager.cpp +++ b/Client/mods/deathmatch/logic/CMapEventManager.cpp @@ -193,6 +193,9 @@ bool CMapEventManager::Call ( const char* szName, const CLuaArguments& Arguments if ( bEnabled ) g_pCore->LogEvent ( 0, "Lua Event", pMapEvent->GetVM ()->GetScriptName (), szName ); + if ( !g_pClientGame->GetDebugHookManager()->OnPreEventFunction( szName, Arguments, pSource, nullptr, pMapEvent ) ) + continue; + // Store the current values of the globals lua_getglobal ( pState, "source" ); CLuaArgument OldSource ( pState, -1 ); @@ -242,11 +245,13 @@ bool CMapEventManager::Call ( const char* szName, const CLuaArguments& Arguments lua_pushstring ( pState, szName ); lua_setglobal ( pState, "eventName" ); - + // Call it pMapEvent->Call ( Arguments ); bCalled = true; + g_pClientGame->GetDebugHookManager()->OnPostEventFunction( szName, Arguments, pSource, nullptr, pMapEvent ); + // Reset the globals on that VM OldSource.Push ( pState ); lua_setglobal ( pState, "source" ); diff --git a/Server/mods/deathmatch/logic/CMapEventManager.cpp b/Server/mods/deathmatch/logic/CMapEventManager.cpp index 642aadf37d9..24dff43d621 100644 --- a/Server/mods/deathmatch/logic/CMapEventManager.cpp +++ b/Server/mods/deathmatch/logic/CMapEventManager.cpp @@ -173,6 +173,9 @@ bool CMapEventManager::Call ( const char* szName, const CLuaArguments& Arguments TIMEUS startTime = GetTimeUs(); + if ( !g_pGame->GetDebugHookManager()->OnPreEventFunction( szName, Arguments, pSource, pCaller, pMapEvent ) ) + continue; + // Store the current values of the globals lua_getglobal ( pState, "source" ); CLuaArgument OldSource ( pState, -1 ); @@ -242,6 +245,8 @@ bool CMapEventManager::Call ( const char* szName, const CLuaArguments& Arguments pMapEvent->Call ( Arguments ); bCalled = true; + g_pGame->GetDebugHookManager()->OnPostEventFunction( szName, Arguments, pSource, pCaller, pMapEvent ); + // Reset the globals on that VM OldSource.Push ( pState ); lua_setglobal ( pState, "source" ); diff --git a/Shared/mods/deathmatch/logic/CDebugHookManager.cpp b/Shared/mods/deathmatch/logic/CDebugHookManager.cpp index bc4dafeb546..4031ab4cd26 100644 --- a/Shared/mods/deathmatch/logic/CDebugHookManager.cpp +++ b/Shared/mods/deathmatch/logic/CDebugHookManager.cpp @@ -57,14 +57,25 @@ CDebugHookManager::~CDebugHookManager( void ) /////////////////////////////////////////////////////////////// std::vector < SDebugHookCallInfo >& CDebugHookManager::GetHookInfoListForType( EDebugHookType hookType ) { - if ( hookType == EDebugHook::PRE_EVENT ) - return m_PreEventHookList; - if ( hookType == EDebugHook::POST_EVENT ) - return m_PostEventHookList; - if ( hookType == EDebugHook::PRE_FUNCTION ) - return m_PreFunctionHookList; - dassert( hookType == EDebugHook::POST_FUNCTION ); - return m_PostFunctionHookList; + switch ( hookType ) + { + case EDebugHookType::PRE_EVENT: + return m_PreEventHookList; + case EDebugHookType::POST_EVENT: + return m_PostEventHookList; + case EDebugHookType::PRE_FUNCTION: + return m_PreFunctionHookList; + case EDebugHookType::POST_FUNCTION: + return m_PostFunctionHookList; + case EDebugHookType::PRE_EVENT_FUNCTION: + return m_PreEventFunctionHookList; + case EDebugHookType::POST_EVENT_FUNCTION: + return m_PostEventFunctionHookList; + case EDebugHookType::MAX_DEBUG_HOOK_TYPE: + default: + dassert ( hookType == EDebugHook::POST_FUNCTION ); + return m_PostFunctionHookList; + } } @@ -132,7 +143,7 @@ bool CDebugHookManager::RemoveDebugHook( EDebugHookType hookType, const CLuaFunc /////////////////////////////////////////////////////////////// void CDebugHookManager::OnLuaMainDestroy( CLuaMain* pLuaMain ) { - for( uint hookType = EDebugHook::PRE_EVENT ; hookType <= EDebugHook::POST_FUNCTION ; hookType++ ) + for( uint hookType = EDebugHook::PRE_EVENT ; hookType < EDebugHook::MAX_DEBUG_HOOK_TYPE ; hookType++ ) { std::vector < SDebugHookCallInfo >& hookInfoList = GetHookInfoListForType( (EDebugHookType)hookType ); for( uint i = 0 ; i < hookInfoList.size() ; ) @@ -179,6 +190,49 @@ void GetDebugInfo( lua_State* luaVM, lua_Debug& debugInfo, const char*& szFilena } +/////////////////////////////////////////////////////////////// +// +// GetMapEventDebugInfo +// +// Get current Lua source file and line number +// +/////////////////////////////////////////////////////////////// +void GetMapEventDebugInfo( CMapEvent* pMapEvent, const char*& szFilename, int& iLineNumber ) +{ + CLuaMain* pLuaMain = pMapEvent->GetVM(); + + if ( !pLuaMain ) + return; + + lua_State* luaVM = pLuaMain->GetVirtualMachine(); + + if ( !luaVM ) + return; + + const CLuaFunctionRef& iLuaFunction = pMapEvent->GetLuaFunction(); + lua_Debug debugInfo; + lua_getref( luaVM, iLuaFunction.ToInt() ); + + if ( lua_getinfo( luaVM, ">lS", &debugInfo ) ) { + // Make sure this function isn't defined in a string + if ( debugInfo.source[0] == '@' ) { + szFilename = debugInfo.source; + iLineNumber = debugInfo.currentline != -1 ? debugInfo.currentline : debugInfo.linedefined; + } + else { + szFilename = debugInfo.short_src; + } + + // Remove path + if ( const char* szNext = strrchr( szFilename, '\\' ) ) + szFilename = szNext + 1; + + if ( const char* szNext = strrchr( szFilename, '/' ) ) + szFilename = szNext + 1; + } +} + + /////////////////////////////////////////////////////////////// // // CDebugHookManager::OnPreFunction @@ -373,6 +427,135 @@ void CDebugHookManager::OnPostEvent( const char* szName, const CLuaArguments& Ar } +/////////////////////////////////////////////////////////////// +// +// CDebugHookManager::OnPreEventFunction +// +// Called before a MTA event function is called +// Returns false if function call should be skipped +// +/////////////////////////////////////////////////////////////// +bool CDebugHookManager::OnPreEventFunction( const char* szName, const CLuaArguments& Arguments, CElement* pSource, CPlayer* pCaller, CMapEvent* pMapEvent ) +{ + DECLARE_PROFILER_SECTION( OnPreEventFunction ) + + if ( m_PreEventFunctionHookList.empty() ) + return true; + + // Check if name is not used + if ( !IsNameAllowed( szName, m_PreEventFunctionHookList ) ) + return true; + + CLuaMain* pEventLuaMain = g_pGame->GetScriptDebugging()->GetTopLuaMain(); + CResource* pEventResource = pEventLuaMain ? pEventLuaMain->GetResource() : NULL; + + // Get file/line number for event + const char* szEventFilename = ""; + int iEventLineNumber = 0; + lua_Debug eventDebugInfo; + lua_State* eventLuaVM = pEventLuaMain ? pEventLuaMain->GetVM() : NULL; + if ( eventLuaVM ) + GetDebugInfo( eventLuaVM, eventDebugInfo, szEventFilename, iEventLineNumber ); + + // Get file/line number for function + const char* szFunctionFilename = ""; + int iFunctionLineNumber = 0; + GetMapEventDebugInfo( pMapEvent, szFunctionFilename, iFunctionLineNumber ); + + CLuaMain* pFunctionLuaMain = pMapEvent->GetVM(); + CResource* pFunctionResource = pFunctionLuaMain ? pFunctionLuaMain->GetResource() : NULL; + + CLuaArguments NewArguments; + // resource eventResource, string eventName, element eventSource, element eventClient, string eventFilename, int eventLineNumber, + if ( pEventResource ) + NewArguments.PushResource( pEventResource ); + else + NewArguments.PushNil(); + + NewArguments.PushString( szName ); + NewArguments.PushElement( pSource ); + NewArguments.PushElement( pCaller ); + NewArguments.PushString( szEventFilename ); + NewArguments.PushNumber( iEventLineNumber ); + + // resource functionResource, string functionFilename, int functionLineNumber, ...args + if ( pFunctionResource ) + NewArguments.PushResource( pFunctionResource ); + else + NewArguments.PushNil(); + + NewArguments.PushString( szFunctionFilename ); + NewArguments.PushNumber( iFunctionLineNumber ); + NewArguments.PushArguments( Arguments ); + + return CallHook( szName, m_PreEventFunctionHookList, NewArguments ); +} + + +/////////////////////////////////////////////////////////////// +// +// CDebugHookManager::OnPostEventFunction +// +// Called after a MTA event function is called +// +/////////////////////////////////////////////////////////////// +void CDebugHookManager::OnPostEventFunction( const char* szName, const CLuaArguments& Arguments, CElement* pSource, CPlayer* pCaller, CMapEvent* pMapEvent ) +{ + DECLARE_PROFILER_SECTION( OnPostEventFunction ) + + if ( m_PostEventFunctionHookList.empty() ) + return; + + // Check if name is not used + if ( !IsNameAllowed( szName, m_PostEventFunctionHookList ) ) + return; + + CLuaMain* pEventLuaMain = g_pGame->GetScriptDebugging()->GetTopLuaMain(); + CResource* pEventResource = pEventLuaMain ? pEventLuaMain->GetResource() : NULL; + + // Get file/line number for event + const char* szEventFilename = ""; + int iEventLineNumber = 0; + lua_Debug eventDebugInfo; + lua_State* eventLuaVM = pEventLuaMain ? pEventLuaMain->GetVM() : NULL; + if ( eventLuaVM ) + GetDebugInfo( eventLuaVM, eventDebugInfo, szEventFilename, iEventLineNumber ); + + // Get file/line number for function + const char* szFunctionFilename = ""; + int iFunctionLineNumber = 0; + GetMapEventDebugInfo( pMapEvent, szFunctionFilename, iFunctionLineNumber ); + + CLuaMain* pFunctionLuaMain = pMapEvent->GetVM(); + CResource* pFunctionResource = pFunctionLuaMain ? pFunctionLuaMain->GetResource() : NULL; + + CLuaArguments NewArguments; + // resource eventResource, string eventName, element eventSource, element eventClient, string eventFilename, int eventLineNumber, + if ( pEventResource ) + NewArguments.PushResource( pEventResource ); + else + NewArguments.PushNil(); + + NewArguments.PushString( szName ); + NewArguments.PushElement( pSource ); + NewArguments.PushElement( pCaller ); + NewArguments.PushString( szEventFilename ); + NewArguments.PushNumber( iEventLineNumber ); + + // resource functionResource, string functionFilename, int functionLineNumber, ...args + if ( pFunctionResource ) + NewArguments.PushResource( pFunctionResource ); + else + NewArguments.PushNil(); + + NewArguments.PushString( szFunctionFilename ); + NewArguments.PushNumber( iFunctionLineNumber ); + NewArguments.PushArguments( Arguments ); + + CallHook( szName, m_PostEventFunctionHookList, NewArguments ); +} + + /////////////////////////////////////////////////////////////// // // CDebugHookManager::IsNameAllowed @@ -458,6 +641,9 @@ bool CDebugHookManager::CallHook( const char* szName, const std::vector < SDebug lua_State* pState = info.pLuaMain->GetVirtualMachine(); + if ( !pState ) + continue; + // Save script MTA globals in case hook messes with them lua_getglobal ( pState, "source" ); CLuaArgument OldSource ( pState, -1 ); diff --git a/Shared/mods/deathmatch/logic/CDebugHookManager.h b/Shared/mods/deathmatch/logic/CDebugHookManager.h index 4646a4f2e4a..b0fd13b2999 100644 --- a/Shared/mods/deathmatch/logic/CDebugHookManager.h +++ b/Shared/mods/deathmatch/logic/CDebugHookManager.h @@ -42,7 +42,8 @@ class CDebugHookManager void OnPostFunction ( lua_CFunction f, lua_State* luaVM ); bool OnPreEvent ( const char* szName, const CLuaArguments& Arguments, CElement* pSource, CPlayer* pCaller ); void OnPostEvent ( const char* szName, const CLuaArguments& Arguments, CElement* pSource, CPlayer* pCaller ); - + bool OnPreEventFunction ( const char* szName, const CLuaArguments& Arguments, CElement* pSource, CPlayer* pCaller, CMapEvent* pMapEvent ); + void OnPostEventFunction ( const char* szName, const CLuaArguments& Arguments, CElement* pSource, CPlayer* pCaller, CMapEvent* pMapEvent ); bool HasPostFunctionHooks ( void ) const { return !m_PostFunctionHookList.empty() || m_uiPostFunctionOverride; } protected: @@ -57,5 +58,7 @@ class CDebugHookManager std::vector < SDebugHookCallInfo > m_PostEventHookList; std::vector < SDebugHookCallInfo > m_PreFunctionHookList; std::vector < SDebugHookCallInfo > m_PostFunctionHookList; + std::vector < SDebugHookCallInfo > m_PreEventFunctionHookList; + std::vector < SDebugHookCallInfo > m_PostEventFunctionHookList; std::map< SString, std::vector > m_MaskArgumentsMap; }; diff --git a/Shared/mods/deathmatch/logic/Enums.cpp b/Shared/mods/deathmatch/logic/Enums.cpp index 39f5dea99bc..eb67638c72b 100644 --- a/Shared/mods/deathmatch/logic/Enums.cpp +++ b/Shared/mods/deathmatch/logic/Enums.cpp @@ -24,10 +24,12 @@ IMPLEMENT_ENUM_BEGIN( EPlayerScreenShotResult::EPlayerScreenShotResultType ) IMPLEMENT_ENUM_END( "player-screenshot-result" ) IMPLEMENT_ENUM_BEGIN( EDebugHook::EDebugHookType ) - ADD_ENUM ( EDebugHook::PRE_EVENT, "preEvent" ) - ADD_ENUM ( EDebugHook::POST_EVENT, "postEvent" ) - ADD_ENUM ( EDebugHook::PRE_FUNCTION, "preFunction" ) - ADD_ENUM ( EDebugHook::POST_FUNCTION, "postFunction" ) + ADD_ENUM ( EDebugHook::PRE_EVENT, "preEvent" ) + ADD_ENUM ( EDebugHook::POST_EVENT, "postEvent" ) + ADD_ENUM ( EDebugHook::PRE_FUNCTION, "preFunction" ) + ADD_ENUM ( EDebugHook::POST_FUNCTION, "postFunction" ) + ADD_ENUM ( EDebugHook::PRE_EVENT_FUNCTION, "preEventFunction" ) + ADD_ENUM ( EDebugHook::POST_EVENT_FUNCTION, "postEventFunction" ) IMPLEMENT_ENUM_END( "debug-hook" ) IMPLEMENT_ENUM_BEGIN( eEulerRotationOrder ) diff --git a/Shared/mods/deathmatch/logic/Enums.h b/Shared/mods/deathmatch/logic/Enums.h index c557882bc83..caad1fdf8aa 100644 --- a/Shared/mods/deathmatch/logic/Enums.h +++ b/Shared/mods/deathmatch/logic/Enums.h @@ -46,6 +46,9 @@ namespace EDebugHook POST_EVENT, PRE_FUNCTION, POST_FUNCTION, + PRE_EVENT_FUNCTION, + POST_EVENT_FUNCTION, + MAX_DEBUG_HOOK_TYPE }; } using EDebugHook::EDebugHookType;