diff --git a/cleo_plugins/DebugUtils/DebugUtils.cpp b/cleo_plugins/DebugUtils/DebugUtils.cpp index bd999f82..a79b9cf4 100644 --- a/cleo_plugins/DebugUtils/DebugUtils.cpp +++ b/cleo_plugins/DebugUtils/DebugUtils.cpp @@ -9,6 +9,7 @@ #include "CLEO.h" #include "CLEO_Utils.h" #include "ScreenLog.h" +#include "ScriptLog.h" using namespace CLEO; @@ -25,6 +26,11 @@ class DebugUtils }; static std::deque pausedScripts; + static ScriptLog currScript; + + // limits for processing after which script is considered hanging + static size_t configLimitCommand; + // breakpoint continue keys static const int KeyFirst = VK_F5; static const size_t KeyCount = 8; // F5 to F12 @@ -43,6 +49,7 @@ class DebugUtils } auto config = GetConfigFilename(); + configLimitCommand = GetPrivateProfileInt("Limits", "Command", 100000, config.c_str()); // register opcodes CLEO_RegisterOpcode(0x00C3, Opcode_DebugOn); @@ -64,6 +71,7 @@ class DebugUtils CLEO_RegisterCallback(eCallbackId::Log, OnLog); CLEO_RegisterCallback(eCallbackId::DrawingFinished, OnDrawingFinished); CLEO_RegisterCallback(eCallbackId::ScriptProcess, OnScriptProcess); + CLEO_RegisterCallback(eCallbackId::ScriptOpcodeProcess, OnScriptOpcodeProcess); CLEO_RegisterCallback(eCallbackId::ScriptsFinalize, OnScriptsFinalize); } @@ -73,6 +81,7 @@ class DebugUtils CLEO_UnregisterCallback(eCallbackId::Log, OnLog); CLEO_UnregisterCallback(eCallbackId::DrawingFinished, OnDrawingFinished); CLEO_UnregisterCallback(eCallbackId::ScriptProcess, OnScriptProcess); + CLEO_UnregisterCallback(eCallbackId::ScriptOpcodeProcess, OnScriptOpcodeProcess); CLEO_UnregisterCallback(eCallbackId::ScriptsFinalize, OnScriptsFinalize); } @@ -172,6 +181,8 @@ class DebugUtils static bool WINAPI OnScriptProcess(CScriptThread* thread) { + currScript.Reset(); + for (size_t i = 0; i < pausedScripts.size(); i++) { if (pausedScripts[i].ptr == thread) @@ -183,6 +194,18 @@ class DebugUtils return true; } + static OpcodeResult WINAPI OnScriptOpcodeProcess(CRunningScript* thread, DWORD opcode) + { + currScript.commandCounter++; + if (currScript.commandCounter > configLimitCommand && !IsLegacyScript(thread)) + { + SHOW_ERROR("Over %d,%03d commands executed in a single frame by script %s \nTo prevent the game from freezing, CLEO suspended this script.\n\nTo ignore this error, increase 'command' property in %s.ini file and restart the game.", configLimitCommand / 1000, configLimitCommand % 1000, ScriptInfoStr(thread).c_str(), TARGET_NAME); + return thread->Suspend(); + } + + return OR_NONE; + } + static void WINAPI OnLog(eLogLevel level, const char* msg) { screenLog.Add(level, msg); @@ -378,6 +401,8 @@ class DebugUtils ScreenLog DebugUtils::screenLog = {}; std::deque DebugUtils::pausedScripts; +ScriptLog DebugUtils::currScript = {}; +size_t DebugUtils::configLimitCommand; bool DebugUtils::keysReleased = true; std::map DebugUtils::logFiles; diff --git a/cleo_plugins/DebugUtils/DebugUtils.vcxproj b/cleo_plugins/DebugUtils/DebugUtils.vcxproj index 926927ce..7e0c1159 100644 --- a/cleo_plugins/DebugUtils/DebugUtils.vcxproj +++ b/cleo_plugins/DebugUtils/DebugUtils.vcxproj @@ -136,6 +136,7 @@ xcopy /Y "$(OutDir)$(TargetName).*" "$(GTA_SA_DIR)\cleo\cleo_plugins\" + diff --git a/cleo_plugins/DebugUtils/DebugUtils.vcxproj.filters b/cleo_plugins/DebugUtils/DebugUtils.vcxproj.filters index 339e6e43..8bcd68a5 100644 --- a/cleo_plugins/DebugUtils/DebugUtils.vcxproj.filters +++ b/cleo_plugins/DebugUtils/DebugUtils.vcxproj.filters @@ -24,6 +24,7 @@ sdk + diff --git a/cleo_plugins/DebugUtils/SA.DebugUtils.ini b/cleo_plugins/DebugUtils/SA.DebugUtils.ini index 03a654fe..d3a7753d 100644 --- a/cleo_plugins/DebugUtils/SA.DebugUtils.ini +++ b/cleo_plugins/DebugUtils/SA.DebugUtils.ini @@ -22,3 +22,7 @@ FontStyle = 1 ColorError = "FF30EEFF" ColorDebug = "FFEE30FF" ColorSystem = "DDDDDDFF" + +; limits between 'wait' commands in script after which it is considered hanging +[Limits] +command = 100000 diff --git a/cleo_plugins/DebugUtils/ScriptLog.h b/cleo_plugins/DebugUtils/ScriptLog.h new file mode 100644 index 00000000..1b0f1ea8 --- /dev/null +++ b/cleo_plugins/DebugUtils/ScriptLog.h @@ -0,0 +1,13 @@ +#pragma once +#include "CLEO.h" + +struct ScriptLog +{ + size_t commandCounter = 0; + + void Reset() + { + commandCounter = 0; + } +}; + diff --git a/examples/Limits_Info.txt b/examples/Limits_Info.txt index c05f50c9..9d190bbc 100644 --- a/examples/Limits_Info.txt +++ b/examples/Limits_Info.txt @@ -5,6 +5,12 @@ script_name {name} "lim_info" +// In case the limits adjuster is used, pools can have really big sizes. +// Iterating through all elements can really create a performance hit. +// The proper solution would be to draw stats on every frame from stats saved in variables, +// and update these stats by processing pools only once every few seconds. +const Pool_Size_Cap = 1111 // walk around, just do not parse all contents of bigger pools + int active = false while true @@ -135,12 +141,22 @@ function GET_POOL_INFO(address :int) : int, int int size = read_memory_with_offset {address} pool {offset} 0x8 {size} 4 // CPool::m_nSize + // for performance reasons do not iterate through all elements of realy big pools + int processSize + if + size < Pool_Size_Cap + then + processSize = size + else + processSize = Pool_Size_Cap + end + // count empty slots int byteMap = read_memory_with_offset {address} pool {offset} 0x4 {size} 4 // CPool::m_byteMap int count = 0 int i = 0 - while i < size + while i < processSize int flags = read_memory_with_offset {address} byteMap {offset} i {size} 1 // tPoolObjectFlags if not is_local_var_bit_set_const {number} flags {n} 7 // tPoolObjectFlags::bEmpty