From e19f9bb4839a7b0c8afad78d068df8e48c9e241c Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 9 May 2025 20:04:44 +0200 Subject: [PATCH 001/112] Fixed typo in NPCWANDERLOOKAROUNDCHANCE --- src/game/CServerConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 34dca2b51..5d7ffda4f 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -665,7 +665,7 @@ enum RC_TYPE RC_NPCTRAINCOST, // m_iTrainSkillCost RC_NPCTRAINMAX, // m_iTrainSkillMax RC_NPCTRAINPERCENT, // m_iTrainSkillPercent - RC_NPCWANDERINGLOOKAROUNDCHANCE, // m_iNPCWanderingLookAroundChance + RC_NPCWANDERLOOKAROUNDCHANCE, // m_iNPCWanderLookAroundChance RC_NTSERVICE, // m_fUseNTService RC_OPTIONFLAGS, // _uiOptionFlags RC_OVERSKILLMULTIPLY, // m_iOverSkillMultiply From 09ada280df4f31e037673a75032c149e9c7cf435 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 9 May 2025 20:05:46 +0200 Subject: [PATCH 002/112] CMake: Added FORCE_DEBUG_INFO option to enable debug info generation also for Nightly and Release builds Useful for profiling --- CMakeLists.txt | 29 +++++++++++++++++------------ cmake/CompilerFlagsBase.cmake | 27 ++++++++++++++++++--------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 89abf7887..1bda3531a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,6 @@ set(CMAKE_CXX_FLAGS_MINSIZEREL "") message(STATUS "Scanning system build tools...") project(SphereServer CXX C) # does a scan for C++ and C compilers - # If we have not specified a toolchain, let's detect which one we should use, using the detected arch. # We need to do this after "project", otherwise CMake doesn't know where it's running. if(NOT TOOLCHAIN_LOADED) @@ -70,18 +69,16 @@ set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") # In some cases, CMake also sets other predefined flags, which we don't want. #[[ -set(CMAKE_C_FLAGS "") set(CMAKE_C_FLAGS_DEBUG "") set(CMAKE_C_FLAGS_NIGHTLY "") set(CMAKE_C_FLAGS_RELEASE "") -set(CMAKE_CXX_FLAGS "") set(CMAKE_CXX_FLAGS_DEBUG "") set(CMAKE_CXX_FLAGS_NIGHTLY "") set(CMAKE_CXX_FLAGS_RELEASE "") -set(CMAKE_EXE_LINKER_FLAGS "") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "") +set(CMAKE_EXE_LINKER_FLAGS_NIGHTLY "") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "") ]] # Example: using MSVC, /RTC1 is globally added (all projects) to DEBUG builds. It's mostly fine, except in some cases where we don't want it... @@ -106,6 +103,7 @@ if(WIN32) option(WIN_GENERATE_CRASHDUMP "For non-Debug builds, add Windows Structured Exception Handling and generate a Crash Dump if a fatal SE is thrown." FALSE) endif() endif() +option(FORCE_DEBUG_INFO "Force generation of debug info, useful for profiling Nightly/Release builds." FALSE) option(CROSSCOMPILE "Are we compiling for a different target architecture?" FALSE) option( RUNTIME_STATIC_LINK @@ -220,10 +218,12 @@ if(SINGLE_TARGET) #target_compile_features(spheresvr_release PUBLIC cxx_std_20) target_precompile_headers(spheresvr_release ${pch_options}) - add_custom_command(TARGET spheresvr_release POST_BUILD - COMMAND ${CMAKE_STRIP} $ - COMMENT "Stripping executable" - ) + if(NOT ${FORCE_DEBUG_INFO}) + add_custom_command(TARGET spheresvr_release POST_BUILD + COMMAND ${CMAKE_STRIP} $ + COMMENT "Stripping executable" + ) + endif() endif() if(("${CMAKE_BUILD_TYPE}" STREQUAL "") OR (${CMAKE_BUILD_TYPE} MATCHES "(N|n?)ightly")) set(TARGETS ${TARGETS} spheresvr_nightly) @@ -238,10 +238,12 @@ if(SINGLE_TARGET) #target_compile_features(spheresvr_nightly PUBLIC cxx_std_20) target_precompile_headers(spheresvr_nightly ${pch_options}) - add_custom_command(TARGET spheresvr_nightly POST_BUILD - COMMAND ${CMAKE_STRIP} $ - COMMENT "Stripping executable" - ) + if(NOT ${FORCE_DEBUG_INFO}) + add_custom_command(TARGET spheresvr_nightly POST_BUILD + COMMAND ${CMAKE_STRIP} $ + COMMENT "Stripping executable" + ) + endif() endif() if(("${CMAKE_BUILD_TYPE}" STREQUAL "") OR (${CMAKE_BUILD_TYPE} MATCHES "(D|d?)ebug")) set(TARGETS ${TARGETS} spheresvr_debug) @@ -294,3 +296,6 @@ message(STATUS) message(STATUS "Retrieving git data...") include("cmake/GitStatus.cmake") message(STATUS) + +get_target_property(sqlite_flags sqlite COMPILE_OPTIONS) +message(STATUS "sqlite flags ${sqlite_flags}") diff --git a/cmake/CompilerFlagsBase.cmake b/cmake/CompilerFlagsBase.cmake index ac6ef5972..45d46ffdb 100644 --- a/cmake/CompilerFlagsBase.cmake +++ b/cmake/CompilerFlagsBase.cmake @@ -19,6 +19,14 @@ if(NOT MSVC) -fdata-sections ) + if(FORCE_DEBUG_INFO) + list( + APPEND + compiler_options_base + -ggdb3 + ) + endif() + list( APPEND compiler_options_warnings_base @@ -95,7 +103,7 @@ if(NOT MSVC) if("${ENABLED_SANITIZER}" STREQUAL "TRUE") # -fno-omit-frame-pointer disables a good optimization which might corrupt the debugger stack trace. - set(local_compile_options_nondebug -ggdb3 -Og -fno-omit-frame-pointer -fno-inline) + set(local_compile_options_nondebug -ggdb3 -O1 -fno-omit-frame-pointer -fno-inline-small-functions) set(local_link_options_nondebug) #-gsplit-dwarf or -gmacro if i want to add lto and there are some missing symbols else() # If using ThinLTO: -flto=thin -fsplit-lto-unit @@ -104,12 +112,13 @@ if(NOT MSVC) set(local_link_options_nondebug)#-flto) endif() - if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - list(APPEND local_link_options_nondebug -Wl,-dead_strip) - else() - list(APPEND local_link_options_nondebug -Wl,--gc-sections) - endif() - + #if(NOT FORCE_DEBUG_INFO) + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + list(APPEND local_link_options_nondebug -Wl,-dead_strip) + else() + list(APPEND local_link_options_nondebug -Wl,--gc-sections) + endif() + #endif() set(custom_compile_options_release ${local_compile_options_nondebug} @@ -125,7 +134,7 @@ if(NOT MSVC) CACHE INTERNAL STRING ) set(custom_compile_options_debug - -ggdb3 -O0 -fno-omit-frame-pointer -fno-inline + -ggdb3 -O0 -fno-omit-frame-pointer -fno-inline-functions #-fno-inline CACHE INTERNAL STRING ) @@ -156,4 +165,4 @@ if(NOT MSVC) message(STATUS "Adding the following linker options specific to 'Debug' build: ${custom_link_options_debug}.") endif() -endif() +endif(NOT MSVC) From 6cdf242da1d2b0dec1a7fc9922be2293a717a705 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 9 May 2025 20:08:22 +0200 Subject: [PATCH 003/112] CMake: external libs are now compiled as obj files, without an intermediate link-in-a-library stage. This speeds up overall compilation, since only one link pass is done, instead of one per target. --- cmake/toolchains/Windows-MSVC.cmake | 9 ++++++--- .../include/Linux-Clang_common.inc.cmake | 18 +++++++++--------- .../include/Linux-GNU_common.inc.cmake | 18 +++++++++--------- .../include/OSX-AppleClang_common.inc.cmake | 18 +++++++++--------- .../include/Windows-Clang_common.inc.cmake | 8 ++++---- .../include/Windows-GNU_common.inc.cmake | 18 +++++++++--------- lib/bcrypt/CMakeLists.txt | 2 +- lib/libev/CMakeLists.txt | 2 +- lib/sqlite/CMakeLists.txt | 2 +- lib/twofish/CMakeLists.txt | 2 +- lib/zlib/CMakeLists.txt | 2 +- 11 files changed, 51 insertions(+), 48 deletions(-) diff --git a/cmake/toolchains/Windows-MSVC.cmake b/cmake/toolchains/Windows-MSVC.cmake index 7944549dd..3f8df0779 100644 --- a/cmake/toolchains/Windows-MSVC.cmake +++ b/cmake/toolchains/Windows-MSVC.cmake @@ -122,9 +122,12 @@ function(toolchain_exe_stuff) # gersemi: off target_compile_options(spheresvr PRIVATE ${cxx_compiler_flags_common} - $<$: ${local_msvc_cmdline_runtime_lib_nondebug} ${local_msvc_exception_handler} /Oy /GL /GA /Gw /Gy /GF $,/O1 /Zi,/O2>> - $<$: ${local_msvc_cmdline_runtime_lib_nondebug} ${local_msvc_exception_handler} /Oy /GL /GA /Gw /Gy /GF $,/O1 /Zi,/O2>> - $<$: ${local_msvc_cmdline_runtime_lib_debug} /EHsc /Oy- /ob1 /Od /Gs $,/Zi,/ZI>> + $<$: ${local_msvc_cmdline_runtime_lib_nondebug} ${local_msvc_exception_handler} /Oy /GL /GA /Gw /Gy /GF + $,/O1,/O2> $<$,/Zi>> + $<$: ${local_msvc_cmdline_runtime_lib_nondebug} ${local_msvc_exception_handler} /Oy /GL /GA /Gw /Gy /GF + $,/O1,/O2> $<$,/Zi>> + $<$: ${local_msvc_cmdline_runtime_lib_debug} /EHsc /Oy- /ob1 /Od /Gs + $,/Zi,/ZI>> # ASan (and compilation for ARM arch) doesn't support edit and continue option (ZI) ) # gersemi: on diff --git a/cmake/toolchains/include/Linux-Clang_common.inc.cmake b/cmake/toolchains/include/Linux-Clang_common.inc.cmake index b12267713..88542f3ab 100644 --- a/cmake/toolchains/include/Linux-Clang_common.inc.cmake +++ b/cmake/toolchains/include/Linux-Clang_common.inc.cmake @@ -29,13 +29,13 @@ function(toolchain_exe_stuff_common) #-- Apply compiler flags, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_options(spheresvr_release PUBLIC ${custom_compile_options_release}) + target_compile_options(spheresvr_release PRIVATE ${custom_compile_options_release}) endif() if(TARGET spheresvr_nightly) - target_compile_options(spheresvr_nightly PUBLIC ${custom_compile_options_nightly}) + target_compile_options(spheresvr_nightly PRIVATE ${custom_compile_options_nightly}) endif() if(TARGET spheresvr_debug) - target_compile_options(spheresvr_debug PUBLIC ${custom_compile_options_debug}) + target_compile_options(spheresvr_debug PRIVATE ${custom_compile_options_debug}) endif() #-- Store common linker flags. @@ -50,13 +50,13 @@ function(toolchain_exe_stuff_common) #-- Apply linker flags, only the ones specific per build type. if(TARGET spheresvr_release) - target_link_options(spheresvr_release PUBLIC ${custom_link_options_release}) + target_link_options(spheresvr_release PRIVATE ${custom_link_options_release}) endif() if(TARGET spheresvr_nightly) - target_link_options(spheresvr_nightly PUBLIC ${custom_link_options_nightly}) + target_link_options(spheresvr_nightly PRIVATE ${custom_link_options_nightly}) endif() if(TARGET spheresvr_debug) - target_link_options(spheresvr_debug PUBLIC ${custom_link_options_debug}) + target_link_options(spheresvr_debug PRIVATE ${custom_link_options_debug}) endif() @@ -73,13 +73,13 @@ function(toolchain_exe_stuff_common) #-- Apply define macros, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_definitions(spheresvr_release PUBLIC NDEBUG) + target_compile_definitions(spheresvr_release PRIVATE NDEBUG) endif(TARGET spheresvr_release) if(TARGET spheresvr_nightly) - target_compile_definitions(spheresvr_nightly PUBLIC NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) + target_compile_definitions(spheresvr_nightly PRIVATE NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) endif(TARGET spheresvr_nightly) if(TARGET spheresvr_debug) - target_compile_definitions(spheresvr_debug PUBLIC _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) + target_compile_definitions(spheresvr_debug PRIVATE _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) endif(TARGET spheresvr_debug) #-- Now add back the common compiler options, preprocessor macros, linker targets and options. diff --git a/cmake/toolchains/include/Linux-GNU_common.inc.cmake b/cmake/toolchains/include/Linux-GNU_common.inc.cmake index 5890a36a9..0a0489b4d 100644 --- a/cmake/toolchains/include/Linux-GNU_common.inc.cmake +++ b/cmake/toolchains/include/Linux-GNU_common.inc.cmake @@ -23,13 +23,13 @@ function(toolchain_exe_stuff_common) #-- Apply compiler flags, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_options(spheresvr_release PUBLIC ${custom_compile_options_release}) + target_compile_options(spheresvr_release PRIVATE ${custom_compile_options_release}) endif() if(TARGET spheresvr_nightly) - target_compile_options(spheresvr_nightly PUBLIC ${custom_compile_options_nightly}) + target_compile_options(spheresvr_nightly PRIVATE ${custom_compile_options_nightly}) endif() if(TARGET spheresvr_debug) - target_compile_options(spheresvr_debug PUBLIC ${custom_compile_options_debug}) + target_compile_options(spheresvr_debug PRIVATE ${custom_compile_options_debug}) endif() #-- Store common linker flags. @@ -46,13 +46,13 @@ function(toolchain_exe_stuff_common) #-- Apply linker flags, only the ones specific per build type. if(TARGET spheresvr_release) - target_link_options(spheresvr_release PUBLIC ${custom_link_options_release}) + target_link_options(spheresvr_release PRIVATE ${custom_link_options_release}) endif() if(TARGET spheresvr_nightly) - target_link_options(spheresvr_nightly PUBLIC ${custom_link_options_nightly}) + target_link_options(spheresvr_nightly PRIVATE ${custom_link_options_nightly}) endif() if(TARGET spheresvr_debug) - target_link_options(spheresvr_debug PUBLIC ${custom_link_options_debug}) + target_link_options(spheresvr_debug PRIVATE ${custom_link_options_debug}) endif() #-- Store common define macros. @@ -68,13 +68,13 @@ function(toolchain_exe_stuff_common) #-- Apply define macros, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_definitions(spheresvr_release PUBLIC NDEBUG) + target_compile_definitions(spheresvr_release PRIVATE NDEBUG) endif(TARGET spheresvr_release) if(TARGET spheresvr_nightly) - target_compile_definitions(spheresvr_nightly PUBLIC NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) + target_compile_definitions(spheresvr_nightly PRIVATE NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) endif(TARGET spheresvr_nightly) if(TARGET spheresvr_debug) - target_compile_definitions(spheresvr_debug PUBLIC _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) + target_compile_definitions(spheresvr_debug PRIVATE _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) endif(TARGET spheresvr_debug) #-- Now apply the common compiler options, preprocessor macros, linker options. diff --git a/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake b/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake index 14d760a63..0a159830c 100644 --- a/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake +++ b/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake @@ -32,13 +32,13 @@ function(toolchain_exe_stuff_common) #-- Apply compiler flags, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_options(spheresvr_release PUBLIC ${custom_compile_options_release}) + target_compile_options(spheresvr_release PRIVATE ${custom_compile_options_release}) endif() if(TARGET spheresvr_nightly) - target_compile_options(spheresvr_nightly PUBLIC ${custom_compile_options_nightly}) + target_compile_options(spheresvr_nightly PRIVATE ${custom_compile_options_nightly}) endif() if(TARGET spheresvr_debug) - target_compile_options(spheresvr_debug PUBLIC ${custom_compile_options_debug}) + target_compile_options(spheresvr_debug PRIVATE ${custom_compile_options_debug}) endif() #-- Store common linker flags. @@ -53,13 +53,13 @@ function(toolchain_exe_stuff_common) #-- Apply linker flags, only the ones specific per build type. if(TARGET spheresvr_release) - target_link_options(spheresvr_release PUBLIC ${custom_link_options_release}) + target_link_options(spheresvr_release PRIVATE ${custom_link_options_release}) endif() if(TARGET spheresvr_nightly) - target_link_options(spheresvr_nightly PUBLIC ${custom_link_options_nightly}) + target_link_options(spheresvr_nightly PRIVATE ${custom_link_options_nightly}) endif() if(TARGET spheresvr_debug) - target_link_options(spheresvr_debug PUBLIC ${custom_link_options_debug}) + target_link_options(spheresvr_debug PRIVATE ${custom_link_options_debug}) endif() #-- Store common define macros. @@ -75,13 +75,13 @@ function(toolchain_exe_stuff_common) #-- Apply define macros, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_definitions(spheresvr_release PUBLIC NDEBUG) + target_compile_definitions(spheresvr_release PRIVATE NDEBUG) endif(TARGET spheresvr_release) if(TARGET spheresvr_nightly) - target_compile_definitions(spheresvr_nightly PUBLIC NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) + target_compile_definitions(spheresvr_nightly PRIVATE NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) endif(TARGET spheresvr_nightly) if(TARGET spheresvr_debug) - target_compile_definitions(spheresvr_debug PUBLIC _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) + target_compile_definitions(spheresvr_debug PRIVATE _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) endif(TARGET spheresvr_debug) #-- Now add back the common compiler options, preprocessor macros, linker options. diff --git a/cmake/toolchains/include/Windows-Clang_common.inc.cmake b/cmake/toolchains/include/Windows-Clang_common.inc.cmake index 47912afa2..5b2b1882d 100644 --- a/cmake/toolchains/include/Windows-Clang_common.inc.cmake +++ b/cmake/toolchains/include/Windows-Clang_common.inc.cmake @@ -133,15 +133,15 @@ function(toolchain_exe_stuff_common) #-- Apply define macros, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_definitions(spheresvr_release PUBLIC NDEBUG THREAD_TRACK_CALLSTACK) + target_compile_definitions(spheresvr_release PRIVATE NDEBUG THREAD_TRACK_CALLSTACK) endif() if(TARGET spheresvr_nightly) - target_compile_definitions(spheresvr_nightly PUBLIC NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) + target_compile_definitions(spheresvr_nightly PRIVATE NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) endif() if(TARGET spheresvr_debug) - target_compile_definitions(spheresvr_debug PUBLIC _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) + target_compile_definitions(spheresvr_debug PRIVATE _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) if(USE_ASAN AND NOT CLANG_USE_GCC_LINKER) - target_compile_definitions(spheresvr_debug PUBLIC _HAS_ITERATOR_DEBUGGING=0 _ITERATOR_DEBUG_LEVEL=0) + target_compile_definitions(spheresvr_debug PRIVATE _HAS_ITERATOR_DEBUGGING=0 _ITERATOR_DEBUG_LEVEL=0) endif() endif() diff --git a/cmake/toolchains/include/Windows-GNU_common.inc.cmake b/cmake/toolchains/include/Windows-GNU_common.inc.cmake index a940c6a26..f3007782a 100644 --- a/cmake/toolchains/include/Windows-GNU_common.inc.cmake +++ b/cmake/toolchains/include/Windows-GNU_common.inc.cmake @@ -25,13 +25,13 @@ function(toolchain_exe_stuff_common) #-- Apply compiler flags, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_options(spheresvr_release PUBLIC ${custom_compile_options_release}) + target_compile_options(spheresvr_release PRIVATE ${custom_compile_options_release}) endif() if(TARGET spheresvr_nightly) - target_compile_options(spheresvr_nightly PUBLIC ${custom_compile_options_nightly}) + target_compile_options(spheresvr_nightly PRIVATE ${custom_compile_options_nightly}) endif() if(TARGET spheresvr_debug) - target_compile_options(spheresvr_debug PUBLIC ${custom_compile_options_debug}) + target_compile_options(spheresvr_debug PRIVATE ${custom_compile_options_debug}) endif() #-- Store common linker flags. @@ -46,13 +46,13 @@ function(toolchain_exe_stuff_common) #-- Apply linker flags, only the ones specific per build type. if(TARGET spheresvr_release) - target_link_options(spheresvr_release PUBLIC ${custom_link_options_release}) + target_link_options(spheresvr_release PRIVATE ${custom_link_options_release}) endif() if(TARGET spheresvr_nightly) - target_link_options(spheresvr_nightly PUBLIC ${custom_link_options_nightly}) + target_link_options(spheresvr_nightly PRIVATE ${custom_link_options_nightly}) endif() if(TARGET spheresvr_debug) - target_link_options(spheresvr_debug PUBLIC ${custom_link_options_debug}) + target_link_options(spheresvr_debug PRIVATE ${custom_link_options_debug}) endif() #-- Store common define macros. @@ -72,13 +72,13 @@ function(toolchain_exe_stuff_common) #-- Apply define macros, only the ones specific per build type. if(TARGET spheresvr_release) - target_compile_definitions(spheresvr_release PUBLIC NDEBUG THREAD_TRACK_CALLSTACK) + target_compile_definitions(spheresvr_release PRIVATE NDEBUG THREAD_TRACK_CALLSTACK) endif() if(TARGET spheresvr_nightly) - target_compile_definitions(spheresvr_nightly PUBLIC NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) + target_compile_definitions(spheresvr_nightly PRIVATE NDEBUG THREAD_TRACK_CALLSTACK _NIGHTLYBUILD) endif() if(TARGET spheresvr_debug) - target_compile_definitions(spheresvr_debug PUBLIC _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) + target_compile_definitions(spheresvr_debug PRIVATE _DEBUG THREAD_TRACK_CALLSTACK _PACKETDUMP) endif() #-- Now apply the common compiler options, preprocessor macros, linker options. diff --git a/lib/bcrypt/CMakeLists.txt b/lib/bcrypt/CMakeLists.txt index 7d330d600..bf865f160 100644 --- a/lib/bcrypt/CMakeLists.txt +++ b/lib/bcrypt/CMakeLists.txt @@ -10,7 +10,7 @@ set(lib_SOURCES bcrypt/wrapper.c ) -add_library(bcrypt STATIC ${lib_SOURCES}) +add_library(bcrypt OBJECT ${lib_SOURCES}) # make the headers available in the include path (accessible with < >). target_include_directories(bcrypt PUBLIC .) diff --git a/lib/libev/CMakeLists.txt b/lib/libev/CMakeLists.txt index c74db4f04..3d01799ab 100644 --- a/lib/libev/CMakeLists.txt +++ b/lib/libev/CMakeLists.txt @@ -25,7 +25,7 @@ set(lib_SOURCES source_group(lib\\libev FILES ${lib_SOURCES}) # create the target. equivalent to add_executable, but for libraries -add_library(libev STATIC ${lib_SOURCES}) +add_library(libev OBJECT ${lib_SOURCES}) # make the headers available in the include path (accessible with < >). target_include_directories(libev PUBLIC .) diff --git a/lib/sqlite/CMakeLists.txt b/lib/sqlite/CMakeLists.txt index ccca6be37..92d0f445e 100644 --- a/lib/sqlite/CMakeLists.txt +++ b/lib/sqlite/CMakeLists.txt @@ -7,7 +7,7 @@ set(lib_SOURCES sqlite/sqlite3.c sqlite/sqlite3.h) source_group(lib\\sqlite FILES ${lib_SOURCES}) # create the target. equivalent to add_executable, but for libraries -add_library(sqlite STATIC ${lib_SOURCES}) +add_library(sqlite OBJECT ${lib_SOURCES}) # make the headers available in the include path (accessible with < >). target_include_directories(sqlite PUBLIC .) diff --git a/lib/twofish/CMakeLists.txt b/lib/twofish/CMakeLists.txt index 59e5d001e..26c750bd3 100644 --- a/lib/twofish/CMakeLists.txt +++ b/lib/twofish/CMakeLists.txt @@ -7,7 +7,7 @@ set(lib_SOURCES twofish/twofish.h twofish/twofish_aux.h twofish/twofish.c) source_group(lib\\twofish FILES ${lib_SOURCES}) # create the target. equivalent to add_executable, but for libraries -add_library(twofish STATIC ${lib_SOURCES}) +add_library(twofish OBJECT ${lib_SOURCES}) # make the headers available in the include path (accessible with < >). target_include_directories(twofish PUBLIC .) diff --git a/lib/zlib/CMakeLists.txt b/lib/zlib/CMakeLists.txt index 5fbbab415..5c3e4411b 100644 --- a/lib/zlib/CMakeLists.txt +++ b/lib/zlib/CMakeLists.txt @@ -94,7 +94,7 @@ set(ZLIB_SRCS source_group(lib\\zlib FILES ${ZLIB_PUBLIC_HDRS}) # make the headers available in the include path (accessible with < >). -add_library(zlib STATIC ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS} ${ZLIB_SRCS}) +add_library(zlib OBJECT ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS} ${ZLIB_SRCS}) # add to the proper directories in the solution explorer (eg. Visual Studio) target_include_directories(zlib PUBLIC .) From 642e88d9e5c139f691e9ef39bf35ba1daa818c61 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 9 May 2025 20:17:27 +0200 Subject: [PATCH 004/112] CMake: fix MSVC toolchain generator expression --- CMakeLists.txt | 3 --- cmake/toolchains/Windows-MSVC.cmake | 32 +++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bda3531a..85b5eb7d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,6 +296,3 @@ message(STATUS) message(STATUS "Retrieving git data...") include("cmake/GitStatus.cmake") message(STATUS) - -get_target_property(sqlite_flags sqlite COMPILE_OPTIONS) -message(STATUS "sqlite flags ${sqlite_flags}") diff --git a/cmake/toolchains/Windows-MSVC.cmake b/cmake/toolchains/Windows-MSVC.cmake index 3f8df0779..b5e6f4561 100644 --- a/cmake/toolchains/Windows-MSVC.cmake +++ b/cmake/toolchains/Windows-MSVC.cmake @@ -119,17 +119,37 @@ function(toolchain_exe_stuff) else() set(local_msvc_exception_handler /EHsc) endif() + # gersemi: off target_compile_options(spheresvr PRIVATE ${cxx_compiler_flags_common} - $<$: ${local_msvc_cmdline_runtime_lib_nondebug} ${local_msvc_exception_handler} /Oy /GL /GA /Gw /Gy /GF - $,/O1,/O2> $<$,/Zi>> - $<$: ${local_msvc_cmdline_runtime_lib_nondebug} ${local_msvc_exception_handler} /Oy /GL /GA /Gw /Gy /GF - $,/O1,/O2> $<$,/Zi>> - $<$: ${local_msvc_cmdline_runtime_lib_debug} /EHsc /Oy- /ob1 /Od /Gs - $,/Zi,/ZI>> + + $<$: + ${local_msvc_cmdline_runtime_lib_nondebug} + ${local_msvc_exception_handler} + /Oy /GL /GA /Gw /Gy /GF + $,/O1,/O2> + $<$,$>:/Zi> + > + + $<$: + ${local_msvc_cmdline_runtime_lib_nondebug} + ${local_msvc_exception_handler} + /Oy /GL /GA /Gw /Gy /GF + $,/O1,/O2> + $<$,$>:/Zi> + > + + $<$: + ${local_msvc_cmdline_runtime_lib_debug} + /EHsc + /Oy- /ob1 /Od + /Gs + $,/Zi,/ZI> + > # ASan (and compilation for ARM arch) doesn't support edit and continue option (ZI) ) + # gersemi: on if("${ARCH}" STREQUAL "x86_64") From 63bfae60e5878d600c899d640bef27a836050209 Mon Sep 17 00:00:00 2001 From: mtwango Date: Sat, 10 May 2025 07:44:09 +0200 Subject: [PATCH 005/112] - allow more extensions to be used as WebPageSrc in WEBPAGE section in sphere.ini (#850) (#1416) - fixed search for another webpage_type, since table isnt sorted --- src/common/resource/sections/CWebPageDef.cpp | 47 ++++++++++++-------- src/common/resource/sections/CWebPageDef.h | 7 +++ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/common/resource/sections/CWebPageDef.cpp b/src/common/resource/sections/CWebPageDef.cpp index 518f17875..42b0da8a7 100644 --- a/src/common/resource/sections/CWebPageDef.cpp +++ b/src/common/resource/sections/CWebPageDef.cpp @@ -163,10 +163,6 @@ bool CWebPageDef::r_LoadVal( CScript & s ) // Load an item Script return SetSourceFile( s.GetArgStr(), nullptr ); case WC_WEBPAGEUPDATE: // (seconds) m_iUpdatePeriod = s.GetArgVal(); - if ( m_iUpdatePeriod && (m_type == WEBPAGE_TEXT) ) - { - m_type = WEBPAGE_TEMPLATE; - } break; default: return CScriptObj::r_LoadVal( s ); @@ -299,9 +295,7 @@ bool CWebPageDef::WebPageUpdate( bool fNow, lpctstr pszDstName, CTextConsole * p if ( pszDstName == nullptr ) pszDstName = m_sDstFilePath; - if ( m_type != WEBPAGE_TEMPLATE || - *pszDstName == '\0' || - m_sSrcFilePath.IsEmpty()) + if (*pszDstName == '\0' || m_sSrcFilePath.IsEmpty()) return false; CScript FileRead; @@ -381,8 +375,6 @@ void CWebPageDef::WebPageLog() ADDTOCALLSTACK("CWebPageDef::WebPageLog"); if ( ! m_iUpdateLog || ! m_iUpdatePeriod ) return; - if ( m_type != WEBPAGE_TEMPLATE ) - return; CSFileText FileRead; if ( ! FileRead.Open( m_sDstFilePath, OF_READ|OF_TEXT )) @@ -417,6 +409,12 @@ lpctstr const CWebPageDef::sm_szPageExt[] = ".JPG", ".JS", ".TXT", + ".PNG", + ".SVG", + ".WEBP", + ".XML", + ".CSV", + ".JSON", }; bool CWebPageDef::SetSourceFile( lpctstr pszName, CClient * pClient ) @@ -425,13 +423,19 @@ bool CWebPageDef::SetSourceFile( lpctstr pszName, CClient * pClient ) static WEBPAGE_TYPE const sm_szPageExtType[] = { WEBPAGE_BMP, - WEBPAGE_GIF, + WEBPAGE_GIF, WEBPAGE_TEMPLATE, WEBPAGE_TEMPLATE, WEBPAGE_JPG, WEBPAGE_JPG, - WEBPAGE_TEXT, - WEBPAGE_TEXT + WEBPAGE_JS, + WEBPAGE_TEXT, + WEBPAGE_PNG, + WEBPAGE_SVG, + WEBPAGE_WEBP, + WEBPAGE_XML, + WEBPAGE_CSV, + WEBPAGE_JSON, }; // attempt to set this to a source file. @@ -444,7 +448,7 @@ bool CWebPageDef::SetSourceFile( lpctstr pszName, CClient * pClient ) if ( pszExt == nullptr || pszExt[0] == '\0' ) return false; - int iType = FindTableSorted( pszExt, sm_szPageExt, ARRAY_COUNT( sm_szPageExt )); + const int iType = FindTable(pszExt, sm_szPageExt, ARRAY_COUNT(sm_szPageExt)); if ( iType < 0 ) return false; m_type = sm_szPageExtType[iType]; @@ -502,11 +506,18 @@ bool CWebPageDef::IsMatch( lpctstr pszMatch ) const lpctstr const CWebPageDef::sm_szPageType[WEBPAGE_QTY+1] = { - "text/html", // WEBPAGE_TEMPLATE (.htm) - "text/html", // WEBPAGE_TEMPLATE (.html) - "image/x-xbitmap", // WEBPAGE_BMP, + "text/html", // WEBPAGE_TEMPLATE + "text/plain", // WEBPAGE_TEXT + "image/bmp", // WEBPAGE_BMP, "image/gif", // WEBPAGE_GIF, "image/jpeg", // WEBPAGE_JPG, + "text/javascript", // WEBPAGE_JS, + "image/png", // WEBPAGE_PNG, + "image/svg+xml", // WEBPAGE_SVG, + "image/webp", // WEBPAGE_WEBP, + "text/xml", // WEBPAGE_XML, + "application/json", // WEBPAGE_JSON, + "text/csv", // WEBPAGE_CSV, nullptr // WEBPAGE_QTY }; @@ -552,7 +563,7 @@ int CWebPageDef::ServPageRequest( CClient * pClient, lpctstr pszURLArgs, CSTime lpctstr pszName; bool fGenerate = false; - if ( m_type == WEBPAGE_TEMPLATE ) // my version of cgi + if (m_type == WEBPAGE_TEMPLATE || m_type == WEBPAGE_TEXT || m_type == WEBPAGE_XML || m_type == WEBPAGE_JSON || m_type == WEBPAGE_CSV) // my version of cgi { pszName = GetDstName(); if ( pszName[0] == '\0' ) @@ -613,7 +624,7 @@ int CWebPageDef::ServPageRequest( CClient * pClient, lpctstr pszURLArgs, CSTime sm_szPageType[m_type] // type of the file. image/gif, image/x-xbitmap, image/jpeg ); - if ( m_type == WEBPAGE_TEMPLATE ) + if (m_type == WEBPAGE_TEMPLATE || m_type == WEBPAGE_TEXT || m_type == WEBPAGE_XML || m_type == WEBPAGE_JSON || m_type == WEBPAGE_CSV) iLen += snprintf(szTmp + iLen, uiWebDataBufSize - iLen, "Expires: 0\r\n"); else iLen += snprintf(szTmp + iLen, uiWebDataBufSize - iLen, "Last-Modified: %s\r\n", CSTime(dateChange).FormatGmt(nullptr)); diff --git a/src/common/resource/sections/CWebPageDef.h b/src/common/resource/sections/CWebPageDef.h index bd4040bbb..67cad6489 100644 --- a/src/common/resource/sections/CWebPageDef.h +++ b/src/common/resource/sections/CWebPageDef.h @@ -27,6 +27,13 @@ enum WEBPAGE_TYPE WEBPAGE_BMP, WEBPAGE_GIF, WEBPAGE_JPG, + WEBPAGE_JS, + WEBPAGE_PNG, + WEBPAGE_SVG, + WEBPAGE_WEBP, + WEBPAGE_XML, + WEBPAGE_CSV, + WEBPAGE_JSON, WEBPAGE_QTY }; From bf1653c8422afcd6c2b6c8594842f136ad382b6b Mon Sep 17 00:00:00 2001 From: mtwango Date: Thu, 15 May 2025 19:55:57 +0200 Subject: [PATCH 006/112] Fixed: Check for followers update only when using pet slots (#1409) (#1420) --- src/game/chars/CCharSpell.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index 62910f1f3..a39b1a773 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -350,7 +350,14 @@ CChar * CChar::Spell_Summon_Place( CChar * pChar, CPointMap ptTarg, int64 iDurat } pChar->StatFlag_Set(STATF_CONJURED); // conjured creates have no loot pChar->NPC_LoadScript(false); - ASSERT(FollowersUpdate(pChar, pChar->GetFollowerSlots(), true)); + + if (IsSetOF(OF_PetSlots)) + { + const bool followers = FollowersUpdate(pChar, pChar->GetFollowerSlots(), true); + ASSERT(followers); + UnreferencedParameter(followers); + } + pChar->NPC_PetSetOwner(this); pChar->MoveToChar(ptTarg); pChar->m_ptHome = ptTarg; From 077e02509e5fe9d8a712b782d508cab0bda41615 Mon Sep 17 00:00:00 2001 From: mtwango Date: Fri, 16 May 2025 07:11:28 +0200 Subject: [PATCH 007/112] Fix dropping items to container with tdata3/4 (#1414) * - fixed dropping items to container with tdata3/4 ending in point 0,0 (second part of #793) - added check for placing items into containers with tdata3/4 with actual values instead of fixed ones - changed generating value for random location in container with tdata3/4 to be same as without tdata3/4 * - changed max X/Y Gump size calculation with tdata on containers - warning, if tdata4 is lower or not set and tdata3 is --- src/game/items/CItemContainer.cpp | 60 ++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/src/game/items/CItemContainer.cpp b/src/game/items/CItemContainer.cpp index 58e0fbc52..6622f9b4a 100644 --- a/src/game/items/CItemContainer.cpp +++ b/src/game/items/CItemContainer.cpp @@ -464,20 +464,22 @@ CPointMap CItemContainer::GetRandContainerLoc() const // Get a random location in the container. const CItemBase *pItemDef = Item_GetDef(); - GUMP_TYPE gump = pItemDef->m_ttContainer.m_idGump; // Get the TDATA2 + const GUMP_TYPE gump = pItemDef->m_ttContainer.m_idGump; // Get the TDATA2 + + const int iRandOnce = g_Rand.GetValFast(UINT16_MAX); // check for custom values in TDATA3/TDATA4 if ( pItemDef->m_ttContainer.m_dwMinXY || pItemDef->m_ttContainer.m_dwMaxXY ) { - int tmp_MinX = pItemDef->m_ttContainer.m_dwMinXY >> 16; - int tmp_MinY = (pItemDef->m_ttContainer.m_dwMinXY & 0x0000FFFF); - int tmp_MaxX = pItemDef->m_ttContainer.m_dwMaxXY >> 16; - int tmp_MaxY = (pItemDef->m_ttContainer.m_dwMaxXY & 0x0000FFFF); - //DEBUG_WARN(("Custom container gump id %d for 0%x\n", gump, GetDispID())); - return CPointMap( - (word)(tmp_MinX + g_Rand.GetValFast(tmp_MaxX - tmp_MinX)), - (word)(tmp_MinY + g_Rand.GetValFast(tmp_MaxY - tmp_MinY)), - 0); + const int tmp_MinX = pItemDef->m_ttContainer.m_dwMinXY >> 16; + const int tmp_MinY = (pItemDef->m_ttContainer.m_dwMinXY & 0x0000FFFF); + const int tmp_MaxX = pItemDef->m_ttContainer.m_dwMaxXY >> 16; + const int tmp_MaxY = (pItemDef->m_ttContainer.m_dwMaxXY & 0x0000FFFF); + + return { + (short)(tmp_MinX + (iRandOnce % (tmp_MaxX - tmp_MinX))), + (short)(tmp_MinY + (iRandOnce % (tmp_MaxY - tmp_MinY))), + 0 }; } // No TDATA3 or no TDATA4: check if we have hardcoded in sm_ContSize the size of the gump indicated by TDATA2 @@ -499,7 +501,6 @@ CPointMap CItemContainer::GetRandContainerLoc() const } } - const int iRandOnce = g_Rand.GetValFast(UINT16_MAX); return { (short)(sm_ContSize[i].m_minx + (iRandOnce % (sm_ContSize[i].m_maxx - sm_ContSize[i].m_minx))), (short)(sm_ContSize[i].m_miny + (iRandOnce % (sm_ContSize[i].m_maxy - sm_ContSize[i].m_miny))), @@ -549,24 +550,40 @@ void CItemContainer::ContentAdd( CItem *pItem, CPointMap pt, bool bForceNoStack, // check for custom values in TDATA3/TDATA4 CItemBase *pContDef = Item_GetDef(); + + // Default rectangle defining size of container (best to define them in scripts as tdata3/4 to avoid visual stripping). + short minValX = 0; + short minValY = 0; + short maxValX = 512; + short maxValY = 512; + if (pContDef->m_ttContainer.m_dwMinXY || pContDef->m_ttContainer.m_dwMaxXY) { const short tmp_MinX = (short)( pContDef->m_ttContainer.m_dwMinXY >> 16 ); const short tmp_MinY = (short)( (pContDef->m_ttContainer.m_dwMinXY & 0x0000FFFF) ); const short tmp_MaxX = (short)( pContDef->m_ttContainer.m_dwMaxXY >> 16 ); const short tmp_MaxY = (short)( (pContDef->m_ttContainer.m_dwMaxXY & 0x0000FFFF) ); - if (pt.m_x < tmp_MinX) - pt.m_x = tmp_MinX; - if (pt.m_x > tmp_MaxX) - pt.m_x = tmp_MaxX; - if (pt.m_y < tmp_MinY) - pt.m_y = tmp_MinY; - if (pt.m_y > tmp_MaxY) - pt.m_y = tmp_MaxY; + + // Rewrite default size. + if (minValX < tmp_MinX) + minValX = tmp_MinX; + if (minValY < tmp_MinY) + minValY = tmp_MinY; + + if (tmp_MaxX > tmp_MinX) + maxValX = tmp_MaxX; + else + g_Log.EventWarn("Container 0%x has invalid max X size (upper 4 bytes) in TDATA4 (lower than TDATA3)\n", pContDef->GetDispID()); + if (tmp_MaxY > tmp_MinY) + maxValY = tmp_MaxY; + else + g_Log.EventWarn("Container 0%x has invalid max Y size (lower 4 bytes) in TDATA4 (lower than TDATA3)\n", pContDef->GetDispID()); } bool fStackInsert = false; - if ( pt.m_x <= 0 || pt.m_y <= 0 || pt.m_x > 512 || pt.m_y > 512 ) // invalid container location ? + + // We are dropping item out of container bounds. + if (pt.m_x <= minValX || pt.m_y <= minValY || pt.m_x > maxValX || pt.m_y > maxValY) { // Try to stack it. if ( !g_Serv.IsLoading() && pItem->Item_GetDef()->IsStackableType() && !bForceNoStack ) @@ -582,7 +599,8 @@ void CItemContainer::ContentAdd( CItem *pItem, CPointMap pt, bool bForceNoStack, } } } - if ( !fStackInsert) + // If we are not stacking it, get random location in container. + if (!fStackInsert) pt = GetRandContainerLoc(); } From 9405a7cfedcb435ca4db3b67f99b0e8506bd7e9d Mon Sep 17 00:00:00 2001 From: mtwango Date: Fri, 16 May 2025 10:03:09 +0200 Subject: [PATCH 008/112] Fix posibly forged layer and different layer during equip trigger (#1391) (#1421) --- Changelog.txt | 13 ++++++++----- src/game/chars/CCharAct.cpp | 14 +++++++++++++- src/network/receive.cpp | 11 +++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 2e34870e8..e6836e13b 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -3967,16 +3967,16 @@ Added: 'H' shortcut for variables to get the value as hexadecimal. 6-12-2024, canerksk - Added: LOCAL.WOPTalkMode in @SpellCast trigger and ini setting WOPTalkMode. In some clients, different operations can be performed based on this. For example, if this talk mode was sent from Sphere as TALKMODE_SPELL, the operation is performed according to the client version. The possibility of changing the talk mode according to the client version used is now provided. By default, TALKMODE_SPELL. - + Default talk mode: TALKMODE_SPELL = 10 - + Sphere.ini: WOPTalkMode = 0/14 - + Trigger: @SpellCast Local.WOPTalkMode = 0/14 - + TALKMODE_SAY = 0 // A character speaking. TALKMODE_SYSTEM = 1 // Display as system prompt. TALKMODE_EMOTE = 2 // *smiles* at object (client shortcut: :+space). @@ -4019,7 +4019,7 @@ Added: 'H' shortcut for variables to get the value as hexadecimal. VerboseItemBounce = 0 // (default) 22-04-2025, canerksk -- Added: New item function, CARVECORPSE: carves a corpse with SRC = char carving the corpse. +- Added: New item function, CARVECORPSE: carves a corpse with SRC = char carving the corpse. - Added: New ini RevealFlag setting REVEALF_ONHORSE, to allow or disallow stealth walking if you are on a mount. - Added: new ini CombatFlags setting COMBAT_ATTACK_NOAGGREIVED. In the old behavior (55i/55r/56b/56c) we were not guilty when we hit a murderer. Now when we attack a criminal or murderer we are guilty towards that person. An ini setting option has been added for this behavior. Although this is a normal behavior in OSI, it can be perceived as a problem since not every server designs games like OSI. @@ -4047,3 +4047,6 @@ When setting a property like MORE to the a spell or skill defname, trying to rea 08-05-2025, Nolok - Added: NPCWanderingLookAroundChance ini setting and TAG.OVERRIDE.LOOKAROUNDCHANCE. It allows to change the chance that a wandering NPC will analyze its surroundings for the current step. This also means that the trigger @NPCLookAtChar will fire more frequently. + +16-05-2025, Mulambo +- Changed: When equipping item by dragging or using Dress macro, the item layer is now showing actual layer of the object in EquipTest and ItemEquipTest trigger (instead of 31 = LAYER_DRAGGING). This is the same behaviour, as equipping by double clicking. diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 62e40e1be..b2824fa8e 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -3285,9 +3285,12 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) if ( !pCharMsg ) pCharMsg = this; + // Remember original layer, because we need to return to it after equiptest trigger. + LAYER_TYPE const requestedLayer = pItem->GetEquipLayer(); + if ( pItem->GetParent() == this ) { - if ( pItem->GetEquipLayer() != LAYER_DRAGGING ) // already equipped. + if ( requestedLayer != LAYER_DRAGGING ) // already equipped. return true; } @@ -3303,8 +3306,14 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) if (IsTrigUsed(TRIGGER_EQUIPTEST) || IsTrigUsed(TRIGGER_ITEMEQUIPTEST)) { + // Swap the layer for real one, because if we don't use dclick for equip, real layer gets rewritten with LAYER_DRAGGING. + pItem->SetEquipLayer(layer); + if (pItem->OnTrigger(ITRIG_EQUIPTEST, this) == TRIGRET_RET_TRUE) { + // Reset layer to the original value, that item doesn't get misplaced. + pItem->SetEquipLayer(requestedLayer); + // since this trigger is called also when creating an item via ITEM=, if the created item has a RETURN 1 in @EquipTest // (or if the NPC has a RETURN 1 in @ItemEquipTest), the item will be created but not placed in the world. // so, if this is an NPC, even if there's a RETURN 1 i need to bounce the item inside his pack @@ -3315,6 +3324,9 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) return false; } + // Reset layer to the original value. + pItem->SetEquipLayer(requestedLayer); + if (pItem->IsDeleted()) return false; } diff --git a/src/network/receive.cpp b/src/network/receive.cpp index 3aa57263a..d77b6bfcf 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -546,6 +546,17 @@ bool PacketItemEquipReq::onReceive(CNetState* net) CChar* target = targetSerial.CharFind(); bool fSuccess = false; + + // Check if player is sending wrong / forged layer. + const LAYER_TYPE itemRealLayer = target->CanEquipLayer(item, LAYER_QTY, target, true); + if (itemLayer != itemRealLayer) + { +#ifdef _DEBUG + g_Log.EventDebug("Player trying to equip item to invalid layer (sent: %d, should be %d)\n", itemLayer, itemRealLayer); +#endif + itemLayer = itemRealLayer; + } + if (target && (itemLayer < LAYER_HORSE) && source->CanDress(target) && source->CanTouch(item)) { //if (target->CanCarry(item)) //Since Weight behavior rework, we want avoid don't be able to equip an item if overweight From 93931ddd7cfb759f8bbdc88049d145cfaa7f217c Mon Sep 17 00:00:00 2001 From: mtwango Date: Fri, 16 May 2025 10:04:55 +0200 Subject: [PATCH 009/112] Feature: script for creating Debian package (#1417) --- .gitignore | 5 +++ README.md | 52 ++++++++++++++++++++++ packaging/debian/.gitignore | 9 ++++ packaging/debian/control | 19 +++++++++ packaging/debian/copyright | 8 ++++ packaging/debian/data/changelog | 5 +++ packaging/debian/data/sphereacct.scp | 7 +++ packaging/debian/rules | 28 ++++++++++++ packaging/debian/source/format | 1 + packaging/debian/sphereserver.6 | 34 +++++++++++++++ packaging/debian/sphereserver.config | 10 +++++ packaging/debian/sphereserver.dirs | 1 + packaging/debian/sphereserver.init | 57 +++++++++++++++++++++++++ packaging/debian/sphereserver.manpages | 1 + packaging/debian/sphereserver.postinst | 32 ++++++++++++++ packaging/debian/sphereserver.postrm | 22 ++++++++++ packaging/debian/sphereserver.service | 18 ++++++++ packaging/debian/sphereserver.templates | 14 ++++++ 18 files changed, 323 insertions(+) create mode 100644 packaging/debian/.gitignore create mode 100644 packaging/debian/control create mode 100644 packaging/debian/copyright create mode 100644 packaging/debian/data/changelog create mode 100644 packaging/debian/data/sphereacct.scp create mode 100644 packaging/debian/rules create mode 100644 packaging/debian/source/format create mode 100644 packaging/debian/sphereserver.6 create mode 100644 packaging/debian/sphereserver.config create mode 100644 packaging/debian/sphereserver.dirs create mode 100644 packaging/debian/sphereserver.init create mode 100644 packaging/debian/sphereserver.manpages create mode 100644 packaging/debian/sphereserver.postinst create mode 100644 packaging/debian/sphereserver.postrm create mode 100644 packaging/debian/sphereserver.service create mode 100644 packaging/debian/sphereserver.templates diff --git a/.gitignore b/.gitignore index 96543c3cc..ea9394397 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,8 @@ CMakeCache.txt /lib/mariadb_connector_c/mysql/ma_config.h /lib/mariadb_connector_c/mysql/mariadb_version.h /lib/zlib/zlib/zconf.h + +# Ignore debian package files. +/sphereserver*.buildinfo +/sphereserver*.changes +/sphereserver*.deb diff --git a/README.md b/README.md index 61473798f..6b1225eae 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,58 @@ Building will require more packages than the ones needed to run Sphere. Just run `cmake --build .` in the build directory (the one where you have asked CMake to create its files). +## Debian package + +Debian package is an installation package for Linux Debian and its derivates (Ubuntu, Linux Mint, etc.) and intended to +be run as a service. Folder structure is as follows: +- Binary is located in `/usr/bin/` directory (use `which sphereserver` to locate it). +- Configuration files (ini) are in `/etc/sphereserver/` directory. +- Log files are stored in `/var/log/sphereserver/` directory. +- Everything else is in `/opt/sphereserver/`. + +These paths are automatically replaced in sphere.ini during installation. + +### Build + +Building deb package is pretty straightforward. The requirements are the same, as you would compile code by hand. One +extra requirement is _debhelper_ package (`sudo apt install debhelper`). + +**Step by step building:** +1) Clone repository. +2) Change directory to wherever you cloned repository and then go to `packaging` directory. +3) Since we are in nightly, there is no actual SemVer changelog that we can include, and we have to use workaround to + generate one. By running `cat debian/data/changelog | sed -e "s/@version@/$(git rev-list --count HEAD)/" -e "s/@date@/$(date -R)/" > debian/changelog` + you will generate one. This command takes deb changelog template (in `debian/data/changelog`) and replaces __version__ + and __date__ variables with git revision and current date. +4) Build package by running `dpkg-buildpackage -us -uc -b` (arguments are `-b` = build, `-us` and `-uc` = don't sign + built files with OpenPGP key). This will compile sources and create debian package, which will be stored in the root + of the project directory (i.e. `/home/dev/source-x/`). The cmake is predefined to build a Linux x86_64 Nightly version + of SphereServer. All build commands and switches are defined in `debian/rules` file. + +### Installation + +When you download or build the package, you have to make it executable `sudo chmod +x FILENAME.deb`. Then you can +install the package by running `sudo apt install FILENAME.deb`. The installation process will download required +dependencies, show you some info about SphereServer being in nightly version, create an owner game account (admin/admin), +create dedicated linux user `sphereserver`, directory structure and service files for you. + +After installation, you will have to provide mul files and scripts to `/opt/sphereserver/mul/` and`/opt/sphereserver/scripts/` +folder and `AGREE=1` in sphere.ini. + +When you are finished with configuration, you can enable/start services by running `sudo systemctl enable sphereserver` +and `sudo systemctl start sphereserver`. To check, if service is running, type `systemctl status sphereserver`, check +the journal (`journalctl -u sphereserver`) or sphere logs. + +### Updating + +Updating is the same as installation, download / build package, run `sudo chmod +x FILENAME.deb` and `sudo apt install FILENAME.deb`. +If any changes happened in ini files and are in conflict with your settings, you will be asked to review them. + +### Removal + +To remove sphereserver, run `sudo apt remove sphereserver` and `sudo apt remove sphereserver --purge`. All files related +to sphereserver will be deleted. + ## Coding Notes (add as you wish to standardize the coding for new contributors) + Make sure you can compile and run the program before pushing a commit. diff --git a/packaging/debian/.gitignore b/packaging/debian/.gitignore new file mode 100644 index 000000000..c06b01b37 --- /dev/null +++ b/packaging/debian/.gitignore @@ -0,0 +1,9 @@ +*.debhelper.log +*.debhelper +*.substvars +debhelper-build-stamp +files +sphereserver/ + +# Temporary disable changelog from versioning, since we will be using bash script during development. +/changelog diff --git a/packaging/debian/control b/packaging/debian/control new file mode 100644 index 000000000..a60b93aa2 --- /dev/null +++ b/packaging/debian/control @@ -0,0 +1,19 @@ +Source: sphereserver +Section: net +Priority: optional +Maintainer: SphereServer development team +Build-Depends: debhelper-compat (= 13), cmake (>= 3.29), libmariadb-dev, git +Standards-Version: 4.7.2 +Vcs-Browser: https://github.com/Sphereserver/Source-X +Vcs-Git: https://github.com/Sphereserver/Source-X.git +Homepage: https://www.sphereserver.net/ + +Package: sphereserver +Architecture: any +Pre-Depends: ${misc:Pre-Depends} +Depends: adduser, mariadb-client, debconf, ${shlibs:Depends}, ${misc:Depends} +Recommends: telnet +Suggests: mariadb-server, nginx +Description: Ultima Online server emulator + SphereServer is an Ultima Online server emulator allowing players to run their + own shards. diff --git a/packaging/debian/copyright b/packaging/debian/copyright new file mode 100644 index 000000000..52277360d --- /dev/null +++ b/packaging/debian/copyright @@ -0,0 +1,8 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Sphereserver +Source: https://github.com/Sphereserver/Source-X + +Files: * +Copyright: + 2025 SphereServer development team +License: Apache 2.0 diff --git a/packaging/debian/data/changelog b/packaging/debian/data/changelog new file mode 100644 index 000000000..90cc6355b --- /dev/null +++ b/packaging/debian/data/changelog @@ -0,0 +1,5 @@ +sphereserver (0.X.nightly-@version@) unstable; urgency=medium + + * See https://github.com/Sphereserver/Source-X/blob/master/Changelog.txt. + + -- SphereServer development team @date@ diff --git a/packaging/debian/data/sphereacct.scp b/packaging/debian/data/sphereacct.scp new file mode 100644 index 000000000..0be37c2a8 --- /dev/null +++ b/packaging/debian/data/sphereacct.scp @@ -0,0 +1,7 @@ +// Accounts are periodically moved to the sphereaccu.scp file. +// All account changes should be made here. +// Use the /ACCOUNT UPDATE command to force accounts to update. + +[admin] +PLEVEL=7 +PASSWORD=admin diff --git a/packaging/debian/rules b/packaging/debian/rules new file mode 100644 index 000000000..aeccdf2e9 --- /dev/null +++ b/packaging/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_configure: + cmake -S ../ -B ../build -DCMAKE_BUILD_TYPE="Nightly" -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/Linux-GNU-x86_64.cmake -DCMAKE_C_FLAGS="-fpch-preprocess" -DCMAKE_CXX_FLAGS="-fpch-preprocess" + +override_dh_auto_build: + cmake --build ../build + +override_dh_auto_install: + # Prepare binary. + install -d debian/sphereserver/usr/bin + install -m 0755 ../build/bin-x86_64/SphereSvrX64_nightly debian/sphereserver/usr/bin/sphereserver + # Prepare accounts file. + install -d debian/sphereserver/tmp + install -m 0644 debian/data/sphereacct.scp debian/sphereserver/tmp/sphereacct.scp + # Copy ini files. + install -m 0644 ../src/sphereCrypt.ini debian/sphereserver/etc/sphereserver/sphereCrypt.ini + # Replace relative paths with absolute one in sphere.ini. + cat ../src/sphere.ini | sed -e "s/ScpFiles=/ScpFiles=\/opt\/sphereserver\//" -e "s/WorldSave=/WorldSave=\/opt\/sphereserver\//" -e "s/AcctFiles=/AcctFiles=\/opt\/sphereserver\//" -e "s/WebPageSrc=/WebPageSrc=\/opt\/sphereserver\//" -e "s/WebPageFile=/WebPageFile=\/opt\/sphereserver\//" -e "s/\/\/MulFiles=/MulFiles=\/opt\/sphereserver\//" -e "s/Log=logs/Log=\/var\/log\/sphereserver/" > debian/sphereserver/etc/sphereserver/sphere.ini + +override_dh_installinit: + dh_installinit --no-enable --no-start --no-restart-after-upgrade + +override_dh_installsystemd: + dh_installsystemd --no-enable --no-start --no-restart-after-upgrade diff --git a/packaging/debian/source/format b/packaging/debian/source/format new file mode 100644 index 000000000..89ae9db8f --- /dev/null +++ b/packaging/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/packaging/debian/sphereserver.6 b/packaging/debian/sphereserver.6 new file mode 100644 index 000000000..76dcea8b0 --- /dev/null +++ b/packaging/debian/sphereserver.6 @@ -0,0 +1,34 @@ +.TH SPHERESERVER "6" "April 2025" "sphereserver" +.SH NAME +SphereServer \- Ultima Online server emulator +.SH DESCRIPTION +SphereServer is an Ultima Online server emulator allowing players to run their +own shards. +.SH OPTIONS +.TP +\fB\-H, -?\fR +display command line switches +.TP +\fB\-I\fR= +set path to ini files. +.TP +\fB\-C\fR +do not use colored console output (default: on). +.TP +\fB\-D\fR +dump global variable DEFNAMEs to defs.txt. +.TP +\fB\-G\fR +defrags sphere saves. +.TP +\fB\-N\fR +set the sphere name. +.TP +\fB\-P\fR +set the port number. +.TP +\fB\-O\fR +output console to this file name. +.TP +\fB\-Q\fR +quit when finished. diff --git a/packaging/debian/sphereserver.config b/packaging/debian/sphereserver.config new file mode 100644 index 000000000..6e7bf02fa --- /dev/null +++ b/packaging/debian/sphereserver.config @@ -0,0 +1,10 @@ +#!/bin/sh + +PRIORITY="medium" + +set -e +. /usr/share/debconf/confmodule + +# Information about manual config. +db_input $PRIORITY sphereserver/info || true +db_go diff --git a/packaging/debian/sphereserver.dirs b/packaging/debian/sphereserver.dirs new file mode 100644 index 000000000..07f589af9 --- /dev/null +++ b/packaging/debian/sphereserver.dirs @@ -0,0 +1 @@ +etc/sphereserver diff --git a/packaging/debian/sphereserver.init b/packaging/debian/sphereserver.init new file mode 100644 index 000000000..2f3c12583 --- /dev/null +++ b/packaging/debian/sphereserver.init @@ -0,0 +1,57 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: sphereserver +# Required-Start: +# Required-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start/stop the sphereserver daemon +# Description: SphereServer - Ultima Online server emulator. +### END INIT INFO + +set -e + +PATH=/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/bin/sphereserver + +# Gracefully exit if the package has been removed. +test -x $DAEMON || exit 0 + +. /lib/lsb/init-functions + +ret=0 + +case "$1" in + start) + log_daemon_msg "Starting SphereServer" + if start-stop-daemon --start --oknodo --quiet \ + --name sphereserver --startas $DAEMON -- -I=/etc/sphereserver/ + then + log_end_msg 0 + else + ret=$? + log_end_msg 1 + fi + ;; + stop) + log_daemon_msg "Stopping SphereServer" + if start-stop-daemon --stop --oknodo --quiet --exec $DAEMON \ + --name sphereserver + then + log_end_msg 0 + else + ret=$? + log_end_msg 1 + fi + ;; + reload|force-reload|restart) + $0 stop + $0 start + ret=$? + ;; + *) + exit 1 + ;; +esac + +exit $ret diff --git a/packaging/debian/sphereserver.manpages b/packaging/debian/sphereserver.manpages new file mode 100644 index 000000000..d53842270 --- /dev/null +++ b/packaging/debian/sphereserver.manpages @@ -0,0 +1 @@ +debian/sphereserver.6 diff --git a/packaging/debian/sphereserver.postinst b/packaging/debian/sphereserver.postinst new file mode 100644 index 000000000..f857d5e1d --- /dev/null +++ b/packaging/debian/sphereserver.postinst @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Post-installation script for Sphereserver. +# + +# Parameters +NAME="sphereserver" + +# Ensure that the scripts abort in case of any errors. +set -e + +. /usr/share/debconf/confmodule + +# Check, if user exists. +if ! getent passwd $NAME >/dev/null; then + # Create user. + adduser --home /opt/$NAME --system --group $NAME +fi + +# Create directories. +install -g $NAME -o $NAME -d /var/log/$NAME +install -g $NAME -o $NAME -d /opt/$NAME/accounts +install -g $NAME -o $NAME -d /opt/$NAME/mul +install -g $NAME -o $NAME -d /opt/$NAME/save +install -g $NAME -o $NAME -d /opt/$NAME/scripts + +# Create sphereacct file, don't overwrite it. +if [ ! -f /opt/$NAME/accounts/sphereacct.scp ]; then + install -g $NAME -o $NAME -m 0644 /tmp/sphereacct.scp /opt/$NAME/accounts/sphereacct.scp +fi + +#DEBHELPER# diff --git a/packaging/debian/sphereserver.postrm b/packaging/debian/sphereserver.postrm new file mode 100644 index 000000000..7befe141e --- /dev/null +++ b/packaging/debian/sphereserver.postrm @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Post-removal script for Sphereserver. +# + +# Parameters +NAME="sphereserver" + +# Ensure that the scripts abort in case of any errors +set -e + +if [ "$1" = "remove" ]; then + # Remove user. + userdel $NAME + + # Remove files. + rm -rf /opt/$NAME + rm -rf /etc/$NAME + rm -rf /var/log/$NAME +fi + +#DEBHELPER# diff --git a/packaging/debian/sphereserver.service b/packaging/debian/sphereserver.service new file mode 100644 index 000000000..31f372be3 --- /dev/null +++ b/packaging/debian/sphereserver.service @@ -0,0 +1,18 @@ +# SphereServer systemd service file +# Copyright 2025 SphereServer development team +# SPDX-License-Identifier: Apache-2.0 + +[Unit] +Description=Sphereserver +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/sphereserver +ExecStart=/usr/bin/sphereserver -I=/etc/sphereserver/ +Restart=on-failure +User=sphereserver +Group=sphereserver + +[Install] +WantedBy=multi-user.target diff --git a/packaging/debian/sphereserver.templates b/packaging/debian/sphereserver.templates new file mode 100644 index 000000000..d113053f2 --- /dev/null +++ b/packaging/debian/sphereserver.templates @@ -0,0 +1,14 @@ +Template: sphereserver/info +Type: note +Description: This package is intended to be run as a system service + Installer will install default configuration and administrator account + (admin/admin). + . + You need to supply scripts to /opt/sphereserver/scripts, mul files to + /opt/sphereserver/mul (or any path defined in sphere.ini) and explicitly + enable AGREE option in sphere.ini. + . + Ini files are placed in /etc/sphereserver/ folder. + . + After configuration, you can enable and start sphereserver service and connect + to game via client or telnet. From c61a91eec0756d4f2f8df62aec5ecce8ee889339 Mon Sep 17 00:00:00 2001 From: Raydie <50953519+raydienull@users.noreply.github.com> Date: Mon, 19 May 2025 15:58:10 +0200 Subject: [PATCH 010/112] update special ability and udpate defmessages --- src/network/receive.cpp | 22 +++++++++++----------- src/tables/defmessages.tbl | 5 ++--- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/network/receive.cpp b/src/network/receive.cpp index d77b6bfcf..d4fed1129 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -2773,7 +2773,10 @@ bool PacketWrestleDisarm::onReceive(CNetState* net) { ADDTOCALLSTACK("PacketWrestleDisarm::onReceive"); - net->getClient()->SysMessageDefault(DEFMSG_MSG_WRESTLING_NOGO); + CClient* client = net->getClient(); + ASSERT(client); + + client->Event_CombatAbilitySelect(0x5); // Disarm return true; } @@ -2793,7 +2796,11 @@ bool PacketWrestleStun::onReceive(CNetState* net) { ADDTOCALLSTACK("PacketWrestleStun::onReceive"); - net->getClient()->SysMessageDefault(DEFMSG_MSG_WRESTLING_NOGO); + CClient* client = net->getClient(); + ASSERT(client); + + client->Event_CombatAbilitySelect(0xB); // Paralyzing Blow + return true; } @@ -4041,19 +4048,12 @@ bool PacketSpecialMove::onReceive(CNetState* net) CClient* client = net->getClient(); ASSERT(client); - CChar* character = client->GetChar(); - if (character == nullptr) - return false; skip(1); dword ability = readInt32(); - if ( IsTrigUsed(TRIGGER_USERSPECIALMOVE) ) - { - CScriptTriggerArgs args; - args.m_iN1 = ability; - character->OnTrigger(CTRIG_UserSpecialMove, character, &args); - } + client->Event_CombatAbilitySelect(ability); + return true; } diff --git a/src/tables/defmessages.tbl b/src/tables/defmessages.tbl index aa54e7be7..4f80adebf 100644 --- a/src/tables/defmessages.tbl +++ b/src/tables/defmessages.tbl @@ -155,7 +155,7 @@ MSG(HEALING_CORPSEG, "Put the corpse on the ground.") MSG(HEALING_CURE_1, "You cure %s of poisons!") MSG(HEALING_CURE_2, "%s has cured you of poisons!") MSG(HEALING_CURE_3, "You have failed to cure your target!") -MSG(HEALING_CURE_4, "The attempt to cure you has failed.") +MSG(HEALING_CURE_4, "The attempt to cure you has failed.") MSG(HEALING_GHOST, "You can't heal a ghost! Try healing their corpse.") MSG(HEALING_HEALTHY, "You are healthy") MSG(HEALING_INTERRUPT, "Your healing was interrupted by the hit!") @@ -390,7 +390,7 @@ MSG(MSG_ARRDEP_1, "%s has %s %s.") MSG(MSG_ARRDEP_2, "arrived in") MSG(MSG_ARRDEP_3, "departed from") MSG(MSG_BOUNCE_PACK, "in your pack") -MSG(MSG_BOUNCE_CONT, "in the %s") +MSG(MSG_BOUNCE_CONT, "in the %s") MSG(MSG_CANTPUSH, "You are not strong enough to push %s out of the way.") MSG(MSG_CANTSLEEP, "Can't sleep here") MSG(MSG_CMD_LACKPRIV, "You lack privilege to do this.") @@ -575,7 +575,6 @@ MSG(MSG_UNCONSCIOUS, "You are unconscious and can't move.") MSG(MSG_WHERE_AREA, "I am in %s (%s)") MSG(MSG_WHERE_ROOM, "I am in %s in %s (%s)") MSG(MSG_WHERE, "I am at %s.") -MSG(MSG_WRESTLING_NOGO, "Sorry wrestling moves not available yet") MSG(MSG_YOUNOTICE_1, "You notice %s %s %s.") MSG(MSG_YOUNOTICE_2, "You notice %s %s %s%s %s") MSG(MSG_YOUNOTICE_S, "'s") From b6c13233ad036936c8f7253c72e7b6c14bb372d9 Mon Sep 17 00:00:00 2001 From: mtwango Date: Mon, 19 May 2025 16:58:05 +0200 Subject: [PATCH 011/112] Fix for container spam / crash (#1423) * - require both tdata3/4 when using custom container dimensions * - fallback values for random container position - warn if we don't have hardcoded value - warn not to use hardcoded value * - remove notice about hardoded values (too much spam on console) * - division by zero check --- src/game/items/CItemContainer.cpp | 77 ++++++++++++++++++++----------- src/game/items/CItemContainer.h | 5 +- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src/game/items/CItemContainer.cpp b/src/game/items/CItemContainer.cpp index 6622f9b4a..f913255af 100644 --- a/src/game/items/CItemContainer.cpp +++ b/src/game/items/CItemContainer.cpp @@ -461,49 +461,72 @@ CPointMap CItemContainer::GetRandContainerLoc() const { GUMP_CHEST_METAL2, 18, 105, 162, 178 } }; - // Get a random location in the container. + // Prepare fallback values, in case we don't have hardcoded one and user didn't define TDATA3/4. + short minValX = 0; + short minValY = 0; + short maxValX = 512; + short maxValY = 512; const CItemBase *pItemDef = Item_GetDef(); - const GUMP_TYPE gump = pItemDef->m_ttContainer.m_idGump; // Get the TDATA2 - - const int iRandOnce = g_Rand.GetValFast(UINT16_MAX); + // Get gump visual from item TDATA2. + const GUMP_TYPE gump = pItemDef->m_ttContainer.m_idGump; + // Prepare a random location in the container. + const int iRandOnce = CSRand::GetValFast(UINT16_MAX); - // check for custom values in TDATA3/TDATA4 - if ( pItemDef->m_ttContainer.m_dwMinXY || pItemDef->m_ttContainer.m_dwMaxXY ) + // Use custom values in TDATA3/TDATA4, if they are defined. + if ( pItemDef->m_ttContainer.m_dwMinXY && pItemDef->m_ttContainer.m_dwMaxXY ) { - const int tmp_MinX = pItemDef->m_ttContainer.m_dwMinXY >> 16; - const int tmp_MinY = (pItemDef->m_ttContainer.m_dwMinXY & 0x0000FFFF); - const int tmp_MaxX = pItemDef->m_ttContainer.m_dwMaxXY >> 16; - const int tmp_MaxY = (pItemDef->m_ttContainer.m_dwMaxXY & 0x0000FFFF); + minValX = pItemDef->m_ttContainer.m_dwMinXY >> 16; + minValY = (pItemDef->m_ttContainer.m_dwMinXY & 0x0000FFFF); + maxValX = pItemDef->m_ttContainer.m_dwMaxXY >> 16; + maxValY = (pItemDef->m_ttContainer.m_dwMaxXY & 0x0000FFFF); + + // Make sure this value aren't same, we can't divide by zero. + if (maxValX == minValX) + maxValX += 1; + if (maxValY == minValY) + maxValY += 1; return { - (short)(tmp_MinX + (iRandOnce % (tmp_MaxX - tmp_MinX))), - (short)(tmp_MinY + (iRandOnce % (tmp_MaxY - tmp_MinY))), + static_cast(minValX + (iRandOnce % (maxValX - minValX))), + static_cast(minValY + (iRandOnce % (maxValY - minValY))), 0 }; } - // No TDATA3 or no TDATA4: check if we have hardcoded in sm_ContSize the size of the gump indicated by TDATA2 + // We may want a keyring with no gump, so no need to show the warning. + if (IsType(IT_KEYRING)) + { + return { + static_cast(minValX + (iRandOnce % (maxValX - minValX))), + static_cast(minValY + (iRandOnce % (maxValY - minValY))), + 0 }; + } + // No TDATA3 and TDATA4: check if we have hardcoded in sm_ContSize the size of the gump indicated by TDATA2. uint i = 0; - // We may want a keyring with no gump, so no need to show the warning. - if (!IsType(IT_KEYRING)) + for (; ; ++i) { - for (; ; ++i) + // We didn't find anything in hardcoded list. + if (i >= std::size(sm_ContSize)) { - if (i >= ARRAY_COUNT(sm_ContSize)) - { - i = 0; // set to default - g_Log.EventWarn("Unknown container gump id %d for 0%x\n", gump, GetDispID()); - break; - } - if (sm_ContSize[i].m_gump == gump) - break; + g_Log.EventWarn("Container 0%x with gump %d doesn't have TDATA3/4 defined.\n", GetDispID(), gump); + break; + } + // We got a hardoded gump sizes, use them. + if (sm_ContSize[i].m_gump == gump) + { + minValX = sm_ContSize[i].m_minx; + minValY = sm_ContSize[i].m_miny; + maxValX = sm_ContSize[i].m_maxx; + maxValY = sm_ContSize[i].m_maxy; + + break; } } return { - (short)(sm_ContSize[i].m_minx + (iRandOnce % (sm_ContSize[i].m_maxx - sm_ContSize[i].m_minx))), - (short)(sm_ContSize[i].m_miny + (iRandOnce % (sm_ContSize[i].m_maxy - sm_ContSize[i].m_miny))), + static_cast(minValX + (iRandOnce % (maxValX - minValX))), + static_cast(minValY + (iRandOnce % (maxValY - minValY))), 0 }; } @@ -557,7 +580,7 @@ void CItemContainer::ContentAdd( CItem *pItem, CPointMap pt, bool bForceNoStack, short maxValX = 512; short maxValY = 512; - if (pContDef->m_ttContainer.m_dwMinXY || pContDef->m_ttContainer.m_dwMaxXY) + if (pContDef->m_ttContainer.m_dwMinXY && pContDef->m_ttContainer.m_dwMaxXY) { const short tmp_MinX = (short)( pContDef->m_ttContainer.m_dwMinXY >> 16 ); const short tmp_MinY = (short)( (pContDef->m_ttContainer.m_dwMinXY & 0x0000FFFF) ); diff --git a/src/game/items/CItemContainer.h b/src/game/items/CItemContainer.h index 61d50c64c..51513a958 100644 --- a/src/game/items/CItemContainer.h +++ b/src/game/items/CItemContainer.h @@ -73,7 +73,10 @@ class CItemContainer : public CItemVendable, public CContainer virtual void DupeCopy( const CObjBase * pItem ) override; // overriding CItem::DupeCopy - CPointMap GetRandContainerLoc() const; + /** + * Gets the random location in container based on sizes defined in tdata3/4. + */ + CPointMap GetRandContainerLoc() const; void OnOpenEvent( CChar * pCharOpener, const CObjBaseTemplate * pObjTop ); }; From 743f67a326e54518756fab1253b3f084430223ee Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 17 May 2025 07:36:56 +0200 Subject: [PATCH 012/112] Speed improvement: created an object pool for CScriptTriggerArgs to avoid heap allocating it each time (happening very often) --- src/CMakeSources.cmake | 44 +- src/common/CDataBase.cpp | 10 +- src/common/CDataBase.h | 3 +- src/common/CExpression.cpp | 15 +- src/common/CExpression.h | 25 +- src/common/CScriptContexts.h | 4 +- src/common/CScriptObj.cpp | 450 +++++++++--------- src/common/CScriptObj.h | 55 +-- src/common/CScriptParserBufs.cpp | 64 +++ src/common/CScriptParserBufs.h | 55 +++ src/common/CScriptTriggerArgs.cpp | 37 +- src/common/CScriptTriggerArgs.h | 20 +- src/common/resource/sections/CDialogDef.cpp | 20 +- .../resource/sections/CRandGroupDef.cpp | 2 +- .../resource/sections/CRegionResourceDef.cpp | 4 +- .../resource/sections/CRegionResourceDef.h | 2 +- src/common/resource/sections/CWebPageDef.cpp | 36 +- src/common/resource/sections/CWebPageDef.h | 5 +- src/common/sphere_library/sobjpool.h | 358 ++++++++++++++ src/common/sphere_library/sstacks.h | 220 ++++++--- src/game/CContainer.cpp | 52 +- src/game/CContainer.h | 20 +- src/game/CObjBase.cpp | 84 ++-- src/game/CObjBase.h | 25 +- src/game/CRegion.cpp | 5 +- src/game/CSector.cpp | 8 +- src/game/CServer.cpp | 7 +- src/game/CServerDef.cpp | 7 +- src/game/CTimedFunctionHandler.cpp | 4 +- src/game/CTimedFunctionHandler.h | 2 +- src/game/CWorld.cpp | 33 +- src/game/CWorldComm.cpp | 9 +- src/game/CWorldMap.cpp | 10 +- src/game/CWorldTimedFunctions.cpp | 4 +- src/game/CWorldTimedFunctions.h | 2 +- src/game/chars/CChar.cpp | 113 +++-- src/game/chars/CChar.h | 22 +- src/game/chars/CCharAct.cpp | 246 +++++----- src/game/chars/CCharAttacker.cpp | 42 +- src/game/chars/CCharFight.cpp | 295 ++++++------ src/game/chars/CCharMemory.cpp | 13 +- src/game/chars/CCharNPC.cpp | 7 +- src/game/chars/CCharNPCAct.cpp | 60 +-- src/game/chars/CCharNPCAct_Fight.cpp | 29 +- src/game/chars/CCharNPCAct_Magic.cpp | 15 +- src/game/chars/CCharNPCPet.cpp | 5 +- src/game/chars/CCharNotoriety.cpp | 66 +-- src/game/chars/CCharSkill.cpp | 268 +++++------ src/game/chars/CCharSpell.cpp | 241 +++++----- src/game/chars/CCharStat.cpp | 100 ++-- src/game/chars/CCharStatus.cpp | 16 +- src/game/chars/CCharUse.cpp | 45 +- src/game/clients/CAccount.cpp | 58 +-- src/game/clients/CClient.cpp | 12 +- src/game/clients/CClient.h | 7 +- src/game/clients/CClientDialog.cpp | 17 +- src/game/clients/CClientEvent.cpp | 261 +++++----- src/game/clients/CClientLog.cpp | 15 +- src/game/clients/CClientMsg.cpp | 110 +++-- src/game/clients/CClientMsg_AOSTooltip.cpp | 15 +- src/game/clients/CClientTarg.cpp | 22 +- src/game/clients/CClientUse.cpp | 73 +-- src/game/clients/CParty.cpp | 32 +- src/game/components/CCChampion.cpp | 44 +- src/game/components/CCChampion.h | 4 +- src/game/components/CCMultiMovable.cpp | 28 +- src/game/components/CCPropsChar.cpp | 2 + src/game/components/CCSpawn.cpp | 47 +- src/game/items/CItem.cpp | 81 ++-- src/game/items/CItem.h | 6 +- src/game/items/CItemContainer.cpp | 38 +- src/game/items/CItemMulti.cpp | 111 ++--- src/game/items/CItemMultiCustom.cpp | 65 +-- src/game/items/CItemPlant.cpp | 29 +- src/game/items/CItemStone.cpp | 15 +- src/game/spheresvr.cpp | 4 +- src/network/CIPHistoryManager.cpp | 11 +- src/network/CNetworkManager.cpp | 33 +- src/network/receive.cpp | 62 +-- 79 files changed, 2643 insertions(+), 1843 deletions(-) create mode 100644 src/common/CScriptParserBufs.cpp create mode 100644 src/common/CScriptParserBufs.h create mode 100644 src/common/sphere_library/sobjpool.h diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index fbecf9e6b..8897cba8d 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -80,26 +80,6 @@ set(network_SRCS ) source_group(network FILES ${network_SRCS}) -# Login encryption handling -set(crypto_SRCS - src/common/crypto/CBCrypt.cpp - src/common/crypto/CBCrypt.h - src/common/crypto/CCrypto.cpp - src/common/crypto/CCrypto.h - src/common/crypto/CCryptoBlowFish.cpp - src/common/crypto/CCryptoLogin.cpp - src/common/crypto/CCryptoKeyCalc.cpp - src/common/crypto/CCryptoKeyCalc.h - src/common/crypto/CCryptoMD5Interface.cpp - src/common/crypto/CCryptoTwoFishInterface.cpp - src/common/crypto/CHuffman.cpp - src/common/crypto/CHuffman.h - src/common/crypto/CMD5.cpp - src/common/crypto/CMD5.h - src/common/crypto/crypto_common.h -) -source_group(common\\crypto FILES ${crypto_SRCS}) - # Handle UO Client files set(uofiles_SRCS src/game/uo_files/CUOHuesRec.h @@ -173,6 +153,8 @@ set(common_SRCS src/common/CScriptContexts.h src/common/CScriptObj.cpp src/common/CScriptObj.h + src/common/CScriptParserBufs.cpp + src/common/CScriptParserBufs.h src/common/CScriptTriggerArgs.cpp src/common/CScriptTriggerArgs.h src/common/CSFileObj.cpp @@ -200,6 +182,27 @@ set(common_SRCS ) source_group(common FILES ${common_SRCS}) +# Login encryption handling +set(crypto_SRCS + src/common/crypto/CBCrypt.cpp + src/common/crypto/CBCrypt.h + src/common/crypto/CCrypto.cpp + src/common/crypto/CCrypto.h + src/common/crypto/CCryptoBlowFish.cpp + src/common/crypto/CCryptoLogin.cpp + src/common/crypto/CCryptoKeyCalc.cpp + src/common/crypto/CCryptoKeyCalc.h + src/common/crypto/CCryptoMD5Interface.cpp + src/common/crypto/CCryptoTwoFishInterface.cpp + src/common/crypto/CHuffman.cpp + src/common/crypto/CHuffman.h + src/common/crypto/CMD5.cpp + src/common/crypto/CMD5.h + src/common/crypto/crypto_common.h +) +source_group(common\\crypto FILES ${crypto_SRCS}) + + set(resource_SRCS src/common/resource/CResourceDef.cpp src/common/resource/CResourceDef.h @@ -286,6 +289,7 @@ set(spherelibrary_SRCS src/common/sphere_library/smap.h src/common/sphere_library/smutex.h src/common/sphere_library/smutex.cpp + src/common/sphere_library/sobjpool.h src/common/sphere_library/squeues.h src/common/sphere_library/sresetevents.cpp src/common/sphere_library/sresetevents.h diff --git a/src/common/CDataBase.cpp b/src/common/CDataBase.cpp index 352359d24..f20db0ec5 100644 --- a/src/common/CDataBase.cpp +++ b/src/common/CDataBase.cpp @@ -10,10 +10,9 @@ #include "CDataBase.h" -CDataBase::CDataBase() +CDataBase::CDataBase() : + m_fConnected(false), _myData(nullptr) { - m_fConnected = false; - _myData = nullptr; } CDataBase::~CDataBase() @@ -291,13 +290,10 @@ bool CDataBase::_OnTick() m_QueryArgs.pop(); } - if ( !g_Serv.r_Call(currentPair.first, &g_Serv, currentPair.second) ) + if ( !g_Serv.r_Call(currentPair.first.GetBuffer(), currentPair.second, &g_Serv) ) { // error } - - ASSERT(currentPair.second != nullptr); - delete currentPair.second; } return true; diff --git a/src/common/CDataBase.h b/src/common/CDataBase.h index d166cd70d..1cf7bc6d0 100644 --- a/src/common/CDataBase.h +++ b/src/common/CDataBase.h @@ -62,7 +62,7 @@ class CDataBase : public CScriptObj static lpctstr const sm_szVerbKeys[]; private: - typedef std::pair FunctionArgsPair_t; + typedef std::pair FunctionArgsPair_t; typedef std::queue QueueFunction_t; protected: @@ -73,6 +73,7 @@ class CDataBase : public CScriptObj private: SimpleMutex m_connectionMutex; SimpleMutex m_resultMutex; + bool addQuery(bool isQuery, lpctstr theFunction, lpctstr theQuery); }; diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index 096ff5022..12f4c841e 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -1,8 +1,9 @@ +#include "CExpression.h" #include "../game/CServerConfig.h" #include "sphere_library/CSRand.h" //#include "CException.h" +#include "CScriptParserBufs.h" #include "CLog.h" -#include "CExpression.h" #include #include #include @@ -1354,7 +1355,7 @@ int CExpression::GetRangeVals(lpctstr & pExpr, int64 * piVals, int iMaxQty, bool } -int CExpression::GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSubexprData)[32], int iMaxQty) // static +int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataArray &psSubExprData, int iMaxQty) // static { ADDTOCALLSTACK("CExpression::GetConditionalSubexpressions"); // Get the start and end pointers for each logical subexpression (delimited by brackets or by logical operators || and &&) inside a conditional statement (IF/ELIF/ELSEIF and QVAL). @@ -1367,7 +1368,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSube //memset((void*)&pSubexprPos, 0, ARRAY_COUNT(pSubexprPos)); int iSubexprQty = 0; // number of subexpressions - using SType = SubexprData::Type; + using SType = CScriptSubExprData::Type; while (pExpr[0] != '\0') { if (++iSubexprQty >= iMaxQty) @@ -1377,7 +1378,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSube } GETNONWHITESPACE(pExpr); - SubexprData& sCurSubexpr = psSubexprData[iSubexprQty - 1]; + CScriptSubExprData& sCurSubexpr = psSubExprData[iSubexprQty - 1]; tchar ch = pExpr[0]; // Init the data for the current subexpression and set the position of the first character of the subexpression. @@ -1504,7 +1505,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSube // I'm here because the whole expression is enclosed by parentheses // + 1 because i want to point to the character after the ')', even if it's the string terminator. sCurSubexpr.ptcEnd = ptcLastClosingBracket + 1; - sCurSubexpr.uiType |= SubexprData::TopParenthesizedExpr; + sCurSubexpr.uiType |= CScriptSubExprData::TopParenthesizedExpr; } else { @@ -1594,7 +1595,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSube } else { - const ushort prevSubexprType = ((iSubexprQty == 1) ? (ushort)SType::None : psSubexprData[iSubexprQty - 2].uiType); + const ushort prevSubexprType = ((iSubexprQty == 1) ? (ushort)SType::None : psSubExprData[iSubexprQty - 2].uiType); if ((prevSubexprType & SType::None)) { // This subexpr is not preceded by a two-way operator, so probably i'm an operator: skip me. @@ -1642,7 +1643,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSube lptstr ptcStart, ptcEnd; for (int i = 0; i < iSubexprQty; ++i) { - SubexprData& sCurSubexpr = psSubexprData[i]; + CScriptSubExprData& sCurSubexpr = psSubExprData[i]; ptcStart = sCurSubexpr.ptcStart; ptcEnd = sCurSubexpr.ptcEnd; diff --git a/src/common/CExpression.h b/src/common/CExpression.h index 759142318..461405dd2 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -54,6 +54,10 @@ inline bool IsWhitespace(const T ch) noexcept { #define EXPRESSION_MAX_KEY_LEN SCRIPT_MAX_SECTION_LEN #define VARDEF_FLOAT_MAXBUFFERSIZE 82 +struct CScriptSubExprData; +static constexpr size_t uiMaxConditionalSubexprs = 32; +using CScriptSubExprDataArray = CScriptSubExprData[uiMaxConditionalSubexprs]; + enum DEFMSG_TYPE { @@ -122,23 +126,6 @@ static lpctstr constexpr sm_IntrinsicFunctions[INTRINSIC_QTY+1] = nullptr }; -struct SubexprData -{ - lptstr ptcStart, ptcEnd; - enum Type : ushort - { - Unknown = 0, - // Powers of two - MaybeNestedSubexpr = 0x1 << 0, // 001 - TopParenthesizedExpr = 0x1 << 1, // 002 - None = 0x1 << 2, // 004 - BinaryNonLogical = 0x1 << 3, // 008 - And = 0x1 << 4, // 010 - Or = 0x1 << 5 // 020 - }; - ushort uiType; - ushort uiNonAssociativeOffset; // How much bytes/characters before the start is (if any) the first non-associative operator preceding the subexpression. -}; extern class CExpression { @@ -167,8 +154,8 @@ extern class CExpression int GetRangeVals(lpctstr& pExpr, int64* piVals, int iMaxQty, bool bNoWarn = false); int64 GetRangeNumber(lpctstr& pExpr); // Evaluate a { } range CSString GetRangeString(lpctstr& pExpr); // STRRANDRANGE - - static int GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSubexprData)[32], int iMaxQty); + + static int GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataArray& psSubExprData, int iMaxQty); // Strict G++ Prototyping produces an error when not casting char*& to const char*& // So this is a rather lazy and const-UNsafe workaround diff --git a/src/common/CScriptContexts.h b/src/common/CScriptContexts.h index 46c0bba96..c1d3292ba 100644 --- a/src/common/CScriptContexts.h +++ b/src/common/CScriptContexts.h @@ -21,10 +21,10 @@ struct CScriptLineContext void Init(); bool IsValid() const; - CScriptLineContext() noexcept : + constexpr CScriptLineContext() noexcept : m_iOffset(-1), m_iLineNum(-1) {}; - CScriptLineContext(int iOffset, int iLineNum) noexcept : + constexpr CScriptLineContext(int iOffset, int iLineNum) noexcept : m_iOffset(iOffset), m_iLineNum(iLineNum) {}; }; diff --git a/src/common/CScriptObj.cpp b/src/common/CScriptObj.cpp index 5698c2d45..31f634e92 100644 --- a/src/common/CScriptObj.cpp +++ b/src/common/CScriptObj.cpp @@ -20,8 +20,7 @@ #include "CFloatMath.h" #include "CExpression.h" #include "CSFileObjContainer.h" -#include "CScriptTriggerArgs.h" -#include +#include "CScriptParserBufs.h" #ifdef _WIN32 # include @@ -221,7 +220,7 @@ bool CScriptObj::r_CanCall(size_t uiFunctionIndex) // static return true; } -bool CScriptObj::r_Call( lpctstr pszFunction, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * psVal, TRIGRET_TYPE * piRet ) +bool CScriptObj::r_Call( lpctstr pszFunction, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * psVal, TRIGRET_TYPE * piRet ) { ADDTOCALLSTACK("CScriptObj::r_Call (FunctionName)"); @@ -229,10 +228,10 @@ bool CScriptObj::r_Call( lpctstr pszFunction, CTextConsole * pSrc, CScriptTrigge if ( !r_CanCall(index) ) return false; - return r_Call(index, pSrc, pArgs, psVal, piRet); + return r_Call(index, pScriptArgs, pSrc, psVal, piRet); } -bool CScriptObj::r_Call( size_t uiFunctionIndex, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * psVal, TRIGRET_TYPE * piRet ) +bool CScriptObj::r_Call( size_t uiFunctionIndex, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * psVal, TRIGRET_TYPE * piRet ) { ADDTOCALLSTACK("CScriptObj::r_Call (FunctionIndex)"); EXC_TRY("Call by index"); @@ -289,7 +288,7 @@ bool CScriptObj::r_Call( size_t uiFunctionIndex, CTextConsole * pSrc, CScriptTri TIME_PROFILE_START; } - TRIGRET_TYPE iRet = OnTriggerRun(sFunction, TRIGRUN_SECTION_TRUE, pSrc, pArgs, psVal); + TRIGRET_TYPE iRet = OnTriggerRun(sFunction, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, psVal); if ( IsSetEF(EF_Script_Profiler) ) { @@ -542,7 +541,7 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc lpctstr ptcArg = ptcKey + 1; if ( r_WriteVal(ptcArg, sVal, pSrc) ) { - if ( *sVal != '-' ) + if ( *sVal != '-' ) sVal.FormatLLVal(Str_ToLL(sVal.GetBuffer()).value_or(0)); return true; } @@ -1002,10 +1001,10 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc for (tchar *iSeperator = iSep + strlen(iSep) - 1; iSeperator > iSep; --iSeperator) *iSeperator = '\0'; - tchar *pArgs = Str_UnQuote(ppArgs[0]); + tchar *pScriptArgs = Str_UnQuote(ppArgs[0]); sVal.Clear(); tchar *ppCmd[255]; - int count = Str_ParseCmdsAdv(pArgs, ppCmd, ARRAY_COUNT(ppCmd), iSep); //Remove unnecessary chars from seperator to avoid issues. + int count = Str_ParseCmdsAdv(pScriptArgs, ppCmd, ARRAY_COUNT(ppCmd), iSep); //Remove unnecessary chars from seperator to avoid issues. tchar *ppArrays[2]; //Getting range of array index... @@ -1026,14 +1025,12 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc { if (iValue > count) return false; - else if (iValue == iValueEnd) { + else if (iValue == iValueEnd) sVal.Format(ppCmd[iValue - 1]); - } else - { + { sVal.Add(ppCmd[iValue - 1]); - int64 i = iValue + 1; - for ( ; i <= iValueEnd; ++i) + for (int64 i = iValue + 1 ; i <= iValueEnd; ++i) { sVal.Add(iSep); sVal.Add(ppCmd[i - 1]); @@ -1495,13 +1492,13 @@ bool CScriptObj::r_Load( CScript & s ) return true; } - -bool CScriptObj::_Evaluate_Conditional_EvalSingle(SubexprData& sdata, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, std::shared_ptr pContext) +// TODO: move to CExpression +bool CScriptObj::_Evaluate_Conditional_EvalSingle(CScriptSubExprData& sdata, CScriptTriggerArgsPtr pScriptArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc) { ADDTOCALLSTACK("CScriptObj::_Evaluate_Conditional_EvalSingle"); ASSERT(sdata.ptcStart); ASSERT(sdata.ptcEnd); - using SType = SubexprData::Type; + using SType = CScriptSubExprData::Type; bool fVal; lptstr ptcSubexpr; @@ -1548,13 +1545,13 @@ bool CScriptObj::_Evaluate_Conditional_EvalSingle(SubexprData& sdata, CTextConso if (fNested) { // Probably this subexpression has other conditional subexpressions inside. - fVal = Evaluate_Conditional(ptcSubexpr, pSrc, pArgs); + fVal = Evaluate_Conditional(ptcSubexpr, pScriptArgs, pContext, pSrc); } else { // If an expression is enclosed by parentheses, ParseScriptText needs to read both the open and the closed one, we cannot // pass the string starting with the character after the '('. - ParseScriptText(ptcSubexpr, pSrc, 0, pArgs); + ParseScriptText(ptcSubexpr, pScriptArgs, pContext, pSrc, 0); fVal = bool(Exp_GetLLVal(ptcSubexpr)); } @@ -1581,33 +1578,38 @@ bool CScriptObj::_Evaluate_Conditional_EvalSingle(SubexprData& sdata, CTextConso return fVal; } -bool CScriptObj::Evaluate_Conditional(lptstr ptcExpr, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, std::shared_ptr pContext) +// TODO: move to CExpression, then we can even move CScriptExprContext as a CExpression member +bool CScriptObj::Evaluate_Conditional(lptstr ptcExpr, CScriptTriggerArgsPtr pScriptArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc) { ADDTOCALLSTACK("CScriptObj::Evaluate_Conditional"); - //g_Log.EventDebug("\nEvaluating conditional expression: \"%s\"\n", ptcExpr); - SubexprData psSubexprData[32]{}; + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + if (!pContext) + pContext = CScriptParserBufs::GetCScriptExprContextDataPtr(); + + CScriptSubExprData psCScriptSubExprData[uiMaxConditionalSubexprs]{}; lptstr ptcExprDbg = ptcExpr; - const int iQty = CExpression::GetConditionalSubexpressions(ptcExprDbg, psSubexprData, ARRAY_COUNT(psSubexprData)); // number of arguments + const int iQty = CExpression::GetConditionalSubexpressions(ptcExprDbg, psCScriptSubExprData, ARRAY_COUNT(psCScriptSubExprData)); // number of arguments /*g_Log.EventDebug("---Qty: %d\n", iQty); for (int i = 0; i < iQty; ++i) - g_Log.EventDebug("---Subexpr %d: \"%.*s\"\n", i, (psSubexprData[i].ptcEnd - psSubexprData[i].ptcStart), psSubexprData[i].ptcStart); + g_Log.EventDebug("---Subexpr %d: \"%.*s\"\n", i, (psCScriptSubExprData[i].ptcEnd - psCScriptSubExprData[i].ptcStart), psCScriptSubExprData[i].ptcStart); */ if (iQty == 0) return 0; - using SType = SubexprData::Type; + using SType = CScriptSubExprData::Type; if (iQty == 1) { // We don't have subexpressions, but only a simple expression. - SubexprData& sCur = psSubexprData[0]; + CScriptSubExprData& sCur = psCScriptSubExprData[0]; ASSERT((sCur.uiType & SType::None) || (sCur.uiType & SType::BinaryNonLogical)); - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pSrc, pArgs, pContext); + const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pScriptArgs, pContext, pSrc); return fVal; } @@ -1616,22 +1618,22 @@ bool CScriptObj::Evaluate_Conditional(lptstr ptcExpr, CTextConsole* pSrc, CScrip bool fWholeExprVal = false; for (int i = 0; i < iQty; ++i) { - SubexprData& sCur = psSubexprData[i]; + CScriptSubExprData& sCur = psCScriptSubExprData[i]; ASSERT(sCur.uiType != SType::Unknown); if (i == 0) { - fWholeExprVal = _Evaluate_Conditional_EvalSingle(sCur, pSrc, pArgs, pContext); + fWholeExprVal = _Evaluate_Conditional_EvalSingle(sCur, pScriptArgs, pContext, pSrc); continue; } - SubexprData& sPrev = psSubexprData[i - 1]; + CScriptSubExprData& sPrev = psCScriptSubExprData[i - 1]; if (sPrev.uiType & SType::Or) { if (fWholeExprVal) return true; - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pSrc, pArgs, pContext); + const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pScriptArgs, pContext, pSrc); fWholeExprVal = fWholeExprVal || fVal; } else if (sPrev.uiType & SType::And) @@ -1639,7 +1641,7 @@ bool CScriptObj::Evaluate_Conditional(lptstr ptcExpr, CTextConsole* pSrc, CScrip if (!fWholeExprVal) return false; - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pSrc, pArgs, pContext); + const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pScriptArgs, pContext, pSrc); fWholeExprVal = (i == 1) ? fVal : (fWholeExprVal && fVal); } @@ -1655,6 +1657,7 @@ bool CScriptObj::Evaluate_Conditional(lptstr ptcExpr, CTextConsole* pSrc, CScrip return fWholeExprVal; } +// TODO: move to CExpression static void Evaluate_QvalConditional_ParseArg(tchar* ptcSrc, tchar** ptcDest, lpctstr ptcSep) { ASSERT(ptcSep && *ptcSep); @@ -1712,7 +1715,8 @@ static void Evaluate_QvalConditional_ParseArg(tchar* ptcSrc, tchar** ptcDest, lp Str_Parse(ptcSrc, ptcDest, ptcSep); } -bool CScriptObj::Evaluate_QvalConditional(lpctstr ptcKey, CSString& sVal, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, std::shared_ptr pContext) +// TODO: move to CExpression +bool CScriptObj::Evaluate_QvalConditional(lpctstr ptcKey, CSString& sVal, CScriptTriggerArgsPtr pScriptArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc) { ADDTOCALLSTACK("CScriptObj::Evaluate_QvalConditional"); // Do a switch ? type statement @@ -1735,13 +1739,13 @@ bool CScriptObj::Evaluate_QvalConditional(lpctstr ptcKey, CSString& sVal, CTextC // (do that in another string, since it may overwrite the arguments, which are written later in the same string). tchar* ptcTemp = Str_GetTemp(); Str_CopyLimitNull(ptcTemp, ppCmds[0], Str_TempLength()); - ParseScriptText(ptcTemp, pSrc, 0, pArgs, pContext); + ParseScriptText(ptcTemp, pScriptArgs, pContext, pSrc, 0); const bool fCondition = Exp_GetLLVal(ptcTemp); // Get the retval we want // (we might as well work on the transformed original string, since at this point we don't care if we corrupt other arguments) ptcTemp = ppCmds[(fCondition ? 1 : 2)]; - ParseScriptText(ptcTemp, pSrc, 0, pArgs, pContext); + ParseScriptText(ptcTemp, pScriptArgs, pContext, pSrc, 0); sVal = ptcTemp; if (sVal.IsEmpty()) @@ -1749,7 +1753,8 @@ bool CScriptObj::Evaluate_QvalConditional(lpctstr ptcKey, CSString& sVal, CTextC return true; } -int CScriptObj::ParseScriptText(tchar * ptcResponse, CTextConsole * pSrc, int iFlags, CScriptTriggerArgs * pArgs, std::shared_ptr pContext) +// TODO: move to CExpression +int CScriptObj::ParseScriptText(tchar * ptcResponse, CScriptTriggerArgsPtr pScriptArgs, CScriptExprContextPtr pContext, CTextConsole * pSrc, int iFlags) { ADDTOCALLSTACK("CScriptObj::ParseScriptText"); //ASSERT(ptcResponse[0] != ' '); // Not needed: i remove whitespaces and invalid characters here. @@ -1769,7 +1774,16 @@ int CScriptObj::ParseScriptText(tchar * ptcResponse, CTextConsole * pSrc, int iF // _iParseScriptText_Reentrant = 0; // _fParseScriptText_Brackets = false; // Am i evaluating a statement? (Am i inside < > brackets of a statement i am currently evaluating?) - ASSERT(pContext->_fParseScriptText_Brackets == false); + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + if (!pContext) + pContext = CScriptParserBufs::GetCScriptExprContextDataPtr(); + else { + ASSERT(pContext->_fParseScriptText_Brackets == false); + } + + const bool fNoRecurseBrackets = ((iFlags & 2) != 0); // General purpose variables. @@ -1898,7 +1912,7 @@ int CScriptObj::ParseScriptText(tchar * ptcResponse, CTextConsole * pSrc, int iF pContext->_fParseScriptText_Brackets = false; tchar* ptcRecurseParse = ptcResponse + i; - const int iLen = ParseScriptText(ptcRecurseParse, pSrc, 4, pArgs); + const int iLen = ParseScriptText(ptcRecurseParse, pScriptArgs, pContext, pSrc, 4); pContext->_fParseScriptText_Brackets = true; -- pContext->_iParseScriptText_Reentrant; @@ -1929,7 +1943,7 @@ int CScriptObj::ParseScriptText(tchar * ptcResponse, CTextConsole * pSrc, int iF // Parse what's inside the open bracket tchar* ptcRecurseParse = ptcResponse + i; - const int iLen = ParseScriptText(ptcRecurseParse, pSrc, 2, pArgs ); + const int iLen = ParseScriptText(ptcRecurseParse, pScriptArgs, pContext, pSrc, 2 ); pContext->_fParseScriptText_Brackets = true; --pContext->_iParseScriptText_Reentrant; @@ -1998,10 +2012,10 @@ int CScriptObj::ParseScriptText(tchar * ptcResponse, CTextConsole * pSrc, int iF bool fRes; if (eQval != QvalStatus::None) { - // Separate evaluation for QVAL. I may need additional script context for it (pArgs isn't available in r_WriteVal). + // Separate evaluation for QVAL. I may need additional script context for it (pScriptArgs isn't available in r_WriteVal). EXC_SET_BLOCK("writeval qval"); ptcKey += 4; // Skip the letters QVAL and pass only the arguments - fRes = Evaluate_QvalConditional(ptcKey, sVal, pSrc, pArgs, pContext); + fRes = Evaluate_QvalConditional(ptcKey, sVal, pScriptArgs, pContext, pSrc); eQval = QvalStatus::None; } else @@ -2013,7 +2027,7 @@ int CScriptObj::ParseScriptText(tchar * ptcResponse, CTextConsole * pSrc, int iF { EXC_SET_BLOCK("writeval args"); // write the value of functions or triggers variables/objects like ARGO, ARGN1/2/3, LOCALs... - if ((pArgs != nullptr) && pArgs->r_WriteVal(ptcKey, sVal, pSrc)) + if ((pScriptArgs != nullptr) && pScriptArgs->r_WriteVal(ptcKey, sVal, pSrc)) fRes = true; } } @@ -2059,14 +2073,14 @@ int CScriptObj::ParseScriptText(tchar * ptcResponse, CTextConsole * pSrc, int iF EXC_CATCH; EXC_DEBUG_START; - g_Log.EventDebug("response '%s' source addr '0%p' flags '%d' args '%p'\n", ptcResponse, static_cast(pSrc), iFlags, static_cast(pArgs)); + g_Log.EventDebug("response '%s' source addr '0%p' flags '%d' args '%p'\n", ptcResponse, static_cast(pSrc), iFlags, static_cast(pScriptArgs.get())); EXC_DEBUG_END; pContext->_fParseScriptText_Brackets = false; return i; } -bool CScriptObj::Execute_Call(CScript& s, CTextConsole* pSrc, CScriptTriggerArgs* pArgs) +bool CScriptObj::Execute_Call(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) { ADDTOCALLSTACK("CScriptObj::Execute_Call"); bool fRes = false; @@ -2099,33 +2113,33 @@ bool CScriptObj::Execute_Call(CScript& s, CTextConsole* pSrc, CScriptTriggerArgs if (z && *z) { - int64 iN1 = pArgs->m_iN1; - int64 iN2 = pArgs->m_iN2; - int64 iN3 = pArgs->m_iN3; - CScriptObj* pO1 = pArgs->m_pO1; - CSString s1 = pArgs->m_s1; - CSString s1_raw = pArgs->m_s1_buf_vec; - pArgs->m_v.clear(); - pArgs->Init(z); - - fRes = pRef->r_Call(argRaw, pSrc, pArgs, &sVal); - - pArgs->m_iN1 = iN1; - pArgs->m_iN2 = iN2; - pArgs->m_iN3 = iN3; - pArgs->m_pO1 = pO1; - pArgs->m_s1 = s1; - pArgs->m_s1_buf_vec = s1_raw; - pArgs->m_v.clear(); + int64 iN1 = pScriptArgs->m_iN1; + int64 iN2 = pScriptArgs->m_iN2; + int64 iN3 = pScriptArgs->m_iN3; + CScriptObj* pO1 = pScriptArgs->m_pO1; + CSString s1 = pScriptArgs->m_s1; + CSString s1_raw = pScriptArgs->m_s1_buf_vec; + pScriptArgs->m_v.clear(); + pScriptArgs->Init(z); + + fRes = pRef->r_Call(argRaw, pScriptArgs, pSrc, &sVal); + + pScriptArgs->m_iN1 = iN1; + pScriptArgs->m_iN2 = iN2; + pScriptArgs->m_iN3 = iN3; + pScriptArgs->m_pO1 = pO1; + pScriptArgs->m_s1 = s1; + pScriptArgs->m_s1_buf_vec = s1_raw; + pScriptArgs->m_v.clear(); } else - fRes = pRef->r_Call(argRaw, pSrc, pArgs, &sVal); + fRes = pRef->r_Call(argRaw, pScriptArgs, pSrc, &sVal); } return fRes; } -bool CScriptObj::Execute_FullTrigger(CScript& s, CTextConsole* pSrc, CScriptTriggerArgs* pArgs) +bool CScriptObj::Execute_FullTrigger(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) { ADDTOCALLSTACK("CScriptObj::Execute_FullTrigger"); bool fRes = false; @@ -2134,6 +2148,7 @@ bool CScriptObj::Execute_FullTrigger(CScript& s, CTextConsole* pSrc, CScriptTrig tchar* ptcTmp = Str_GetTemp(); Str_CopyLimitNull(ptcTmp, s.GetArgRaw(), Str_TempLength()); int iArgQty = Str_ParseCmds(ptcTmp, piCmd, ARRAY_COUNT(piCmd), " ,\t"); + CScriptObj* pRef = this; if (iArgQty == 2) { @@ -2171,29 +2186,29 @@ bool CScriptObj::Execute_FullTrigger(CScript& s, CTextConsole* pSrc, CScriptTrig if (z && *z) { - int64 iN1 = pArgs->m_iN1; - int64 iN2 = pArgs->m_iN2; - int64 iN3 = pArgs->m_iN3; - CScriptObj* pO1 = pArgs->m_pO1; - CSString s1 = pArgs->m_s1; - CSString s1_raw = pArgs->m_s1_buf_vec; - pArgs->m_v.clear(); - pArgs->Init(z); - - tRet = pRef->OnTrigger(ptcTmp, pSrc, pArgs); - - pArgs->m_iN1 = iN1; - pArgs->m_iN2 = iN2; - pArgs->m_iN3 = iN3; - pArgs->m_pO1 = pO1; - pArgs->m_s1 = s1; - pArgs->m_s1_buf_vec = s1_raw; - pArgs->m_v.clear(); + int64 iN1 = pScriptArgs->m_iN1; + int64 iN2 = pScriptArgs->m_iN2; + int64 iN3 = pScriptArgs->m_iN3; + CScriptObj* pO1 = pScriptArgs->m_pO1; + CSString s1 = pScriptArgs->m_s1; + CSString s1_raw = pScriptArgs->m_s1_buf_vec; + pScriptArgs->m_v.clear(); + pScriptArgs->Init(z); + + tRet = pRef->OnTrigger(ptcTmp, pScriptArgs, pSrc); + + pScriptArgs->m_iN1 = iN1; + pScriptArgs->m_iN2 = iN2; + pScriptArgs->m_iN3 = iN3; + pScriptArgs->m_pO1 = pO1; + pScriptArgs->m_s1 = s1; + pScriptArgs->m_s1_buf_vec = s1_raw; + pScriptArgs->m_v.clear(); } else - tRet = pRef->OnTrigger(ptcTmp, pSrc, pArgs); + tRet = pRef->OnTrigger(ptcTmp, pScriptArgs, pSrc); - pArgs->m_VarsLocal.SetNum("return", tRet, false); + pScriptArgs->m_VarsLocal.SetNum("return", tRet, false); fRes = (tRet > 0) ? 1 : 0; } @@ -2218,7 +2233,7 @@ bool CScriptObj::OnTriggerFind( CScript & s, lpctstr pszTrigName ) return false; } -TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, lpctstr pszTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) +TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, lpctstr pszTrigName, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc) { ADDTOCALLSTACK("CScriptObj::OnTriggerScript"); // look for exact trigger matches @@ -2279,7 +2294,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, lpctstr pszTrigName, CTex TIME_PROFILE_START; } - TRIGRET_TYPE iRet = OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs); + TRIGRET_TYPE iRet = OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc); if ( IsSetEF(EF_Script_Profiler) && pTrig != nullptr ) { @@ -2287,7 +2302,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, lpctstr pszTrigName, CTex TIME_PROFILE_END; llTicksStart = llTicksEnd - llTicksStart; pTrig->total += llTicksStart; - pTrig->average = (pTrig->total/pTrig->called); + pTrig->average = (pTrig->total / pTrig->called); if ( pTrig->max < llTicksStart ) pTrig->max = llTicksStart; if (( pTrig->min > llTicksStart ) || ( !pTrig->min )) @@ -2298,11 +2313,12 @@ TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, lpctstr pszTrigName, CTex return iRet; } -TRIGRET_TYPE CScriptObj::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs) +TRIGRET_TYPE CScriptObj::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc) { UnreferencedParameter(pszTrigName); UnreferencedParameter(pSrc); - UnreferencedParameter(pArgs); + UnreferencedParameter(pScriptArgs); + ASSERT(false); // I shouldn't get here? return( TRIGRET_RET_DEFAULT ); } @@ -2381,13 +2397,16 @@ lpctstr const CScriptObj::sm_szScriptKeys[SK_QTY+1] = nullptr }; -TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult) +TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult) { ADDTOCALLSTACK("CScriptObj::OnTriggerLoopGeneric"); // loop from start here to the ENDFOR // See WebPageScriptList for dealing with Arrays. - CScriptLineContext StartContext = s.GetContext(); + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + CScriptLineContext StartContext = s.GetContext(); CScriptLineContext EndContext(StartContext); int LoopsMade = 0; @@ -2406,14 +2425,17 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol tchar* ptcCond = tsConditionBuf.buffer(); Str_CopyLimitNull(ptcCond, tsOrig.buffer(), tsConditionBuf.capacity()); - ParseScriptText(ptcCond, pSrc, 0, pArgs); + + + + ParseScriptText(ptcCond, pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); if (!Exp_GetLLVal(ptcCond)) break; - pArgs->m_VarsLocal.SetNum("_WHILE", iWhile, false); + pScriptArgs->m_VarsLocal.SetNum("_WHILE", iWhile, false); ++iWhile; - TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK) { EndContext = StartContext; @@ -2430,7 +2452,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol } else { - ParseScriptText(s.GetArgStr(), pSrc, 0, pArgs); + ParseScriptText(s.GetArgStr(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); } @@ -2482,8 +2504,8 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol if (g_Cfg.m_iMaxLoopTimes && (LoopsMade >= g_Cfg.m_iMaxLoopTimes)) goto toomanyloops; - pArgs->m_VarsLocal.SetNum(sLoopVar, i, false); - TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + pScriptArgs->m_VarsLocal.SetNum(sLoopVar, i, false); + TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK) { EndContext = StartContext; @@ -2505,8 +2527,8 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol if (g_Cfg.m_iMaxLoopTimes && (LoopsMade >= g_Cfg.m_iMaxLoopTimes)) goto toomanyloops; - pArgs->m_VarsLocal.SetNum(sLoopVar, i, false); - TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + pScriptArgs->m_VarsLocal.SetNum(sLoopVar, i, false); + TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK) { EndContext = StartContext; @@ -2553,7 +2575,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol CItem* pItem = AreaItems->GetItem(); if (pItem == nullptr) break; - TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK) { EndContext = StartContext; @@ -2585,7 +2607,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol continue; if ((iType & 0x20) && (pChar->m_pPlayer == nullptr)) // FORPLAYERS continue; - TRIGRET_TYPE iRet = pChar->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pChar->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK) { EndContext = StartContext; @@ -2664,7 +2686,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol goto toomanyloops; // Execute script on this object - TRIGRET_TYPE iRet = pObj->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pObj->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK) { EndContext = StartContext; @@ -2699,7 +2721,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol char funcname[1024]; Str_CopyLimitNull(funcname, ptcArgs, sizeof(funcname)); - TRIGRET_TYPE iRet = CWorldTimedFunctions::Loop(funcname, LoopsMade, StartContext, s, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = CWorldTimedFunctions::Loop(funcname, LoopsMade, StartContext, s, pScriptArgs, pSrc, pResult); if ((iRet != TRIGRET_ENDIF) && (iRet != TRIGRET_CONTINUE)) return iRet; } @@ -2715,7 +2737,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol if (EndContext.m_iOffset <= StartContext.m_iOffset) { // just skip to the end. - TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); if (iRet != TRIGRET_ENDIF) return iRet; } @@ -2725,7 +2747,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CTextConsol return TRIGRET_ENDIF; } -TRIGRET_TYPE CScriptObj::OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult) +TRIGRET_TYPE CScriptObj::OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult) { ADDTOCALLSTACK("CScriptObj::OnTriggerLoopForCharSpecial"); TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; @@ -2735,29 +2757,29 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, C { if (s.HasArgs()) { - ParseScriptText(s.GetArgRaw(), pSrc, 0, pArgs); + ParseScriptText(s.GetArgRaw(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); if (iCmd == SK_FORCHARLAYER) - iRet = pCharThis->OnCharTrigForLayerLoop(s, pSrc, pArgs, pResult, (LAYER_TYPE)s.GetArgVal()); + iRet = pCharThis->OnCharTrigForLayerLoop(s, pScriptArgs, pSrc, pResult, (LAYER_TYPE)s.GetArgVal()); else - iRet = pCharThis->OnCharTrigForMemTypeLoop(s, pSrc, pArgs, pResult, s.GetArgWVal()); + iRet = pCharThis->OnCharTrigForMemTypeLoop(s, pScriptArgs, pSrc, pResult, s.GetArgWVal()); } else { g_Log.EventError("FORCHAR[layer/memorytype] called on char 0%" PRIx32 " (%s) without arguments.\n", (dword)(pCharThis->GetUID()), pCharThis->GetName()); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); } } else { g_Log.EventError("FORCHAR[layer/memorytype] called on non-char object '%s'.\n", GetName()); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); } return iRet; } -TRIGRET_TYPE CScriptObj::OnTriggerLoopForCont(CScript& s, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult) +TRIGRET_TYPE CScriptObj::OnTriggerLoopForCont(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult) { ADDTOCALLSTACK("CScriptObj::OnTriggerLoopForCont"); TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; @@ -2772,7 +2794,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForCont(CScript& s, CTextConsole* pSrc, CS TemporaryString tsOrigValue; tchar* ptcOrigValue = tsOrigValue.buffer(); Str_ConcatLimitNull(ptcOrigValue, ppArgs[0], tsOrigValue.capacity()); - ParseScriptText(ptcOrigValue, pSrc, 0, pArgs); + ParseScriptText(ptcOrigValue, pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); CUID pCurUid(Exp_GetDWVal(ptcOrigValue)); if (pCurUid.IsValidUID()) @@ -2785,101 +2807,106 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForCont(CScript& s, CTextConsole* pSrc, CS CScriptLineContext StartContext = s.GetContext(); CScriptLineContext EndContext = StartContext; - iRet = pContThis->OnGenericContTriggerForLoop(s, pSrc, pArgs, pResult, StartContext, EndContext, ppArgs[1] != nullptr ? Exp_GetVal(ppArgs[1]) : 255); + iRet = pContThis->OnGenericContTriggerForLoop(s, pScriptArgs, pSrc, pResult, StartContext, EndContext, ppArgs[1] != nullptr ? Exp_GetVal(ppArgs[1]) : 255); } else { g_Log.EventError("FORCONT called on invalid uid/invalid container (UID: 0%x).\n", pCurUid.GetObjUID()); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); } } else { g_Log.EventError("FORCONT called with invalid arguments (UID: 0%x, LEVEL: %s).\n", pCurUid.GetObjUID(), (ppArgs[1] && *ppArgs[1]) ? ppArgs[1] : "255"); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); } } else { g_Log.EventError("FORCONT called with insufficient arguments.\n"); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); } } else { g_Log.EventError("FORCONT called without arguments.\n"); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); } return iRet; } -TRIGRET_TYPE CScriptObj::OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult) +TRIGRET_TYPE CScriptObj::OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult) { - ADDTOCALLSTACK("CScriptObj::OnTriggerLoopForContSpecial"); - TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; + ADDTOCALLSTACK("CScriptObj::OnTriggerLoopForContSpecial"); + TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; - CObjBase* pObjCont = dynamic_cast (this); - CContainer* pCont = dynamic_cast (this); - if (pObjCont && pCont) - { - if (s.HasArgs()) - { - lpctstr ptcKey = s.GetArgRaw(); - SKIP_SEPARATORS(ptcKey); + CObjBase* pObjCont = dynamic_cast (this); + CContainer* pCont = dynamic_cast (this); + if (!pObjCont || !pCont) + { + g_Log.EventError("FORCONT[id/type] called on non-container object '%s'.\n", GetName()); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); + return iRet; + } - tchar* ppArgs[2]; + if (!s.HasArgs()) + { + g_Log.EventError("FORCONT[id/type] called on container 0%x without arguments.\n", (dword)pObjCont->GetUID()); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); + return iRet; + } - if (Str_ParseCmds(const_cast(ptcKey), ppArgs, ARRAY_COUNT(ppArgs), " \t,") >= 1) - { - TemporaryString tsParsedArg0; - Str_CopyLimitNull(tsParsedArg0.buffer(), ppArgs[0], tsParsedArg0.capacity()); - if ((ParseScriptText(tsParsedArg0.buffer(), pSrc, 0, pArgs) > 0)) - { - TemporaryString tsParsedArg1; - if (ppArgs[1] != nullptr) - { - Str_CopyLimitNull(tsParsedArg1.buffer(), ppArgs[1], tsParsedArg0.capacity()); - if (ParseScriptText(tsParsedArg1.buffer(), pSrc, 0, pArgs) <= 0) - goto forcont_incorrect_args; - } + lptstr ptcKey = s.GetArgRaw(); + SKIP_SEPARATORS(ptcKey); - CScriptLineContext StartContext = s.GetContext(); - CScriptLineContext EndContext(StartContext); - lpctstr ptcParsedArg1 = tsParsedArg1.buffer(); - iRet = pCont->OnContTriggerForLoop(s, pSrc, pArgs, pResult, StartContext, EndContext, - g_Cfg.ResourceGetID(((iCmd == SK_FORCONTID) ? RES_ITEMDEF : RES_TYPEDEF), tsParsedArg0.buffer()), - 0, ((ppArgs[1] != nullptr) ? Exp_GetVal(ptcParsedArg1) : 255)); - } - else - { - forcont_incorrect_args: - g_Log.EventError("FORCONT[id/type] called on container 0%x with incorrect arguments.\n", (dword)pObjCont->GetUID()); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); - } - } - else - { - g_Log.EventError("FORCONT[id/type] called on container 0%x with incorrect arguments.\n", (dword)pObjCont->GetUID()); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); - } - } - else - { - g_Log.EventError("FORCONT[id/type] called on container 0%x without arguments.\n", (dword)pObjCont->GetUID()); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); - } - } - else - { - g_Log.EventError("FORCONT[id/type] called on non-container object '%s'.\n", GetName()); - iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); - } + tchar* ppArgs[2]; + if (Str_ParseCmds(ptcKey, ppArgs, ARRAY_COUNT(ppArgs), " \t,") < 1) + { + g_Log.EventError("FORCONT[id/type] called on container 0%x with incorrect arguments.\n", (dword)pObjCont->GetUID()); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); + return iRet; + } - return iRet; + TemporaryString tsParsedArg0; + Str_CopyLimitNull(tsParsedArg0.buffer(), ppArgs[0], tsParsedArg0.capacity()); + if ((ParseScriptText(tsParsedArg0.buffer(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0) <= 0)) + { + g_Log.EventError("FORCONT[id/type] called on container 0%x with incorrect argument 0.\n", (dword)pObjCont->GetUID()); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); + return iRet; + } + + TemporaryString tsParsedArg1; + if (ppArgs[1] != nullptr) + { + Str_CopyLimitNull(tsParsedArg1.buffer(), ppArgs[1], tsParsedArg0.capacity()); + if (ParseScriptText(tsParsedArg1.buffer(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0) <= 0) + { + g_Log.EventError("FORCONT[id/type] called on container 0%x with incorrect argument 1.\n", (dword)pObjCont->GetUID()); + iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); + return iRet; + } + } + + CScriptLineContext StartContext = s.GetContext(); + CScriptLineContext EndContext(StartContext); + lpctstr ptcParsedArg1 = tsParsedArg1.buffer(); + + const RES_TYPE ridExpectedType = ((iCmd == SK_FORCONTID) ? RES_ITEMDEF : RES_TYPEDEF); + const CResourceID &rid = g_Cfg.ResourceGetID(ridExpectedType, tsParsedArg0.buffer()); + + constexpr dword dwArg = 0; + const int iDescendLevels = ((ppArgs[1] != nullptr) ? Exp_GetVal(ptcParsedArg1) : 255); + iRet = pCont->OnContTriggerForLoop( + s, pScriptArgs, pSrc, pResult, + StartContext, EndContext, + rid, dwArg, iDescendLevels); + + return iRet; } -TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult ) +TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * pResult ) { ADDTOCALLSTACK("CScriptObj::OnTriggerRun"); // ARGS: @@ -2893,16 +2920,10 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo // DEBUGCHECK( this == g_Log.m_pObjectContext ); // all scripts should have args for locals to work. - std::unique_ptr argsEmpty; - if ( !pArgs ) - { - argsEmpty = std::make_unique(); - pArgs = argsEmpty.get(); - } static constexpr uint g_reentrant_OnTriggerRun_limit = 75; static thread_local size_t g_reentrant_OnTriggerRun = 0; - auto clean_return = [](const TRIGRET_TYPE ret) -> TRIGRET_TYPE { + auto clean_return = [](const TRIGRET_TYPE ret) noexcept -> TRIGRET_TYPE { g_reentrant_OnTriggerRun -= 1; return ret; }; @@ -2914,10 +2935,13 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo return clean_return(TRIGRET_RET_ABORTED); } + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + // Script execution is always not threaded action EXC_TRY("TriggerRun"); - bool fSectionFalse = (trigrun == TRIGRUN_SECTION_FALSE || trigrun == TRIGRUN_SINGLE_FALSE); + const bool fSectionFalse = (trigrun == TRIGRUN_SECTION_FALSE || trigrun == TRIGRUN_SINGLE_FALSE); if ( trigrun == TRIGRUN_SECTION_EXEC || trigrun == TRIGRUN_SINGLE_EXEC ) // header was already read in. goto jump_in; @@ -2963,7 +2987,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo EXC_SET_BLOCK("if statement"); do { - iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult ); + iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult ); } while ( iRet == TRIGRET_ELSEIF || iRet == TRIGRET_ELSE ); break; case SK_WHILE: @@ -2984,7 +3008,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo case SK_DOSWITCH: case SK_BEGIN: EXC_SET_BLOCK("begin/loop cycle"); - iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult ); + iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult ); break; default: break; @@ -3004,34 +3028,34 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo case SK_CONTINUE: return clean_return(TRIGRET_CONTINUE); - case SK_FORITEM: EXC_SET_BLOCK("foritem"); iRet = OnTriggerLoopGeneric(s, 1, pSrc, pArgs, pResult); break; - case SK_FORCHAR: EXC_SET_BLOCK("forchar"); iRet = OnTriggerLoopGeneric(s, 2, pSrc, pArgs, pResult); break; - case SK_FORCLIENTS: EXC_SET_BLOCK("forclients"); iRet = OnTriggerLoopGeneric(s, 0x12, pSrc, pArgs, pResult); break; - case SK_FOROBJ: EXC_SET_BLOCK("forobjs"); iRet = OnTriggerLoopGeneric(s, 3, pSrc, pArgs, pResult); break; - case SK_FORPLAYERS: EXC_SET_BLOCK("forplayers"); iRet = OnTriggerLoopGeneric(s, 0x22, pSrc, pArgs, pResult); break; - case SK_FOR: EXC_SET_BLOCK("for"); iRet = OnTriggerLoopGeneric(s, 4, pSrc, pArgs, pResult); break; - case SK_WHILE: EXC_SET_BLOCK("while"); iRet = OnTriggerLoopGeneric(s, 8, pSrc, pArgs, pResult); break; - case SK_FORINSTANCE:EXC_SET_BLOCK("forinstance"); iRet = OnTriggerLoopGeneric(s, 0x40, pSrc, pArgs, pResult); break; - case SK_FORTIMERF: EXC_SET_BLOCK("fortimerf"); iRet = OnTriggerLoopGeneric(s, 0x100,pSrc, pArgs, pResult); break; + case SK_FORITEM: EXC_SET_BLOCK("foritem"); iRet = OnTriggerLoopGeneric(s, 1, pScriptArgs, pSrc, pResult); break; + case SK_FORCHAR: EXC_SET_BLOCK("forchar"); iRet = OnTriggerLoopGeneric(s, 2, pScriptArgs, pSrc, pResult); break; + case SK_FORCLIENTS: EXC_SET_BLOCK("forclients"); iRet = OnTriggerLoopGeneric(s, 0x12, pScriptArgs, pSrc, pResult); break; + case SK_FOROBJ: EXC_SET_BLOCK("forobjs"); iRet = OnTriggerLoopGeneric(s, 3, pScriptArgs, pSrc, pResult); break; + case SK_FORPLAYERS: EXC_SET_BLOCK("forplayers"); iRet = OnTriggerLoopGeneric(s, 0x22, pScriptArgs, pSrc, pResult); break; + case SK_FOR: EXC_SET_BLOCK("for"); iRet = OnTriggerLoopGeneric(s, 4, pScriptArgs, pSrc, pResult); break; + case SK_WHILE: EXC_SET_BLOCK("while"); iRet = OnTriggerLoopGeneric(s, 8, pScriptArgs, pSrc, pResult); break; + case SK_FORINSTANCE:EXC_SET_BLOCK("forinstance"); iRet = OnTriggerLoopGeneric(s, 0x40, pScriptArgs, pSrc, pResult); break; + case SK_FORTIMERF: EXC_SET_BLOCK("fortimerf"); iRet = OnTriggerLoopGeneric(s, 0x100,pScriptArgs, pSrc, pResult); break; case SK_FORCHARLAYER: case SK_FORCHARMEMORYTYPE: { EXC_SET_BLOCK("forchar[layer/memorytype]"); - iRet = OnTriggerLoopForCharSpecial(s, iCmd, pSrc, pArgs, pResult); + iRet = OnTriggerLoopForCharSpecial(s, iCmd, pScriptArgs, pSrc, pResult); } break; case SK_FORCONT: { EXC_SET_BLOCK("forcont"); - iRet = OnTriggerLoopForCont(s, pSrc, pArgs, pResult); + iRet = OnTriggerLoopForCont(s, pScriptArgs, pSrc, pResult); } break; case SK_FORCONTID: case SK_FORCONTTYPE: { EXC_SET_BLOCK("forcont[id/type]"); - iRet = OnTriggerLoopForContSpecial(s, iCmd, pSrc, pArgs, pResult); + iRet = OnTriggerLoopForContSpecial(s, iCmd, pScriptArgs, pSrc, pResult); } break; case SK_IF: @@ -3048,16 +3072,16 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo { EXC_SET_BLOCK("parsing <> in a key"); TemporaryString tsBuf; - Str_CopyLimitNull(tsBuf.buffer(), s.GetKey(), tsBuf.capacity()); - Str_ConcatLimitNull(tsBuf.buffer(), " ", tsBuf.capacity()); - Str_ConcatLimitNull(tsBuf.buffer(), s.GetArgRaw(), tsBuf.capacity()); - ParseScriptText(tsBuf.buffer(), pSrc, 0, pArgs); + Str_CopyLimitNull (tsBuf.buffer(), s.GetKey(), tsBuf.capacity()); + Str_ConcatLimitNull(tsBuf.buffer(), " ", tsBuf.capacity()); + Str_ConcatLimitNull(tsBuf.buffer(), s.GetArgRaw(), tsBuf.capacity()); + ParseScriptText(tsBuf.buffer(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); s.ParseKey(tsBuf.buffer()); } else { - ParseScriptText( s.GetArgRaw(), pSrc, 0, pArgs ); + ParseScriptText( s.GetArgRaw(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); } } } @@ -3095,7 +3119,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo iVal = g_Rand.GetLLVal(iVal); for ( ; ; --iVal ) { - iRet = OnTriggerRun( s, (iVal == 0) ? TRIGRUN_SINGLE_TRUE : TRIGRUN_SINGLE_FALSE, pSrc, pArgs, pResult ); + iRet = OnTriggerRun( s, (iVal == 0) ? TRIGRUN_SINGLE_TRUE : TRIGRUN_SINGLE_FALSE, pScriptArgs, pSrc, pResult ); if ( iRet == TRIGRET_RET_DEFAULT ) continue; if ( iRet == TRIGRET_ENDIF ) @@ -3119,11 +3143,11 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo EXC_SET_BLOCK("if statement"); // At this point, we have to parse the conditional expression const lptstr ptcArg = s.GetArgStr(); - bool fTrigger = Evaluate_Conditional(ptcArg, pSrc, pArgs); + bool fTrigger = Evaluate_Conditional(ptcArg, pScriptArgs, CScriptExprContextPtr{}, pSrc); bool fBeenTrue = false; for (;;) { - iRet = OnTriggerRun( s, fTrigger ? TRIGRUN_SECTION_TRUE : TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult ); + iRet = OnTriggerRun( s, fTrigger ? TRIGRUN_SECTION_TRUE : TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult ); if (( iRet < TRIGRET_ENDIF ) || ( iRet >= TRIGRET_RET_HALFBAKED )) return clean_return(iRet); if ( iRet == TRIGRET_ENDIF ) @@ -3135,7 +3159,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo else if ( iRet == TRIGRET_ELSE ) fTrigger = true; else if ( iRet == TRIGRET_ELSEIF ) - fTrigger = Evaluate_Conditional(s.GetArgStr(), pSrc, pArgs); + fTrigger = Evaluate_Conditional(s.GetArgStr(), pScriptArgs, CScriptExprContextPtr{}, pSrc); } } break; @@ -3144,7 +3168,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo // Do this block here. { EXC_SET_BLOCK("begin/loop cycle"); - iRet = OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult ); + iRet = OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult ); if ( iRet != TRIGRET_ENDIF ) return clean_return(iRet); } @@ -3152,18 +3176,18 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo default: EXC_SET_BLOCK("parsing standard statement"); - if ( !pArgs->r_Verb(s, pSrc) ) + if ( !pScriptArgs->r_Verb(s, pSrc) ) { bool fRes; if (!strnicmp(s.GetKey(), "CALL", 4)) { EXC_SET_BLOCK("call"); - fRes = Execute_Call(s, pSrc, pArgs); + fRes = Execute_Call(s, pScriptArgs, pSrc); } else if ( !strnicmp(s.GetKey(), "FullTrigger", 11 ) ) { EXC_SET_BLOCK("FullTrigger"); - fRes = Execute_FullTrigger(s, pSrc, pArgs); + fRes = Execute_FullTrigger(s, pScriptArgs, pSrc); } else { @@ -3184,12 +3208,12 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextCo EXC_DEBUG_START; g_Log.EventDebug("key '%s' runtype '%d' pargs '%p' ret '%s' [%p]\n", - s.GetKey(), trigrun, static_cast(pArgs), (pResult == nullptr ? "" : pResult->GetBuffer()), static_cast(pSrc)); + s.GetKey(), trigrun, static_cast(pScriptArgs.get()), (pResult == nullptr ? "" : pResult->GetBuffer()), static_cast(pSrc)); EXC_DEBUG_END; return clean_return(TRIGRET_RET_DEFAULT); } -TRIGRET_TYPE CScriptObj::OnTriggerRunVal( CScript &s, TRIGRUN_TYPE trigrun, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) +TRIGRET_TYPE CScriptObj::OnTriggerRunVal( CScript &s, TRIGRUN_TYPE trigrun, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) { // Get the TRIGRET_TYPE that is returned by the script // This should be used instead of OnTriggerRun() when pReturn is not used @@ -3198,7 +3222,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRunVal( CScript &s, TRIGRUN_TYPE trigrun, CTex CSString sVal; TRIGRET_TYPE tr = TRIGRET_RET_DEFAULT; - OnTriggerRun( s, trigrun, pSrc, pArgs, &sVal ); + OnTriggerRun( s, trigrun, pScriptArgs, pSrc, &sVal ); lpctstr pszVal = sVal.GetBuffer(); if ( pszVal && *pszVal ) diff --git a/src/common/CScriptObj.h b/src/common/CScriptObj.h index df8b0e095..4f576ffb2 100644 --- a/src/common/CScriptObj.h +++ b/src/common/CScriptObj.h @@ -8,15 +8,21 @@ #include "../common/common.h" -struct SubexprData; class CScript; -class CScriptTriggerArgs; class CTextConsole; class CSFileText; class CSString; class CUID; class CChar; +struct CScriptSubExprData; + +struct CScriptExprContextData; +using CScriptExprContextPtr = std::shared_ptr; + +class CScriptTriggerArgs; +using CScriptTriggerArgsPtr = std::shared_ptr; + enum SK_TYPE : int; @@ -36,7 +42,7 @@ enum TRIGRET_TYPE // trigger script returns. TRIGRET_RET_FALSE = 0, // default return. (script might not have been handled) TRIGRET_RET_TRUE = 1, TRIGRET_RET_DEFAULT, // we just came to the end of the script. - TRIGRET_ENDIF, + TRIGRET_ENDIF, TRIGRET_ELSE, TRIGRET_ELSEIF, TRIGRET_RET_HALFBAKED, @@ -46,14 +52,6 @@ enum TRIGRET_TYPE // trigger script returns. }; -struct ScriptedExprContext -{ - // Recursion counters and state variables - short _iEvaluate_Conditional_Reentrant; - short _iParseScriptText_Reentrant; - bool _fParseScriptText_Brackets; -}; - class CScriptObj { // This object can be scripted. (but might not be) @@ -94,8 +92,7 @@ class CScriptObj /* * @brief Do the first-level parsing of a script line and eventually replace requested values got by r_WriteVal. */ - int ParseScriptText( tchar * pszResponse, CTextConsole * pSrc, int iFlags = 0, CScriptTriggerArgs * pArgs = nullptr, - std::shared_ptr pContext = std::make_shared() ); + int ParseScriptText( tchar * pszResponse, CScriptTriggerArgsPtr pArgs, CScriptExprContextPtr pContext, CTextConsole * pSrc, int iFlags = 0 ); /* * @brief Execute a script command. @@ -125,35 +122,34 @@ class CScriptObj // FUNCTION methods static size_t r_GetFunctionIndex(lpctstr pszFunction); static bool r_CanCall(size_t uiFunctionIndex); - bool r_Call( lpctstr ptcFunction, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * psVal = nullptr, TRIGRET_TYPE * piRet = nullptr ); // Try to execute function - bool r_Call( size_t uiFunctionIndex, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * psVal = nullptr, TRIGRET_TYPE * piRet = nullptr ); // Try to execute function + bool r_Call( lpctstr ptcFunction, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * psVal = nullptr, TRIGRET_TYPE * piRet = nullptr ); // Try to execute function + bool r_Call( size_t uiFunctionIndex, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * psVal = nullptr, TRIGRET_TYPE * piRet = nullptr ); // Try to execute function // Generic section parsing - virtual TRIGRET_TYPE OnTrigger(lpctstr pszTrigName, CTextConsole* pSrc, CScriptTriggerArgs* pArgs = nullptr); + virtual TRIGRET_TYPE OnTrigger(lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); bool OnTriggerFind(CScript& s, lpctstr pszTrigName); - TRIGRET_TYPE OnTriggerScript(CScript& s, lpctstr pszTrigName, CTextConsole* pSrc, CScriptTriggerArgs* pArgs = nullptr); - TRIGRET_TYPE OnTriggerRun(CScript& s, TRIGRUN_TYPE trigger, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pReturn); - TRIGRET_TYPE OnTriggerRunVal(CScript& s, TRIGRUN_TYPE trigger, CTextConsole* pSrc, CScriptTriggerArgs* pArgs); + TRIGRET_TYPE OnTriggerScript(CScript& s, lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + TRIGRET_TYPE OnTriggerRun(CScript& s, TRIGRUN_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc, CSString* pReturn); + TRIGRET_TYPE OnTriggerRunVal(CScript& s, TRIGRUN_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); // Special statements private: // While, standard for loop and some special for loops - TRIGRET_TYPE OnTriggerLoopGeneric(CScript& s, int iType, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult); - TRIGRET_TYPE OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult); - TRIGRET_TYPE OnTriggerLoopForCont(CScript& s, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult); - TRIGRET_TYPE OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult); + TRIGRET_TYPE OnTriggerLoopGeneric(CScript& s, int iType, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); + TRIGRET_TYPE OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); + TRIGRET_TYPE OnTriggerLoopForCont(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); + TRIGRET_TYPE OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); // Special statements - bool _Evaluate_Conditional_EvalSingle(SubexprData& sdata, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, std::shared_ptr pContext); - bool Evaluate_Conditional(lptstr ptcExpression, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, - std::shared_ptr pContext = std::make_shared()); // IF, ELIF, ELSEIF + bool _Evaluate_Conditional_EvalSingle(CScriptSubExprData& sdata, CScriptTriggerArgsPtr pArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc); + bool Evaluate_Conditional(lptstr ptcExpression, CScriptTriggerArgsPtr pArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc); // IF, ELIF, ELSEIF - bool Evaluate_QvalConditional(lpctstr ptcKey, CSString& sVal, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, std::shared_ptr pContext); + bool Evaluate_QvalConditional(lpctstr ptcKey, CSString& sVal, CScriptTriggerArgsPtr pArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc); - bool Execute_Call(CScript& s, CTextConsole* pSrc, CScriptTriggerArgs* pArgs); - bool Execute_FullTrigger(CScript& s, CTextConsole* pSrc, CScriptTriggerArgs* pArgs); + bool Execute_Call(CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + bool Execute_FullTrigger(CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); // Utilities @@ -168,6 +164,7 @@ class CScriptObj CScriptObj(const CScriptObj& copy) = delete; CScriptObj& operator=(const CScriptObj& other) = delete; + CScriptObj(CScriptObj&& move) = delete; }; #endif // _INC_CSCRIPTOBJ_H diff --git a/src/common/CScriptParserBufs.cpp b/src/common/CScriptParserBufs.cpp new file mode 100644 index 000000000..9cc9baea0 --- /dev/null +++ b/src/common/CScriptParserBufs.cpp @@ -0,0 +1,64 @@ +#include "CScriptParserBufs.h" +#include "CScriptTriggerArgs.h" +#include "CLog.h" +#include "sphere_library/sobjpool.h" +//#include //for memset + + +struct CScriptParserBufsImpl +{ + inline static CScriptParserBufsImpl& Get() + { + static auto inst = std::make_unique(); + return *inst.get(); + } + + static constexpr bool sm_allow_fallback_objects = true; + + // We stack allocate this POD struct, so we don't need to keep them in a pool. + //sl::ObjectPool + //m_poolCScriptSubExprData; + + // While it's a POD struct (so trivial/simple to construct), we still need to allocate it on the heap, + // and malloc/new have a computional impact. + // An alternative could be to allocate the buffer on the stack with 'alloca' and pass around the pointer... Is it worth it? + sl::ObjectPool + m_poolCScriptExprContextData; + + // This is even more expensive to construct, so will definitely benefit a lot from having allocated, cached instances. + sl::ObjectPool + m_poolScriptTriggerArgs; +}; + +auto CScriptParserBufs::GetCScriptExprContextDataPtr() -> CScriptExprContextPtr +{ + auto& pool = CScriptParserBufsImpl::Get().m_poolCScriptExprContextData; + auto ptr = pool.acquireShared(); + if (!pool.isFromPool(ptr)) + { + static_assert(CScriptParserBufsImpl::sm_allow_fallback_objects); + g_Log.EventDebug( + "Requesting CScriptExprContext from an exhausted pool (max size: %" PRIuSIZE_T "). Alive new heap-allocated fallback objects: %" PRIuSIZE_T ".\n", + pool.sm_pool_size, pool.getFallbackCount()); + } + + *ptr.get() = {}; + //memset(ptr.get(), 0, sizeof(CScriptExprContextData)); + return ptr; +} + +auto CScriptParserBufs::GetCScriptTriggerArgsPtr() -> CScriptTriggerArgsPtr +{ + auto& pool = CScriptParserBufsImpl::Get().m_poolScriptTriggerArgs; + auto ptr = pool.acquireShared(); + if (!pool.isFromPool(ptr)) + { + static_assert(CScriptParserBufsImpl::sm_allow_fallback_objects); + g_Log.EventDebug( + "Requesting CScriptTriggerArgs from an exhausted pool (max size: %" PRIuSIZE_T "). Alive new heap-allocated fallback objects: %" PRIuSIZE_T ".\n", + pool.sm_pool_size, pool.getFallbackCount()); + } + + ptr->Clear(); + return ptr; +} diff --git a/src/common/CScriptParserBufs.h b/src/common/CScriptParserBufs.h new file mode 100644 index 000000000..9f3cd7f44 --- /dev/null +++ b/src/common/CScriptParserBufs.h @@ -0,0 +1,55 @@ +#ifndef _INC_CSCRIPTPARSERBUFS_H +#define _INC_CSCRIPTPARSERBUFS_H + +#include "CScriptTriggerArgs.h" + + +struct CScriptSubExprData +{ + lptstr ptcStart, ptcEnd; + enum Type : ushort + { + Unknown = 0, + // Powers of two + MaybeNestedSubexpr = 0x1 << 0, // 001 + TopParenthesizedExpr = 0x1 << 1, // 002 + None = 0x1 << 2, // 004 + BinaryNonLogical = 0x1 << 3, // 008 + And = 0x1 << 4, // 010 + Or = 0x1 << 5 // 020 + }; + ushort uiType; + ushort uiNonAssociativeOffset; // How much bytes/characters before the start is (if any) the first non-associative operator preceding the subexpression. +}; +using CScriptSubExprDataPtr = std::unique_ptr; + +struct CScriptExprContextData +{ + // Recursion counters and state variables + short _iEvaluate_Conditional_Reentrant; + short _iParseScriptText_Reentrant; + bool _fParseScriptText_Brackets; +}; +using CScriptExprContextPtr = std::shared_ptr; + +using CScriptTriggerArgsPtr = std::shared_ptr; +/* +class CScriptTriggerArgsPtr : public std::shared_ptr +{ + CScriptTriggerArgsPtr(std::nullptr_t) = delete; +} +*/ + + +class CScriptParserBufs +{ + CScriptParserBufs() noexcept = default; + ~CScriptParserBufs() noexcept = default; + +public: + //static CScriptSubExprDataPtr GetCScriptSubExprDataPtr(); + static CScriptExprContextPtr GetCScriptExprContextDataPtr(); + static CScriptTriggerArgsPtr GetCScriptTriggerArgsPtr(); +}; + +#endif // _INC_CSCRIPTPARSERBUFS_H diff --git a/src/common/CScriptTriggerArgs.cpp b/src/common/CScriptTriggerArgs.cpp index f967d1d32..196aa9099 100644 --- a/src/common/CScriptTriggerArgs.cpp +++ b/src/common/CScriptTriggerArgs.cpp @@ -5,10 +5,9 @@ #include "CScriptTriggerArgs.h" -CScriptTriggerArgs::CScriptTriggerArgs() : - m_iN1(0), m_iN2(0), m_iN3(0) +CScriptTriggerArgs::CScriptTriggerArgs() noexcept : + m_iN1(0), m_iN2(0), m_iN3(0), m_pO1(nullptr) { - m_pO1 = nullptr; } CScriptTriggerArgs::CScriptTriggerArgs(lpctstr pszStr) @@ -16,25 +15,13 @@ CScriptTriggerArgs::CScriptTriggerArgs(lpctstr pszStr) Init(pszStr); } -CScriptTriggerArgs::CScriptTriggerArgs(CScriptObj* pObj) : +CScriptTriggerArgs::CScriptTriggerArgs(CScriptObj* pObj) noexcept : m_iN1(0), m_iN2(0), m_iN3(0), m_pO1(pObj) { } -CScriptTriggerArgs::CScriptTriggerArgs(int64 iVal1) : - m_iN1(iVal1), m_iN2(0), m_iN3(0) -{ - m_pO1 = nullptr; -} - -CScriptTriggerArgs::CScriptTriggerArgs(int64 iVal1, int64 iVal2, int64 iVal3) : - m_iN1(iVal1), m_iN2(iVal2), m_iN3(iVal3) -{ - m_pO1 = nullptr; -} - -CScriptTriggerArgs::CScriptTriggerArgs(int64 iVal1, int64 iVal2, CScriptObj* pObj) : - m_iN1(iVal1), m_iN2(iVal2), m_iN3(0), m_pO1(pObj) +CScriptTriggerArgs::CScriptTriggerArgs(int64 iVal1, int64 iVal2, int64 iVal3, CScriptObj* pObj) noexcept : + m_iN1(iVal1), m_iN2(iVal2), m_iN3(iVal3), m_pO1(pObj) { } @@ -143,6 +130,20 @@ void CScriptTriggerArgs::Init( lpctstr pszStr ) } } +void CScriptTriggerArgs::Init(CScriptObj* pObj) +{ + Clear(); + m_pO1 = pObj; +} + +void CScriptTriggerArgs::Init(int64 iVal1, int64 iVal2, int64 iVal3, CScriptObj* pObj) +{ + Clear(); + m_iN1 = iVal1; + m_iN2 = iVal2; + m_iN3 = iVal3; + m_pO1 = pObj; +} void CScriptTriggerArgs::GetArgNs(int64* iVar1, int64* iVar2, int64* iVar3) //Puts the ARGN's into the specified variables { diff --git a/src/common/CScriptTriggerArgs.h b/src/common/CScriptTriggerArgs.h index 72bd8afbd..21c9169cf 100644 --- a/src/common/CScriptTriggerArgs.h +++ b/src/common/CScriptTriggerArgs.h @@ -10,6 +10,7 @@ #include "CLocalVarsExtra.h" #include + class CScriptTriggerArgs : public CScriptObj { // All the args an event will need. @@ -34,25 +35,26 @@ class CScriptTriggerArgs : public CScriptObj CLocalObjMap m_VarObjs; // "REFx" = local object x public: - CScriptTriggerArgs(); + CScriptTriggerArgs() noexcept; explicit CScriptTriggerArgs(lpctstr pszStr); - explicit CScriptTriggerArgs(CScriptObj* pObj); - explicit CScriptTriggerArgs(int64 iVal1); - CScriptTriggerArgs(int64 iVal1, int64 iVal2, int64 iVal3 = 0); - CScriptTriggerArgs(int64 iVal1, int64 iVal2, CScriptObj* pObj); + explicit CScriptTriggerArgs(CScriptObj* pObj) noexcept; + CScriptTriggerArgs(int64 iVal1, int64 iVal2, int64 iVal3, CScriptObj* pObj) noexcept; virtual ~CScriptTriggerArgs() = default; -private: - CScriptTriggerArgs(const CScriptTriggerArgs& copy); - CScriptTriggerArgs& operator=(const CScriptTriggerArgs& other); + CScriptTriggerArgs(const CScriptTriggerArgs& copy) = delete; + CScriptTriggerArgs& operator=(const CScriptTriggerArgs& other) = delete; + CScriptTriggerArgs(CScriptTriggerArgs&& move) = delete; public: //Puts the ARGN's into the specified variables void GetArgNs(int64* iVar1 = nullptr, int64* iVar2 = nullptr, int64* iVar3 = nullptr); void Clear(); - void Init( lpctstr pszStr ); + void Init(lpctstr pszStr); + void Init(CScriptObj* pObj); + void Init(int64 iVal1, int64 iVal2, int64 iVal3, CScriptObj *pObj); + bool r_Verb( CScript & s, CTextConsole * pSrc ) override; bool r_LoadVal( CScript & s ) override; bool r_GetRef( lpctstr & ptcKey, CScriptObj * & pRef ) override; diff --git a/src/common/resource/sections/CDialogDef.cpp b/src/common/resource/sections/CDialogDef.cpp index 4ef0dc7be..e2900043c 100644 --- a/src/common/resource/sections/CDialogDef.cpp +++ b/src/common/resource/sections/CDialogDef.cpp @@ -4,7 +4,7 @@ #include "../../../sphere/threads.h" #include "../../CException.h" #include "../../CExpression.h" -#include "../../CScriptTriggerArgs.h" +#include "../../CScriptParserBufs.h" #include "../CResourceLock.h" #include "CDialogDef.h" @@ -131,8 +131,9 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t { // RES_FUNCTION call CSString sVal; - CScriptTriggerArgs Args(s.GetArgRaw()); - if ( r_Call(uiFunctionIndex, pSrc, &Args, &sVal) ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(s.GetArgRaw()); + if ( r_Call(uiFunctionIndex, pScriptArgs, pSrc, &sVal) ) return true; } @@ -552,11 +553,12 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp m_wPage = (word)(iPage); m_fNoDispose = false; - CScriptTriggerArgs Args(iPage, 0, pObjSrc); - //DEBUG_ERR(("Args.m_s1_buf_vec %s Args.m_s1 %s Arguments 0x%x\n",Args.m_s1_buf_vec, Args.m_s1, Arguments)); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iPage, 0, 0, pObjSrc); + //DEBUG_ERR(("pScriptArgs->m_s1_buf_vec %s pScriptArgs->m_s1 %s Arguments 0x%x\n",pScriptArgs->m_s1_buf_vec, pScriptArgs->m_s1, Arguments)); if (Arguments) { - Args.m_s1_buf_vec = Args.m_s1 = Arguments; + pScriptArgs->m_s1_buf_vec = pScriptArgs->m_s1 = Arguments; } // read text first @@ -564,7 +566,7 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp { while ( s.ReadKey()) { - m_pObj->ParseScriptText( s.GetKeyBuffer(), pClient->GetChar() ); + m_pObj->ParseScriptText( s.GetKeyBuffer(), CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, pClient->GetChar() ); m_sText.emplace_back(false) = s.GetKey(); } } @@ -582,13 +584,13 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp // starting x,y location. int64 iSizes[2]; tchar * pszBuf = s.GetKeyBuffer(); - m_pObj->ParseScriptText( pszBuf, pClient->GetChar() ); + m_pObj->ParseScriptText( pszBuf, CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, pClient->GetChar() ); Str_ParseCmds( pszBuf, iSizes, ARRAY_COUNT(iSizes) ); m_x = (int)(iSizes[0]); m_y = (int)(iSizes[1]); - const auto trigRet = OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, pClient->GetChar(), &Args ); + const auto trigRet = OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, pScriptArgs, pClient->GetChar() ); m_sText.shrink_to_fit(); m_sControls.shrink_to_fit(); diff --git a/src/common/resource/sections/CRandGroupDef.cpp b/src/common/resource/sections/CRandGroupDef.cpp index 346cb7ce9..b9d5d5c74 100644 --- a/src/common/resource/sections/CRandGroupDef.cpp +++ b/src/common/resource/sections/CRandGroupDef.cpp @@ -258,7 +258,7 @@ size_t CRandGroupDef::GetRandMemberIndex( CChar * pCharSrc, bool fTrigger ) cons if (IsTrigUsed(TRIGGER_RESOURCETEST)) { - if (fTrigger && pOreDef->OnTrigger("@ResourceTest", pCharSrc, nullptr) == TRIGRET_RET_TRUE) + if (fTrigger && pOreDef->OnTrigger("@ResourceTest", CScriptTriggerArgsPtr{}, pCharSrc) == TRIGRET_RET_TRUE) continue; } } diff --git a/src/common/resource/sections/CRegionResourceDef.cpp b/src/common/resource/sections/CRegionResourceDef.cpp index 074752d74..8f431c40b 100644 --- a/src/common/resource/sections/CRegionResourceDef.cpp +++ b/src/common/resource/sections/CRegionResourceDef.cpp @@ -37,7 +37,7 @@ lpctstr const CRegionResourceDef::sm_szTrigName[RRTRIG_QTY+1] = // static }; -TRIGRET_TYPE CRegionResourceDef::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) +TRIGRET_TYPE CRegionResourceDef::OnTrigger(lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc) { ADDTOCALLSTACK("CRegionResourceDef::OnTrigger"); // Attach some trigger to the cchar. (PC or NPC) @@ -46,7 +46,7 @@ TRIGRET_TYPE CRegionResourceDef::OnTrigger( lpctstr pszTrigName, CTextConsole * CResourceLock s; if ( ResourceLock( s )) { - TRIGRET_TYPE iRet = CScriptObj::OnTriggerScript( s, pszTrigName, pSrc, pArgs ); + TRIGRET_TYPE iRet = CScriptObj::OnTriggerScript( s, pszTrigName, pArgs, pSrc); return iRet; } return TRIGRET_RET_DEFAULT; diff --git a/src/common/resource/sections/CRegionResourceDef.h b/src/common/resource/sections/CRegionResourceDef.h index f5415d6a2..a806cf794 100644 --- a/src/common/resource/sections/CRegionResourceDef.h +++ b/src/common/resource/sections/CRegionResourceDef.h @@ -58,7 +58,7 @@ class CRegionResourceDef : public CResourceLink public: virtual bool r_LoadVal( CScript & s ) override; virtual bool r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc = nullptr, bool fNoCallParent = false, bool fNoCallChildren = false ) override; - virtual TRIGRET_TYPE OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) override; + virtual TRIGRET_TYPE OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) override; }; #endif // _INC_CREGIONRESOURCEDEF_H diff --git a/src/common/resource/sections/CWebPageDef.cpp b/src/common/resource/sections/CWebPageDef.cpp index 42b0da8a7..31212a072 100644 --- a/src/common/resource/sections/CWebPageDef.cpp +++ b/src/common/resource/sections/CWebPageDef.cpp @@ -8,6 +8,7 @@ #include "../../sphere_library/CSFileList.h" #include "../../CException.h" #include "../../CExpression.h" +#include "../../CScriptParserBufs.h" #include "../../sphereversion.h" #include "../CResourceLock.h" #include "CWebPageDef.h" @@ -215,7 +216,10 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on if ( pszArgs[0] == '\0' ) pszArgs = "%NAME%%REGION.NAME%\n"; Str_CopyLimitNull( pszTmp2, pszArgs, Str_TempLength() ); - pChar->ParseScriptText( Str_MakeFiltered( pszTmp2 ), &g_Serv, 1 ); + pChar->ParseScriptText( + Str_MakeFiltered(pszTmp2), + CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + &g_Serv, 1 ); pSrc->SysMessage( pszTmp2 ); } } @@ -238,7 +242,10 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on ++sm_iListIndex; Str_CopyLimitNull(pszTmp2, s.GetArgStr(), Str_TempLength()); - pStone->ParseScriptText(Str_MakeFiltered(pszTmp2), &g_Serv, 1); + pStone->ParseScriptText( + Str_MakeFiltered(pszTmp2), + CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + &g_Serv, 1); pSrc->SysMessage(pszTmp2); } } @@ -253,7 +260,10 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on CGMPage* pPage = sptrPage.get(); ++sm_iListIndex; Str_CopyLimitNull( pszTmp2, s.GetArgStr(), Str_TempLength()); - pPage->ParseScriptText( Str_MakeFiltered( pszTmp2 ), &g_Serv, 1 ); + pPage->ParseScriptText( + Str_MakeFiltered(pszTmp2), + CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + &g_Serv, 1 ); pSrc->SysMessage( pszTmp2 ); } } @@ -324,7 +334,9 @@ bool CWebPageDef::WebPageUpdate( bool fNow, lpctstr pszDstName, CTextConsole * p // Deal with the stuff preceding the scripts. *pszHead = '\0'; pszHead += 26; - ParseScriptText( pszTmp, pSrc, 1 ); + ParseScriptText( pszTmp, + CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + pSrc, 1 ); FileOut.SysMessage( pszTmp ); fScriptMode = true; } @@ -363,7 +375,9 @@ bool CWebPageDef::WebPageUpdate( bool fNow, lpctstr pszDstName, CTextConsole * p } // Look for stuff we can displace here. %STUFF% - ParseScriptText( pszHead, pSrc, 1 ); + ParseScriptText( pszHead, + CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + pSrc, 1 ); FileOut.SysMessage( pszHead ); } @@ -545,7 +559,7 @@ int CWebPageDef::ServPageRequest( CClient * pClient, lpctstr pszURLArgs, CSTime CResourceLock s; if ( ResourceLock(s)) { - if (CScriptObj::OnTriggerScript( s, sm_szTrigName[WTRIG_Load], pClient, nullptr ) == TRIGRET_RET_TRUE) + if (CScriptObj::OnTriggerScript( s, sm_szTrigName[WTRIG_Load], CScriptTriggerArgsPtr{}, pClient ) == TRIGRET_RET_TRUE) return 0; // Block further action. } } @@ -725,7 +739,9 @@ bool CWebPageDef::ServPagePost( CClient * pClient, lpctstr pszURLArgs, tchar * p // B or BTN or BUTTON = the buttons // C or CHK or CHECK = the check boxes - CDialogResponseArgs resp; + // TODO: just split CDialogResponseArgs into two objects (CScriptTriggerArgs and a struct for other data?) + // Or just make CScriptTriggerArgs a member of CDialogResponseArgs... Favor composition over inheritance! + auto resp = std::make_shared(); dword dwButtonID = UINT32_MAX; for ( int i = 0; i < iArgs; ++i ) { @@ -753,7 +769,7 @@ bool CWebPageDef::ServPagePost( CClient * pClient, lpctstr pszURLArgs, tchar * p continue; if ( atoi(pszNum) ) { - resp.m_CheckArray.push_back(iNum); + resp->m_CheckArray.push_back(iNum); } break; case 'T': @@ -761,7 +777,7 @@ bool CWebPageDef::ServPagePost( CClient * pClient, lpctstr pszURLArgs, tchar * p { tchar *pszData = Str_GetTemp(); HtmlDeCode( pszData, pszNum ); - resp.AddText((word)(iNum), pszData); + resp->AddText((word)(iNum), pszData); } break; } @@ -777,7 +793,7 @@ bool CWebPageDef::ServPagePost( CClient * pClient, lpctstr pszURLArgs, tchar * p { if ( !s.IsKeyHead("ON", 2) || ( (dword)s.GetArgVal() != dwButtonID )) continue; - OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, pClient, &resp); + OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, resp, pClient); return true; } diff --git a/src/common/resource/sections/CWebPageDef.h b/src/common/resource/sections/CWebPageDef.h index 67cad6489..358e2d0d4 100644 --- a/src/common/resource/sections/CWebPageDef.h +++ b/src/common/resource/sections/CWebPageDef.h @@ -163,9 +163,8 @@ class CWebPageDef : public CResourceLink explicit CWebPageDef(CResourceID id); virtual ~CWebPageDef() = default; -private: - CWebPageDef(const CWebPageDef& copy); - CWebPageDef& operator=(const CWebPageDef& other); + CWebPageDef(const CWebPageDef& copy) = delete; + CWebPageDef& operator=(const CWebPageDef& other) = delete; }; diff --git a/src/common/sphere_library/sobjpool.h b/src/common/sphere_library/sobjpool.h new file mode 100644 index 000000000..0ce864bb2 --- /dev/null +++ b/src/common/sphere_library/sobjpool.h @@ -0,0 +1,358 @@ +#ifndef _INC_SOBJPOOL_H +#define _INC_SOBJPOOL_H + +#include +#include +#include +#include +#include +#include +#include +#include "sstacks.h" + +namespace sl +{ + +// ObjectPool pre-constructs a fixed number of T objects stored in an internal array. +// It provides two interfaces: acquireUnique() returns a std::unique_ptr and +// acquireShared() returns a std::shared_ptr. When no free objects are available, +// the behavior depends on the AllowFallback template parameter: +// - If AllowFallback is true, a new, dynamically allocated object is returned. +// - Otherwise, a std::runtime_error is thrown. + +template +// tp: template parameter +class ObjectPool +{ +public: + using index_t = uint32_t; // do we really need size_t elements? + static constexpr size_t sm_pool_size = static_cast(tp_pool_size); + static constexpr bool sm_allow_fallback = tp_allow_fallback; + + // Ensure that T is default constructible and destructible. + static_assert(std::is_default_constructible_v, + "ObjectPool requires the type T to be default constructible"); + static_assert(std::is_destructible_v, + "ObjectPool requires the type T to be destructible"); + + ObjectPool() + : m_fallbackObjsCount(0) + { + // Initialize m_freeIndices: each index corresponds to an available object. + for (index_t i = 0; i < sm_pool_size; ++i) { + m_freeIndices.push(i); + } + } + + // Disable copy and move. + ObjectPool(const ObjectPool&) = delete; + ObjectPool& operator=(const ObjectPool&) = delete; + + // Custom deleter used for both pooled and fallback allocations. + // The optional index indicates whether the object came from the pool. + // When index is engaged, the object is one managed by the pool, and release() is called. + // When not engaged, the deleter deletes the dynamically allocated object. + struct PoolDeleter + { + ObjectPool* m_pool; + std::optional m_index; // Engaged if pooled + + void operator()(T* ptr) const + { + if (m_pool) + { + if (m_index.has_value()) + { + // Object is from the pool; return it. + m_pool->release(m_index.value()); + } + else + { + // Fallback allocation: decrement the fallback counter. + m_pool->fallbackReleased(); + delete ptr; + } + } + else + { + // Should not typically happen if pool pointer is always set. + delete ptr; + } + } + }; + + // Type alias for unique_ptr using PoolDeleter. + using UniquePtr_t = std::unique_ptr; + + // Acquires an object and returns it as a unique_ptr. + // If no pooled objects are available, either returns a fallback allocation or throws. + UniquePtr_t acquireUnique() + { + if (!m_freeIndices.empty()) [[likely]] + { + const auto idx = m_freeIndices.top(); + m_freeIndices.pop(); + T* ptr = &m_objects[idx]; + return UniquePtr_t(ptr, PoolDeleter {this, idx}); + } + else [[unlikely]] if constexpr (sm_allow_fallback) + { + // Return a fallback (dynamically allocated) object. + T* ptr = new T(); + m_fallbackObjsCount += 1;; + return UniquePtr_t(ptr, PoolDeleter {this, std::nullopt}); + } + else + { + throw std::runtime_error("No free object available in pool"); + } + } + + // Type alias for shared_ptr. The deleter goes in the constructor, unlike unique_ptr. + using SharedPtr_t = std::shared_ptr; + + // Acquires an object and returns it as a shared_ptr. + // If no pooled objects are available, either returns a fallback allocation or throws. + SharedPtr_t acquireShared() + { + if (!m_freeIndices.empty()) [[likely]] + { + const auto idx = m_freeIndices.top(); + m_freeIndices.pop(); + T* ptr = &m_objects[idx]; + return std::shared_ptr(ptr, PoolDeleter {this, idx}); + } + else [[unlikely]] if constexpr (sm_allow_fallback) + { + // Return a fallback (dynamically allocated) object. + T* ptr = new T(); + m_fallbackObjsCount += 1; + return std::shared_ptr(ptr, PoolDeleter {this, std::nullopt}); + } + else + { + throw std::runtime_error("No free object available in pool"); + } + } + + // Returns the current number of outstanding fallback allocations. + [[nodiscard]] + index_t getFallbackCount() const noexcept + { + return m_fallbackObjsCount; + } + + // Static helper method to check whether an object acquired as a unique_ptr was from the pool. + // Hides the internal PoolDeleter implementation details. + [[nodiscard]] + static bool isFromPool(const UniquePtr_t &ptr) noexcept + { + return ptr.get_deleter().m_index.has_value(); + } + + // Static helper method for shared_ptr. + [[nodiscard]] + static bool isFromPool(const std::shared_ptr &ptr) + { + if (auto deleterPtr = std::get_deleter(ptr)) + return deleterPtr->m_index.has_value(); + return false; + } + +private: + // Called by the custom deleter to return a pooled object to the m_freeIndices stack. + void release(index_t index) + { + m_freeIndices.push(index); + } + + // Called by the custom deleter when a fallback allocation is released. + void fallbackReleased() noexcept + { + m_fallbackObjsCount -= 1; + } + + // Pre-constructed objects stored in an array. + std::array m_objects{}; + + // Stack tracked indices of available objects. + sl::fixed_comptime_stack m_freeIndices; + + // Track outstanding fallback objects. + index_t m_fallbackObjsCount; +}; + + +template +// tp: template parameter +// ts: thread safe +class TSObjectPool +{ +public: + using index_t = uint32_t; // do we really need size_t elements? + static constexpr size_t sm_pool_size = static_cast(tp_pool_size); + static constexpr bool sm_allow_fallback = tp_allow_fallback; + + // Ensure that T is default constructible and destructible. + static_assert(std::is_default_constructible_v, + "ObjectPool requires the type T to be default constructible"); + static_assert(std::is_destructible_v, + "ObjectPool requires the type T to be destructible"); + + TSObjectPool() + : m_fallbackObjsCount(0) + { + // Initialize m_freeIndices: each index corresponds to an available object. + for (index_t i = 0; i < sm_pool_size; ++i) { + m_freeIndices.push(i); + } + } + + // Disable copy and move. + TSObjectPool(const TSObjectPool&) = delete; + TSObjectPool& operator=(const TSObjectPool&) = delete; + + // Custom deleter used for both pooled and fallback allocations. + // The optional index indicates whether the object came from the pool. + // When index is engaged, the object is one managed by the pool, and release() is called. + // When not engaged, the deleter deletes the dynamically allocated object. + struct PoolDeleter + { + TSObjectPool* m_pool; + std::optional m_index; // Engaged if pooled + + void operator()(T* ptr) const + { + if (m_pool) + { + if (m_index.has_value()) + { + // Object is from the pool; return it. + m_pool->release(m_index.value()); + } + else + { + // Fallback allocation: decrement the fallback counter. + m_pool->fallbackReleased(); + delete ptr; + } + } + else + { + // Should not typically happen if pool pointer is always set. + delete ptr; + } + } + }; + + // Type alias for unique_ptr using PoolDeleter. + using UniquePtr_t = std::unique_ptr; + + // Acquires an object and returns it as a unique_ptr. + // If no pooled objects are available, either returns a fallback allocation or throws. + UniquePtr_t acquireUnique() + { + std::lock_guard lock(m_mtx); + if (!m_freeIndices.empty()) [[likely]] + { + const auto idx = m_freeIndices.top(); + m_freeIndices.pop(); + T* ptr = &m_objects[idx]; + return UniquePtr_t(ptr, PoolDeleter {this, idx}); + } + else [[unlikely]] if constexpr (sm_allow_fallback) + { + // Return a fallback (dynamically allocated) object. + T* ptr = new T(); + m_fallbackObjsCount.fetch_add(1, std::memory_order_relaxed); + return UniquePtr_t(ptr, PoolDeleter {this, std::nullopt}); + } + else + { + throw std::runtime_error("No free object available in pool"); + } + } + + // Type alias for shared_ptr. The deleter goes in the constructor, unlike unique_ptr. + using SharedPtr_t = std::shared_ptr; + + // Acquires an object and returns it as a shared_ptr. + // If no pooled objects are available, either returns a fallback allocation or throws. + SharedPtr_t acquireShared() + { + std::lock_guard lock(m_mtx); + if (!m_freeIndices.empty()) [[likely]] + { + const auto idx = m_freeIndices.top(); + m_freeIndices.pop(); + T* ptr = &m_objects[idx]; + return std::shared_ptr(ptr, PoolDeleter {this, idx}); + } + else [[unlikely]] if constexpr (sm_allow_fallback) + { + // Return a fallback (dynamically allocated) object. + T* ptr = new T(); + m_fallbackObjsCount.fetch_add(1, std::memory_order_relaxed); + return std::shared_ptr(ptr, PoolDeleter {this, std::nullopt}); + } + else + { + throw std::runtime_error("No free object available in pool"); + } + } + + // Returns the current number of outstanding fallback allocations. + [[nodiscard]] + index_t getFallbackCount() const noexcept + { + return m_fallbackObjsCount.load(std::memory_order_relaxed); + } + + // Static helper method to check whether an object acquired as a unique_ptr was from the pool. + // Hides the internal PoolDeleter implementation details. + [[nodiscard]] + static bool isFromPool(const UniquePtr_t &ptr) noexcept + { + return ptr.get_deleter().m_index.has_value(); + } + + // Static helper method for shared_ptr. + [[nodiscard]] + static bool isFromPool(const std::shared_ptr &ptr) + { + if (auto deleterPtr = std::get_deleter(ptr)) + return deleterPtr->m_index.has_value(); + return false; + } + +private: + // Called by the custom deleter to return a pooled object to the m_freeIndices stack. + void release(index_t index) + { + std::lock_guard lock(m_mtx); + m_freeIndices.push(index); + } + + // Called by the custom deleter when a fallback allocation is released. + void fallbackReleased() noexcept + { + m_fallbackObjsCount.fetch_sub(1, std::memory_order_relaxed); + } + + // Pre-constructed objects stored in an array. + std::array m_objects{}; + + // Stack tracked indices of available objects. + sl::fixed_comptime_stack m_freeIndices; + + // Mutex to ensure thread safety. + mutable std::mutex m_mtx; + + // Atomic counter tracking outstanding fallback objects. + mutable std::atomic m_fallbackObjsCount; +}; + + +} // namespace sl + +#endif // _INC_SOBJPOOL_H diff --git a/src/common/sphere_library/sstacks.h b/src/common/sphere_library/sstacks.h index 3617ed073..7f8eed1f8 100644 --- a/src/common/sphere_library/sstacks.h +++ b/src/common/sphere_library/sstacks.h @@ -9,16 +9,43 @@ #include #include "../CException.h" -#define _SPHERE_STACK_DEFAULT_SIZE 10 +namespace sl +{ + +static constexpr size_t _SPHERE_STACK_DEFAULT_SIZE = 10; + +template +class fixed_comptime_stack +{ +public: + constexpr fixed_comptime_stack(); + + constexpr bool empty() const; + constexpr bool full() const; + constexpr size_t size() const; + constexpr size_t capacity() const; + + void push(const T& value); + void pop(); + T& top(); + + const T& top() const; + const T* data() const; + //T* data(); + +private: + T m_data[N]; + size_t m_top; +}; template -class fixedstack { +class fixed_runtime_stack { public: - fixedstack(size_t size=_SPHERE_STACK_DEFAULT_SIZE); - fixedstack(const fixedstack & o); - ~fixedstack(); + fixed_runtime_stack(size_t size=_SPHERE_STACK_DEFAULT_SIZE); + fixed_runtime_stack(const fixed_runtime_stack & o); + ~fixed_runtime_stack(); - fixedstack & operator=(const fixedstack & o); + fixed_runtime_stack & operator=(const fixed_runtime_stack & o); void push(T t); void pop(); @@ -34,13 +61,13 @@ class fixedstack { }; template -class fixedgrowingstack { +class fixed_growing_stack { public: - fixedgrowingstack(size_t size=_SPHERE_STACK_DEFAULT_SIZE); - fixedgrowingstack(const fixedgrowingstack & o); - ~fixedgrowingstack(); + fixed_growing_stack(size_t size=_SPHERE_STACK_DEFAULT_SIZE); + fixed_growing_stack(const fixed_growing_stack & o); + ~fixed_growing_stack(); - fixedgrowingstack & operator=(const fixedgrowingstack & o); + fixed_growing_stack & operator=(const fixed_growing_stack & o); void push(T t); void pop(); @@ -55,15 +82,15 @@ class fixedgrowingstack { }; template -class dynamicstack { +class dynamic_list_stack { public: - dynamicstack(); + dynamic_list_stack(); // To allow thread secure wrapper constructor. - dynamicstack(size_t _); - dynamicstack(const dynamicstack & o); - ~dynamicstack(); + dynamic_list_stack(size_t _); + dynamic_list_stack(const dynamic_list_stack & o); + ~dynamic_list_stack(); - dynamicstack & operator=(const dynamicstack & o); + dynamic_list_stack & operator=(const dynamic_list_stack & o); void push(T t); void pop(); @@ -74,13 +101,13 @@ class dynamicstack { size_t size() const; private: - struct _dynamicstackitem { + struct _dynamicliststackitem { public: - _dynamicstackitem(T item, _dynamicstackitem * next); + _dynamicliststackitem(T item, _dynamicliststackitem * next); T _item; - _dynamicstackitem * _next; + _dynamicliststackitem * _next; }; - _dynamicstackitem * _top; + _dynamicliststackitem * _top; size_t _size; }; @@ -104,28 +131,85 @@ class tsstack { S _s; }; +template +using ts_fixed_comptime_stack = tsstack>; + template -using tsfixedstack = tsstack>; +using ts_fixed_runtime_stack = tsstack>; template -using tsfixedgrowingstack = tsstack>; +using ts_fixed_growing_stack = tsstack>; template -using tsdynamicstack = tsstack>; +using ts_dynamic_list_stack = tsstack>; /**** * Implementations. */ +template +constexpr fixed_comptime_stack::fixed_comptime_stack() : m_top(0) {} + +template +constexpr bool fixed_comptime_stack::empty() const { return m_top == 0; } + +template + +constexpr bool fixed_comptime_stack::full() const { return m_top == N; } + +template +constexpr size_t fixed_comptime_stack::size() const { return m_top; } + +template +constexpr size_t fixed_comptime_stack::capacity() const { return N; } + +template +void fixed_comptime_stack::push(const T& value) { + if (full()) [[unlikely]] + throw std::overflow_error("Stack full"); + m_data[m_top++] = value; +} + +template +void fixed_comptime_stack::pop() { + if (empty()) [[unlikely]] + throw std::underflow_error("Stack empty"); + --m_top; +} + +template +T& fixed_comptime_stack::top() { + if (empty()) [[unlikely]] + throw std::underflow_error("Stack empty"); + return m_data[m_top - 1]; +} + +template +const T& fixed_comptime_stack::top() const { + if (empty()) [[unlikely]] + throw std::underflow_error("Stack empty"); + return m_data[m_top - 1]; +} + +template +const T* fixed_comptime_stack::data() const { + return m_data; +} +/* +template +T* fixed_comptime_stack::data() { + return m_data; +} +*/ template -fixedstack::fixedstack(size_t size) { +fixed_runtime_stack::fixed_runtime_stack(size_t size) { _stack = new T[size]; _top = 0; _size = size; } template -fixedstack::fixedstack(const fixedstack & o) { +fixed_runtime_stack::fixed_runtime_stack(const fixed_runtime_stack & o) { _stack = new T[o._size]; _top = o._top; _size = o._size; @@ -133,12 +217,12 @@ fixedstack::fixedstack(const fixedstack & o) { } template -fixedstack::~fixedstack() { +fixed_runtime_stack::~fixed_runtime_stack() { delete[] _stack; } template -fixedstack & fixedstack::operator=(const fixedstack & o) { +fixed_runtime_stack & fixed_runtime_stack::operator=(const fixed_runtime_stack & o) { delete[] _stack; _stack = new T[o._size]; _top = o._top; @@ -148,7 +232,7 @@ fixedstack & fixedstack::operator=(const fixedstack & o) { } template -void fixedstack::push(T t) { +void fixed_runtime_stack::push(T t) { if (_top == _size) { throw CSError(LOGL_FATAL, 0, "stack is full."); } @@ -156,7 +240,7 @@ void fixedstack::push(T t) { } template -void fixedstack::pop() { +void fixed_runtime_stack::pop() { if (!_top) { throw CSError(LOGL_FATAL, 0, "stack is empty."); } @@ -164,7 +248,7 @@ void fixedstack::pop() { } template -T fixedstack::top() const { +T fixed_runtime_stack::top() const { if (!_top) { throw CSError(LOGL_FATAL, 0, "stack is empty."); } @@ -172,31 +256,31 @@ T fixedstack::top() const { } template -void fixedstack::clear() { +void fixed_runtime_stack::clear() { while(!empty()) { pop(); } } template -bool fixedstack::empty() const { +bool fixed_runtime_stack::empty() const { return _top == 0; } template -size_t fixedstack::size() const { +size_t fixed_runtime_stack::size() const { return _top; } template -fixedgrowingstack::fixedgrowingstack(size_t size) { -_stack = new T[size]; -_top = 0; -_size = size; +fixed_growing_stack::fixed_growing_stack(size_t size) { + _stack = new T[size]; + _top = 0; + _size = size; } template -fixedgrowingstack::fixedgrowingstack(const fixedgrowingstack & o) { +fixed_growing_stack::fixed_growing_stack(const fixed_growing_stack & o) { _stack = new T[o._size]; _top = o._top; _size = o._size; @@ -204,12 +288,12 @@ fixedgrowingstack::fixedgrowingstack(const fixedgrowingstack & o) { } template -fixedgrowingstack::~fixedgrowingstack() { +fixed_growing_stack::~fixed_growing_stack() { delete[] _stack; } template -fixedgrowingstack & fixedgrowingstack::operator=(const fixedgrowingstack & o) { +fixed_growing_stack & fixed_growing_stack::operator=(const fixed_growing_stack & o) { _stack = new T[o._size]; _top = o._top; _size = o._size; @@ -218,7 +302,7 @@ fixedgrowingstack & fixedgrowingstack::operator=(const fixedgrowingstack -void fixedgrowingstack::push(T t) { +void fixed_growing_stack::push(T t) { if (_top == _size) { T * nstack = new T[_size + _SPHERE_STACK_DEFAULT_SIZE]; for(size_t i = 0; i < _top; ++i) nstack[i] = _stack[i]; @@ -230,7 +314,7 @@ void fixedgrowingstack::push(T t) { } template -void fixedgrowingstack::pop() { +void fixed_growing_stack::pop() { if (!_top) { throw CSError(LOGL_FATAL, 0, "stack is empty."); } @@ -238,7 +322,7 @@ void fixedgrowingstack::pop() { } template -T fixedgrowingstack::top() const { +T fixed_growing_stack::top() const { if (!_top) { throw CSError(LOGL_FATAL, 0, "stack is empty."); } @@ -246,42 +330,42 @@ T fixedgrowingstack::top() const { } template -void fixedgrowingstack::clear() { +void fixed_growing_stack::clear() { while(!empty()) { pop(); } } template -bool fixedgrowingstack::empty() const { +bool fixed_growing_stack::empty() const { return _top == 0; } template -size_t fixedgrowingstack::size() const { +size_t fixed_growing_stack::size() const { return _top; } template -dynamicstack::dynamicstack() { +dynamic_list_stack::dynamic_list_stack() { _top = nullptr; _size = 0; } template -dynamicstack::dynamicstack(size_t _) : dynamicstack() { UnreferencedParameter(_); } +dynamic_list_stack::dynamic_list_stack(size_t _) : dynamic_list_stack() { UnreferencedParameter(_); } template -dynamicstack::dynamicstack(const dynamicstack & o) { +dynamic_list_stack::dynamic_list_stack(const dynamic_list_stack & o) { _size = o._size; _top = nullptr; - _dynamicstackitem * next = o._top, * prev; + _dynamicliststackitem * next = o._top, * prev; if (next) { - _top = new _dynamicstackitem(next->_item, nullptr); + _top = new _dynamicliststackitem(next->_item, nullptr); prev = _top; next = next->_next; while (next) { - prev->_next = new _dynamicstackitem(next->_item, nullptr); + prev->_next = new _dynamicliststackitem(next->_item, nullptr); prev = prev->_next; next = next->_next; } @@ -289,21 +373,21 @@ dynamicstack::dynamicstack(const dynamicstack & o) { } template -dynamicstack::~dynamicstack() { +dynamic_list_stack::~dynamic_list_stack() { clear(); }; template -dynamicstack & dynamicstack::operator=(const dynamicstack & o) { +dynamic_list_stack & dynamic_list_stack::operator=(const dynamic_list_stack & o) { _size = o._size; _top = nullptr; - _dynamicstackitem * next = o._top, * prev; + _dynamicliststackitem * next = o._top, * prev; if (next) { - _top = new _dynamicstackitem(next->_item, nullptr); + _top = new _dynamicliststackitem(next->_item, nullptr); prev = _top; next = next->_next; while (next) { - prev->_next = new _dynamicstackitem(next->_item, nullptr); + prev->_next = new _dynamicliststackitem(next->_item, nullptr); prev = prev->_next; next = next->_next; } @@ -312,25 +396,25 @@ dynamicstack & dynamicstack::operator=(const dynamicstack & o) { } template -void dynamicstack::push(T t) { - _dynamicstackitem * ntop = new _dynamicstackitem(t, _top); +void dynamic_list_stack::push(T t) { + _dynamicliststackitem * ntop = new _dynamicliststackitem(t, _top); _top = ntop; _size++; } template -void dynamicstack::pop() { +void dynamic_list_stack::pop() { if (_top == nullptr) { throw CSError(LOGL_FATAL, 0, "stack is empty."); } - _dynamicstackitem * oldtop = _top; + _dynamicliststackitem * oldtop = _top; _top = oldtop->_next; delete oldtop; _size--; } template -T dynamicstack::top() const { +T dynamic_list_stack::top() const { if (_top == nullptr) { throw CSError(LOGL_FATAL, 0, "stack is empty."); } @@ -338,24 +422,24 @@ T dynamicstack::top() const { } template -void dynamicstack::clear() { +void dynamic_list_stack::clear() { while(!empty()) { pop(); } } template -bool dynamicstack::empty() const { +bool dynamic_list_stack::empty() const { return _top == nullptr; } template -size_t dynamicstack::size() const { +size_t dynamic_list_stack::size() const { return _size; } template -dynamicstack::_dynamicstackitem::_dynamicstackitem(T item, _dynamicstackitem * next) { +dynamic_list_stack::_dynamicliststackitem::_dynamicliststackitem(T item, _dynamicliststackitem * next) { _item = item; _next = next; } @@ -424,4 +508,6 @@ size_t tsstack::size() const { return s; } +} + #endif //_INC_CSTACK_H diff --git a/src/game/CContainer.cpp b/src/game/CContainer.cpp index 1fc04c21f..52d86bdb0 100644 --- a/src/game/CContainer.cpp +++ b/src/game/CContainer.cpp @@ -182,13 +182,13 @@ void CContainer::ContentAddPrivate( CItem *pItem ) } } -void CContainer::OnRemoveObj( CSObjContRec *pObRec ) // Override this = called when removed from list. +void CContainer::OnRemoveObj(CSObjContRec *pObjRec ) // Override this = called when removed from list. { ADDTOCALLSTACK("CContainer::OnRemoveObj"); // remove this object from the container list. // Overload the RemoveAt for general lists to come here. - DEBUG_ASSERT(dynamic_cast(pObRec)); - CItem *pItem = static_cast(pObRec); + DEBUG_ASSERT(dynamic_cast(pObjRec)); + CItem *pItem = static_cast(pObjRec); ASSERT(pItem); CSObjCont::OnRemoveObj(pItem); @@ -213,7 +213,7 @@ void CContainer::r_WriteContent( CScript &s ) const } } -CItem *CContainer::ContentFind( CResourceID const& rid, dword dwArg, int iDecendLevels ) const +CItem *CContainer::ContentFind(CResourceID const& rid, dword dwArg, int iDescendLevels ) const { ADDTOCALLSTACK("CContainer::ContentFind"); // send all the items in the container. @@ -227,7 +227,7 @@ CItem *CContainer::ContentFind( CResourceID const& rid, dword dwArg, int iDecend if ( pItem->IsResourceMatch(rid, dwArg) ) return pItem; - if ( iDecendLevels <= 0 ) + if ( iDescendLevels <= 0 ) continue; CItemContainer *pCont = dynamic_cast(pItem); @@ -235,7 +235,7 @@ CItem *CContainer::ContentFind( CResourceID const& rid, dword dwArg, int iDecend { if ( !pCont->IsSearchable() ) continue; - CItem *pItemInCont = pCont->ContentFind(rid, dwArg, iDecendLevels - 1); + CItem *pItemInCont = pCont->ContentFind(rid, dwArg, iDescendLevels - 1); if ( pItemInCont ) return pItemInCont; } @@ -243,10 +243,12 @@ CItem *CContainer::ContentFind( CResourceID const& rid, dword dwArg, int iDecend return nullptr; } -TRIGRET_TYPE CContainer::OnContTriggerForLoop(CScript &s, CTextConsole *pSrc, CScriptTriggerArgs *pArgs, - CSString *pResult, CScriptLineContext &StartContext, CScriptLineContext &EndContext, const CResourceID &rid, dword dwArg, int iDecendLevels ) +TRIGRET_TYPE CContainer::OnContTriggerForLoop( + CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole *pSrc, + CSString *pResult, CScriptLineContext &StartContext, CScriptLineContext &EndContext, + const CResourceID &rid, dword dwArg, int iDescendLevels ) { - ADDTOCALLSTACK("CContainer::OnContTriggerForLoop"); + ADDTOCALLSTACK("CContainer::OnContTriggerForLoop"); if ( rid.GetResIndex() != 0 ) { for (CSObjContRec *pObjRec : GetIterationSafeContReverse()) @@ -255,7 +257,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop(CScript &s, CTextConsole *pSrc, CS if ( pItem->IsResourceMatch(rid, dwArg) ) { s.SeekContext(StartContext); - TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK || iRet == TRIGRET_RET_ABORTED) { EndContext = StartContext; @@ -268,7 +270,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop(CScript &s, CTextConsole *pSrc, CS else EndContext = s.GetContext(); } - if ( iDecendLevels <= 0 ) + if ( iDescendLevels <= 0 ) continue; CItemContainer *pCont = dynamic_cast(pItem); @@ -277,7 +279,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop(CScript &s, CTextConsole *pSrc, CS if ( pCont->IsSearchable() ) { CContainer *pContBase = dynamic_cast(pCont); - TRIGRET_TYPE iRet = pContBase->OnContTriggerForLoop(s, pSrc, pArgs, pResult, StartContext, EndContext, rid, dwArg, iDecendLevels - 1); + TRIGRET_TYPE iRet = pContBase->OnContTriggerForLoop(s, pArgs, pSrc, pResult, StartContext, EndContext, rid, dwArg, iDescendLevels - 1); if ( iRet != TRIGRET_ENDIF ) return iRet; @@ -291,7 +293,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop(CScript &s, CTextConsole *pSrc, CS if ( EndContext.m_iOffset <= StartContext.m_iOffset ) { CScriptObj *pScript = dynamic_cast(this); - TRIGRET_TYPE iRet = pScript->OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pScript->OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pArgs, pSrc, pResult); if ( iRet != TRIGRET_ENDIF ) return iRet; } @@ -303,7 +305,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop(CScript &s, CTextConsole *pSrc, CS } TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( - CScript &s, CTextConsole *pSrc, CScriptTriggerArgs *pArgs, + CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole *pSrc, CSString *pResult, CScriptLineContext &StartContext, CScriptLineContext &EndContext, int iDecendLevels ) { ADDTOCALLSTACK("CContainer::OnGenericContTriggerForLoop"); @@ -311,7 +313,7 @@ TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( { CItem* pItem = static_cast(pObjRec); s.SeekContext(StartContext); - TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK || iRet == TRIGRET_RET_ABORTED) { EndContext = StartContext; @@ -330,7 +332,7 @@ TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( if ( pCont && pCont->IsSearchable() ) { CContainer *pContBase = dynamic_cast(pCont); - iRet = pContBase->OnGenericContTriggerForLoop(s, pSrc, pArgs, pResult, StartContext, EndContext, iDecendLevels - 1); + iRet = pContBase->OnGenericContTriggerForLoop(s, pArgs, pSrc, pResult, StartContext, EndContext, iDecendLevels - 1); if ( iRet != TRIGRET_ENDIF ) return iRet; @@ -342,7 +344,7 @@ TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( if ( EndContext.m_iOffset <= StartContext.m_iOffset ) { CScriptObj *pScript = dynamic_cast(this); - TRIGRET_TYPE iRet = pScript->OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pScript->OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pArgs, pSrc, pResult); if ( iRet != TRIGRET_ENDIF ) return iRet; } @@ -368,7 +370,7 @@ CItem *CContainer::ContentFindRandom() const return static_cast(GetContentIndex(g_Rand.GetVal((int32)GetContentCount()))); } -int CContainer::ContentConsumeTest( const CResourceID& rid, int amount, dword dwArg ) const +int CContainer::ContentConsumeTest( const CResourceID& rid, int iAmount, dword dwArg ) const { ADDTOCALLSTACK("CContainer::ContentConsumeTest"); // ARGS: @@ -378,7 +380,7 @@ int CContainer::ContentConsumeTest( const CResourceID& rid, int amount, dword dw // # = number left to be consumed. (still required) if ( rid.GetResIndex() == 0 ) - return amount; // from skills menus. + return iAmount; // from skills menus. for (const CSObjContRec* pObjRec : *this) { @@ -386,9 +388,9 @@ int CContainer::ContentConsumeTest( const CResourceID& rid, int amount, dword dw if ( pItem->IsResourceMatch(rid, dwArg) ) { const word wAmountMax = pItem->GetAmount(); - const word wAmountToConsume = (word)minimum(amount,UINT16_MAX); - amount -= (wAmountMax > wAmountToConsume ) ? wAmountToConsume : wAmountMax; - if ( amount <= 0 ) + const word wAmountToConsume = (word)minimum(iAmount,UINT16_MAX); + iAmount -= (wAmountMax > wAmountToConsume ) ? wAmountToConsume : wAmountMax; + if ( iAmount <= 0 ) break; } @@ -405,12 +407,12 @@ int CContainer::ContentConsumeTest( const CResourceID& rid, int amount, dword dw if ( !pCont->IsSearchable() ) continue; } - amount = pCont->ContentConsumeTest(rid, amount, dwArg); - if ( amount <= 0 ) + iAmount = pCont->ContentConsumeTest(rid, iAmount, dwArg); + if ( iAmount <= 0 ) break; } } - return amount; + return iAmount; } int CContainer::ContentConsume( const CResourceID& rid, int amount, dword dwArg ) diff --git a/src/game/CContainer.h b/src/game/CContainer.h index 239b21433..226d9222d 100644 --- a/src/game/CContainer.h +++ b/src/game/CContainer.h @@ -48,7 +48,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m * @brief Override this = called when removed from list. * @param [in,out] pObRec If non-null, the ob record. */ - virtual void OnRemoveObj(CSObjContRec* pObRec) override; + virtual void OnRemoveObj(CSObjContRec* pObjRec) override; /** * @fn void CContainer::ContentAddPrivate( CItem * pItem ); @@ -126,17 +126,17 @@ class CContainer : public CSObjCont // This class contains a list of items but m // For resource usage and gold. /** - * @fn CItem * CContainer::ContentFind( RESOURCE_ID_BASE rid, dword dwArg = 0, int iDecendLevels = 255 ) const; + * @fn CItem * CContainer::ContentFind( RESOURCE_ID_BASE rid, dword dwArg = 0, int iDescendLevels = 255 ) const; * @brief Content find. * @param rid The rid. * @param dwArg The argument. - * @param iDecendLevels Zero-based index of the decend levels. + * @param iDescendLevels Zero-based index of the decend levels. * @return null if it fails, else a pointer to a CItem. */ - CItem * ContentFind( CResourceID const& rid, dword dwArg = 0, int iDecendLevels = 255 ) const; + CItem * ContentFind( CResourceID const& rid, dword dwArg = 0, int iDescendLevels = 255 ) const; /** - * @fn TRIGRET_TYPE CContainer::OnContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, RESOURCE_ID_BASE rid, dword dwArg = 0, int iDecendLevels = 255 ); + * @fn TRIGRET_TYPE CContainer::OnContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, RESOURCE_ID_BASE rid, dword dwArg = 0, int iDescendLevels = 255 ); * @brief Executes the container trigger for loop action. * @param [in,out] s The CScript to process. * @param [in,out] pSrc If non-null, source for the. @@ -146,11 +146,11 @@ class CContainer : public CSObjCont // This class contains a list of items but m * @param [in,out] EndContext Context for the end. * @param rid The rid. * @param dwArg The argument. - * @param iDecendLevels Zero-based index of the decend levels. + * @param iDescendLevels Zero-based index of the decend levels. * * @return A TRIGRET_TYPE. */ - TRIGRET_TYPE OnContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, CResourceID const& rid, dword dwArg = 0, int iDecendLevels = 255 ); + TRIGRET_TYPE OnContTriggerForLoop(CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, CResourceID const& rid, dword dwArg = 0, int iDescendLevels = 255 ); /** * @fn TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); @@ -165,7 +165,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m * * @return A TRIGRET_TYPE. */ - TRIGRET_TYPE OnGenericContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); + TRIGRET_TYPE OnGenericContTriggerForLoop(CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); /** * @fn int CContainer::ContentCount( RESOURCE_ID_BASE rid, dword dwArg = 0 ); @@ -203,7 +203,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m * * @return 0 = all consumed, # = number left to be consumed. */ - int ContentConsumeTest( const CResourceID& rid, int amount, dword dwArg = 0) const; + int ContentConsumeTest(const CResourceID& rid, int iAmount, dword dwArg = 0) const; /** * @fn int CContainer::ResourceConsume( const CResourceQtyArray * pResources, int iReplicationQty, bool fTest = false, dword dwArg = 0 ); @@ -242,7 +242,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m * @param [in,out] pItem If non-null, the item. * @param [in,out] bForceNoStack Do not stack on other identical items, even if it's a stackable type. */ - virtual void ContentAdd( CItem * pItem, bool bForceNoStack = false ) = 0; + virtual void ContentAdd( CItem * pItem, bool fForceNoStack = false ) = 0; }; diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index 7b4eba652..1e6fbb4b3 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -2,8 +2,9 @@ #include "../common/resource/CResourceLock.h" #include "../common/CException.h" #include "../common/CExpression.h" -#include "../common/sphereversion.h" +#include "../common/CScriptParserBufs.h" #include "../common/CLog.h" +#include "../common/sphereversion.h" #include "../network/CClientIterator.h" #include "../network/send.h" #include "../sphere/ProfileTask.h" @@ -340,18 +341,19 @@ void CObjBase::SetHue( HUE_TYPE wHue, bool fAvoidTrigger, CTextConsole *pSrc, CO lpctstr ptcTrig = (IsChar() ? CChar::sm_szTrigName[CTRIG_DYE] : CItem::sm_szTrigName[ITRIG_DYE]); if (IsTrigUsed(ptcTrig)) { - CScriptTriggerArgs args(wHue, iSound, pSourceObj); - TRIGRET_TYPE iRet = OnTrigger(ptcTrig, pSrc, &args); + CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgs->Init(wHue, iSound, 0, pSourceObj); + TRIGRET_TYPE iRet = OnTrigger(ptcTrig, pArgs, pSrc); if (iRet == TRIGRET_RET_TRUE) return; - if (args.m_iN2 > 0) // No sound? No checks for who can hear, packets... + if (pArgs->m_iN2 > 0) // No sound? No checks for who can hear, packets... { - Sound((SOUND_TYPE)(args.m_iN2)); + Sound((SOUND_TYPE)(pArgs->m_iN2)); } - m_wHue = (HUE_TYPE)(args.m_iN1); + m_wHue = (HUE_TYPE)(pArgs->m_iN1); return; } } @@ -804,7 +806,9 @@ bool CObjBase::MoveNear(CPointMap pt, ushort iSteps ) return MoveTo(pt); } -void CObjBase::UpdateObjMessage( lpctstr pTextThem, lpctstr pTextYou, CClient * pClientExclude, HUE_TYPE wHue, TALKMODE_TYPE mode, FONT_TYPE font, bool fUnicode ) const +void CObjBase::UpdateObjMessage( + lpctstr pTextThem, lpctstr pTextYou, CClient * pClientExclude, + HUE_TYPE wHue, TALKMODE_TYPE iMode, FONT_TYPE iFont, bool fUnicode ) const { ADDTOCALLSTACK("CObjBase::UpdateObjMessage"); // Show everyone a msg coming from this object. @@ -818,9 +822,9 @@ void CObjBase::UpdateObjMessage( lpctstr pTextThem, lpctstr pTextYou, CClient * continue; if (( pClient->GetChar() == this ) && pTextYou != nullptr ) - pClient->addBarkParse(pTextYou, this, wHue, mode, font, fUnicode ); + pClient->addBarkParse(pTextYou, this, wHue, iMode, iFont, fUnicode ); else if (( pClient->GetChar() != this ) && pTextThem != nullptr ) - pClient->addBarkParse(pTextThem, this, wHue, mode, font, fUnicode ); + pClient->addBarkParse(pTextThem, this, wHue, iMode, iFont, fUnicode ); //pClient->addBarkParse(( pClient->GetChar() == this )? pTextYou : pTextThem, this, wHue, mode, font, bUnicode ); } @@ -843,7 +847,7 @@ void CObjBase::UpdateCanSee(PacketSend *packet, CClient *exclude) const delete packet; } -TRIGRET_TYPE CObjBase::OnHearTrigger( CResourceLock & s, lpctstr pszCmd, CChar * pSrc, TALKMODE_TYPE & mode, HUE_TYPE wHue) +TRIGRET_TYPE CObjBase::OnHearTrigger( CResourceLock & s, lpctstr pszCmd, CChar * pSrc, TALKMODE_TYPE & iModeRef, HUE_TYPE wHue) { ADDTOCALLSTACK("CObjBase::OnHearTrigger"); // Check all the keys in this script section. @@ -851,8 +855,8 @@ TRIGRET_TYPE CObjBase::OnHearTrigger( CResourceLock & s, lpctstr pszCmd, CChar * // RETURN: // TRIGRET_ENDIF = no match. // TRIGRET_DEFAULT = found match but it had no RETURN - std::unique_ptr Args; bool fMatch = false; + CScriptTriggerArgsPtr pArgs; while ( s.ReadKeyParse()) { @@ -869,22 +873,18 @@ TRIGRET_TYPE CObjBase::OnHearTrigger( CResourceLock & s, lpctstr pszCmd, CChar * if ( ! fMatch ) continue; // look for the next "ON" section. - if (!Args) - { - // Allocate when needed - Args = std::make_unique(pszCmd); - Args->m_iN1 = mode; - Args->m_iN2 = wHue; - } - TRIGRET_TYPE iRet = CObjBase::OnTriggerRunVal( s, TRIGRUN_SECTION_EXEC, pSrc, Args.get() ); + pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgs->m_iN1 = iModeRef; + pArgs->m_iN2 = wHue; + TRIGRET_TYPE iRet = CObjBase::OnTriggerRunVal( s, TRIGRUN_SECTION_EXEC, pArgs, pSrc ); if ( iRet != TRIGRET_RET_FALSE ) return iRet; fMatch = false; } - if (Args) - mode = TALKMODE_TYPE(Args->m_iN1); + if (pArgs) + iModeRef = TALKMODE_TYPE(pArgs->m_iN1); return TRIGRET_ENDIF; // continue looking. } @@ -985,8 +985,9 @@ bool CObjBase::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc, SKIP_SEPARATORS(pszArgs); } - CScriptTriggerArgs Args( pszArgs != nullptr ? pszArgs : "" ); - if (r_Call(uiFunctionIndex, pSrc, &Args, &sVal)) + CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgs->Init(pszArgs != nullptr ? pszArgs : ""); + if (r_Call(uiFunctionIndex, pArgs, pSrc, &sVal)) { return true; } @@ -2104,8 +2105,9 @@ bool CObjBase::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command fro { // RES_FUNCTION call CSString sVal; - CScriptTriggerArgs Args( s.GetArgRaw() ); - if ( r_Call( uiFunctionIndex, pSrc, &Args, &sVal ) ) + CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgs->Init( s.GetArgRaw() ); + if ( r_Call( uiFunctionIndex, pArgs, pSrc, &sVal ) ) return true; } @@ -3625,7 +3627,7 @@ void CObjBase::DupeCopy( const CObjBase * pObj ) CEntityProps::Copy(pObj); } -TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CChar * pSrc, CScriptTriggerArgs * pArgs ) +TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CScriptTriggerArgsPtr pArgs, CChar * pSrc ) { ADDTOCALLSTACK("CObjBase::Spell_OnTrigger"); CSpellDef * pSpellDef = g_Cfg.GetSpellDef( spell ); @@ -3638,7 +3640,7 @@ TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CCh CResourceLock s; if ( pSpellDef->ResourceLock( s )) { - return CScriptObj::OnTriggerScript( s, CSpellDef::sm_szTrigName[stage], pSrc, pArgs ); + return CScriptObj::OnTriggerScript( s, CSpellDef::sm_szTrigName[stage], pArgs, pSrc ); } } return TRIGRET_RET_DEFAULT; @@ -3658,7 +3660,7 @@ bool CObjBase::CallPersonalTrigger(tchar * pArgs, CTextConsole * pSrc, TRIGRET_T if ( iResultArgs > 0 ) { lpctstr callTrigger = ppCmdTrigger[0]; - CScriptTriggerArgs csTriggerArgs; + CScriptTriggerArgsPtr pTriggerArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr();; if ( iResultArgs == 3 ) { @@ -3670,25 +3672,25 @@ bool CObjBase::CallPersonalTrigger(tchar * pArgs, CTextConsole * pSrc, TRIGRET_T iResultArgs = Str_ParseCmds(ppCmdTrigger[2], Arg_piCmd, ARRAY_COUNT(Arg_piCmd), ","); if ( iResultArgs == 3 ) - csTriggerArgs.m_iN3 = Arg_piCmd[2]; + pTriggerArgs->m_iN3 = Arg_piCmd[2]; if ( iResultArgs >= 2 ) - csTriggerArgs.m_iN2 = Arg_piCmd[1]; + pTriggerArgs->m_iN2 = Arg_piCmd[1]; if ( iResultArgs >= 1 ) - csTriggerArgs.m_iN1 = Arg_piCmd[0]; + pTriggerArgs->m_iN1 = Arg_piCmd[0]; } else if ( iTriggerArgType == 2 ) // ARGS { - csTriggerArgs.m_s1 = ppCmdTrigger[2]; - csTriggerArgs.m_s1_buf_vec = ppCmdTrigger[2]; + pTriggerArgs->m_s1 = ppCmdTrigger[2]; + pTriggerArgs->m_s1_buf_vec = ppCmdTrigger[2]; } else if ( iTriggerArgType == 3 ) // ARGO { CUID guTriggerArg(Exp_GetVal(ppCmdTrigger[2])); CObjBase * pTriggerArgObj = guTriggerArg.ObjFind(); if ( pTriggerArgObj ) - csTriggerArgs.m_pO1 = pTriggerArgObj; + pTriggerArgs->m_pO1 = pTriggerArgObj; } else if ( iTriggerArgType == 4 ) // FULLTRIGGER { @@ -3698,27 +3700,27 @@ bool CObjBase::CallPersonalTrigger(tchar * pArgs, CTextConsole * pSrc, TRIGRET_T // ARGS if ( iResultArgs == 5 ) { - csTriggerArgs.m_s1 = Arg_ppCmd[4]; - csTriggerArgs.m_s1_buf_vec = Arg_ppCmd[4]; + pTriggerArgs->m_s1 = Arg_ppCmd[4]; + pTriggerArgs->m_s1_buf_vec = Arg_ppCmd[4]; } // ARGNs if ( iResultArgs >= 4 ) - csTriggerArgs.m_iN3 = Exp_GetVal(Arg_ppCmd[3]); + pTriggerArgs->m_iN3 = Exp_GetVal(Arg_ppCmd[3]); if ( iResultArgs >= 3 ) - csTriggerArgs.m_iN2 = Exp_GetVal(Arg_ppCmd[2]); + pTriggerArgs->m_iN2 = Exp_GetVal(Arg_ppCmd[2]); if ( iResultArgs >= 2 ) - csTriggerArgs.m_iN1 = Exp_GetVal(Arg_ppCmd[1]); + pTriggerArgs->m_iN1 = Exp_GetVal(Arg_ppCmd[1]); // ARGO if ( iResultArgs >= 1 ) { CObjBase * pTriggerArgObj = CUID::ObjFindFromUID(Exp_GetVal(Arg_ppCmd[0])); if ( pTriggerArgObj ) - csTriggerArgs.m_pO1 = pTriggerArgObj; + pTriggerArgs->m_pO1 = pTriggerArgObj; } } } - trResult = OnTrigger(callTrigger, pSrc, &csTriggerArgs); + trResult = OnTrigger(callTrigger, pTriggerArgs, pSrc); return true; } diff --git a/src/game/CObjBase.h b/src/game/CObjBase.h index 082a21daa..a47592438 100644 --- a/src/game/CObjBase.h +++ b/src/game/CObjBase.h @@ -822,7 +822,7 @@ public: virtual bool IsDeleted() const override; void UpdateCanSee( PacketSend * pPacket, CClient * pClientExclude = nullptr ) const; /** - * @fn void CObjBase::UpdateObjMessage( lpctstr pTextThem, lpctstr pTextYou, CClient * pClientExclude, HUE_TYPE wHue, TALKMODE_TYPE mode, FONT_TYPE font = FONT_NORMAL, bool bUnicode = false ) const; + * @fn void CObjBase::UpdateObjMessage( lpctstr pTextThem, lpctstr pTextYou, CClient * pClientExclude, HUE_TYPE wHue, TALKMODE_TYPE iMode, FONT_TYPE iFont = FONT_NORMAL, bool fUnicode = false ) const; * * @brief Updates the object message. * @@ -830,26 +830,29 @@ public: virtual bool IsDeleted() const override; * @param pTextYou The text you. * @param [in,out] pClientExclude If non-null, the client exclude. * @param wHue The hue. - * @param mode The mode. - * @param font The font. - * @param bUnicode true to unicode. + * @param iMode The iMode. + * @param iFont The iFont. + * @param fUnicode true to unicode. */ - void UpdateObjMessage( lpctstr pTextThem, lpctstr pTextYou, CClient * pClientExclude, HUE_TYPE wHue, TALKMODE_TYPE mode, FONT_TYPE font = FONT_NORMAL, bool bUnicode = false ) const; + void UpdateObjMessage( + lpctstr pTextThem, lpctstr pTextYou, CClient * pClientExclude, + HUE_TYPE wHue, TALKMODE_TYPE iMode, + FONT_TYPE iFont = FONT_NORMAL, bool fUnicode = false ) const; /** - * @fn TRIGRET_TYPE CObjBase::OnHearTrigger(CResourceLock &s, lpctstr pCmd, CChar *pSrc, TALKMODE_TYPE &mode, HUE_TYPE wHue = HUE_DEFAULT); + * @fn TRIGRET_TYPE CObjBase::OnHearTrigger(CResourceLock &s, lpctstr pCmd, CChar *pSrc, TALKMODE_TYPE &iModeRef, HUE_TYPE wHue = HUE_DEFAULT); * * @brief Executes the hear trigger action. * * @param [in,out] s The CResourceLock to process. * @param pCmd The command. * @param [in,out] pSrc If non-null, source for the. - * @param [in,out] mode The mode. + * @param [in,out] iModeRef The iModeRef. * @param wHue The hue. * * @return A TRIGRET_TYPE. */ - TRIGRET_TYPE OnHearTrigger(CResourceLock &s, lpctstr pCmd, CChar *pSrc, TALKMODE_TYPE &mode, HUE_TYPE wHue = HUE_DEFAULT); + TRIGRET_TYPE OnHearTrigger(CResourceLock &s, lpctstr pCmd, CChar *pSrc, TALKMODE_TYPE &iModeRef, HUE_TYPE wHue = HUE_DEFAULT); /** * @fn bool CObjBase::IsContainer() const; @@ -885,11 +888,11 @@ public: virtual bool IsDeleted() const override; * @param [in,out] pCharSrc If non-null, the character source. * @param iSkillLevel Zero-based index of the skill level. * @param [in,out] pSourceItem If non-null, source item. - * @param bReflecting true to reflecting. + * @param fReflecting true to reflecting. * * @return true if it succeeds, false if it fails. */ - virtual bool OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, CItem * pSourceItem, bool bReflecting = false, int64 iDuration = 0 ) + virtual bool OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, CItem * pSourceItem, bool fReflecting = false, int64 iDuration = 0 ) = 0; /** @@ -904,7 +907,7 @@ public: virtual bool IsDeleted() const override; * * @return A TRIGRET_TYPE. */ - TRIGRET_TYPE Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CChar * pSrc, CScriptTriggerArgs * pArgs ); + TRIGRET_TYPE Spell_OnTrigger(SPELL_TYPE spell, SPTRIG_TYPE stage, CScriptTriggerArgsPtr pArgs, CChar * pSrc); protected: virtual void _GoAwake() override; diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 42a44f355..8e2923dd9 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -2,6 +2,7 @@ #include "../common/resource/sections/CRandGroupDef.h" #include "../common/resource/CResourceLock.h" #include "../common/CExpression.h" +#include "../common/CScriptParserBufs.h" #include "../network/CClientIterator.h" #include "chars/CChar.h" #include "clients/CClient.h" @@ -874,7 +875,7 @@ TRIGRET_TYPE CRegion::OnRegionTrigger( CTextConsole * pSrc, RTRIG_TYPE iAction ) CResourceLock s; if ( pLink->ResourceLock(s) ) { - iRet = CScriptObj::OnTriggerScript(s, sm_szTrigName[iAction], pSrc); + iRet = CScriptObj::OnTriggerScript(s, sm_szTrigName[iAction], CScriptTriggerArgsPtr{}, pSrc); if ( iRet == TRIGRET_RET_TRUE ) return iRet; } @@ -891,7 +892,7 @@ TRIGRET_TYPE CRegion::OnRegionTrigger( CTextConsole * pSrc, RTRIG_TYPE iAction ) if ( !pLink->ResourceLock(s) ) continue; - iRet = CScriptObj::OnTriggerScript(s, sm_szTrigName[iAction], pSrc); + iRet = CScriptObj::OnTriggerScript(s, sm_szTrigName[iAction], CScriptTriggerArgsPtr{}, pSrc); if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) return iRet; } diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index a62ce8152..14b9dbd50 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -803,7 +803,7 @@ void CSector::SetLightNow( bool fFlash ) // don't fire trigger when server is loading or light is flashing if (( ! g_Serv.IsLoading() && fFlash == false ) && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) { - pChar->OnTrigger( CTRIG_EnvironChange, pChar ); + pChar->OnTrigger( CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar ); } } } @@ -886,7 +886,7 @@ void CSector::SetWeather( WEATHER_TYPE w ) pChar->GetClientActive()->addWeather( w ); if ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) ) - pChar->OnTrigger( CTRIG_EnvironChange, pChar ); + pChar->OnTrigger( CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar ); } } @@ -907,7 +907,7 @@ void CSector::SetSeason( SEASON_TYPE season ) pChar->GetClientActive()->addSeason(season); if ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) ) - pChar->OnTrigger(CTRIG_EnvironChange, pChar); + pChar->OnTrigger(CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar); } } @@ -1289,7 +1289,7 @@ bool CSector::_OnTick() ASSERT(pChar); if (fEnvironChange && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) - pChar->OnTrigger(CTRIG_EnvironChange, pChar); + pChar->OnTrigger(CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar); if ( pChar->IsClientActive()) { diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index 9539ff4d3..ec87619f7 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -16,7 +16,7 @@ #include "../common/CException.h" #include "../common/CExpression.h" #include "../common/CLog.h" -#include "../common/CTextConsole.h" +#include "../common/CScriptParserBufs.h" #include "../common/CUOClientVersion.h" #include "../common/sphereversion.h" #include "../network/CClientIterator.h" @@ -1764,8 +1764,9 @@ bool CServer::r_Verb( CScript &s, CTextConsole * pSrc ) { // RES_FUNCTION call CSString sVal; - CScriptTriggerArgs Args( s.GetArgRaw() ); - if ( r_Call( uiFunctionIndex, pSrc, &Args, &sVal ) ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(s.GetArgRaw()); + if ( r_Call( uiFunctionIndex, pScriptArgs, pSrc, &sVal ) ) return true; } diff --git a/src/game/CServerDef.cpp b/src/game/CServerDef.cpp index ec84ff743..ad30d5b73 100644 --- a/src/game/CServerDef.cpp +++ b/src/game/CServerDef.cpp @@ -4,7 +4,7 @@ #include "../common/sphereproto.h" #include "../common/sphereversion.h" #include "../common/CLog.h" -#include "../common/CScriptTriggerArgs.h" +#include "../common/CScriptParserBufs.h" #include "../sphere/threads.h" #include "CServer.h" #include "CServerConfig.h" @@ -514,8 +514,9 @@ bool CServerDef::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc if (pszArgs != nullptr) GETNONWHITESPACE(pszArgs); - CScriptTriggerArgs Args( pszArgs ? pszArgs : "" ); - if ( r_Call( uiFunctionIndex, pSrc, &Args, &sVal ) ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init( pszArgs ? pszArgs : "" ); + if ( r_Call( uiFunctionIndex, pScriptArgs, pSrc, &sVal ) ) return true; } return (fNoCallParent ? false : CScriptObj::r_WriteVal( ptcKey, sVal, pSrc, false )); diff --git a/src/game/CTimedFunctionHandler.cpp b/src/game/CTimedFunctionHandler.cpp index 85b4fcd72..7bde9dfe4 100644 --- a/src/game/CTimedFunctionHandler.cpp +++ b/src/game/CTimedFunctionHandler.cpp @@ -64,7 +64,7 @@ void CTimedFunctionHandler::Clear() } TRIGRET_TYPE CTimedFunctionHandler::Loop(lpctstr ptcCommand, int iLoopsMade, CScriptLineContext StartContext, - CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult) + CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult) { ADDTOCALLSTACK("CTimedFunctionHandler::Loop"); for (CSObjContRec* obj : _timedFunctions.GetIterationSafeCont()) @@ -86,7 +86,7 @@ TRIGRET_TYPE CTimedFunctionHandler::Loop(lpctstr ptcCommand, int iLoopsMade, CSc break; } - TRIGRET_TYPE iRet = pObj->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pObj->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK) { diff --git a/src/game/CTimedFunctionHandler.h b/src/game/CTimedFunctionHandler.h index f877512e0..54a61eb53 100644 --- a/src/game/CTimedFunctionHandler.h +++ b/src/game/CTimedFunctionHandler.h @@ -40,7 +40,7 @@ class CTimedFunctionHandler void Stop(const CUID& uid, lpctstr ptcCommand); void Clear(); TRIGRET_TYPE Loop(lpctstr ptcCommand, int iLoopsMade, CScriptLineContext StartContext, - CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult); + CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult); int64 IsTimer(const CUID& uid, lpctstr ptcCommand) const; }; #endif // _INC_CTIMEDFUNCTIONHANDLER_H diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index be06000d4..194279234 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -1,5 +1,6 @@ #include "../common/CException.h" #include "../common/CExpression.h" +#include "../common/CScriptParserBufs.h" #include "../common/CLog.h" #include "../common/sphereversion.h" #include "../network/CClientIterator.h" @@ -895,14 +896,14 @@ bool CWorld::SaveStage() // Save world state in stages. llong llTicksStart = _iSaveTimer; TIME_PROFILE_END; - tchar * time = Str_GetTemp(); - snprintf(time, Str_TempLength(), "%lld.%04lld", TIME_PROFILE_GET_HI, TIME_PROFILE_GET_LO); + tchar * ptcTime = Str_GetTemp(); + snprintf(ptcTime, Str_TempLength(), "%lld.%04lld", TIME_PROFILE_GET_HI, TIME_PROFILE_GET_LO); - g_Log.Event(LOGM_SAVE, "World save completed, took %s seconds.\n", time); + g_Log.Event(LOGM_SAVE, "World save completed, took %s seconds.\n", ptcTime); - CScriptTriggerArgs Args; - Args.Init(time); - g_Serv.r_Call("f_onserver_save_finished", &g_Serv, &Args); + CScriptTriggerArgsPtr pScriptArgs; + pScriptArgs->Init(ptcTime); + g_Serv.r_Call("f_onserver_save_finished", pScriptArgs, &g_Serv); // Now clean up all the held over UIDs SaveThreadClose(); @@ -1169,6 +1170,7 @@ bool CWorld::Save( bool fForceImmediate ) // Save world state ADDTOCALLSTACK("CWorld::Save"); bool fSaved = false; + CScriptTriggerArgsPtr pScriptArgs = std::make_shared(); try { if (!CheckAvailableSpaceForSave(false)) @@ -1176,12 +1178,13 @@ bool CWorld::Save( bool fForceImmediate ) // Save world state //-- Ok we can start the save process, in which we eventually remove the previous saves and create the other. - CScriptTriggerArgs Args(fForceImmediate, _iSaveStage); - enum TRIGRET_TYPE tr; + pScriptArgs->Init(fForceImmediate, _iSaveStage, 0, nullptr); + enum TRIGRET_TYPE tr{}; - if ( g_Serv.r_Call("f_onserver_save", &g_Serv, &Args, nullptr, &tr) ) + if ( g_Serv.r_Call("f_onserver_save", pScriptArgs, &g_Serv, nullptr, &tr) ) if ( tr == TRIGRET_RET_TRUE ) return false; + //Flushing before the server should fix #2306 //The scripts fills the clients buffer and the server flush //the data during the save. @@ -1199,7 +1202,7 @@ bool CWorld::Save( bool fForceImmediate ) // Save world state #endif } - fForceImmediate = (Args.m_iN1 != 0); + fForceImmediate = (pScriptArgs->m_iN1 != 0); fSaved = SaveTry(fForceImmediate); } catch ( const CSError& e ) @@ -1223,8 +1226,8 @@ bool CWorld::Save( bool fForceImmediate ) // Save world state GetCurrentProfileData().Count(PROFILE_STAT_FAULTS, 1); } - CScriptTriggerArgs Args(fForceImmediate, _iSaveStage); - g_Serv.r_Call((fSaved ? "f_onserver_save_ok" : "f_onserver_save_fail"), &g_Serv, &Args); + pScriptArgs->Init(fForceImmediate, _iSaveStage, 0, nullptr); + g_Serv.r_Call((fSaved ? "f_onserver_save_ok" : "f_onserver_save_fail"), pScriptArgs, &g_Serv); return fSaved; } @@ -1826,8 +1829,10 @@ void CWorld::_OnTick() { EXC_SET_BLOCK("f_onserver_timer"); _iTimeLastCallUserFunc = iCurTime + g_Cfg._iTimerCall; - CScriptTriggerArgs args(g_Cfg._iTimerCallUnit ? g_Cfg._iTimerCall / (MSECS_PER_SEC) : g_Cfg._iTimerCall / (60 * MSECS_PER_SEC)); - g_Serv.r_Call("f_onserver_timer", &g_Serv, &args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = g_Cfg._iTimerCall / + (g_Cfg._iTimerCallUnit ? (MSECS_PER_SEC) : (60 * MSECS_PER_SEC)); + g_Serv.r_Call("f_onserver_timer", pScriptArgs, &g_Serv); } } diff --git a/src/game/CWorldComm.cpp b/src/game/CWorldComm.cpp index 78713a5ca..d7ccb051c 100644 --- a/src/game/CWorldComm.cpp +++ b/src/game/CWorldComm.cpp @@ -1,4 +1,5 @@ #include "../common/sphere_library/CSRand.h" +#include "../common/CScriptParserBufs.h" #include "../network/CClientIterator.h" #include "chars/CChar.h" #include "clients/CClient.h" @@ -224,13 +225,13 @@ void CWorldComm::Broadcast(lpctstr pMsg) // static // System broadcast in bold text ADDTOCALLSTACK("CWorldComm::Broadcast"); - CScriptTriggerArgs args; - args.Init(pMsg); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pMsg); TRIGRET_TYPE iRet = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onserver_broadcast", &g_Serv, &args, nullptr, &iRet); + g_Serv.r_Call("f_onserver_broadcast", pScriptArgs, &g_Serv, nullptr, &iRet); if (iRet == TRIGRET_RET_TRUE) return; - pMsg = args.m_s1; + pMsg = pScriptArgs->m_s1; Speak( nullptr, pMsg, HUE_TEXT_DEF, TALKMODE_BROADCAST, FONT_BOLD ); } diff --git a/src/game/CWorldMap.cpp b/src/game/CWorldMap.cpp index 90acaf6dc..5c45eed36 100644 --- a/src/game/CWorldMap.cpp +++ b/src/game/CWorldMap.cpp @@ -2,7 +2,7 @@ #include "../common/resource/sections/CRandGroupDef.h" #include "../common/resource/sections/CRegionResourceDef.h" #include "../common/CException.h" -#include "../common/CScriptTriggerArgs.h" +#include "../common/CScriptParserBufs.h" #include "../common/CRect.h" #include "../common/CLog.h" #include "../sphere/threads.h" @@ -149,12 +149,14 @@ CItem * CWorldMap::CheckNaturalResource(const CPointMap & pt, IT_TYPE iType, boo EXC_SET_BLOCK("resourcefound"); if ( pCharSrc != nullptr ) { - CScriptTriggerArgs Args(0, 0, pResBit); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(0, 0, 0, pResBit); TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; + if ( IsTrigUsed(TRIGGER_REGIONRESOURCEFOUND) ) - tRet = pCharSrc->OnTrigger(CTRIG_RegionResourceFound, pCharSrc, &Args); + tRet = pCharSrc->OnTrigger(CTRIG_RegionResourceFound, pScriptArgs, pCharSrc); if ( IsTrigUsed(TRIGGER_RESOURCEFOUND) ) - tRet = pOreDef->OnTrigger("@ResourceFound", pCharSrc, &Args); + tRet = pOreDef->OnTrigger("@ResourceFound", pScriptArgs, pCharSrc); if (tRet == TRIGRET_RET_TRUE) { diff --git a/src/game/CWorldTimedFunctions.cpp b/src/game/CWorldTimedFunctions.cpp index 04f27f765..2d83091bd 100644 --- a/src/game/CWorldTimedFunctions.cpp +++ b/src/game/CWorldTimedFunctions.cpp @@ -24,9 +24,9 @@ void CWorldTimedFunctions::Clear() // static } TRIGRET_TYPE CWorldTimedFunctions::Loop(lpctstr ptcCommand, int LoopsMade, CScriptLineContext StartContext, - CScript& s, CTextConsole* pSrc, CScriptTriggerArgs* pArgs, CSString* pResult) // static + CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc, CSString* pResult) // static { - return g_World._Ticker._TimedFunctions.Loop(ptcCommand, LoopsMade, StartContext, s, pSrc, pArgs, pResult); + return g_World._Ticker._TimedFunctions.Loop(ptcCommand, LoopsMade, StartContext, s, pArgs, pSrc, pResult); } void CWorldTimedFunctions::Add(const CUID& uid, int64 iTimeout, lpctstr ptcCommand) // static diff --git a/src/game/CWorldTimedFunctions.h b/src/game/CWorldTimedFunctions.h index f145fa39d..8119d6cce 100644 --- a/src/game/CWorldTimedFunctions.h +++ b/src/game/CWorldTimedFunctions.h @@ -23,7 +23,7 @@ class CWorldTimedFunctions static void Stop(const CUID& uid, lpctstr ptcCommand); static void Clear(); static TRIGRET_TYPE Loop(lpctstr ptcCommand, int LoopsMade, CScriptLineContext StartContext, - CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult); + CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult); static int64 IsTimer(const CUID& uid, lpctstr funcname); }; #endif // _INC_CWORLDTIMEDFUNCTIONS_H diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index c712e956e..4f3242532 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -4,6 +4,7 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CUID.h" #include "../../common/CRect.h" #include "../../common/CLog.h" @@ -416,7 +417,8 @@ bool CChar::NotifyDelete(bool fForce) { //We can forbid the deletion in here with no pain //If Delete is forced, we must avoid the possibility to block deletion (will create infinite loop) - if (CChar::OnTrigger(CTRIG_Destroy, &g_Serv) == TRIGRET_RET_TRUE && !fForce) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + if (CChar::OnTrigger(CTRIG_Destroy, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE && !fForce) return false; } @@ -424,10 +426,10 @@ bool CChar::NotifyDelete(bool fForce) if (m_pPlayer) { TRIGRET_TYPE trigReturn; - CScriptTriggerArgs Args; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); if (m_pClient) - Args.m_pO1 = m_pClient; - r_Call("f_onchar_delete", this, &Args, nullptr, &trigReturn); + pScriptArgs->m_pO1 = m_pClient; + r_Call("f_onchar_delete", pScriptArgs, this, nullptr, &trigReturn); //If Delete is forced, we must avoid the possibility to block deletion (will create infinite loop) if (trigReturn == TRIGRET_RET_TRUE && !fForce) return false; @@ -1319,17 +1321,18 @@ bool CChar::ReadScriptReduced(CResourceLock &s, bool fVendor) continue; lptstr ptcFunctionName = s.GetArgRaw(); - std::unique_ptr pScriptArgs; - // Locate arguments for the called function + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + // Locate arguments for the called function tchar* ptcArgs = strchr(ptcFunctionName, ' '); if (ptcArgs) { *ptcArgs = 0; ++ptcArgs; GETNONWHITESPACE(ptcArgs); - pScriptArgs = std::make_unique(ptcArgs); - } - pItem->r_Call(ptcFunctionName, this, pScriptArgs.get()); + pScriptArgs->Init(ptcArgs); + } + pItem->r_Call(ptcFunctionName, pScriptArgs, this); if (pItem->IsDeleted()) { pItem = nullptr; @@ -1452,7 +1455,8 @@ bool CChar::ReadScriptReduced(CResourceLock &s, bool fVendor) else if (!fItemCreated) { // I'm setting an attribute to myself, not the item (e.g. @Create trigger). Run that script line. - TRIGRET_TYPE tRet = OnTriggerRun( s, TRIGRUN_SINGLE_EXEC, &g_Serv, nullptr, nullptr ); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + TRIGRET_TYPE tRet = OnTriggerRun( s, TRIGRUN_SINGLE_EXEC, pScriptArgs, &g_Serv, nullptr ); if ((tRet == TRIGRET_RET_FALSE) && fFullInterp) ; else if ( tRet != TRIGRET_RET_DEFAULT ) @@ -1738,10 +1742,10 @@ void CChar::InitPlayer( CClient *pClient, const char *pszCharname, bool fFemale, if ( fNameIsAccepted && IsTrigUsed(TRIGGER_RENAME) ) { - CScriptTriggerArgs args; - args.m_s1 = zCharName; - args.m_pO1 = this; - if ( OnTrigger(CTRIG_Rename, this, &args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_s1 = zCharName; + pScriptArgs->m_pO1 = this; + if ( OnTrigger(CTRIG_Rename, pScriptArgs, this) == TRIGRET_RET_TRUE ) fNameIsAccepted = false; } @@ -3930,12 +3934,12 @@ bool CChar::r_LoadVal( CScript & s ) { if (IsTrigUsed(TRIGGER_RENAME)) { - CScriptTriggerArgs args; - args.m_s1 = s.GetArgStr(); - args.m_pO1 = this; - if (this->OnTrigger(CTRIG_Rename, this, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_s1 = s.GetArgStr(); + pScriptArgs->m_pO1 = this; + if (this->OnTrigger(CTRIG_Rename, pScriptArgs, this) == TRIGRET_RET_TRUE) return false; - SetName(args.m_s1); + SetName(pScriptArgs->m_s1); } else SetName(s.GetArgStr()); @@ -3954,14 +3958,16 @@ bool CChar::r_LoadVal( CScript & s ) if ( s.GetArgStr() ) { tchar * ppArgs[4]; - int iQty = Str_ParseCmds(const_cast(s.GetArgStr()), ppArgs, ARRAY_COUNT( ppArgs )); + int iQty = Str_ParseCmds(s.GetArgStr(), ppArgs, ARRAY_COUNT( ppArgs )); if ( iQty >= 2 ) { SKILL_TYPE iSkill = g_Cfg.FindSkillKey( ppArgs[0] ); if ( iSkill == SKILL_NONE ) return false; - Skill_UseQuick( iSkill, Exp_GetVal( ppArgs[1] ), true, (Exp_GetVal(ppArgs[2]) != 0 ? false : true), (Exp_GetVal(ppArgs[3]) != 0 ? true : false)); + Skill_UseQuick( iSkill, Exp_GetVal( ppArgs[1] ), true, + (Exp_GetVal(ppArgs[2]) != 0 ? false : true), + (Exp_GetVal(ppArgs[3]) != 0 ? true : false)); return true; } } @@ -4418,10 +4424,11 @@ bool CChar::r_Verb( CScript &s, CTextConsole * pSrc ) // Execute command from sc if (IsTrigUsed(TRIGGER_AFKMODE)) { - CScriptTriggerArgs args(fAFK, fMode); - TRIGRET_TYPE iRet = OnTrigger(CTRIG_AfkMode, this, &args); - fAFK = args.m_iN1 > 0 ? true : false; - fMode = args.m_iN2 > 0 ? true : false; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(fAFK, fMode, 0, nullptr); + TRIGRET_TYPE iRet = OnTrigger(CTRIG_AfkMode, pScriptArgs, this); + fAFK = pScriptArgs->m_iN1 > 0 ? true : false; + fMode = pScriptArgs->m_iN2 > 0 ? true : false; if (iRet == TRIGRET_RET_TRUE) //Block AFK mode switching if RETURN 1 in Trigger. break; @@ -5108,7 +5115,7 @@ static uint Calc_ExpGet_Level(uint exp) } } -void CChar::ChangeExperience(llong delta, CChar *pCharDead) +void CChar::ChangeExperience(llong iExpDelta, CChar *pCharDead) { ADDTOCALLSTACK("CChar::ChangeExperience"); @@ -5127,9 +5134,9 @@ void CChar::ChangeExperience(llong delta, CChar *pCharDead) DEFMSG_MSG_EXP_CHANGE_8 }; - if (delta != 0 || pCharDead)// zero call will sync the exp level + if (iExpDelta != 0 || pCharDead)// zero call will sync the exp level { - if (delta < 0) + if (iExpDelta < 0) { if (!(g_Cfg.m_iExperienceMode&EXP_MODE_ALLOW_DOWN)) // do not allow changes to minus return; @@ -5137,39 +5144,40 @@ void CChar::ChangeExperience(llong delta, CChar *pCharDead) if (g_Cfg.m_fLevelSystem && g_Cfg.m_iExperienceMode&EXP_MODE_DOWN_NOLEVEL) { uint exp = Calc_ExpGet_Exp(m_level); - if (delta + m_exp < exp) - delta = (llong)exp - m_exp; + if (iExpDelta + m_exp < exp) + iExpDelta = (llong)exp - m_exp; } } - if ((g_Cfg.m_iDebugFlags & DEBUGF_EXP) && (delta != 0)) + if ((g_Cfg.m_iDebugFlags & DEBUGF_EXP) && (iExpDelta != 0)) { g_Log.EventDebug("%s %s experience change (was %u, delta %lld, now %u)\n", - (m_pNPC ? "NPC" : "Player"), GetName(), m_exp, delta, (uint)(m_exp + delta)); + (m_pNPC ? "NPC" : "Player"), GetName(), m_exp, iExpDelta, (uint)(m_exp + iExpDelta)); } bool fShowMsg = (m_pClient != nullptr); if (IsTrigUsed(TRIGGER_EXPCHANGE)) { - CScriptTriggerArgs args(delta, fShowMsg); - args.m_pO1 = pCharDead; - if (OnTrigger(CTRIG_ExpChange, this, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iExpDelta, fShowMsg, 0, nullptr); + pScriptArgs->m_pO1 = pCharDead; + if (OnTrigger(CTRIG_ExpChange, pScriptArgs, this) == TRIGRET_RET_TRUE) return; - delta = args.m_iN1; - fShowMsg = (args.m_iN2 != 0); + iExpDelta = pScriptArgs->m_iN1; + fShowMsg = (pScriptArgs->m_iN2 != 0); } // Do not allow an underflow due to negative Exp Change. - if( delta < 0 && m_exp < abs(delta) ) + if( iExpDelta < 0 && m_exp < abs(iExpDelta) ) m_exp = 0; else - m_exp = (uint)(m_exp + delta); + m_exp = (uint)(m_exp + iExpDelta); - if (m_pClient && fShowMsg && delta) + if (m_pClient && fShowMsg && iExpDelta) { int iWord = 0; - llong absval = abs(delta); + llong absval = abs(iExpDelta); llong maxval = (g_Cfg.m_fLevelSystem && g_Cfg.m_iLevelNextAt) ? maximum(g_Cfg.m_iLevelNextAt, 1000) : 1000; if (absval >= maxval) // 100% @@ -5188,7 +5196,7 @@ void CChar::ChangeExperience(llong delta, CChar *pCharDead) iWord = 1; m_pClient->SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_CHANGE_0), - (delta > 0) ? g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_CHANGE_GAIN) : g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_CHANGE_LOST), + (iExpDelta > 0) ? g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_CHANGE_GAIN) : g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_CHANGE_LOST), g_Cfg.GetDefaultMsg(keyWords[iWord])); } } @@ -5199,23 +5207,24 @@ void CChar::ChangeExperience(llong delta, CChar *pCharDead) if (level != m_level) { - delta = level - m_level; + iExpDelta = level - m_level; bool fShowMsg = (m_pClient != nullptr); if (IsTrigUsed(TRIGGER_EXPLEVELCHANGE)) { - CScriptTriggerArgs args(delta); - if (OnTrigger(CTRIG_ExpLevelChange, this, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iExpDelta, 0, 0, nullptr); + if (OnTrigger(CTRIG_ExpLevelChange, pScriptArgs, this) == TRIGRET_RET_TRUE) return; - delta = args.m_iN1; - fShowMsg = (args.m_iN2 != 0); + iExpDelta = pScriptArgs->m_iN1; + fShowMsg = (pScriptArgs->m_iN2 != 0); } - level = delta + m_level; + level = iExpDelta + m_level; // Prevent integer underflow due to negative level change - if (delta < 0 && abs(delta) > m_level) + if (iExpDelta < 0 && abs(iExpDelta) > m_level) { level = 0; } @@ -5223,15 +5232,15 @@ void CChar::ChangeExperience(llong delta, CChar *pCharDead) if (g_Cfg.m_iDebugFlags & DEBUGF_LEVEL) { g_Log.EventDebug("%s %s level change (was %u, delta %lld, now %u)\n", - (m_pNPC ? "NPC" : "Player"), GetName(), m_level, delta, (uint)level); + (m_pNPC ? "NPC" : "Player"), GetName(), m_level, iExpDelta, (uint)level); } m_level = (uint)level; if (m_pClient && fShowMsg) { m_pClient->SysMessagef( - ((abs(delta) == 1) ? g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_LVLCHANGE_0) : g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_LVLCHANGE_1)), - ((delta > 0) ? g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_LVLCHANGE_GAIN) : g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_LVLCHANGE_LOST)) + ((abs(iExpDelta) == 1) ? g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_LVLCHANGE_0) : g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_LVLCHANGE_1)), + ((iExpDelta > 0) ? g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_LVLCHANGE_GAIN) : g_Cfg.GetDefaultMsg(DEFMSG_MSG_EXP_LVLCHANGE_LOST)) ); } } diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index 6f2c5eec0..ecc8637b9 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -589,8 +589,8 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; CItem * LayerFind( LAYER_TYPE layer ) const; void LayerAdd( CItem * pItem, LAYER_TYPE layer = LAYER_QTY ); - TRIGRET_TYPE OnCharTrigForLayerLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, LAYER_TYPE layer ); - TRIGRET_TYPE OnCharTrigForMemTypeLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, word wMemType ); + TRIGRET_TYPE OnCharTrigForLayerLoop(CScript &s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * pResult, LAYER_TYPE layer ); + TRIGRET_TYPE OnCharTrigForMemTypeLoop( CScript &s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * pResult, word wMemType ); virtual void OnWeightChange( int iChange ) override; virtual int GetWeight(word amount = 0) const override; @@ -618,8 +618,8 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; */ void SetTriggerActive(lpctstr trig = nullptr); - virtual TRIGRET_TYPE OnTrigger( lpctstr pTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) override; - TRIGRET_TYPE OnTrigger( CTRIG_TYPE trigger, CTextConsole * pSrc, CScriptTriggerArgs * pArgs = nullptr ); + virtual TRIGRET_TYPE OnTrigger( lpctstr pTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) override; + TRIGRET_TYPE OnTrigger( CTRIG_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc); public: // Load/Save---------------------------------- @@ -871,10 +871,10 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * * Main function for default Level system. * Triggers @ExpChange and @LevelChange if needed - * @param delta, amount of exp gaining (or losing?) + * @param iExpDelta, amount of exp gaining (or losing?) * @param ppCharDead from who we gained the experience. */ - void ChangeExperience(llong delta = 0, CChar *pCharDead = nullptr); + void ChangeExperience(llong iExpDelta = 0, CChar *pCharDead = nullptr); uint GetSkillTotal(int what = 0, bool how = true); /* @@ -916,7 +916,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; void Skill_SetBase( SKILL_TYPE skill, ushort uiValue ); void Skill_AddBase( SKILL_TYPE skill, int iChange ); - bool Skill_UseQuick( SKILL_TYPE skill, int64 difficulty, bool fAllowGain = true, bool fUseBellCurve = true, bool fForceCheck = false); + bool Skill_UseQuick( SKILL_TYPE skill, int64 iDifficulty, bool fAllowGain = true, bool fUseBellCurve = true, bool fForceCheck = false); bool Skill_CheckSuccess(SKILL_TYPE skill, int iDifficulty, bool fUseBellCurve = true ) const; bool Skill_Wait( SKILL_TYPE skilltry ); @@ -926,11 +926,9 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; ANIM_TYPE Skill_GetAnim( SKILL_TYPE skill); SOUND_TYPE Skill_GetSound( SKILL_TYPE skill); int Skill_Stage( SKTRIG_TYPE stage ); - TRIGRET_TYPE Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage); - TRIGRET_TYPE Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgs * pArgs); //pArgs.m_iN1 will be rewritten with skill - TRIGRET_TYPE Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig); - TRIGRET_TYPE Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgs * pArgs); //pArgs.m_iN1 will be rewritten with skill + TRIGRET_TYPE Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr pArgs); //pArgs.m_iN1 will be rewritten with skill + TRIGRET_TYPE Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr pArgs); //pArgs.m_iN1 will be rewritten with skill bool Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ); bool Skill_Tracking( CUID uidTarg, DIR_TYPE & dirPrv, int iDistMax = INT16_MAX ); @@ -1114,7 +1112,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; inline int GetAttackersCount() { return (int)m_lastAttackers.size(); } - bool Attacker_Add(CChar * pChar, int threat = 0); + bool Attacker_Add(CChar * pChar, int iThreat = 0); CChar * Attacker_GetLast() const; bool Attacker_Delete(std::vector::iterator &itAttacker, bool fForced = false, ATTACKER_CLEAR_TYPE type = ATTACKER_CLEAR_FORCED); bool Attacker_Delete(int attackerIndex, bool fForced = false, ATTACKER_CLEAR_TYPE type = ATTACKER_CLEAR_FORCED); diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index b2824fa8e..ef361f0e5 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -2,6 +2,7 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CUOInstall.h" #include "../../network/CClientIterator.h" #include "../../network/send.h" @@ -154,16 +155,17 @@ void CChar::Jail( CTextConsole * pSrc, bool fSet, int iCell ) { ADDTOCALLSTACK("CChar::Jail"); - CScriptTriggerArgs Args( fSet? 1 : 0, (int64)(iCell), (int64)(0)); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(fSet, (int64)iCell, 0, nullptr); + + if ( IsTrigUsed(TRIGGER_JAILED) ) + { + if ( OnTrigger( CTRIG_Jailed, pScriptArgs, pSrc ) == TRIGRET_RET_TRUE ) + return; + } if ( fSet ) // set the jailed flag. { - if ( IsTrigUsed(TRIGGER_JAILED) ) - { - if ( OnTrigger( CTRIG_Jailed, pSrc, &Args ) == TRIGRET_RET_TRUE ) - return; - } - if ( m_pPlayer ) // allow setting of this to offline chars. { CAccount *pAccount = m_pPlayer->GetAccount(); @@ -191,12 +193,6 @@ void CChar::Jail( CTextConsole * pSrc, bool fSet, int iCell ) } else // forgive. { - if ( IsTrigUsed(TRIGGER_JAILED) ) - { - if ( OnTrigger( CTRIG_Jailed, pSrc, &Args ) == TRIGRET_RET_TRUE ) - return; - } - if ( IsClientActive()) { if ( ! m_pClient->IsPriv( PRIV_JAILED )) @@ -225,9 +221,9 @@ void CChar::AddGoldToPack( int iAmount, CItemContainer * pPack, bool fForceNoSta if ( pPack == nullptr ) pPack = GetPackSafe(); - if (iAmount > 25000000) + if (iAmount > 25'000'000) { - iAmount = 25000000; + iAmount = 25'000'000; g_Log.EventWarn("The amount of the new Gold to create is too big. Limited to 25.000.000.\n"); } word iMax = 0; @@ -286,10 +282,9 @@ void CChar::LayerAdd( CItem * pItem, LAYER_TYPE layer ) { if ((IsTrigUsed(TRIGGER_MEMORYEQUIP)) || (IsTrigUsed(TRIGGER_ITEMMEMORYEQUIP))) { - //CScriptTriggerArgs pArgs; - CScriptTriggerArgs pArgs(pItem); // added "argo" argument - pArgs.m_iN1 = layer; - if (pItem->OnTrigger(ITRIG_MemoryEquip, this, &pArgs) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(layer, 0, 0, pItem); // added "argo" argument + if (pItem->OnTrigger(ITRIG_MemoryEquip, pScriptArgs, this) == TRIGRET_RET_TRUE) { pItem->Delete(); return; @@ -413,7 +408,7 @@ void CChar::OnRemoveObj( CSObjContRec* pObRec ) // Override this = called when r if (( IsTrigUsed(TRIGGER_UNEQUIP) ) || ( IsTrigUsed(TRIGGER_ITEMUNEQUIP) )) { if ( layer != LAYER_DRAGGING && ! g_Serv.IsLoading()) - pItem->OnTrigger( ITRIG_UNEQUIP, this ); + pItem->OnTrigger( ITRIG_UNEQUIP, CScriptTriggerArgsPtr{}, this ); } CContainer::OnRemoveObj( pObRec ); @@ -2992,8 +2987,9 @@ int CChar::ItemPickup(CItem * pItem, word amount) { if (( IsTrigUsed(CItem::sm_szTrigName[trigger]) ) || ( IsTrigUsed(sm_szTrigName[(CTRIG_itemAfterClick - 1) + trigger]) )) //ITRIG_PICKUP_GROUND, ITRIG_PICKUP_PACK { - CScriptTriggerArgs Args( amount ); - if ( pItem->OnTrigger( trigger, this, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(amount, 0, 0, nullptr); + if ( pItem->OnTrigger( trigger, pScriptArgs, this) == TRIGRET_RET_TRUE ) return -1; } if (( trigger == ITRIG_PICKUP_PACK ) && (( IsTrigUsed(TRIGGER_PICKUP_SELF) ) || ( IsTrigUsed(TRIGGER_ITEMPICKUP_SELF) ))) @@ -3001,14 +2997,14 @@ int CChar::ItemPickup(CItem * pItem, word amount) CItem * pContItem = dynamic_cast ( pItem->GetContainer() ); if ( pContItem ) { - CScriptTriggerArgs Args1(pItem); - if ( pContItem->OnTrigger(ITRIG_PICKUP_SELF, this, &Args1) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pItem); + if ( pContItem->OnTrigger(ITRIG_PICKUP_SELF, pScriptArgs, this) == TRIGRET_RET_TRUE ) return -1; } } } - if ( amount < iAmountMax && pItem->Item_GetDef()->IsStackableType() && pItem->CanSendAmount() ) { // Create an leftover item when pick up only part of the stack @@ -3018,8 +3014,9 @@ int CChar::ItemPickup(CItem * pItem, word amount) if (IsTrigUsed(TRIGGER_PICKUP_STACK) || IsTrigUsed(TRIGGER_ITEMPICKUP_STACK)) { - CScriptTriggerArgs Args2(pItemNew); - if (pItem->OnTrigger(ITRIG_PICKUP_STACK, this, &Args2) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pItemNew); + if (pItem->OnTrigger(ITRIG_PICKUP_STACK, pScriptArgs, this) == TRIGRET_RET_TRUE) return false; } } @@ -3096,8 +3093,9 @@ bool CChar::ItemBounce( CItem * pItem, bool fDisplayMsg ) fCanAddToPack = true; if (IsTrigUsed(TRIGGER_DROPON_ITEM)) { - CScriptTriggerArgs Args(pPack); - pItem->OnTrigger(ITRIG_DROPON_ITEM, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pPack); + pItem->OnTrigger(ITRIG_DROPON_ITEM, pScriptArgs, this); if (pItem->IsDeleted()) // the trigger had deleted the item return false; @@ -3106,11 +3104,15 @@ bool CChar::ItemBounce( CItem * pItem, bool fDisplayMsg ) if (IsTrigUsed(TRIGGER_DROPON_SELF) || IsTrigUsed(TRIGGER_ITEMDROPON_SELF)) { const CItem* pPrevCont = dynamic_cast(pItem->GetContainer()); - CScriptTriggerArgs Args(pItem); - const TRIGRET_TYPE ret = pPack->OnTrigger(ITRIG_DROPON_SELF, this, &Args); - if (pItem->IsDeleted()) // the trigger had deleted the item + + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pItem); + const TRIGRET_TYPE ret = pPack->OnTrigger(ITRIG_DROPON_SELF, pScriptArgs, this); + + if (pItem->IsDeleted()) // the trigger had deleted the item return false; - if (ret == TRIGRET_RET_TRUE) + + if (ret == TRIGRET_RET_TRUE) { fCanAddToPack = false; const CItem* pCont = dynamic_cast(pItem->GetContainer()); @@ -3159,19 +3161,19 @@ bool CChar::ItemBounce( CItem * pItem, bool fDisplayMsg ) TRIGRET_TYPE ttResult = TRIGRET_RET_DEFAULT; if (IsTrigUsed(TRIGGER_DROPON_GROUND) || IsTrigUsed(TRIGGER_ITEMDROPON_GROUND)) { - CScriptTriggerArgs args; - args.m_iN1 = iDecayTime / MSECS_PER_TENTH; // ARGN1 = Decay time for the dropped item (in tenths of second) - args.m_iN2 = 1; - args.m_s1 = ptDrop.WriteUsed(); - ttResult = pItem->OnTrigger(ITRIG_DROPON_GROUND, this, &args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = iDecayTime / MSECS_PER_TENTH; // ARGN1 = Decay time for the dropped item (in tenths of second) + pScriptArgs->m_iN2 = 1; + pScriptArgs->m_s1 = ptDrop.WriteUsed(); + ttResult = pItem->OnTrigger(ITRIG_DROPON_GROUND, pScriptArgs, this); if (IsDeleted()) return false; - iDecayTime = args.m_iN1 * MSECS_PER_TENTH; + iDecayTime = pScriptArgs->m_iN1 * MSECS_PER_TENTH; // Warning: here we ignore the read-onlyness of CSString's buffer only because we know that CPointMap constructor won't write past the end, but only replace some characters with '\0'. It's not worth it to build another string just for that. - tchar* ptcArgs = const_cast(args.m_s1.GetBuffer()); + tchar* ptcArgs = const_cast(pScriptArgs->m_s1.GetBuffer()); const CPointMap ptDropNew(ptcArgs); if (!ptDropNew.IsValidPoint()) g_Log.EventError("Trying to override item drop P with an invalid P. Using the original one.\n"); @@ -3307,9 +3309,9 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) if (IsTrigUsed(TRIGGER_EQUIPTEST) || IsTrigUsed(TRIGGER_ITEMEQUIPTEST)) { // Swap the layer for real one, because if we don't use dclick for equip, real layer gets rewritten with LAYER_DRAGGING. - pItem->SetEquipLayer(layer); + pItem->SetEquipLayer(layer); - if (pItem->OnTrigger(ITRIG_EQUIPTEST, this) == TRIGRET_RET_TRUE) + if (pItem->OnTrigger(ITRIG_EQUIPTEST, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE) { // Reset layer to the original value, that item doesn't get misplaced. pItem->SetEquipLayer(requestedLayer); @@ -3344,7 +3346,7 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) if (( IsTrigUsed(TRIGGER_EQUIP) ) || ( IsTrigUsed(TRIGGER_ITEMEQUIP) )) { - if ( pItem->OnTrigger(ITRIG_EQUIP, this) == TRIGRET_RET_TRUE ) + if ( pItem->OnTrigger(ITRIG_EQUIP, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE ) return false; } @@ -3458,20 +3460,21 @@ void CChar::EatAnim(CItem* pItem, ushort uiQty) ushort uiStatsLimit = 0; if (IsTrigUsed(TRIGGER_EAT)) { - CScriptTriggerArgs Args(uiStatsLimit); - Args.m_VarsLocal.SetNumNew("Hits", uiHits); - Args.m_VarsLocal.SetNumNew("Mana", uiMana); - Args.m_VarsLocal.SetNumNew("Stam", uiStam); - Args.m_VarsLocal.SetNumNew("Food", uiFood); - Args.m_pO1 = pItem; - if ( OnTrigger(CTRIG_Eat, this, &Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = uiStatsLimit; + pScriptArgs->m_VarsLocal.SetNumNew("Hits", uiHits); + pScriptArgs->m_VarsLocal.SetNumNew("Mana", uiMana); + pScriptArgs->m_VarsLocal.SetNumNew("Stam", uiStam); + pScriptArgs->m_VarsLocal.SetNumNew("Food", uiFood); + pScriptArgs->m_pO1 = pItem; + if ( OnTrigger(CTRIG_Eat, pScriptArgs, this) == TRIGRET_RET_TRUE ) return; - uiHits = (ushort)(Args.m_VarsLocal.GetKeyNum("Hits")) + Stat_GetVal(STAT_STR); - uiMana = (ushort)(Args.m_VarsLocal.GetKeyNum("Mana")) + Stat_GetVal(STAT_INT); - uiStam = (ushort)(Args.m_VarsLocal.GetKeyNum("Stam")) + Stat_GetVal(STAT_DEX); - uiFood = (ushort)(Args.m_VarsLocal.GetKeyNum("Food")) + Stat_GetVal(STAT_FOOD); - uiStatsLimit = (ushort)(Args.m_iN1); + uiHits = (ushort)(pScriptArgs->m_VarsLocal.GetKeyNum("Hits")) + Stat_GetVal(STAT_STR); + uiMana = (ushort)(pScriptArgs->m_VarsLocal.GetKeyNum("Mana")) + Stat_GetVal(STAT_INT); + uiStam = (ushort)(pScriptArgs->m_VarsLocal.GetKeyNum("Stam")) + Stat_GetVal(STAT_DEX); + uiFood = (ushort)(pScriptArgs->m_VarsLocal.GetKeyNum("Food")) + Stat_GetVal(STAT_FOOD); + uiStatsLimit = (ushort)(pScriptArgs->m_iN1); } if ( uiHits ) @@ -3497,7 +3500,7 @@ bool CChar::Reveal( uint64 iFlags ) if (IsTrigUsed(TRIGGER_REVEAL)) { - if (OnTrigger(CTRIG_Reveal, this, nullptr) == TRIGRET_RET_TRUE) + if (OnTrigger(CTRIG_Reveal, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE) return false; } @@ -3974,12 +3977,13 @@ bool CChar::Horse_Mount(CChar *pHorse) if ( IsTrigUsed(TRIGGER_MOUNT) ) { - CScriptTriggerArgs Args(pHorse); - Args.m_iN1 = memoryId; - if ( OnTrigger(CTRIG_Mount, this, &Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pHorse); + pScriptArgs->m_iN1 = memoryId; + if ( OnTrigger(CTRIG_Mount, pScriptArgs, this) == TRIGRET_RET_TRUE ) return false; else - memoryId = ITEMID_TYPE(Args.m_iN1);//(ITEMID_TYPE) Args.m_iN1; + memoryId = ITEMID_TYPE(pScriptArgs->m_iN1);//(ITEMID_TYPE) Args.m_iN1; } // Create the figurine for the horse (Memory item equiped on layer 25 of the player) @@ -4018,8 +4022,9 @@ bool CChar::Horse_UnMount() CChar * pPet = pMountItem->m_itFigurine.m_UID.CharFind(); if (pPet && IsTrigUsed(TRIGGER_DISMOUNT) && pPet->IsDisconnected() && !pPet->IsDeleted() ) // valid horse for trigger { - CScriptTriggerArgs Args(pPet); - if ( OnTrigger(CTRIG_Dismount, this, &Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pPet); + if ( OnTrigger(CTRIG_Dismount, pScriptArgs, this) == TRIGRET_RET_TRUE ) return false; } @@ -4112,21 +4117,21 @@ bool CChar::OnTickEquip( CItem * pItem ) if ( ! m_pPlayer || m_pPlayer->m_wMurders <= 0 ) return false; - CScriptTriggerArgs args; - args.m_iN1 = m_pPlayer->m_wMurders - 1; - args.m_iN2 = g_Cfg.m_iMurderDecayTime; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = m_pPlayer->m_wMurders - 1; + pScriptArgs->m_iN2 = g_Cfg.m_iMurderDecayTime; if ( IsTrigUsed(TRIGGER_MURDERDECAY) ) { - OnTrigger(CTRIG_MurderDecay, this, &args); - if ( args.m_iN1 < 0 ) args.m_iN1 = 0; - if ( args.m_iN2 < 1 ) args.m_iN2 = g_Cfg.m_iMurderDecayTime; + OnTrigger(CTRIG_MurderDecay, pScriptArgs, this); + if ( pScriptArgs->m_iN1 < 0 ) pScriptArgs->m_iN1 = 0; + if ( pScriptArgs->m_iN2 < 1 ) pScriptArgs->m_iN2 = g_Cfg.m_iMurderDecayTime; } - m_pPlayer->m_wMurders = (word)(args.m_iN1); + m_pPlayer->m_wMurders = (word)(pScriptArgs->m_iN1); NotoSave_Update(); if ( m_pPlayer->m_wMurders == 0 ) return false; - pItem->SetTimeout(args.m_iN2); // update it's decay time. + pItem->SetTimeout(pScriptArgs->m_iN2); // update it's decay time. return true; } @@ -4314,7 +4319,7 @@ CChar::DeathRequestResult CChar::Death() if ( IsTrigUsed(TRIGGER_DEATH) ) { - if ( OnTrigger(CTRIG_Death, this) == TRIGRET_RET_TRUE ) + if ( OnTrigger(CTRIG_Death, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE ) return DeathRequestResult::Aborted; } //Dismount now. Later is may be too late and cause problems @@ -4358,10 +4363,9 @@ CChar::DeathRequestResult CChar::Death() { if ( IsTrigUsed(TRIGGER_KILL) ) { - CScriptTriggerArgs args(this); - args.m_iN1 = GetAttackersCount(); - args.m_pO1 = this; - if ( pKiller->OnTrigger(CTRIG_Kill, pKiller, &args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(GetAttackersCount(), 0, 0, this); + if ( pKiller->OnTrigger(CTRIG_Kill, pScriptArgs, pKiller) == TRIGRET_RET_TRUE ) continue; } @@ -4409,8 +4413,9 @@ CChar::DeathRequestResult CChar::Death() { if ( IsTrigUsed(TRIGGER_DEATHCORPSE) ) { - CScriptTriggerArgs Args(pCorpse); - OnTrigger(CTRIG_DeathCorpse, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pCorpse); + OnTrigger(CTRIG_DeathCorpse, pScriptArgs, this); } } m_lastAttackers.clear(); // clear list of attackers @@ -4630,19 +4635,21 @@ bool CChar::ShoveCharAtPosition(CPointMap const& ptDst, ushort *uiStaminaRequire { if (IsTrigUsed(TRIGGER_PERSONALSPACE)) { - CScriptTriggerArgs Args(uiLocalStamReq); - iRet = pChar->OnTrigger(CTRIG_PersonalSpace, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = uiLocalStamReq; + iRet = pChar->OnTrigger(CTRIG_PersonalSpace, pScriptArgs, this); if (iRet == TRIGRET_RET_TRUE) goto set_and_return_false; - uiLocalStamReq = (ushort)(Args.m_iN1); + uiLocalStamReq = (ushort)(pScriptArgs->m_iN1); } if (IsTrigUsed(TRIGGER_CHARSHOVE)) { - CScriptTriggerArgs Args(uiLocalStamReq); - iRet = this->OnTrigger(CTRIG_charShove, pChar, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = uiLocalStamReq; + iRet = this->OnTrigger(CTRIG_charShove, pScriptArgs, pChar); if (iRet == TRIGRET_RET_TRUE) goto set_and_return_false; - uiLocalStamReq = (ushort)(Args.m_iN1); + uiLocalStamReq = (ushort)(pScriptArgs->m_iN1); } } @@ -4767,8 +4774,9 @@ CRegion * CChar::CanMoveWalkTo( CPointMap & ptDst, bool fCheckChars, bool fCheck //char is falling if ( IsTrigUsed(TRIGGER_FALLING) ) { - CScriptTriggerArgs Args(ptDst.m_x, ptDst.m_y, ptDst.m_z); - OnTrigger(CTRIG_Falling, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(ptDst.m_x, ptDst.m_y, ptDst.m_z, nullptr); + OnTrigger(CTRIG_Falling, pScriptArgs, this); } } @@ -4833,7 +4841,7 @@ void CChar::CheckRevealOnMove() return; if ( IsTrigUsed(TRIGGER_STEPSTEALTH) ) - OnTrigger(CTRIG_StepStealth, this); + OnTrigger(CTRIG_StepStealth, CScriptTriggerArgsPtr{}, this); if (g_Cfg.m_iRevealFlags & REVEALF_ONHORSE && IsStatFlag(STATF_ONHORSE)) Reveal(); @@ -4936,8 +4944,9 @@ TRIGRET_TYPE CChar::CheckLocationEffects(bool fStanding) else { _uiRecursingItemStep += 1; - CScriptTriggerArgs Args(fStanding ? 1 : 0); - TRIGRET_TYPE iRet = pItem->OnTrigger(ITRIG_STEP, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = fStanding ? 1 : 0; + TRIGRET_TYPE iRet = pItem->OnTrigger(ITRIG_STEP, pScriptArgs, this); _uiRecursingItemStep -= 1; if (iRet == TRIGRET_RET_TRUE) // block walk { @@ -5113,8 +5122,9 @@ bool CChar::MoveToRegion( CRegionWorld * pNewArea, bool fAllowReject ) if ( IsTrigUsed(TRIGGER_REGIONLEAVE) ) { - CScriptTriggerArgs Args(m_pArea); - if ( OnTrigger(CTRIG_RegionLeave, this, & Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(m_pArea); + if ( OnTrigger(CTRIG_RegionLeave, pScriptArgs, this) == TRIGRET_RET_TRUE ) { if ( pNewArea && fAllowReject ) return false; @@ -5174,8 +5184,9 @@ bool CChar::MoveToRegion( CRegionWorld * pNewArea, bool fAllowReject ) } if ( IsTrigUsed(TRIGGER_REGIONENTER) ) { - CScriptTriggerArgs Args(pNewArea); - if ( OnTrigger(CTRIG_RegionEnter, this, & Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pNewArea); + if ( OnTrigger(CTRIG_RegionEnter, pScriptArgs, this) == TRIGRET_RET_TRUE ) { if ( m_pArea && fAllowReject ) return false; @@ -5219,8 +5230,9 @@ bool CChar::MoveToRoom( CRegion * pNewRoom, bool fAllowReject) if ( IsTrigUsed(TRIGGER_REGIONLEAVE) ) { - CScriptTriggerArgs Args(m_pRoom); - if ( OnTrigger(CTRIG_RegionLeave, this, & Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(m_pRoom); + if ( OnTrigger(CTRIG_RegionLeave, pScriptArgs, this) == TRIGRET_RET_TRUE ) { if (fAllowReject ) return false; @@ -5241,8 +5253,9 @@ bool CChar::MoveToRoom( CRegion * pNewRoom, bool fAllowReject) } if ( IsTrigUsed(TRIGGER_REGIONENTER) ) { - CScriptTriggerArgs Args(pNewRoom); - if ( OnTrigger(CTRIG_RegionEnter, this, & Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pNewRoom); + if ( OnTrigger(CTRIG_RegionEnter, pScriptArgs, this) == TRIGRET_RET_TRUE ) { if (fAllowReject ) return false; @@ -5314,8 +5327,13 @@ bool CChar::MoveToChar(const CPointMap& pt, bool fStanding, bool fCheckLocationE { if ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) ) { - CScriptTriggerArgs Args(ptOld.m_x, ptOld.m_y, ((uchar)ptOld.m_z << 16) | ptOld.m_map); - OnTrigger(CTRIG_EnvironChange, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init( + ptOld.m_x, + ptOld.m_y, + ((uchar)ptOld.m_z << 16) | ptOld.m_map, + nullptr); + OnTrigger(CTRIG_EnvironChange, pScriptArgs, this); } } @@ -5506,13 +5524,16 @@ void CChar::SetTriggerActive(lpctstr trig) // 4) CHARDEF // 5) EVENTSPET/EVENTSPLAYER set on .ini file // RETURNS = TRIGRET_TYPE (in cscriptobj.h) -TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) +TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) { ADDTOCALLSTACK("CChar::OnTrigger"); if ( IsTriggerActive( pszTrigName ) ) //This should protect any char trigger from infinite loop return TRIGRET_RET_DEFAULT; + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + if ( !pSrc ) pSrc = &g_Serv; @@ -5540,7 +5561,7 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript EXC_SET_BLOCK("chardef"); const CUID uidOldAct = pChar->m_Act_UID; pChar->m_Act_UID = GetUID(); - iRet = pChar->OnTrigger(ptcCharTrigName, pSrc, pArgs); + iRet = pChar->OnTrigger(ptcCharTrigName, pScriptArgs, pSrc); pChar->m_Act_UID = uidOldAct; if (iRet == TRIGRET_RET_TRUE) goto stopandret; // Block further action. @@ -5571,7 +5592,7 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript continue; executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) goto stopandret; @@ -5599,7 +5620,7 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript continue; executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) goto stopandret; } @@ -5614,7 +5635,7 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript CResourceLock s; if ( pCharDef->ResourceLock(s) ) { - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if (( iRet != TRIGRET_RET_FALSE ) && ( iRet != TRIGRET_RET_DEFAULT )) goto stopandret; } @@ -5636,7 +5657,7 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript continue; executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) goto stopandret; } @@ -5658,7 +5679,7 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript continue; executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) goto stopandret; } @@ -5676,10 +5697,10 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript return iRet; } -TRIGRET_TYPE CChar::OnTrigger( CTRIG_TYPE trigger, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) +TRIGRET_TYPE CChar::OnTrigger( CTRIG_TYPE trigger, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) { ASSERT( (trigger > CTRIG_AAAUNUSED) && (trigger < CTRIG_QTY) ); - return OnTrigger( CChar::sm_szTrigName[trigger], pSrc, pArgs ); + return OnTrigger( CChar::sm_szTrigName[trigger], pScriptArgs, pSrc); } // process m_fStatusUpdate flags @@ -6036,7 +6057,12 @@ bool CChar::OnTickPeriodic() int64 CChar::PayGold(CChar * pCharSrc, int64 iGold, CItem * pGold, ePayGold iReason) { ADDTOCALLSTACK("CChar::PayGold"); - CScriptTriggerArgs Args(iGold,iReason,pGold); - OnTrigger(CTRIG_PayGold,pCharSrc,&Args); - return Args.m_iN1; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init( + iGold, + iReason, + 0, + pGold); + OnTrigger(CTRIG_PayGold, pScriptArgs, pCharSrc); + return pScriptArgs->m_iN1; } diff --git a/src/game/chars/CCharAttacker.cpp b/src/game/chars/CCharAttacker.cpp index d899bde75..37846249b 100644 --- a/src/game/chars/CCharAttacker.cpp +++ b/src/game/chars/CCharAttacker.cpp @@ -1,12 +1,13 @@ // Actions specific to an NPC. #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../clients/CClient.h" #include "../triggers.h" #include "CChar.h" // Add some enemy to my Attacker list -bool CChar::Attacker_Add(CChar * pChar, int threat) +bool CChar::Attacker_Add(CChar * pChar, int iThreat) { ADDTOCALLSTACK("CChar::Attacker_Add"); const dword uid = pChar->GetUID().GetObjUID(); @@ -21,7 +22,7 @@ bool CChar::Attacker_Add(CChar * pChar, int threat) } else if (IsTrigUsed(TRIGGER_COMBATSTART)) { - TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatStart, pChar, nullptr); + TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatStart, CScriptTriggerArgsPtr{}, pChar); if (tRet == TRIGRET_RET_TRUE) return false; else @@ -31,30 +32,30 @@ bool CChar::Attacker_Add(CChar * pChar, int threat) } } - CScriptTriggerArgs Args; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); bool fIgnore = false; - Args.m_iN1 = threat; - Args.m_iN2 = fIgnore; + pScriptArgs->m_iN1 = iThreat; + pScriptArgs->m_iN2 = fIgnore; if (IsTrigUsed(TRIGGER_COMBATADD)) { - TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatAdd, pChar, &Args); + TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatAdd, pScriptArgs, pChar); if (tRet == TRIGRET_RET_TRUE) return false; - threat = (int)Args.m_iN1; - fIgnore = (Args.m_iN2 != 0); + iThreat = (int)pScriptArgs->m_iN1; + fIgnore = (pScriptArgs->m_iN2 != 0); } - LastAttackers attacker; - attacker.amountDone = 0; - attacker.charUID = uid; - attacker.elapsed = 0; - attacker.threat = (m_pPlayer) ? 0 : threat; - attacker.ignore = fIgnore; - m_lastAttackers.emplace_back(std::move(attacker)); + m_lastAttackers.emplace_back(LastAttackers{ + .elapsed = 0, + .charUID = uid, + .amountDone = 0, + .threat = (m_pPlayer) ? 0 : iThreat, + .ignore = fIgnore + }); // Record the start of the fight. Memory_Fight_Start(pChar); - if (!attacker.ignore) + if (!fIgnore) { tchar *z = Str_GetTemp(); CClient *pClient = pChar->GetClientActive(); @@ -256,7 +257,7 @@ void CChar::Attacker_Clear() { if (m_lastAttackers.empty() || !Fight_IsActive() || !m_Fight_Targ_UID.IsValidUID() || !m_Fight_Targ_UID.CharFind()) { - OnTrigger(CTRIG_CombatEnd, this, nullptr); + OnTrigger(CTRIG_CombatEnd, CScriptTriggerArgsPtr{}, this); } } @@ -323,10 +324,11 @@ bool CChar::Attacker_Delete(std::vector::iterator &itAttacker, bo { if (IsTrigUsed(TRIGGER_COMBATDELETE)) { + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); CScriptTriggerArgs Args; - Args.m_iN1 = fForced; - Args.m_iN2 = (int)type; - TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatDelete, pChar, &Args); + pScriptArgs->m_iN1 = fForced; + pScriptArgs->m_iN2 = (int)type; + TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatDelete, pScriptArgs, pChar); if ((tRet == TRIGRET_RET_TRUE) && !fForced) return false; } diff --git a/src/game/chars/CCharFight.cpp b/src/game/chars/CCharFight.cpp index 6e7164b23..b76aa4381 100644 --- a/src/game/chars/CCharFight.cpp +++ b/src/game/chars/CCharFight.cpp @@ -3,6 +3,7 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/send.h" #include "../clients/CClient.h" #include "../components/CCPropsChar.h" @@ -44,11 +45,11 @@ void CChar::OnNoticeCrime( CChar * pCriminal, CChar * pCharMark ) bool fMakeCriminal = false; //We don't need to call guards automatically in default. if (IsTrigUsed(TRIGGER_SEECRIME)) { - CScriptTriggerArgs Args; - Args.m_iN1 = fMakeCriminal; - Args.m_pO1 = pCharMark; - OnTrigger(CTRIG_SeeCrime, pCriminal, &Args); - fMakeCriminal = Args.m_iN1 ? true : false; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = fMakeCriminal; + pScriptArgs->m_pO1 = pCharMark; + OnTrigger(CTRIG_SeeCrime, pScriptArgs, pCriminal); + fMakeCriminal = pScriptArgs->m_iN1 ? true : false; } Memory_AddObjTypes(pCriminal, MEMORY_SAWCRIME); //Memory should always be added to the player. if (fMakeCriminal) //We call guards automatically if ARGN1 set to 1 (true) in trigger. @@ -139,11 +140,11 @@ bool CChar::CheckCrimeSeen( SKILL_TYPE SkillToSee, CChar * pCharMark, const CObj { if (IsTrigUsed(TRIGGER_SEESNOOP)) { - CScriptTriggerArgs Args(ptcAction); - Args.m_iN1 = SkillToSee; - Args.m_iN2 = pItem ? (dword)pItem->GetUID() : 0; // here i can modify pItem via scripts, so it isn't really const - Args.m_pO1 = pCharMark; - TRIGRET_TYPE iRet = pChar->OnTrigger(CTRIG_SeeSnoop, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = SkillToSee; + pScriptArgs->m_iN2 = pItem ? (dword)pItem->GetUID() : 0; // here i can modify pItem via scripts, so it isn't really const + pScriptArgs->m_pO1 = pCharMark; + TRIGRET_TYPE iRet = pChar->OnTrigger(CTRIG_SeeSnoop, pScriptArgs, this); if (iRet == TRIGRET_RET_TRUE) continue; @@ -254,17 +255,17 @@ bool CChar::CallGuards( CChar * pCriminal ) CResourceID rid = g_Cfg.ResourceGetIDType(RES_CHARDEF, (pVarDefGuards ? pVarDefGuards->GetValStr() : "GUARDS")); if (IsTrigUsed(TRIGGER_CALLGUARDS)) { - CScriptTriggerArgs Args(pGuard); - Args.m_iN1 = rid.GetResIndex(); - Args.m_iN2 = 0; - Args.m_VarObjs.Insert(1, pCriminal, true); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = rid.GetResIndex(); + pScriptArgs->m_iN2 = 0; + pScriptArgs->m_VarObjs.Insert(1, pCriminal, true); - if (OnTrigger(CTRIG_CallGuards, pCriminal, &Args) == TRIGRET_RET_TRUE) + if (OnTrigger(CTRIG_CallGuards, pScriptArgs, pCriminal) == TRIGRET_RET_TRUE) return false; - if ( (uint)Args.m_iN1 != rid.GetResIndex()) - rid = CResourceID(RES_CHARDEF, (int)Args.m_iN1); - if (Args.m_iN2 > 0) //ARGN2: If set to 1, a new guard will be spawned regardless of whether a nearby guard is available. + if ( (uint)pScriptArgs->m_iN1 != rid.GetResIndex()) + rid = CResourceID(RES_CHARDEF, (int)pScriptArgs->m_iN1); + if (pScriptArgs->m_iN2 > 0) //ARGN2: If set to 1, a new guard will be spawned regardless of whether a nearby guard is available. pGuard = nullptr; } if (!pGuard) // spawn a new guard @@ -384,18 +385,20 @@ bool CChar::OnAttackedBy(CChar * pCharSrc, bool fCommandPet, bool fShouldReveal) // Armor layers that can be damaged on combat // PS: Hand layers (weapons/shields) are not included here -static const LAYER_TYPE sm_ArmorDamageLayers[] = { LAYER_SHOES, LAYER_PANTS, LAYER_SHIRT, LAYER_HELM, LAYER_GLOVES, LAYER_COLLAR, LAYER_HALF_APRON, LAYER_CHEST, LAYER_TUNIC, LAYER_ARMS, LAYER_CAPE, LAYER_ROBE, LAYER_SKIRT, LAYER_LEGS }; +static constexpr LAYER_TYPE sm_ArmorDamageLayers[] = { + LAYER_SHOES, LAYER_PANTS, LAYER_SHIRT, LAYER_HELM, LAYER_GLOVES, LAYER_COLLAR, + LAYER_HALF_APRON, LAYER_CHEST, LAYER_TUNIC, LAYER_ARMS, LAYER_CAPE, LAYER_ROBE, LAYER_SKIRT, LAYER_LEGS }; // Layers covering the armor zone -static const LAYER_TYPE sm_ArmorLayerHead[] = { LAYER_HELM }; // ARMOR_HEAD -static const LAYER_TYPE sm_ArmorLayerNeck[] = { LAYER_COLLAR }; // ARMOR_NECK -static const LAYER_TYPE sm_ArmorLayerBack[] = { LAYER_SHIRT, LAYER_CHEST, LAYER_TUNIC, LAYER_CAPE, LAYER_ROBE }; // ARMOR_BACK -static const LAYER_TYPE sm_ArmorLayerChest[] = { LAYER_SHIRT, LAYER_CHEST, LAYER_TUNIC, LAYER_ROBE }; // ARMOR_CHEST -static const LAYER_TYPE sm_ArmorLayerArms[] = { LAYER_ARMS, LAYER_CAPE, LAYER_ROBE }; // ARMOR_ARMS -static const LAYER_TYPE sm_ArmorLayerHands[] = { LAYER_GLOVES }; // ARMOR_HANDS -static const LAYER_TYPE sm_ArmorLayerLegs[] = { LAYER_PANTS, LAYER_SKIRT, LAYER_HALF_APRON, LAYER_ROBE, LAYER_LEGS }; // ARMOR_LEGS -static const LAYER_TYPE sm_ArmorLayerFeet[] = { LAYER_SHOES, LAYER_LEGS }; // ARMOR_FEET -static const LAYER_TYPE sm_ArmorLayerShield[] = { LAYER_HAND2 }; +static constexpr LAYER_TYPE sm_ArmorLayerHead[] = { LAYER_HELM }; // ARMOR_HEAD +static constexpr LAYER_TYPE sm_ArmorLayerNeck[] = { LAYER_COLLAR }; // ARMOR_NECK +static constexpr LAYER_TYPE sm_ArmorLayerBack[] = { LAYER_SHIRT, LAYER_CHEST, LAYER_TUNIC, LAYER_CAPE, LAYER_ROBE }; // ARMOR_BACK +static constexpr LAYER_TYPE sm_ArmorLayerChest[] = { LAYER_SHIRT, LAYER_CHEST, LAYER_TUNIC, LAYER_ROBE }; // ARMOR_CHEST +static constexpr LAYER_TYPE sm_ArmorLayerArms[] = { LAYER_ARMS, LAYER_CAPE, LAYER_ROBE }; // ARMOR_ARMS +static constexpr LAYER_TYPE sm_ArmorLayerHands[] = { LAYER_GLOVES }; // ARMOR_HANDS +static constexpr LAYER_TYPE sm_ArmorLayerLegs[] = { LAYER_PANTS, LAYER_SKIRT, LAYER_HALF_APRON, LAYER_ROBE, LAYER_LEGS }; // ARMOR_LEGS +static constexpr LAYER_TYPE sm_ArmorLayerFeet[] = { LAYER_SHOES, LAYER_LEGS }; // ARMOR_FEET +static constexpr LAYER_TYPE sm_ArmorLayerShield[] = { LAYER_HAND2 }; struct CArmorLayerType { @@ -403,7 +406,7 @@ struct CArmorLayerType const LAYER_TYPE * m_pLayers; }; -static const CArmorLayerType sm_ArmorLayers[ARMOR_QTY] = +static constexpr CArmorLayerType sm_ArmorLayers[ARMOR_QTY] = { { 15, sm_ArmorLayerHead }, // ARMOR_HEAD, 15% of the armour value will be applied. { 7, sm_ArmorLayerNeck }, // ARMOR_NECK, 7% of the armour value will be applied. @@ -744,48 +747,51 @@ int CChar::OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uiType, int iDmgPhy } } - CScriptTriggerArgs Args( iDmg, uiType, (int64)(0) ); - Args.m_VarsLocal.SetNum("ItemDamageLayer", sm_ArmorDamageLayers[(size_t)g_Rand.Get16ValFast(ARRAY_COUNT(sm_ArmorDamageLayers))]); - Args.m_VarsLocal.SetNum("ItemDamageChance", 25); - Args.m_VarsLocal.SetNum("Spell", (int)spell); - - if ( fElemental ) - { - Args.m_VarsLocal.SetNum("DamagePercentPhysical", iDmgPhysical); - Args.m_VarsLocal.SetNum("DamagePercentFire", iDmgFire); - Args.m_VarsLocal.SetNum("DamagePercentCold", iDmgCold); - Args.m_VarsLocal.SetNum("DamagePercentPoison", iDmgPoison); - Args.m_VarsLocal.SetNum("DamagePercentEnergy", iDmgEnergy); - } + { + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_VarsLocal.SetNum("ItemDamageLayer", sm_ArmorDamageLayers[(size_t)g_Rand.Get16ValFast(ARRAY_COUNT(sm_ArmorDamageLayers))]); + pScriptArgs->m_VarsLocal.SetNum("ItemDamageChance", 25); + pScriptArgs->m_VarsLocal.SetNum("Spell", (int)spell); - CItem* pItemHit = nullptr; - if ( IsTrigUsed(TRIGGER_GETHIT) ) - { - if ( OnTrigger( CTRIG_GetHit, pSrc, &Args ) == TRIGRET_RET_TRUE ) - return 0; - iDmg = (int)(Args.m_iN1); - uiType = (DAMAGE_TYPE)(Args.m_iN2); + if ( fElemental ) + { + pScriptArgs->m_VarsLocal.SetNum("DamagePercentPhysical", iDmgPhysical); + pScriptArgs->m_VarsLocal.SetNum("DamagePercentFire", iDmgFire); + pScriptArgs->m_VarsLocal.SetNum("DamagePercentCold", iDmgCold); + pScriptArgs->m_VarsLocal.SetNum("DamagePercentPoison", iDmgPoison); + pScriptArgs->m_VarsLocal.SetNum("DamagePercentEnergy", iDmgEnergy); + } - LAYER_TYPE iHitLayer = (LAYER_TYPE)(Args.m_VarsLocal.GetKeyNum("ItemDamageLayer")); - pItemHit = LayerFind(iHitLayer); - if (pItemHit) + CItem* pItemHit = nullptr; + if ( IsTrigUsed(TRIGGER_GETHIT) ) { - Args.m_pO1 = this; - // "ItemDamageLayer" will only be readable. - if (pItemHit->OnTrigger(ITRIG_GetHit, pSrc, &Args) == TRIGRET_RET_TRUE) + + if ( OnTrigger( CTRIG_GetHit, pScriptArgs, pSrc ) == TRIGRET_RET_TRUE ) return 0; - iDmg = (int)(Args.m_iN1); //Update damage amount and type again after @Hit trigger under item. - uiType = (DAMAGE_TYPE)(Args.m_iN2); - // We don't need to update iHitLayer as it's already called on item + iDmg = (int)(pScriptArgs->m_iN1); + uiType = (DAMAGE_TYPE)(pScriptArgs->m_iN2); + + LAYER_TYPE iHitLayer = (LAYER_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("ItemDamageLayer")); + pItemHit = LayerFind(iHitLayer); + if (pItemHit) + { + pScriptArgs->m_pO1 = this; + // "ItemDamageLayer" will only be readable. + if (pItemHit->OnTrigger(ITRIG_GetHit, pScriptArgs, pSrc) == TRIGRET_RET_TRUE) + return 0; + iDmg = (int)(pScriptArgs->m_iN1); //Update damage amount and type again after @Hit trigger under item. + uiType = (DAMAGE_TYPE)(pScriptArgs->m_iN2); + // We don't need to update iHitLayer as it's already called on item + } } - } - int iItemDamageChance = (int)(Args.m_VarsLocal.GetKeyNum("ItemDamageChance")); - if ( (iItemDamageChance > g_Rand.GetVal(100)) && !Can(CAN_C_NONHUMANOID) ) - { - if ( pItemHit ) - pItemHit->OnTakeDamage(iDmg, pSrc, uiType); - } + int iItemDamageChance = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("ItemDamageChance")); + if ( (iItemDamageChance > g_Rand.GetVal(100)) && !Can(CAN_C_NONHUMANOID) ) + { + if ( pItemHit ) + pItemHit->OnTakeDamage(iDmg, pSrc, uiType); + } + } CSpellDef* pSpellDef = nullptr; // Remove stuck/paralyze effect @@ -898,8 +904,9 @@ int CChar::OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uiType, int iDmgPhy bool fInterrupt = true; if (IsTrigUsed(TRIGGER_SPELLINTERRUPT)) { - CScriptTriggerArgs ArgsInterrupt(m_atMagery.m_iSpell, iDisturbChance); - if (pSrc->OnTrigger(CTRIG_SpellInterrupt, this, &ArgsInterrupt) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pArgsInterrupt = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgsInterrupt->Init(m_atMagery.m_iSpell, iDisturbChance, 0, nullptr); + if (pSrc->OnTrigger(CTRIG_SpellInterrupt, pArgsInterrupt, this) == TRIGRET_RET_TRUE) fInterrupt = false; } @@ -957,21 +964,21 @@ int CChar::OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uiType, int iDmgPhy if (IsTrigUsed(TRIGGER_HITREACTIVE)) { - CScriptTriggerArgs HitReactiveArgs; - HitReactiveArgs.m_VarsLocal.SetNum("Sound", ReactiveSnd); // SOUND - HitReactiveArgs.m_VarsLocal.SetNum("EffectID", ReactiveEffectID); // EFFECTID - HitReactiveArgs.m_VarsLocal.SetNum("Damage", iReactiveDamage); // DAMAGE VALUE - HitReactiveArgs.m_VarsLocal.SetNum("ReflectDamage", iReactiveRefDam); // REFLECTED DAM - HitReactiveArgs.m_VarsLocal.SetNum("ReduceDamage", iReactiveRedDam); // REDUCED DAM - HitReactiveArgs.m_VarsLocal.SetNum("DamageType", ReactiveDamType); // DAMAGE TYPE - OnTrigger(CTRIG_HitReactive, pSrc, &HitReactiveArgs); - - ReactiveSnd = (SOUND_TYPE)HitReactiveArgs.m_VarsLocal.GetKeyNum("Sound"); // SOUND - ReactiveEffectID = (ITEMID_TYPE)HitReactiveArgs.m_VarsLocal.GetKeyNum("EffectID"); // EFFECTID - iReactiveDamage = (int)HitReactiveArgs.m_VarsLocal.GetKeyNum("Damage"); // DAMAGE VALUE - iReactiveRefDam = (int)HitReactiveArgs.m_VarsLocal.GetKeyNum("ReflectDamage"); // REFLECTED DAMAGE VALUE - iReactiveRedDam = (int)HitReactiveArgs.m_VarsLocal.GetKeyNum("ReduceDamage"); // REDUCED DAMAGE VALUE - ReactiveDamType = (DAMAGE_TYPE)HitReactiveArgs.m_VarsLocal.GetKeyNum("DamageType"); // DAMAGE TYPE + CScriptTriggerArgsPtr pHitReactiveArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pHitReactiveArgs->m_VarsLocal.SetNum("Sound", ReactiveSnd); // SOUND + pHitReactiveArgs->m_VarsLocal.SetNum("EffectID", ReactiveEffectID); // EFFECTID + pHitReactiveArgs->m_VarsLocal.SetNum("Damage", iReactiveDamage); // DAMAGE VALUE + pHitReactiveArgs->m_VarsLocal.SetNum("ReflectDamage", iReactiveRefDam); // REFLECTED DAM + pHitReactiveArgs->m_VarsLocal.SetNum("ReduceDamage", iReactiveRedDam); // REDUCED DAM + pHitReactiveArgs->m_VarsLocal.SetNum("DamageType", ReactiveDamType); // DAMAGE TYPE + OnTrigger(CTRIG_HitReactive, pHitReactiveArgs, pSrc); + + ReactiveSnd = (SOUND_TYPE)pHitReactiveArgs->m_VarsLocal.GetKeyNum("Sound"); // SOUND + ReactiveEffectID = (ITEMID_TYPE)pHitReactiveArgs->m_VarsLocal.GetKeyNum("EffectID"); // EFFECTID + iReactiveDamage = (int)pHitReactiveArgs->m_VarsLocal.GetKeyNum("Damage"); // DAMAGE VALUE + iReactiveRefDam = (int)pHitReactiveArgs->m_VarsLocal.GetKeyNum("ReflectDamage"); // REFLECTED DAMAGE VALUE + iReactiveRedDam = (int)pHitReactiveArgs->m_VarsLocal.GetKeyNum("ReduceDamage"); // REDUCED DAMAGE VALUE + ReactiveDamType = (DAMAGE_TYPE)pHitReactiveArgs->m_VarsLocal.GetKeyNum("DamageType"); // DAMAGE TYPE // should it be zero ? //if (iReactiveDamage < 1) // iReactiveDamage = 1; @@ -1422,13 +1429,13 @@ bool CChar::Fight_Attack( CChar *pCharTarg, bool fToldByMaster ) bool ignored = Attacker_GetIgnore(pTarget); if ( ((IsTrigUsed(TRIGGER_ATTACK)) || (IsTrigUsed(TRIGGER_CHARATTACK))) && m_Fight_Targ_UID != pCharTarg->GetUID() ) { - CScriptTriggerArgs Args; - Args.m_iN1 = threat; - Args.m_iN2 = (int)ignored; - if ( OnTrigger(CTRIG_Attack, pTarget, &Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = threat; + pScriptArgs->m_iN2 = (int)ignored; + if ( OnTrigger(CTRIG_Attack, pScriptArgs, pTarget) == TRIGRET_RET_TRUE ) return false; - threat = (int)Args.m_iN1; - ignored = (bool)Args.m_iN2; + threat = (int)pScriptArgs->m_iN1; + ignored = (bool)pScriptArgs->m_iN2; } Attacker_SetIgnore(pTarget, ignored); @@ -1754,19 +1761,20 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) if ( IsTrigUsed(TRIGGER_HITCHECK) ) { - CScriptTriggerArgs pArgs; - pArgs.m_iN1 = m_atFight.m_iWarSwingState; - pArgs.m_iN2 = iDmgType; - pArgs.m_VarsLocal.SetNum("Recoil_NoRange", (int)fSwingNoRange); - TRIGRET_TYPE tRet = OnTrigger(CTRIG_HitCheck, pCharTarg, &pArgs); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = m_atFight.m_iWarSwingState; + pScriptArgs->m_iN2 = iDmgType; + pScriptArgs->m_VarsLocal.SetNum("Recoil_NoRange", (int)fSwingNoRange); + + TRIGRET_TYPE tRet = OnTrigger(CTRIG_HitCheck, pScriptArgs, pCharTarg); if ( tRet == TRIGRET_RET_TRUE ) - return (WAR_SWING_TYPE)pArgs.m_iN1; + return (WAR_SWING_TYPE)pScriptArgs->m_iN1; if ( tRet == -1 ) return WAR_SWING_INVALID; - m_atFight.m_iWarSwingState = (WAR_SWING_TYPE)(pArgs.m_iN1); - iDmgType = (DAMAGE_TYPE)(pArgs.m_iN2); - fSwingNoRange = (bool)pArgs.m_VarsLocal.GetKeyNum("Recoil_NoRange"); + m_atFight.m_iWarSwingState = (WAR_SWING_TYPE)(pScriptArgs->m_iN1); + iDmgType = (DAMAGE_TYPE)(pScriptArgs->m_iN2); + fSwingNoRange = (bool)pScriptArgs->m_VarsLocal.GetKeyNum("Recoil_NoRange"); if (tRet != -2) // if @HitCheck returns -2, just continue with the hardcoded stuff { @@ -1921,15 +1929,17 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) int16& iARGN1Var = IsSetCombatFlags(COMBAT_ANIM_HIT_SMOOTH) ? m_atFight.m_iSwingAnimationDelay : m_atFight.m_iRecoilDelay; int16& iAnimDelayVar = IsSetCombatFlags(COMBAT_ANIM_HIT_SMOOTH) ? m_atFight.m_iRecoilDelay : m_atFight.m_iSwingAnimationDelay; - CScriptTriggerArgs Args(iARGN1Var, 0, pWeapon); - Args.m_VarsLocal.SetNum("Anim", m_atFight.m_iSwingAnimation); - Args.m_VarsLocal.SetNum("AnimDelay", iAnimDelayVar); - if ( OnTrigger(CTRIG_HitTry, pCharTarg, &Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iARGN1Var, 0, 0, pWeapon); + pScriptArgs->m_VarsLocal.SetNum("Anim", m_atFight.m_iSwingAnimation); + pScriptArgs->m_VarsLocal.SetNum("AnimDelay", iAnimDelayVar); + + if ( OnTrigger(CTRIG_HitTry, pScriptArgs, pCharTarg) == TRIGRET_RET_TRUE ) return WAR_SWING_READY; - m_atFight.m_iSwingAnimation = (int16)(Args.m_VarsLocal.GetKeyNum("Anim")); - iARGN1Var = (int16)(Args.m_iN1); - iAnimDelayVar = (int16)(Args.m_VarsLocal.GetKeyNum("AnimDelay")); + m_atFight.m_iSwingAnimation = (int16)(pScriptArgs->m_VarsLocal.GetKeyNum("Anim")); + iARGN1Var = (int16)(pScriptArgs->m_iN1); + iAnimDelayVar = (int16)(pScriptArgs->m_VarsLocal.GetKeyNum("AnimDelay")); //if (m_atFight.m_iSwingAnimation < (ANIM_TYPE)-1) // -1 is a valid value // m_atFight.m_iSwingAnimation = (int16)animSwingDefault; if ( m_atFight.m_iRecoilDelay < 1 ) @@ -2016,13 +2026,15 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) { if ( IsTrigUsed(TRIGGER_HITMISS) ) { - CScriptTriggerArgs Args(0, 0, pWeapon); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pWeapon; if ( pAmmo && pAmmo->GetUID().IsValidUID()) - Args.m_VarsLocal.SetNum("Arrow", (dword)pAmmo->GetUID()); - if ( OnTrigger(CTRIG_HitMiss, pCharTarg, &Args) == TRIGRET_RET_TRUE ) + pScriptArgs->m_VarsLocal.SetNum("Arrow", (dword)pAmmo->GetUID()); + + if ( OnTrigger(CTRIG_HitMiss, pScriptArgs, pCharTarg) == TRIGRET_RET_TRUE ) return WAR_SWING_EQUIPPING_NOWAIT; - if ( Args.m_VarsLocal.GetKeyNum("ArrowHandled") != 0 ) // if arrow is handled by script, do nothing with it further! + if ( pScriptArgs->m_VarsLocal.GetKeyNum("ArrowHandled") != 0 ) // if arrow is handled by script, do nothing with it further! pAmmo = nullptr; } @@ -2088,22 +2100,23 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) local.ItemParryDamage = The chance that the parrying item will be damaged. local.Damage = The amount of damage (raw) before parrying reduction. */ - CScriptTriggerArgs Args(iParryReduction, iDmgType, pItemHit); - Args.m_VarsLocal.SetNum("ParryChance", iParryChance); - Args.m_VarsLocal.SetNum("ParrySkillID", ParrySkill); - Args.m_VarsLocal.SetNum("ItemParryDamageChance", 100); - Args.m_VarsLocal.SetNum("Damage", iDmg); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iParryReduction, iDmgType, 0, pItemHit); + pScriptArgs->m_VarsLocal.SetNum("ParryChance", iParryChance); + pScriptArgs->m_VarsLocal.SetNum("ParrySkillID", ParrySkill); + pScriptArgs->m_VarsLocal.SetNum("ItemParryDamageChance", 100); + pScriptArgs->m_VarsLocal.SetNum("Damage", iDmg); if (IsTrigUsed(TRIGGER_HITPARRY)) { - if (pCharTarg->OnTrigger(CTRIG_HitParry, this, &Args) == TRIGRET_RET_TRUE) + if (pCharTarg->OnTrigger(CTRIG_HitParry, pScriptArgs, this) == TRIGRET_RET_TRUE) return WAR_SWING_EQUIPPING_NOWAIT; - iParryReduction = (int)(Args.m_iN1); - iDmgType = (DAMAGE_TYPE)(Args.m_iN2); - iDmg = (int)Args.m_VarsLocal.GetKeyNum("Damage"); - iParryChance = (int)Args.m_VarsLocal.GetKeyNum("ParryChance"); - ParrySkill = (SKILL_TYPE)Args.m_VarsLocal.GetKeyNum("ParrySkillID"); + iParryReduction = (int)(pScriptArgs->m_iN1); + iDmgType = (DAMAGE_TYPE)(pScriptArgs->m_iN2); + iDmg = (int)pScriptArgs->m_VarsLocal.GetKeyNum("Damage"); + iParryChance = (int)pScriptArgs->m_VarsLocal.GetKeyNum("ParryChance"); + ParrySkill = (SKILL_TYPE)pScriptArgs->m_VarsLocal.GetKeyNum("ParrySkillID"); } if (iParryChance > 0 && pCharTarg->Skill_UseQuick(ParrySkill, iParryChance, true, false)) @@ -2115,7 +2128,7 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) if (g_Cfg.m_iFeatureSE & FEATURE_SE_NINJASAM && g_Cfg.m_iCombatParryingEra & PARRYERA_SEFORMULA && !pCharTarg->IsStatFlag(STATF_HASSHIELD)) pCharTarg->Skill_Experience(SKILL_BUSHIDO, iParryChance); - int iParryDamageChance = (int)(Args.m_VarsLocal.GetKeyNum("ItemParryDamageChance")); + int iParryDamageChance = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("ItemParryDamageChance")); if ( pItemHit && (iParryDamageChance > g_Rand.GetVal(100)) ) pItemHit->OnTakeDamage(1, this, iDmgType); @@ -2130,24 +2143,24 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) } - - CScriptTriggerArgs Args(iDmg, iDmgType, pWeapon); - Args.m_VarsLocal.SetNum("ItemDamageChance", 25); - Args.m_VarsLocal.SetNum("ItemPoisonReductionChance", 100); - Args.m_VarsLocal.SetNum("ItemPoisonReductionAmount", 1); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iDmg, iDmgType, 0, pWeapon); + pScriptArgs->m_VarsLocal.SetNum("ItemDamageChance", 25); + pScriptArgs->m_VarsLocal.SetNum("ItemPoisonReductionChance", 100); + pScriptArgs->m_VarsLocal.SetNum("ItemPoisonReductionAmount", 1); int32 iPoison = 0; if (pWeapon) { iPoison = g_Rand.GetVal(pWeapon->m_itWeapon.m_poison_skill); - Args.m_VarsLocal.SetNum("ItemPoisonReductionAmount", iPoison / 2); + pScriptArgs->m_VarsLocal.SetNum("ItemPoisonReductionAmount", iPoison / 2); } if ( pAmmo && pAmmo->GetUID().IsValidUID() ) - Args.m_VarsLocal.SetNum("Arrow",(dword)pAmmo->GetUID()); + pScriptArgs->m_VarsLocal.SetNum("Arrow",(dword)pAmmo->GetUID()); if ( IsTrigUsed(TRIGGER_SKILLSUCCESS) ) { - if ( Skill_OnCharTrigger(skill, CTRIG_SkillSuccess) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger(skill, CTRIG_SkillSuccess, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return WAR_SWING_EQUIPPING; // ok, so no hit - skill failed. Pah! @@ -2155,7 +2168,7 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) } if ( IsTrigUsed(TRIGGER_SUCCESS) ) { - if ( Skill_OnTrigger(skill, SKTRIG_SUCCESS) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_SUCCESS, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return WAR_SWING_EQUIPPING; // ok, so no hit - skill failed. Pah! @@ -2164,26 +2177,26 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) if ( IsTrigUsed(TRIGGER_HIT) ) { - if ( OnTrigger(CTRIG_Hit, pCharTarg, &Args) == TRIGRET_RET_TRUE ) + if ( OnTrigger(CTRIG_Hit, pScriptArgs, pCharTarg) == TRIGRET_RET_TRUE ) return WAR_SWING_EQUIPPING; - if ( Args.m_VarsLocal.GetKeyNum("ArrowHandled") != 0 ) // if arrow is handled by script, do nothing with it further + if ( pScriptArgs->m_VarsLocal.GetKeyNum("ArrowHandled") != 0 ) // if arrow is handled by script, do nothing with it further pAmmo = nullptr; - iDmg = (int)(Args.m_iN1); - iDmgType = (DAMAGE_TYPE)(Args.m_iN2); + iDmg = (int)(pScriptArgs->m_iN1); + iDmgType = (DAMAGE_TYPE)(pScriptArgs->m_iN2); if (pWeapon) { - Args.m_pO1 = this; - if (pWeapon->OnTrigger(ITRIG_Hit, pCharTarg, &Args) == TRIGRET_RET_TRUE) + pScriptArgs->m_pO1 = this; + if (pWeapon->OnTrigger(ITRIG_Hit, pScriptArgs, pCharTarg) == TRIGRET_RET_TRUE) return WAR_SWING_EQUIPPING; - if (Args.m_VarsLocal.GetKeyNum("ArrowHandled") != 0) // if arrow is handled by script, do nothing with it further + if (pScriptArgs->m_VarsLocal.GetKeyNum("ArrowHandled") != 0) // if arrow is handled by script, do nothing with it further pAmmo = nullptr; - iDmg = (int)(Args.m_iN1); - iDmgType = (DAMAGE_TYPE)(Args.m_iN2); + iDmg = (int)(pScriptArgs->m_iN1); + iDmgType = (DAMAGE_TYPE)(pScriptArgs->m_iN2); } } @@ -2217,15 +2230,15 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) byte iPoisonDeliver = (byte)(iPoison); pCharTarg->SetPoison(10 * iPoisonDeliver, iPoisonDeliver / 5, this); - if (Args.m_VarsLocal.GetKeyNum("ItemPoisonReductionChance") > g_Rand.GetVal(100)) + if (pScriptArgs->m_VarsLocal.GetKeyNum("ItemPoisonReductionChance") > g_Rand.GetVal(100)) { - pWeapon->m_itWeapon.m_poison_skill -= (byte)(Args.m_VarsLocal.GetKeyNum("ItemPoisonReductionAmount")); // reduce weapon poison charges + pWeapon->m_itWeapon.m_poison_skill -= (byte)(pScriptArgs->m_VarsLocal.GetKeyNum("ItemPoisonReductionAmount")); // reduce weapon poison charges pWeapon->UpdatePropertyFlag(); } } // Check if the weapon will be damaged - int iDamageChance = (int)(Args.m_VarsLocal.GetKeyNum("ItemDamageChance")); + int iDamageChance = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("ItemDamageChance")); if ( iDamageChance > g_Rand.GetVal(100) ) pWeapon->OnTakeDamage(iDmg, pCharTarg); } diff --git a/src/game/chars/CCharMemory.cpp b/src/game/chars/CCharMemory.cpp index aec3dc846..5cf0c867c 100644 --- a/src/game/chars/CCharMemory.cpp +++ b/src/game/chars/CCharMemory.cpp @@ -1,6 +1,7 @@ // Actions specific to an NPC. #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/send.h" #include "../items/CItemMemory.h" #include "../items/CItemStone.h" @@ -299,7 +300,7 @@ CItemMemory * CChar::Memory_AddObj( const CObjBase * pObj, word MemTypes ) } // Looping through all memories ( ForCharMemoryType ). -TRIGRET_TYPE CChar::OnCharTrigForMemTypeLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, word wMemType ) +TRIGRET_TYPE CChar::OnCharTrigForMemTypeLoop( CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult, word wMemType ) { ADDTOCALLSTACK("CChar::OnCharTrigForMemTypeLoop"); const CScriptLineContext StartContext = s.GetContext(); @@ -312,7 +313,7 @@ TRIGRET_TYPE CChar::OnCharTrigForMemTypeLoop( CScript &s, CTextConsole * pSrc, C CItem* pItem = static_cast(pObjRec); if ( !pItem->IsMemoryTypes(wMemType) ) continue; - TRIGRET_TYPE iRet = pItem->OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult ); + TRIGRET_TYPE iRet = pItem->OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pArgs, pSrc, pResult ); if ( iRet == TRIGRET_BREAK ) { EndContext = StartContext; @@ -330,7 +331,7 @@ TRIGRET_TYPE CChar::OnCharTrigForMemTypeLoop( CScript &s, CTextConsole * pSrc, C if ( EndContext.m_iOffset <= StartContext.m_iOffset ) { // just skip to the end. - TRIGRET_TYPE iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult ); + TRIGRET_TYPE iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pArgs, pSrc, pResult ); if ( iRet != TRIGRET_ENDIF ) return iRet; } @@ -402,7 +403,11 @@ void CChar::Memory_Fight_Retreat( CChar * pTarg, CItemMemory * pFight ) return; } - SysMessagef(fCowardice ? g_Cfg.GetDefaultMsg(DEFMSG_MSG_COWARD_1) : g_Cfg.GetDefaultMsg(DEFMSG_MSG_COWARD_2), pTarg->GetName()); + SysMessagef( + fCowardice + ? g_Cfg.GetDefaultMsg(DEFMSG_MSG_COWARD_1) + : g_Cfg.GetDefaultMsg(DEFMSG_MSG_COWARD_2), + pTarg->GetName()); // Lose some fame. if ( fCowardice ) diff --git a/src/game/chars/CCharNPC.cpp b/src/game/chars/CCharNPC.cpp index 8f855ef37..26d5c47ca 100644 --- a/src/game/chars/CCharNPC.cpp +++ b/src/game/chars/CCharNPC.cpp @@ -1,13 +1,14 @@ // Actions specific to an NPC. -#include #include "../../common/resource/CResourceLock.h" #include "../../common/CException.h" +//#include "../../common/CScriptParserBufs.h" #include "../clients/CClient.h" #include "../items/CItemContainer.h" #include "../CServer.h" #include "../triggers.h" #include "CCharNPC.h" +#include lpctstr const CCharNPC::sm_szLoadKeys[CNC_QTY+1] = @@ -318,7 +319,7 @@ void CChar::NPC_CreateTrigger() continue; executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, this, nullptr); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, CScriptTriggerArgsPtr{}, this); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) return; } @@ -335,7 +336,7 @@ void CChar::NPC_CreateTrigger() continue; executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, this, nullptr); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, CScriptTriggerArgsPtr{}, this); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) return; } diff --git a/src/game/chars/CCharNPCAct.cpp b/src/game/chars/CCharNPCAct.cpp index bc2987b9b..5d7f1d1d6 100644 --- a/src/game/chars/CCharNPCAct.cpp +++ b/src/game/chars/CCharNPCAct.cpp @@ -5,6 +5,7 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/receive.h" #include "../clients/CClient.h" #include "../items/CItemCorpse.h" @@ -303,7 +304,7 @@ void CChar::NPC_OnHear( lpctstr pszCmd, CChar * pSrc, bool fAllPets ) // This or CTRIG_SeeNewPlayer will be our first contact with people. if ( IsTrigUsed(TRIGGER_NPCHEARGREETING) ) { - if ( OnTrigger( CTRIG_NPCHearGreeting, pSrc ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCHearGreeting, CScriptTriggerArgsPtr{}, pSrc ) == TRIGRET_RET_TRUE ) return; } @@ -367,7 +368,7 @@ void CChar::NPC_OnHear( lpctstr pszCmd, CChar * pSrc, bool fAllPets ) // can't figure you out. if ( IsTrigUsed(TRIGGER_NPCHEARUNKNOWN) ) { - if ( OnTrigger( CTRIG_NPCHearUnknown, pSrc ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCHearUnknown, CScriptTriggerArgsPtr{}, pSrc ) == TRIGRET_RET_TRUE ) return; } @@ -949,15 +950,15 @@ bool CChar::NPC_LookAtItem( CItem * pItem, int iDist ) { if ( IsTrigUsed(TRIGGER_NPCLOOKATITEM) && !pItem->IsAttr(ATTR_MOVE_NEVER|ATTR_LOCKEDDOWN|ATTR_SECURE) ) { - - CScriptTriggerArgs Args(iDist, iWantThisItem, pItem); - switch ( OnTrigger(CTRIG_NPCLookAtItem, this, &Args) ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iDist, iWantThisItem, 0, pItem); + switch ( OnTrigger(CTRIG_NPCLookAtItem, pScriptArgs, this) ) { case TRIGRET_RET_TRUE: return true; case TRIGRET_RET_FALSE: return false; default: break; } - iWantThisItem = (int)(Args.m_iN2); + iWantThisItem = (int)(pScriptArgs->m_iN2); } } @@ -1017,7 +1018,7 @@ bool CChar::NPC_LookAtChar( CChar * pChar, int iDist ) if ( IsTrigUsed(TRIGGER_NPCLOOKATCHAR) ) { - switch ( OnTrigger(CTRIG_NPCLookAtChar, pChar) ) + switch ( OnTrigger(CTRIG_NPCLookAtChar, CScriptTriggerArgsPtr{}, pChar) ) { case TRIGRET_RET_TRUE: return true; case TRIGRET_RET_FALSE: return false; @@ -1045,7 +1046,7 @@ bool CChar::NPC_LookAtChar( CChar * pChar, int iDist ) { if ( IsTrigUsed(TRIGGER_NPCSEENEWPLAYER) ) { - if ( OnTrigger( CTRIG_NPCSeeNewPlayer, pChar ) != TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCSeeNewPlayer, CScriptTriggerArgsPtr{}, pChar ) != TRIGRET_RET_TRUE ) { // record that we attempted to speak to them. CItemMemory * pMemory = Memory_AddObjTypes( pChar, MEMORY_SPEAK ); @@ -1267,12 +1268,13 @@ void CChar::NPC_Act_Wander() if (IsTrigUsed(TRIGGER_NPCACTWANDER)) { - CScriptTriggerArgs Args(iStopWandering, iReturnToHome); - if (OnTrigger(CTRIG_NPCActWander, this, &Args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iStopWandering, iReturnToHome, 0, nullptr); + if (OnTrigger(CTRIG_NPCActWander, pScriptArgs, this) == TRIGRET_RET_TRUE) return; - iStopWandering = (int)Args.m_iN1; - iReturnToHome = (int)Args.m_iN2; + iStopWandering = (int)pScriptArgs->m_iN1; + iReturnToHome = (int)pScriptArgs->m_iN2; } if (iStopWandering) @@ -1354,8 +1356,9 @@ bool CChar::NPC_Act_Follow(bool fFlee, int maxDistance, bool fMoveAway) EXC_SET_BLOCK("Trigger"); if (IsTrigUsed(TRIGGER_NPCACTFOLLOW)) { - CScriptTriggerArgs Args(fFlee, maxDistance, fMoveAway); - switch (OnTrigger(CTRIG_NPCActFollow, pChar, &Args)) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(fFlee, maxDistance, fMoveAway, nullptr); + switch (OnTrigger(CTRIG_NPCActFollow, pScriptArgs, pChar)) { case TRIGRET_RET_TRUE: { @@ -1371,9 +1374,9 @@ bool CChar::NPC_Act_Follow(bool fFlee, int maxDistance, bool fMoveAway) } } - fFlee = (Args.m_iN1 != 0); - maxDistance = static_cast(Args.m_iN2); - fMoveAway = (Args.m_iN3 != 0); + fFlee = (pScriptArgs->m_iN1 != 0); + maxDistance = static_cast(pScriptArgs->m_iN2); + fMoveAway = (pScriptArgs->m_iN3 != 0); } EXC_SET_BLOCK("CanSee"); @@ -1548,8 +1551,9 @@ void CChar::NPC_Act_GoHome() { if ( IsTrigUsed(TRIGGER_NPCLOSTTELEPORT) ) { - CScriptTriggerArgs Args(iDistance); // ARGN1 - distance - if ( OnTrigger(CTRIG_NPCLostTeleport, this, &Args) != TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = iDistance; // ARGN1 - distance + if ( OnTrigger(CTRIG_NPCLostTeleport, pScriptArgs, this) != TRIGRET_RET_TRUE ) Spell_Teleport(m_ptHome, true, false); } else @@ -1624,8 +1628,9 @@ void CChar::NPC_Act_Looting() if ( IsTrigUsed(TRIGGER_NPCSEEWANTITEM) ) { - CScriptTriggerArgs Args(pItem); - if ( OnTrigger(CTRIG_NPCSeeWantItem, this, &Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pItem; + if ( OnTrigger(CTRIG_NPCSeeWantItem, pScriptArgs, this) == TRIGRET_RET_TRUE ) return; } @@ -1983,7 +1988,7 @@ void CChar::NPC_Act_Idle() { if ( IsTrigUsed(TRIGGER_NPCSPECIALACTION) ) { - if ( OnTrigger( CTRIG_NPCSpecialAction, this ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCSpecialAction, CScriptTriggerArgsPtr{}, this ) == TRIGRET_RET_TRUE ) return; } @@ -2062,10 +2067,11 @@ bool CChar::NPC_OnItemGive( CChar *pCharSrc, CItem *pItem ) if ( !pCharSrc ) return false; - CScriptTriggerArgs Args(pItem); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pItem; if ( IsTrigUsed(TRIGGER_RECEIVEITEM) ) { - if ( OnTrigger(CTRIG_ReceiveItem, pCharSrc, &Args) == TRIGRET_RET_TRUE ) + if ( OnTrigger(CTRIG_ReceiveItem, pScriptArgs, pCharSrc) == TRIGRET_RET_TRUE ) return false; } @@ -2194,7 +2200,7 @@ bool CChar::NPC_OnItemGive( CChar *pCharSrc, CItem *pItem ) { if ( IsTrigUsed(TRIGGER_NPCREFUSEITEM) ) { - if ( OnTrigger(CTRIG_NPCRefuseItem, pCharSrc, &Args) != TRIGRET_RET_TRUE ) + if ( OnTrigger(CTRIG_NPCRefuseItem, pScriptArgs, pCharSrc) != TRIGRET_RET_TRUE ) { pCharSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NPC_GENERIC_DONTWANT)); return false; @@ -2206,7 +2212,7 @@ bool CChar::NPC_OnItemGive( CChar *pCharSrc, CItem *pItem ) if ( IsTrigUsed(TRIGGER_NPCACCEPTITEM) ) { - if ( OnTrigger(CTRIG_NPCAcceptItem, pCharSrc, &Args) == TRIGRET_RET_TRUE ) + if ( OnTrigger(CTRIG_NPCAcceptItem, pScriptArgs, pCharSrc) == TRIGRET_RET_TRUE ) return false; } @@ -2675,7 +2681,7 @@ void CChar::NPC_ExtraAI() EXC_SET_BLOCK("init"); if ( IsTrigUsed(TRIGGER_NPCACTION) ) { - if ( OnTrigger( CTRIG_NPCAction, this ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCAction, CScriptTriggerArgsPtr{}, this ) == TRIGRET_RET_TRUE ) return; } diff --git a/src/game/chars/CCharNPCAct_Fight.cpp b/src/game/chars/CCharNPCAct_Fight.cpp index f13374ace..b606cce01 100644 --- a/src/game/chars/CCharNPCAct_Fight.cpp +++ b/src/game/chars/CCharNPCAct_Fight.cpp @@ -1,7 +1,7 @@ // Actions specific to an NPC. #include "../../common/sphere_library/CSRand.h" #include "../../common/CLog.h" -#include "../../common/CScriptTriggerArgs.h" +#include "../../common/CScriptParserBufs.h" #include "../components/CCPropsItemWeapon.h" #include "../items/item_types.h" #include "../uo_files/uofiles_enums_creid.h" @@ -98,15 +98,15 @@ CChar * CChar::NPC_FightFindBestTarget(const std::vector* pvExcludeList) if (refAttacker.ignore) { - bool bIgnore = true; + bool fIgnore = true; if (IsTrigUsed(TRIGGER_HITIGNORE)) { - CScriptTriggerArgs Args; - Args.m_iN1 = bIgnore; - OnTrigger(CTRIG_HitIgnore, pChar, &Args); - bIgnore = Args.m_iN1 ? true : false; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = fIgnore; + OnTrigger(CTRIG_HitIgnore, pScriptArgs, pChar); + fIgnore = pScriptArgs->m_iN1 ? true : false; } - if (bIgnore) + if (fIgnore) { ++i; continue; @@ -222,20 +222,21 @@ void CChar::NPC_Act_Fight() bool fSkipHardcoded = false; if (IsTrigUsed(TRIGGER_NPCACTFIGHT)) { - CScriptTriggerArgs Args(iDist, iMotivation); - switch (OnTrigger(CTRIG_NPCActFight, pChar, &Args)) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iDist, iMotivation, 0, nullptr); + switch (OnTrigger(CTRIG_NPCActFight, pScriptArgs, pChar)) { case TRIGRET_RET_TRUE: return; case TRIGRET_RET_FALSE: fSkipHardcoded = true; break; - case (TRIGRET_TYPE)(2) : + case TRIGRET_RET_DEFAULT: //(TRIGRET_TYPE)(2) : { - SKILL_TYPE iSkillforced = (SKILL_TYPE)ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("skill")); + SKILL_TYPE iSkillforced = (SKILL_TYPE)ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("skill")); if (iSkillforced) { - SPELL_TYPE iSpellforced = (SPELL_TYPE)ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("spell")); + SPELL_TYPE iSpellforced = (SPELL_TYPE)ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("spell")); if (g_Cfg.IsSkillFlag(iSkillforced, SKF_MAGIC)) { m_atMagery.m_iSpell = iSpellforced; @@ -250,8 +251,8 @@ void CChar::NPC_Act_Fight() break; } - iDist = (int)(Args.m_iN1); - iMotivation = (int)(Args.m_iN2); + iDist = (int)(pScriptArgs->m_iN1); + iMotivation = (int)(pScriptArgs->m_iN2); } if (!IsStatFlag(STATF_PET)) diff --git a/src/game/chars/CCharNPCAct_Magic.cpp b/src/game/chars/CCharNPCAct_Magic.cpp index 1f7f5814d..be86aeefd 100644 --- a/src/game/chars/CCharNPCAct_Magic.cpp +++ b/src/game/chars/CCharNPCAct_Magic.cpp @@ -1,7 +1,7 @@ // Actions specific to an NPC. #include "../../common/sphere_library/CSRand.h" -#include "../../common/CScriptTriggerArgs.h" +#include "../../common/CScriptParserBufs.h" #include "../items/CItemContainer.h" #include "../items/CItemMemory.h" #include "../triggers.h" @@ -222,17 +222,18 @@ bool CChar::NPC_FightMagery(CChar * pChar) } if (IsTrigUsed(TRIGGER_NPCACTCAST)) { - CScriptTriggerArgs Args((int)spell, (int)bWandUse, pTarg); - Args.m_VarsLocal.SetNum("HealThreshold", iHealThreshold); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init((int)spell, (int)bWandUse, 0, pTarg); + pScriptArgs->m_VarsLocal.SetNum("HealThreshold", iHealThreshold); - switch (OnTrigger(CTRIG_NPCActCast, this, &Args)) + switch (OnTrigger(CTRIG_NPCActCast, pScriptArgs, this)) { case TRIGRET_RET_TRUE: return false; default: break; } - spell = (SPELL_TYPE)Args.m_iN1; - iHealThreshold = (int)Args.m_VarsLocal.GetKeyNum("HealThreshold"); - CObjBase* pNewTarg = Args.m_VarObjs.Get(1); //We switch to a new targ if REF1 is set in the trigger. + spell = (SPELL_TYPE)pScriptArgs->m_iN1; + iHealThreshold = (int)pScriptArgs->m_VarsLocal.GetKeyNum("HealThreshold"); + CObjBase* pNewTarg = pScriptArgs->m_VarObjs.Get(1); //We switch to a new targ if REF1 is set in the trigger. if (pNewTarg) { pTarg = pNewTarg; diff --git a/src/game/chars/CCharNPCPet.cpp b/src/game/chars/CCharNPCPet.cpp index 0b9ef724c..466be7d9d 100644 --- a/src/game/chars/CCharNPCPet.cpp +++ b/src/game/chars/CCharNPCPet.cpp @@ -2,6 +2,7 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +//#include "../../common/CScriptParserBufs.h" #include "../clients/CClient.h" #include "../components/CCSpawn.h" #include "../items/CItemContainer.h" @@ -869,7 +870,7 @@ void CChar::NPC_PetRelease() if (IsTrigUsed(TRIGGER_PETRELEASE)) { - if (OnTrigger(CTRIG_PetRelease, pCharOwn, nullptr) == TRIGRET_RET_TRUE) + if (OnTrigger(CTRIG_PetRelease, CScriptTriggerArgsPtr{}, pCharOwn) == TRIGRET_RET_TRUE) return; } @@ -911,7 +912,7 @@ void CChar::NPC_PetDesert() if ( IsTrigUsed(TRIGGER_PETDESERT) ) { - if ( OnTrigger( CTRIG_PetDesert, pCharOwn, nullptr ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_PetDesert, CScriptTriggerArgsPtr{}, pCharOwn) == TRIGRET_RET_TRUE ) return; } diff --git a/src/game/chars/CCharNotoriety.cpp b/src/game/chars/CCharNotoriety.cpp index 310bd26fd..26047afc1 100644 --- a/src/game/chars/CCharNotoriety.cpp +++ b/src/game/chars/CCharNotoriety.cpp @@ -1,7 +1,7 @@ // Actions specific to an NPC. #include "../../common/CExpression.h" #include "../../common/CException.h" -#include "../../common/CScriptTriggerArgs.h" +#include "../../common/CScriptParserBufs.h" #include "../items/CItemMemory.h" #include "../items/CItemStone.h" #include "../triggers.h" @@ -99,6 +99,7 @@ bool CChar::Noto_IsNeutral() const NOTO_TYPE CChar::Noto_GetFlag(const CChar * pCharViewer, bool fAllowIncog, bool fAllowInvul, bool fOnlyColor) const { ADDTOCALLSTACK("CChar::Noto_GetFlag"); + // TODO: CONST-CORRECTNESS! CChar * pThis = const_cast(this); CChar * pTarget = const_cast(pCharViewer); NOTO_TYPE iNoto = NOTO_INVALID; @@ -118,10 +119,10 @@ NOTO_TYPE CChar::Noto_GetFlag(const CChar * pCharViewer, bool fAllowIncog, bool if (IsTrigUsed(TRIGGER_NOTOSEND)) { - CScriptTriggerArgs args; - pThis->OnTrigger(CTRIG_NotoSend, pTarget, &args); - iNoto = (NOTO_TYPE)(args.m_iN1); - iColor = (NOTO_TYPE)(args.m_iN2); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pThis->OnTrigger(CTRIG_NotoSend, pScriptArgs, pTarget); + iNoto = (NOTO_TYPE)(pScriptArgs->m_iN1); + iColor = (NOTO_TYPE)(pScriptArgs->m_iN2); } if (iNoto == NOTO_INVALID) @@ -160,6 +161,7 @@ NOTO_TYPE CChar::Noto_CalcFlag(const CChar * pCharViewer, bool fAllowIncog, bool if (m_pArea && m_pArea->IsFlag(REGION_FLAG_ARENA)) // everyone is neutral here. return NOTO_NEUTRAL; + // TODO: refactor this nesting mess and return early if (this != pCharViewer) // Am I checking myself? { if (m_pNPC) @@ -396,12 +398,12 @@ bool CChar::Noto_Criminal( CChar * pCharViewer, bool fFromSawCrime ) TRIGRET_TYPE retCriminal = TRIGRET_RET_DEFAULT; if ( IsTrigUsed(TRIGGER_CRIMINAL) ) { - CScriptTriggerArgs Args; - Args.m_iN1 = decay / (60*MSECS_PER_SEC); // convert in minutes - Args.m_iN2 = fFromSawCrime; - Args.m_pO1 = pCharViewer; - retCriminal = OnTrigger(CTRIG_Criminal, this, &Args); - decay = (Args.m_iN1 * (60*MSECS_PER_SEC)); // back in ms + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = decay / (60*MSECS_PER_SEC); // convert in minutes + pScriptArgs->m_iN2 = fFromSawCrime; + pScriptArgs->m_pO1 = pCharViewer; + retCriminal = OnTrigger(CTRIG_Criminal, pScriptArgs, this); + decay = (pScriptArgs->m_iN1 * (60*MSECS_PER_SEC)); // back in ms } if (retCriminal == TRIGRET_RET_TRUE) @@ -498,7 +500,7 @@ void CChar::Noto_Fame( int iFameChange, CChar* pNPC ) // if ( retType == TRIGRET_RET_TRUE ) // return; - // iFameChange = (int)(Args.m_iN1); + // iFameChange = (int)(pScriptArgs->m_iN1); //} //if ( ! iFameChange ) @@ -536,7 +538,7 @@ void CChar::Noto_Karma( int iKarmaChange, int iBottom, bool fMessage, CChar* pNP // if ( retType == TRIGRET_RET_TRUE ) // return; - // iKarmaChange = (int)(Args.m_iN1); + // iKarmaChange = (int)(pScriptArgs->m_iN1); //} //if ( ! iKarmaChange ) @@ -582,23 +584,23 @@ void CChar::Noto_Kill(CChar * pKill, int iTotalKillers) // I'm a murderer ! if (!IsPriv(PRIV_GM)) { - CScriptTriggerArgs args; - args.m_iN1 = m_pPlayer->m_wMurders + 1LL; - args.m_iN2 = true; - args.m_iN3 = false; - args.m_pO1 = pKill; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = m_pPlayer->m_wMurders + 1LL; + pScriptArgs->m_iN2 = true; + pScriptArgs->m_iN3 = false; + pScriptArgs->m_pO1 = pKill; if ( IsTrigUsed(TRIGGER_MURDERMARK) ) { - OnTrigger(CTRIG_MurderMark, this, &args); - if (args.m_iN1 < 0) - args.m_iN1 = 0; + OnTrigger(CTRIG_MurderMark, pScriptArgs, this); + if (pScriptArgs->m_iN1 < 0) + pScriptArgs->m_iN1 = 0; } - if (args.m_iN3 < 1) + if (pScriptArgs->m_iN3 < 1) { - m_pPlayer->m_wMurders = (word)(args.m_iN1); - if (args.m_iN2) + m_pPlayer->m_wMurders = (word)(pScriptArgs->m_iN1); + if (pScriptArgs->m_iN2) Noto_Criminal(); Noto_Murder(); @@ -652,7 +654,8 @@ void CChar::NotoSave_Add( CChar * pChar, NOTO_TYPE value, NOTO_TYPE color ) ADDTOCALLSTACK("CChar::NotoSave_Add"); if ( !pChar ) return; - const CUID& uid = pChar->GetUID(); + + const CUID& uid(pChar->GetUID()); if ( !m_notoSaves.empty() ) // Checking if I already have him in the list, only if there 's any list. { for (std::vector::iterator it = m_notoSaves.begin(), end = m_notoSaves.end(); it != end; ++it) @@ -669,12 +672,13 @@ void CChar::NotoSave_Add( CChar * pChar, NOTO_TYPE value, NOTO_TYPE color ) } } } - NotoSaves refNoto; - refNoto.charUID = pChar->GetUID().GetObjUID(); - refNoto.time = 0; - refNoto.value = value; - refNoto.color = color; - m_notoSaves.emplace_back(std::move(refNoto)); + + m_notoSaves.emplace_back(NotoSaves{ + .time = 0, + .charUID = pChar->GetUID().GetObjUID(), + .color = color, + .value = value + }); } NOTO_TYPE CChar::NotoSave_GetValue(int id, bool fGetColor ) diff --git a/src/game/chars/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index 7a1f58f55..bff953940 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -5,6 +5,7 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CLog.h" #include "../clients/CClient.h" #include "../items/CItemCorpse.h" @@ -158,9 +159,10 @@ ushort CChar::Skill_GetAdjusted( SKILL_TYPE skill ) const if (pSkillDef != nullptr) { - uint uiPureBonus =( pSkillDef->m_StatBonus[STAT_STR] * Stat_GetAdjusted(STAT_STR) ) + - ( pSkillDef->m_StatBonus[STAT_INT] * Stat_GetAdjusted(STAT_INT) ) + - ( pSkillDef->m_StatBonus[STAT_DEX] * Stat_GetAdjusted(STAT_DEX) ); + const uint uiPureBonus = + ( pSkillDef->m_StatBonus[STAT_STR] * Stat_GetAdjusted(STAT_STR) ) + + ( pSkillDef->m_StatBonus[STAT_INT] * Stat_GetAdjusted(STAT_INT) ) + + ( pSkillDef->m_StatBonus[STAT_DEX] * Stat_GetAdjusted(STAT_DEX) ); uiAdjSkill = (ushort)IMulDiv( pSkillDef->m_StatPercent, uiPureBonus, 10000 ); } @@ -193,27 +195,27 @@ void CChar::Skill_SetBase( SKILL_TYPE skill, ushort uiValue ) bool fUpdateStats = false; if ( IsTrigUsed(TRIGGER_SKILLCHANGE) ) { - CScriptTriggerArgs args; - args.m_iN1 = (int64)skill; - args.m_iN2 = (int64)uiValue; - if ( OnTrigger(CTRIG_SkillChange, this, &args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = (int64)skill; + pScriptArgs->m_iN2 = (int64)uiValue; + if ( OnTrigger(CTRIG_SkillChange, pScriptArgs, this) == TRIGRET_RET_TRUE ) return; - const llong iN2Old = args.m_iN2; - if (args.m_iN2 > UINT16_MAX) + const llong iN2Old = pScriptArgs->m_iN2; + if (pScriptArgs->m_iN2 > UINT16_MAX) { - args.m_iN2 = UINT16_MAX; + pScriptArgs->m_iN2 = UINT16_MAX; } - else if (args.m_iN2 < 0) + else if (pScriptArgs->m_iN2 < 0) { - args.m_iN2 = 0; + pScriptArgs->m_iN2 = 0; } - if (iN2Old != args.m_iN2) + if (iN2Old != pScriptArgs->m_iN2) { - g_Log.EventWarn("Trying to set skill '%s' to invalid value=%lld. Defaulting it to %" PRId64 ".\n", Skill_GetName(skill), iN2Old, args.m_iN2); + g_Log.EventWarn("Trying to set skill '%s' to invalid value=%lld. Defaulting it to %" PRId64 ".\n", Skill_GetName(skill), iN2Old, pScriptArgs->m_iN2); } - uiValue = (ushort)(args.m_iN2); + uiValue = (ushort)(pScriptArgs->m_iN2); } m_Skill[skill] = uiValue; @@ -412,18 +414,19 @@ void CChar::Skill_Experience( SKILL_TYPE skill, int iDifficulty ) int64 iChance = pSkillDef->m_AdvRate.GetChancePercent(uiSkillLevel); int64 iSkillMax = Skill_GetMax(skill); // max advance for this skill. - CScriptTriggerArgs pArgs(0, iChance, iSkillMax); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(0, iChance, iSkillMax, nullptr); if ( IsTrigUsed(TRIGGER_SKILLGAIN) ) { - if ( Skill_OnCharTrigger( skill, CTRIG_SkillGain, &pArgs ) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger( skill, CTRIG_SkillGain, pScriptArgs ) == TRIGRET_RET_TRUE ) return; } if ( IsTrigUsed(TRIGGER_GAIN) ) { - if ( Skill_OnTrigger( skill, SKTRIG_GAIN, &pArgs ) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger( skill, SKTRIG_GAIN, pScriptArgs) == TRIGRET_RET_TRUE ) return; } - pArgs.GetArgNs( nullptr, &iChance, &iSkillMax ); + pScriptArgs->GetArgNs( nullptr, &iChance, &iSkillMax ); if ( iChance <= 0 ) return; @@ -536,7 +539,7 @@ bool CChar::Skill_CheckSuccess( SKILL_TYPE skill, int iDifficulty, bool fUseBell return ( iSuccessChance >= g_Rand.GetVal(1000) ); } -bool CChar::Skill_UseQuick(SKILL_TYPE skill, int64 difficulty, bool fAllowGain, bool fUseBellCurve, bool fForceCheck ) +bool CChar::Skill_UseQuick(SKILL_TYPE skill, int64 iDifficulty, bool fAllowGain, bool fUseBellCurve, bool fForceCheck ) { ADDTOCALLSTACK("CChar::Skill_UseQuick"); // ARGS: @@ -545,19 +548,20 @@ bool CChar::Skill_UseQuick(SKILL_TYPE skill, int64 difficulty, bool fAllowGain, // bAllowGain = can gain skill from this? // bUseBellCurve = check skill success chance using bell curve or a simple percent check? // Use a skill instantly. No wait at all. - // No interference with other skills. + // No interference with other skills. if (g_Cfg.IsSkillFlag(skill, SKF_SCRIPTED) && !fForceCheck) return false; - int64 result = Skill_CheckSuccess( skill, (int)difficulty, fUseBellCurve ); - CScriptTriggerArgs pArgs( 0 , difficulty, result); - TRIGRET_TYPE ret = TRIGRET_RET_DEFAULT; + int64 result = Skill_CheckSuccess( skill, (int)iDifficulty, fUseBellCurve ); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(0, iDifficulty, result, nullptr); + TRIGRET_TYPE ret = TRIGRET_RET_DEFAULT; if ( IsTrigUsed(TRIGGER_SKILLUSEQUICK) ) { - ret = Skill_OnCharTrigger( skill, CTRIG_SkillUseQuick, &pArgs ); - pArgs.GetArgNs( nullptr, &difficulty, &result); + ret = Skill_OnCharTrigger( skill, CTRIG_SkillUseQuick, pScriptArgs ); + pScriptArgs->GetArgNs( nullptr, &iDifficulty, &result); if ( ret == TRIGRET_RET_TRUE ) return true; @@ -566,8 +570,8 @@ bool CChar::Skill_UseQuick(SKILL_TYPE skill, int64 difficulty, bool fAllowGain, } if ( IsTrigUsed(TRIGGER_USEQUICK) ) { - ret = Skill_OnTrigger( skill, SKTRIG_USEQUICK, &pArgs ); - pArgs.GetArgNs( nullptr, &difficulty, &result ); + ret = Skill_OnTrigger( skill, SKTRIG_USEQUICK, pScriptArgs ); + pScriptArgs->GetArgNs( nullptr, &iDifficulty, &result ); if ( ret == TRIGRET_RET_TRUE ) return true; @@ -578,13 +582,13 @@ bool CChar::Skill_UseQuick(SKILL_TYPE skill, int64 difficulty, bool fAllowGain, if ( result ) // success { if ( fAllowGain ) - Skill_Experience( skill, (int)(difficulty) ); + Skill_Experience( skill, (int)(iDifficulty) ); return true; } else // fail { if ( fAllowGain ) - Skill_Experience( skill, (int)(-difficulty) ); + Skill_Experience( skill, (int)(-iDifficulty) ); return false; } } @@ -811,10 +815,13 @@ bool CChar::Skill_MakeItem_Success() bool notify = true; if ( IsTrigUsed(TRIGGER_SKILLMAKEITEM) ) { - CScriptTriggerArgs Args(iSkillLevel, quality, uidOldAct.ObjFind()); - Args.m_VarsLocal.SetNum("Notify", 1); - iRet = OnTrigger(CTRIG_SkillMakeItem, this, &Args); - notify = Args.m_VarsLocal.GetKeyNum("Notify") ? true : false; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iSkillLevel, quality, 0, uidOldAct.ObjFind()); + pScriptArgs->m_VarsLocal.SetNum("Notify", 1); + + iRet = OnTrigger(CTRIG_SkillMakeItem, pScriptArgs, this); + + notify = pScriptArgs->m_VarsLocal.GetKeyNum("Notify") ? true : false; } m_Act_UID = uidOldAct; // restore @@ -1020,23 +1027,23 @@ CItem * CChar::Skill_NaturalResource_Create( CItem * pResBit, SKILL_TYPE skill ) wAmount = pResBit->GetAmount(); //(Region)ResourceGather behaviour - CScriptTriggerArgs Args(0, 0, pResBit); - Args.m_VarsLocal.SetNum("ResourceID",pOreDef->m_ReapItem); - Args.m_pO1 = pResBit; - Args.m_iN1 = wAmount; - TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(wAmount, 0, 0, pResBit); + pScriptArgs->m_VarsLocal.SetNum("ResourceID",pOreDef->m_ReapItem); + + TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; if ( IsTrigUsed(TRIGGER_REGIONRESOURCEGATHER) ) - tRet = this->OnTrigger(CTRIG_RegionResourceGather, this, &Args); + tRet = this->OnTrigger(CTRIG_RegionResourceGather, pScriptArgs, this); if ( IsTrigUsed(TRIGGER_RESOURCEGATHER) ) - tRet = pOreDef->OnTrigger("@ResourceGather", this, &Args); + tRet = pOreDef->OnTrigger("@ResourceGather", pScriptArgs, this); if ( tRet == TRIGRET_RET_TRUE ) return nullptr; //Creating the 'id' variable with the local given through->by the trigger(s) instead on top of method - ITEMID_TYPE id = (ITEMID_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("ResourceID"))); + ITEMID_TYPE id = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("ResourceID"))); - wAmount = pResBit->ConsumeAmount( (word)(Args.m_iN1) ); // amount i used up. + wAmount = pResBit->ConsumeAmount( (word)(pScriptArgs->m_iN1) ); // amount i used up. if ( wAmount <= 0 ) return nullptr; @@ -1134,7 +1141,8 @@ bool CChar::Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ) word iResourceQty = 0; size_t iResourceTotalQty = pOreDef->m_BaseResources.size(); //This is the total amount of different resources obtained from smelting. - CScriptTriggerArgs Args(iMiningSkill, iResourceTotalQty); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iMiningSkill, iResourceTotalQty, 0, nullptr); if ( pOreDef->IsType( IT_ORE )) { @@ -1147,9 +1155,9 @@ bool CChar::Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ) } iResourceQty = 1; // ingots per ore. iResourceTotalQty = 1; //Ores only gives one type of resouce. - Args.m_iN2 = iResourceTotalQty; - Args.m_VarsLocal.SetNum("resource.0.ID", pBaseDef->GetID()); - Args.m_VarsLocal.SetNum("resource.0.amount", iResourceQty); + pScriptArgs->m_iN2 = iResourceTotalQty; + pScriptArgs->m_VarsLocal.SetNum("resource.0.ID", pBaseDef->GetID()); + pScriptArgs->m_VarsLocal.SetNum("resource.0.amount", iResourceQty); } else { @@ -1167,32 +1175,32 @@ bool CChar::Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ) tchar* pszTmp = Str_GetTemp(); snprintf(pszTmp, Str_TempLength(), "resource.%u.ID", (int)i); - Args.m_VarsLocal.SetNum(pszTmp,(int64)id); + pScriptArgs->m_VarsLocal.SetNum(pszTmp,(int64)id); iResourceQty = (word)(pOreDef->m_BaseResources[i].GetResQty()); snprintf(pszTmp, Str_TempLength(), "resource.%u.amount", (int)i); - Args.m_VarsLocal.SetNum(pszTmp, iResourceQty); + pScriptArgs->m_VarsLocal.SetNum(pszTmp, iResourceQty); } } if (IsTrigUsed(TRIGGER_SMELT) || IsTrigUsed(TRIGGER_ITEMSMELT)) { - switch (pItemOre->OnTrigger(ITRIG_Smelt, this, &Args)) + switch (pItemOre->OnTrigger(ITRIG_Smelt, pScriptArgs, this)) { case TRIGRET_RET_TRUE: return false; default: break; } } - iMiningSkill = (ushort)Args.m_iN1; - fSkipMiningSmeltReq = (bool)Args.m_iN3; + iMiningSkill = (ushort)pScriptArgs->m_iN1; + fSkipMiningSmeltReq = (bool)pScriptArgs->m_iN3; std::vector ingots; for (size_t i = 0; i < iResourceTotalQty; ++i) { tchar* pszTmp = Str_GetTemp(); snprintf(pszTmp, Str_TempLength(), "resource.%u.ID", (int)i); - const CItemBase* pBaseDef = CItemBase::FindItemBase((ITEMID_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum(pszTmp)))); + const CItemBase* pBaseDef = CItemBase::FindItemBase((ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum(pszTmp)))); //We have finished the ore or the item being smelted. if (iOreQty <= 0) @@ -1208,7 +1216,7 @@ bool CChar::Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ) } snprintf(pszTmp, Str_TempLength(), "resource.%u.amount", (int)i); - iResourceQty =(word)Args.m_VarsLocal.GetKeyNum(pszTmp); + iResourceQty =(word)pScriptArgs->m_VarsLocal.GetKeyNum(pszTmp); iResourceQty *= iOreQty; // max amount if (pBaseDef->IsType(IT_GEM)) @@ -3579,29 +3587,29 @@ int CChar::Skill_Stroke() if ( IsTrigUsed(TRIGGER_SKILLSTROKE) || (IsTrigUsed(TRIGGER_STROKE)) ) { - CScriptTriggerArgs args; - args.m_VarsLocal.SetNum("Skill", skill); - args.m_VarsLocal.SetNum("Sound", sound); - args.m_VarsLocal.SetNum("Delay", delay); - args.m_VarsLocal.SetNum("Anim", anim); - args.m_iN1 = 1; //UpdateDir() ? - args.m_VarsLocal.SetNum("Strokes", uiStroke); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_VarsLocal.SetNum("Skill", skill); + pScriptArgs->m_VarsLocal.SetNum("Sound", sound); + pScriptArgs->m_VarsLocal.SetNum("Delay", delay); + pScriptArgs->m_VarsLocal.SetNum("Anim", anim); + pScriptArgs->m_iN1 = 1; //UpdateDir() ? + pScriptArgs->m_VarsLocal.SetNum("Strokes", uiStroke); - if ( OnTrigger(CTRIG_SkillStroke, this, &args) == TRIGRET_RET_TRUE ) + if ( OnTrigger(CTRIG_SkillStroke, pScriptArgs, this) == TRIGRET_RET_TRUE ) return -SKTRIG_ABORT; - if ( Skill_OnTrigger(skill, SKTRIG_STROKE, &args) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_STROKE, pScriptArgs) == TRIGRET_RET_TRUE ) return -SKTRIG_ABORT; - sound = (SOUND_TYPE)(args.m_VarsLocal.GetKeyNum("Sound")); - delay = args.m_VarsLocal.GetKeyNum("Delay"); - anim = static_cast(args.m_VarsLocal.GetKeyNum("Anim")); + sound = (SOUND_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("Sound")); + delay = pScriptArgs->m_VarsLocal.GetKeyNum("Delay"); + anim = static_cast(pScriptArgs->m_VarsLocal.GetKeyNum("Anim")); - if ( args.m_iN1 == 1 ) + if ( pScriptArgs->m_iN1 == 1 ) UpdateDir(m_Act_p); if ( fResource ) - m_atResource.m_dwStrokeCount = (word)(args.m_VarsLocal.GetKeyNum("Strokes")); + m_atResource.m_dwStrokeCount = (word)(pScriptArgs->m_VarsLocal.GetKeyNum("Strokes")); else - m_atCreate.m_dwStrokeCount = (word)(args.m_VarsLocal.GetKeyNum("Strokes")); + m_atCreate.m_dwStrokeCount = (word)(pScriptArgs->m_VarsLocal.GetKeyNum("Strokes")); } if ( sound ) @@ -3797,15 +3805,15 @@ void CChar::Skill_Fail( bool fCancel ) m_Act_Difficulty = -m_Act_Difficulty; if ( !fCancel ) - { + { if ( IsTrigUsed(TRIGGER_SKILLFAIL) ) { - if ( Skill_OnCharTrigger(skill, CTRIG_SkillFail) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger(skill, CTRIG_SkillFail, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) fCancel = true; } if ( IsTrigUsed(TRIGGER_FAIL) && !fCancel ) { - if ( Skill_OnTrigger(skill, SKTRIG_FAIL) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_FAIL, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) fCancel = true; } } @@ -3813,7 +3821,7 @@ void CChar::Skill_Fail( bool fCancel ) { if ( IsTrigUsed(TRIGGER_SKILLABORT) ) { - if ( Skill_OnCharTrigger(skill, CTRIG_SkillAbort) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger(skill, CTRIG_SkillAbort, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return; @@ -3821,7 +3829,7 @@ void CChar::Skill_Fail( bool fCancel ) } if ( IsTrigUsed(TRIGGER_ABORT) ) { - if ( Skill_OnTrigger(skill, SKTRIG_ABORT) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_ABORT, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return; @@ -3829,7 +3837,7 @@ void CChar::Skill_Fail( bool fCancel ) } } - if ( Skill_Stage((fCancel)? SKTRIG_ABORT:SKTRIG_FAIL) >= 0 ) + if ( Skill_Stage(fCancel ? SKTRIG_ABORT : SKTRIG_FAIL) >= 0 ) { // Get some experience for failure ? if ( !fCancel ) @@ -3839,20 +3847,8 @@ void CChar::Skill_Fail( bool fCancel ) Skill_Cleanup(); } -TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage ) -{ - CScriptTriggerArgs pArgs; - return Skill_OnTrigger(skill, stage, &pArgs); -} - -TRIGRET_TYPE CChar::Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE stage ) -{ - CScriptTriggerArgs pArgs; - return Skill_OnCharTrigger(skill, stage, &pArgs); -} - -TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgs *pArgs ) +TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr pArgs ) { ADDTOCALLSTACK("CChar::Skill_OnTrigger"); if ( !IsSkillBase(skill) ) @@ -3873,13 +3869,13 @@ TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScri // RES_SKILL CResourceLock s; if ( pSkillDef->ResourceLock(s) ) - iRet = CScriptObj::OnTriggerScript(s, CSkillDef::sm_szTrigName[stage], this, pArgs); + iRet = CScriptObj::OnTriggerScript(s, CSkillDef::sm_szTrigName[stage], pArgs, this); } return iRet; } -TRIGRET_TYPE CChar::Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgs *pArgs ) +TRIGRET_TYPE CChar::Skill_OnCharTrigger(SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr pArgs ) { ADDTOCALLSTACK("CChar::Skill_OnCharTrigger"); if ( !IsSkillBase(skill) ) @@ -3892,7 +3888,7 @@ TRIGRET_TYPE CChar::Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig, CSc if ( g_Cfg.IsSkillFlag(skill, SKF_MAGIC) ) pArgs->m_VarsLocal.SetNum("spell", m_atMagery.m_iSpell, true, false); - return OnTrigger(ctrig, this, pArgs); + return OnTrigger(ctrig, pArgs, this); } @@ -3928,22 +3924,25 @@ int CChar::Skill_Done() if ( m_Act_Difficulty < 0 ) // was Bound to fail, but we had to wait for the timer anyhow. return -SKTRIG_FAIL; - CScriptTriggerArgs args; - args.m_VarsLocal.SetNumNew("ITEMDAMAGECHANCE", 25); - args.m_VarsLocal.SetNumNew("ITEMDAMAGEAMOUNT", 1); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_VarsLocal.SetNumNew("ITEMDAMAGECHANCE", 25); + pScriptArgs->m_VarsLocal.SetNumNew("ITEMDAMAGEAMOUNT", 1); + if ( IsTrigUsed(TRIGGER_SKILLSUCCESS) ) { - if (Skill_OnCharTrigger(skill, CTRIG_SkillSuccess, &args) == TRIGRET_RET_TRUE) + if (Skill_OnCharTrigger(skill, CTRIG_SkillSuccess, pScriptArgs) == TRIGRET_RET_TRUE) return -SKTRIG_ABORT; } if ( IsTrigUsed(TRIGGER_SUCCESS) ) { - if (Skill_OnTrigger(skill, SKTRIG_SUCCESS, &args) == TRIGRET_RET_TRUE) + if (Skill_OnTrigger(skill, SKTRIG_SUCCESS, pScriptArgs) == TRIGRET_RET_TRUE) return -SKTRIG_ABORT; } - int chance = std::min(std::max((int)args.m_VarsLocal.GetKeyNum("ITEMDAMAGECHANCE"), (int)0), (int)100); - if (IsSetEF(EF_DamageTools) && g_Cfg.IsSkillFlag(skill, SKF_GATHER) && chance > 0) + const int iChance = (int)std::clamp( + pScriptArgs->m_VarsLocal.GetKeyNum("ITEMDAMAGECHANCE"), + (int64)0, (int64)100); + if (IsSetEF(EF_DamageTools) && g_Cfg.IsSkillFlag(skill, SKF_GATHER) && iChance > 0) { CItem* pTool = LayerFind(LAYER_HAND1); if (!pTool || !pTool->IsTypeWeapon()) @@ -3951,10 +3950,10 @@ int CChar::Skill_Done() if (pTool && pTool->IsTypeWeapon()) { - if (g_Rand.GetVal(100) < chance) + if (g_Rand.GetVal(100) < iChance) { - int amount = std::max(std::min((int)args.m_VarsLocal.GetKeyNum("ITEMDAMAGEAMOUNT"), (int)pTool->m_itWeapon.m_wHitsCur), 0); - pTool->OnTakeDamage(amount, nullptr, DAMAGE_GOD); + const int iAmount = std::max(std::min((int)pScriptArgs->m_VarsLocal.GetKeyNum("ITEMDAMAGEAMOUNT"), (int)pTool->m_itWeapon.m_wHitsCur), 0); + pTool->OnTakeDamage(iAmount, nullptr, DAMAGE_GOD); } } } @@ -3979,11 +3978,12 @@ bool CChar::Skill_Wait( SKILL_TYPE skilltry ) // If this is the same skill then tell them to wait. SKILL_TYPE skill = Skill_GetActive(); - CScriptTriggerArgs pArgs(skilltry, skill); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(skilltry, skill, 0, nullptr); if ( IsTrigUsed(TRIGGER_SKILLWAIT) ) { - switch ( Skill_OnCharTrigger(skilltry, CTRIG_SkillWait, &pArgs) ) + switch ( Skill_OnCharTrigger(skilltry, CTRIG_SkillWait, pScriptArgs) ) { case TRIGRET_RET_TRUE: return true; @@ -3996,7 +3996,7 @@ bool CChar::Skill_Wait( SKILL_TYPE skilltry ) } if ( IsTrigUsed(TRIGGER_WAIT) ) { - switch ( Skill_OnTrigger(skilltry, SKTRIG_WAIT, &pArgs) ) + switch ( Skill_OnTrigger(skilltry, SKTRIG_WAIT, pScriptArgs) ) { case TRIGRET_RET_TRUE: return true; @@ -4016,7 +4016,9 @@ bool CChar::Skill_Wait( SKILL_TYPE skilltry ) if ( skill == SKILL_NONE ) // not currently doing anything. { - if ((skilltry == SKILL_STEALTH) || ((skilltry == SKILL_SNOOPING) && (g_Cfg.m_iRevealFlags & REVEALF_SNOOPING)) || ((skilltry == SKILL_STEALING) && (g_Cfg.m_iRevealFlags & REVEALF_STEALING))) + if ((skilltry == SKILL_STEALTH) + || ((skilltry == SKILL_SNOOPING) && (g_Cfg.m_iRevealFlags & REVEALF_SNOOPING)) + || ((skilltry == SKILL_STEALING) && (g_Cfg.m_iRevealFlags & REVEALF_STEALING))) return false; else Reveal(); @@ -4419,16 +4421,11 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) anim = Skill_GetAnim(skill); } - CScriptTriggerArgs pArgs; - pArgs.m_iN1 = skill; - pArgs.m_VarsLocal.SetNumNew("Sound", sound); - pArgs.m_VarsLocal.SetNumNew("Anim", anim); - // Some skill can start right away. Need no targetting. // 0-100 scale of Difficulty if ( IsTrigUsed(TRIGGER_SKILLPRESTART) ) { - if ( Skill_OnCharTrigger(skill, CTRIG_SkillPreStart) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger(skill, CTRIG_SkillPreStart, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return false; @@ -4436,7 +4433,7 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) } if ( IsTrigUsed(TRIGGER_PRESTART) ) { - if ( Skill_OnTrigger(skill, SKTRIG_PRESTART) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_PRESTART, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return false; @@ -4469,18 +4466,23 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) m_Act_Effect = pSkillDef->m_vcEffect.GetRandom(); } } - pArgs.m_iN2 = iWaitTime; - // Execute the @START trigger and pass various craft parameters there - CResourceID pResBase(RES_ITEMDEF, (fCraftSkill ? m_atCreate.m_iItemID : ITEMID_NOTHING), 0); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = skill; + pScriptArgs->m_iN2 = iWaitTime; + pScriptArgs->m_VarsLocal.SetNumNew("Sound", sound); + pScriptArgs->m_VarsLocal.SetNumNew("Anim", anim); + // Execute the @START trigger and pass various craft parameters there if ( fCraftSkill ) { - m_atCreate.m_dwStrokeCount = 1; // This matches the new strokes amount used on OSI. - pArgs.m_VarsLocal.SetNum("CraftItemdef", pResBase.GetPrivateUID()); - // pArgs.m_VarsLocal.SetStr("CraftItemdef", g_Cfg.ResourceGetName(pResBase), false); - pArgs.m_VarsLocal.SetNum("CraftStrokeCnt", m_atCreate.m_dwStrokeCount); - pArgs.m_VarsLocal.SetNum("CraftAmount", m_atCreate.m_dwAmount); + m_atCreate.m_dwStrokeCount = 1; // This matches the new strokes amount used on OSI. + + const CResourceID ridResBase(RES_ITEMDEF, (fCraftSkill ? m_atCreate.m_iItemID : ITEMID_NOTHING), 0); + pScriptArgs->m_VarsLocal.SetNum("CraftItemdef", ridResBase.GetPrivateUID()); + // ppScriptArgs->m_VarsLocal.SetStr("CraftItemdef", g_Cfg.ResourceGetName(pResBase), false); + pScriptArgs->m_VarsLocal.SetNum("CraftStrokeCnt", m_atCreate.m_dwStrokeCount); + pScriptArgs->m_VarsLocal.SetNum("CraftAmount", m_atCreate.m_dwAmount); } if ( fGatherSkill ) { @@ -4489,33 +4491,33 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) m_Act_UID = pResBit->GetUID(); m_atResource.m_dwBounceItem = 1; - pArgs.m_VarsLocal.SetNum("GatherStrokeCnt", m_atResource.m_dwStrokeCount); + pScriptArgs->m_VarsLocal.SetNum("GatherStrokeCnt", m_atResource.m_dwStrokeCount); } if ( IsTrigUsed(TRIGGER_SKILLSTART) ) { //If we are using a combat skill and m_Act_Difficult(actdiff) is < 0 combat will be blocked. - if ( (Skill_OnCharTrigger(skill, CTRIG_SkillStart, &pArgs) == TRIGRET_RET_TRUE) || (m_Act_Difficulty < 0) ) + if ( (Skill_OnCharTrigger(skill, CTRIG_SkillStart, pScriptArgs) == TRIGRET_RET_TRUE) || (m_Act_Difficulty < 0) ) { Skill_Cleanup(); return false; } - sound = (SOUND_TYPE)(pArgs.m_VarsLocal.GetKeyNum("Sound")); - anim = static_cast(pArgs.m_VarsLocal.GetKeyNum("Anim")); + sound = (SOUND_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("Sound")); + anim = (ANIM_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("Anim")); } if ( IsTrigUsed(TRIGGER_START) ) { //If we are using a combat skill and m_Act_Difficulty(actdiff) is < 0 combat will be blocked. - if ( (Skill_OnTrigger(skill, SKTRIG_START, &pArgs) == TRIGRET_RET_TRUE) || (m_Act_Difficulty < 0) ) + if ( (Skill_OnTrigger(skill, SKTRIG_START, pScriptArgs) == TRIGRET_RET_TRUE) || (m_Act_Difficulty < 0) ) { Skill_Cleanup(); return false; } - sound = (SOUND_TYPE)(pArgs.m_VarsLocal.GetKeyNum("Sound")); - anim = static_cast(pArgs.m_VarsLocal.GetKeyNum("Anim")); + sound = (SOUND_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("Sound")); + anim = (ANIM_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("Anim")); } - iWaitTime = (int)pArgs.m_iN2; + iWaitTime = (int)pScriptArgs->m_iN2; if (IsSkillBase(skill) && iWaitTime > 0) SetTimeoutD(iWaitTime); // How long before complete skill. @@ -4527,14 +4529,14 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) if ( fCraftSkill ) { // read crafting parameters - pResBase = CResourceID( (dword)(pArgs.m_VarsLocal.GetKeyNum("CraftItemdef")), 0 ); - m_atCreate.m_dwStrokeCount = (word)pArgs.m_VarsLocal.GetKeyNum("CraftStrokeCnt"); + const CResourceID ridResBase( (dword)(pScriptArgs->m_VarsLocal.GetKeyNum("CraftItemdef")), 0 ); + m_atCreate.m_dwStrokeCount = (word)pScriptArgs->m_VarsLocal.GetKeyNum("CraftStrokeCnt"); m_atCreate.m_dwStrokeCount = maximum(1,m_atCreate.m_dwStrokeCount); - m_atCreate.m_iItemID = (ITEMID_TYPE)(pResBase.GetResIndex()); - m_atCreate.m_dwAmount = (word)(pArgs.m_VarsLocal.GetKeyNum("CraftAmount")); + m_atCreate.m_iItemID = (ITEMID_TYPE)(ridResBase.GetResIndex()); + m_atCreate.m_dwAmount = (word)(pScriptArgs->m_VarsLocal.GetKeyNum("CraftAmount")); } if ( fGatherSkill ) - m_atResource.m_dwStrokeCount = (word)(pArgs.m_VarsLocal.GetKeyNum("GatherStrokeCnt")); + m_atResource.m_dwStrokeCount = (word)(pScriptArgs->m_VarsLocal.GetKeyNum("GatherStrokeCnt")); // Casting sound & animation when starting, Skill_Stroke() will do it the next times. if ( fCraftSkill || fGatherSkill ) diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index a39b1a773..6a5dbc413 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -1,5 +1,6 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/CClientIterator.h" #include "../../network/send.h" #include "../components/CCPropsChar.h" @@ -378,10 +379,10 @@ bool CChar::Spell_Recall(CItem * pRune, bool fGate) ADDTOCALLSTACK("CChar::Spell_Recall"); if (pRune && (IsTrigUsed(TRIGGER_SPELLEFFECT) || IsTrigUsed(TRIGGER_ITEMSPELL))) { - CScriptTriggerArgs Args; - Args.m_iN1 = fGate ? SPELL_Gate_Travel : SPELL_Recall; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = fGate ? SPELL_Gate_Travel : SPELL_Recall; - if (pRune->OnTrigger(ITRIG_SPELLEFFECT, this, &Args) == TRIGRET_RET_FALSE) + if (pRune->OnTrigger(ITRIG_SPELLEFFECT, pScriptArgs, this) == TRIGRET_RET_FALSE) return true; } @@ -446,22 +447,25 @@ bool CChar::Spell_Resurrection(CItemCorpse * pCorpse, CChar * pCharSrc, bool fNo return false; } - ushort hits = (ushort)IMulDiv(Stat_GetMaxAdjusted(STAT_STR), g_Cfg.m_iHitpointPercentOnRez, 100); + ushort uiHits = (ushort)IMulDiv(Stat_GetMaxAdjusted(STAT_STR), g_Cfg.m_iHitpointPercentOnRez, 100); if (!pCorpse) pCorpse = FindMyCorpse(); if (IsTrigUsed(TRIGGER_RESURRECT)) { - CScriptTriggerArgs Args(hits, 0, pCorpse); - if (OnTrigger(CTRIG_Resurrect, pCharSrc, &Args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(uiHits, 0, 0, pCorpse); + + if (OnTrigger(CTRIG_Resurrect, pScriptArgs, pCharSrc) == TRIGRET_RET_TRUE) return false; - hits = (ushort)(Args.m_iN1); + + uiHits = (ushort)(pScriptArgs->m_iN1); } SetID(_iPrev_id); SetHue(_wPrev_Hue); StatFlag_Clear(STATF_DEAD|STATF_INSUBSTANTIAL); - Stat_SetVal(STAT_STR, maximum(hits, 1)); + Stat_SetVal(STAT_STR, maximum(uiHits, 1)); if (m_pNPC && m_pNPC->m_bonded) m_CanMask &= ~CAN_C_GHOST; @@ -553,19 +557,21 @@ void CChar::Spell_Effect_Remove(CItem * pSpell) if (IsTrigUsed(TRIGGER_SPELLEFFECTREMOVE)) { - CScriptTriggerArgs Args; - Args.m_pO1 = pSpell; - Args.m_iN1 = spell; - TRIGRET_TYPE iRet = OnTrigger(CTRIG_SpellEffectRemove, pCaster, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pSpell; + pScriptArgs->m_iN1 = spell; + + TRIGRET_TYPE iRet = OnTrigger(CTRIG_SpellEffectRemove, pScriptArgs, pCaster); if (iRet == TRIGRET_RET_FALSE) // Return 0: remove the spell memory item but don't execute the default spell behaviour. return; } if (IsTrigUsed(TRIGGER_EFFECTREMOVE)) { - CScriptTriggerArgs Args; - Args.m_pO1 = pSpell; - Args.m_iN1 = spell; - TRIGRET_TYPE iRet = Spell_OnTrigger(spell, SPTRIG_EFFECTREMOVE, pCaster, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pSpell; + pScriptArgs->m_iN1 = spell; + + TRIGRET_TYPE iRet = Spell_OnTrigger(spell, SPTRIG_EFFECTREMOVE, pScriptArgs, pCaster); if (iRet == TRIGRET_RET_FALSE) // Return 0: remove the spell memory item but don't execute the default spell behaviour. return; } @@ -977,10 +983,11 @@ void CChar::Spell_Effect_Add( CItem * pSpell ) if (IsTrigUsed(TRIGGER_SPELLEFFECTADD)) { - CScriptTriggerArgs Args; - Args.m_pO1 = pSpell; - Args.m_iN1 = spell; - TRIGRET_TYPE iRet = OnTrigger(CTRIG_SpellEffectAdd, pCaster, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pSpell; + pScriptArgs->m_iN1 = spell; + + TRIGRET_TYPE iRet = OnTrigger(CTRIG_SpellEffectAdd, pScriptArgs, pCaster); if (iRet == TRIGRET_RET_TRUE) // Return 1: We don't want nothing to happen, removing memory also. { pSpell->Delete(true); @@ -992,10 +999,11 @@ void CChar::Spell_Effect_Add( CItem * pSpell ) if (IsTrigUsed(TRIGGER_EFFECTADD)) { - CScriptTriggerArgs Args; - Args.m_pO1 = pSpell; - Args.m_iN1 = spell; - TRIGRET_TYPE iRet = Spell_OnTrigger(spell,SPTRIG_EFFECTADD, pCaster, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pSpell; + pScriptArgs->m_iN1 = spell; + + TRIGRET_TYPE iRet = Spell_OnTrigger(spell,SPTRIG_EFFECTADD, pScriptArgs, pCaster); if (iRet == TRIGRET_RET_TRUE) // Return 1: We don't want nothing to happen, removing memory also. { pSpell->Delete(true); @@ -1962,15 +1970,17 @@ bool CChar::Spell_Equip_OnTick( CItem * pItem ) } break; } - CScriptTriggerArgs Args((int)(spell), iLevel, pItem); - Args.m_VarsLocal.SetNum("Charges", iCharges); - Args.m_VarsLocal.SetNum("Delay", iSecondsDelay); - Args.m_VarsLocal.SetNum("DamageType", iDmgType); - Args.m_VarsLocal.SetNum("Effect", iEffect); + + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init((int)spell, iLevel, 0, pItem); + pScriptArgs->m_VarsLocal.SetNum("Charges", iCharges); + pScriptArgs->m_VarsLocal.SetNum("Delay", iSecondsDelay); + pScriptArgs->m_VarsLocal.SetNum("DamageType", iDmgType); + pScriptArgs->m_VarsLocal.SetNum("Effect", iEffect); if (IsTrigUsed(TRIGGER_SPELLEFFECTTICK)) { - switch (OnTrigger(CTRIG_SpellEffectTick, this, &Args)) + switch (OnTrigger(CTRIG_SpellEffectTick, pScriptArgs, this)) { case TRIGRET_RET_TRUE: pItem->Delete(true); return false; case TRIGRET_RET_FALSE: if (pSpellDef->IsSpellType(SPELLFLAG_SCRIPTED)) return true; @@ -1980,21 +1990,21 @@ bool CChar::Spell_Equip_OnTick( CItem * pItem ) if (IsTrigUsed(TRIGGER_EFFECTTICK)) { - switch (Spell_OnTrigger(spell, SPTRIG_EFFECTTICK, this, &Args)) + switch (Spell_OnTrigger(spell, SPTRIG_EFFECTTICK, pScriptArgs, this)) { case TRIGRET_RET_TRUE: pItem->Delete(true); return false; case TRIGRET_RET_FALSE: if (pSpellDef->IsSpellType(SPELLFLAG_SCRIPTED)) return true; default: break; } } - iLevel = (int)(Args.m_iN2); //This is probably not necessary. - iSecondsDelay = (int64)(Args.m_VarsLocal.GetKeyNum("Delay")); - iEffect = (int)(Args.m_VarsLocal.GetKeyNum("Effect")); - iCharges = (int)(Args.m_VarsLocal.GetKeyNum("Charges")); + iLevel = (int)(pScriptArgs->m_iN2); //This is probably not necessary. + iSecondsDelay = (int64)(pScriptArgs->m_VarsLocal.GetKeyNum("Delay")); + iEffect = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("Effect")); + iCharges = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("Charges")); if (pSpellDef->IsSpellType(SPELLFLAG_HARM)) { - iDmgType = (DAMAGE_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("DamageType"))); + iDmgType = (DAMAGE_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("DamageType"))); if (iDmgType > 0 && iEffect > 0) // This is necessary if we have a spell that is harmful but does no damage periodically. { // @@ -2354,16 +2364,17 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo ushort uiManaUse = g_Cfg.Calc_SpellManaCost(this, pSpellDef, pSrc); ushort uiTithingUse = g_Cfg.Calc_SpellTithingCost(this, pSpellDef, pSrc); - CScriptTriggerArgs Args( spellRef, uiManaUse, pSrc ); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(spellRef, uiManaUse, 0, pSrc); if ( fTest ) - Args.m_iN3 |= 0x0001; + pScriptArgs->m_iN3 |= 0x0001; if ( fFailMsg ) - Args.m_iN3 |= 0x0002; - Args.m_VarsLocal.SetNum("TithingUse",uiTithingUse); + pScriptArgs->m_iN3 |= 0x0002; + pScriptArgs->m_VarsLocal.SetNum("TithingUse",uiTithingUse); if ( IsTrigUsed(TRIGGER_SELECT) ) { - TRIGRET_TYPE iRet = Spell_OnTrigger( spellRef, SPTRIG_SELECT, this, &Args ); + TRIGRET_TYPE iRet = Spell_OnTrigger( spellRef, SPTRIG_SELECT, pScriptArgs, this ); if ( iRet == TRIGRET_RET_TRUE ) return false; @@ -2376,7 +2387,7 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo if ( IsTrigUsed(TRIGGER_SPELLSELECT) ) { - TRIGRET_TYPE iRet = OnTrigger(CTRIG_SpellSelect, this, &Args ); + TRIGRET_TYPE iRet = OnTrigger(CTRIG_SpellSelect, pScriptArgs, this ); if ( iRet == TRIGRET_RET_TRUE ) return false; @@ -2384,15 +2395,15 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo return true; } - if ( spellRef != Args.m_iN1 ) + if ( spellRef != pScriptArgs->m_iN1 ) { pSpellDef = g_Cfg.GetSpellDef(spellRef); if ( pSpellDef == nullptr ) return false; - spellRef = (SPELL_TYPE)(Args.m_iN1); + spellRef = (SPELL_TYPE)(pScriptArgs->m_iN1); } - uiManaUse = (ushort)(Args.m_iN2); - uiTithingUse = (ushort)(Args.m_VarsLocal.GetKeyNum("TithingUse")); + uiManaUse = (ushort)(pScriptArgs->m_iN2); + uiTithingUse = (ushort)(pScriptArgs->m_VarsLocal.GetKeyNum("TithingUse")); if ( !pSrc->IsChar() )// Looking for non-character sources { @@ -2914,18 +2925,19 @@ bool CChar::Spell_CastDone() uint uiFieldGauge = 0; uint uiAreaRadius = 0; - CScriptTriggerArgs Args(spell, iSkillLevel, pObjSrc); - Args.m_VarsLocal.SetNum("Duration", GetSpellDuration(spell, iSkillLevel, this), true); // tenths of second + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(spell, iSkillLevel, 0, pObjSrc); + pScriptArgs->m_VarsLocal.SetNum("Duration", GetSpellDuration(spell, iSkillLevel, this), true); // tenths of second if (fIsSpellArea) { - Args.m_VarsLocal.SetNum("AreaRadius", 0); + pScriptArgs->m_VarsLocal.SetNum("AreaRadius", 0); } if (fIsSpellField) { - Args.m_VarsLocal.SetNum("FieldWidth", 0); - Args.m_VarsLocal.SetNum("FieldGauge", 0); + pScriptArgs->m_VarsLocal.SetNum("FieldWidth", 0); + pScriptArgs->m_VarsLocal.SetNum("FieldGauge", 0); switch (spell) // Only setting ids and locals for field spells { @@ -2937,28 +2949,28 @@ bool CChar::Spell_CastDone() default: break; } - Args.m_VarsLocal.SetNum("CreateObject1", uiCreatedItemID_1, false); - Args.m_VarsLocal.SetNum("CreateObject2", uiCreatedItemID_2, false); + pScriptArgs->m_VarsLocal.SetNum("CreateObject1", uiCreatedItemID_1, false); + pScriptArgs->m_VarsLocal.SetNum("CreateObject2", uiCreatedItemID_2, false); } if (fIsSpellSummon) { - Args.m_VarsLocal.SetNum("FollowerSlotsOverride", iFollowerSlotsOverride); + pScriptArgs->m_VarsLocal.SetNum("FollowerSlotsOverride", iFollowerSlotsOverride); } if (IsTrigUsed(TRIGGER_SPELLSUCCESS)) { - if (OnTrigger(CTRIG_SpellSuccess, this, &Args) == TRIGRET_RET_TRUE) + if (OnTrigger(CTRIG_SpellSuccess, pScriptArgs, this) == TRIGRET_RET_TRUE) return false; } if (IsTrigUsed(TRIGGER_SUCCESS)) { - if (Spell_OnTrigger(spell, SPTRIG_SUCCESS, this, &Args) == TRIGRET_RET_TRUE) + if (Spell_OnTrigger(spell, SPTRIG_SUCCESS, pScriptArgs, this) == TRIGRET_RET_TRUE) return false; } - iSkillLevel = (int)(Args.m_iN2); + iSkillLevel = (int)(pScriptArgs->m_iN2); ITEMID_TYPE it1test = ITEMID_NOTHING; ITEMID_TYPE it2test = ITEMID_NOTHING; @@ -2966,20 +2978,20 @@ bool CChar::Spell_CastDone() if (fIsSpellField) { //Setting new IDs as another variables to pass as different arguments to the field function. - it1test = (ITEMID_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("CreateObject1"))); - it2test = (ITEMID_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("CreateObject2"))); - uiFieldWidth = (uint)Args.m_VarsLocal.GetKeyNum("FieldWidth"); - uiFieldGauge = (uint)Args.m_VarsLocal.GetKeyNum("FieldGauge"); + it1test = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("CreateObject1"))); + it2test = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("CreateObject2"))); + uiFieldWidth = (uint)pScriptArgs->m_VarsLocal.GetKeyNum("FieldWidth"); + uiFieldGauge = (uint)pScriptArgs->m_VarsLocal.GetKeyNum("FieldGauge"); } - uiSummonedCreatureID = (CREID_TYPE)(Args.m_VarsLocal.GetKeyNum("CreateObject1") & 0xFFFF); - uiAreaRadius = (uint)Args.m_VarsLocal.GetKeyNum("AreaRadius"); - int iDuration = (int)(std::max((int64)0, Args.m_VarsLocal.GetKeyNum("Duration"))); - uiColor = (HUE_TYPE)(Args.m_VarsLocal.GetKeyNum("EffectColor")); + uiSummonedCreatureID = (CREID_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("CreateObject1") & 0xFFFF); + uiAreaRadius = (uint)pScriptArgs->m_VarsLocal.GetKeyNum("AreaRadius"); + int iDuration = (int)(std::max((int64)0, pScriptArgs->m_VarsLocal.GetKeyNum("Duration"))); + uiColor = (HUE_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("EffectColor")); if (fIsSpellSummon) { - iFollowerSlotsOverride = n64_narrow_n16(Args.m_VarsLocal.GetKeyNum("FollowerSlotsOverride")); + iFollowerSlotsOverride = n64_narrow_n16(pScriptArgs->m_VarsLocal.GetKeyNum("FollowerSlotsOverride")); if (!pSpellDef->IsSpellType(SPELLFLAG_TARG_OBJ | SPELLFLAG_TARG_XYZ)) m_Act_p = GetTopPoint(); @@ -3328,29 +3340,30 @@ void CChar::Spell_CastFail(bool fAbort) iTithingLoss = g_Cfg.Calc_SpellTithingCost(this, pSpell, m_Act_Prv_UID.ObjFind()); } - CScriptTriggerArgs Args( m_atMagery.m_iSpell, iManaLoss, m_Act_Prv_UID.ObjFind() ); - Args.m_VarsLocal.SetNum("CreateObject1",iT1); - Args.m_VarsLocal.SetNum("TithingLoss", iTithingLoss); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(m_atMagery.m_iSpell, iManaLoss, 0, m_Act_Prv_UID.ObjFind()); + pScriptArgs->m_VarsLocal.SetNum("CreateObject1",iT1); + pScriptArgs->m_VarsLocal.SetNum("TithingLoss", iTithingLoss); if ( IsTrigUsed(TRIGGER_SPELLFAIL) ) { - if ( OnTrigger( CTRIG_SpellFail, this, &Args ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_SpellFail, pScriptArgs, this ) == TRIGRET_RET_TRUE ) return; } if ( IsTrigUsed(TRIGGER_FAIL) ) { - if ( Spell_OnTrigger( m_atMagery.m_iSpell, SPTRIG_FAIL, this, &Args ) == TRIGRET_RET_TRUE ) + if ( Spell_OnTrigger( m_atMagery.m_iSpell, SPTRIG_FAIL, pScriptArgs, this ) == TRIGRET_RET_TRUE ) return; } - iManaLoss = (ushort)Args.m_iN2; - iTithingLoss = (ushort)Args.m_VarsLocal.GetKeyNum("TithingLoss"); + iManaLoss = (ushort)pScriptArgs->m_iN2; + iTithingLoss = (ushort)pScriptArgs->m_VarsLocal.GetKeyNum("TithingLoss"); - HUE_TYPE iColor = (HUE_TYPE)(Args.m_VarsLocal.GetKeyNum("EffectColor")); - dword dwRender = (dword)Args.m_VarsLocal.GetKeyNum("EffectRender"); + HUE_TYPE iColor = (HUE_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("EffectColor")); + dword dwRender = (dword)pScriptArgs->m_VarsLocal.GetKeyNum("EffectRender"); - iT1 = (ITEMID_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("CreateObject1"))); + iT1 = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("CreateObject1"))); if (iT1) Effect(EFFECT_OBJ, iT1, this, 1, 30, false, iColor, dwRender); Sound( SOUND_SPELL_FIZZLE ); @@ -3492,9 +3505,10 @@ int CChar::Spell_CastStart() if ( iWaitTime < 1 ) iWaitTime = 1; - CScriptTriggerArgs Args((int)m_atMagery.m_iSpell, iDifficulty, pItem); - Args.m_iN3 = iWaitTime; - Args.m_VarsLocal.SetNum("WOP", fWOP); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init((int)m_atMagery.m_iSpell, iDifficulty, 0, pItem); + pScriptArgs->m_iN3 = iWaitTime; + pScriptArgs->m_VarsLocal.SetNum("WOP", fWOP); int64 WOPFont = g_Cfg.m_iWordsOfPowerFont; int64 WOPColor; TALKMODE_TYPE WOPTalkMode = g_Cfg.m_iWordsOfPowerTalkMode ? g_Cfg.m_iWordsOfPowerTalkMode : TALKMODE_SPELL; @@ -3511,19 +3525,19 @@ int CChar::Spell_CastStart() else WOPColor = HUE_TEXT_DEF; - Args.m_VarsLocal.SetNum("WOPColor", WOPColor, true); - Args.m_VarsLocal.SetNum("WOPFont", WOPFont, true); - Args.m_VarsLocal.SetNum("WOPTalkMode", WOPTalkMode, true); + pScriptArgs->m_VarsLocal.SetNum("WOPColor", WOPColor, true); + pScriptArgs->m_VarsLocal.SetNum("WOPFont", WOPFont, true); + pScriptArgs->m_VarsLocal.SetNum("WOPTalkMode", WOPTalkMode, true); if ( IsTrigUsed(TRIGGER_SPELLCAST) ) { - if ( OnTrigger(CTRIG_SpellCast, this, &Args) == TRIGRET_RET_TRUE ) + if ( OnTrigger(CTRIG_SpellCast, pScriptArgs, this) == TRIGRET_RET_TRUE ) return -1; } if ( IsTrigUsed(TRIGGER_START) ) { - if ( Spell_OnTrigger((SPELL_TYPE)(Args.m_iN1), SPTRIG_START, this, &Args) == TRIGRET_RET_TRUE ) + if ( Spell_OnTrigger((SPELL_TYPE)(pScriptArgs->m_iN1), SPTRIG_START, pScriptArgs, this) == TRIGRET_RET_TRUE ) return -1; } @@ -3536,9 +3550,9 @@ int CChar::Spell_CastStart() return -1; } - m_atMagery.m_iSpell = (SPELL_TYPE)Args.m_iN1; - iDifficulty = (int)Args.m_iN2; - iWaitTime = Args.m_iN3; + m_atMagery.m_iSpell = (SPELL_TYPE)pScriptArgs->m_iN1; + iDifficulty = (int)pScriptArgs->m_iN2; + iWaitTime = pScriptArgs->m_iN3; pSpellDef = g_Cfg.GetSpellDef(m_atMagery.m_iSpell); if ( !pSpellDef ) @@ -3553,12 +3567,12 @@ int CChar::Spell_CastStart() if ( !pSpellDef->IsSpellType(SPELLFLAG_NO_CASTANIM) && !IsSetMagicFlags(MAGICF_NOANIM) ) UpdateAnimate(pSpellDef->IsSpellType(SPELLFLAG_DIR_ANIM) ? ANIM_CAST_DIR : ANIM_CAST_AREA); - fWOP = Args.m_VarsLocal.GetKeyNum("WOP") > 0 ? true : false; + fWOP = pScriptArgs->m_VarsLocal.GetKeyNum("WOP") > 0 ? true : false; if ( fWOP ) { - WOPColor = Args.m_VarsLocal.GetKeyNum("WOPColor"); - WOPFont = Args.m_VarsLocal.GetKeyNum("WOPFont"); - WOPTalkMode = (TALKMODE_TYPE)Args.m_VarsLocal.GetKeyNum("WOPTalkMode"); + WOPColor = pScriptArgs->m_VarsLocal.GetKeyNum("WOPColor"); + WOPFont = pScriptArgs->m_VarsLocal.GetKeyNum("WOPFont"); + WOPTalkMode = (TALKMODE_TYPE)pScriptArgs->m_VarsLocal.GetKeyNum("WOPTalkMode"); // Correct talk mode for spells WOP is TALKMODE_SPELL, but sphere doesn't have any delay between spell casts this can allow WOP flood on screen. if ( pSpellDef->m_sRunes[0] == '.' ) @@ -3686,18 +3700,19 @@ bool CChar::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, } } - CScriptTriggerArgs Args((int)(spell), iSkillLevel, pSourceItem); - Args.m_VarsLocal.SetNum("DamageType", 0); - Args.m_VarsLocal.SetNum("CreateObject1", pSpellDef->m_idEffect); - Args.m_VarsLocal.SetNum("Explode", fExplode); - Args.m_VarsLocal.SetNum("Sound", iSound); - Args.m_VarsLocal.SetNum("Effect", iEffect); - Args.m_VarsLocal.SetNum("Resist", uiResist); - Args.m_VarsLocal.SetNum("Duration", iDuration); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init((int)spell, iSkillLevel, 0, pSourceItem); + pScriptArgs->m_VarsLocal.SetNum("DamageType", 0); + pScriptArgs->m_VarsLocal.SetNum("CreateObject1", pSpellDef->m_idEffect); + pScriptArgs->m_VarsLocal.SetNum("Explode", fExplode); + pScriptArgs->m_VarsLocal.SetNum("Sound", iSound); + pScriptArgs->m_VarsLocal.SetNum("Effect", iEffect); + pScriptArgs->m_VarsLocal.SetNum("Resist", uiResist); + pScriptArgs->m_VarsLocal.SetNum("Duration", iDuration); if ( IsTrigUsed(TRIGGER_SPELLEFFECT) ) { - switch ( OnTrigger(CTRIG_SpellEffect, pCharSrc ? pCharSrc : this, &Args) ) + switch ( OnTrigger(CTRIG_SpellEffect, pScriptArgs, pCharSrc ? pCharSrc : this) ) { case TRIGRET_RET_TRUE: return false; case TRIGRET_RET_FALSE: if ( pSpellDef->IsSpellType(SPELLFLAG_SCRIPTED) ) return true; @@ -3707,7 +3722,7 @@ bool CChar::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, if ( IsTrigUsed(TRIGGER_EFFECT) ) { - switch ( Spell_OnTrigger(spell, SPTRIG_EFFECT, pCharSrc ? pCharSrc : this, &Args) ) + switch ( Spell_OnTrigger(spell, SPTRIG_EFFECT, pScriptArgs, pCharSrc ? pCharSrc : this) ) { case TRIGRET_RET_TRUE: return false; case TRIGRET_RET_FALSE: if ( pSpellDef->IsSpellType(SPELLFLAG_SCRIPTED) ) return true; @@ -3715,18 +3730,18 @@ bool CChar::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, } } - spell = (SPELL_TYPE)(Args.m_iN1); - iSkillLevel = (int)(Args.m_iN2); // remember that effect/duration is calculated before triggers - DAMAGE_TYPE iDmgType = (DAMAGE_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("DamageType"))); - ITEMID_TYPE iEffectID = (ITEMID_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("CreateObject1"))); - fExplode = Args.m_VarsLocal.GetKeyNum("EffectExplode") > 0 ? true : false; - iSound = (SOUND_TYPE)(Args.m_VarsLocal.GetKeyNum("Sound")); - iEffect = (int)(Args.m_VarsLocal.GetKeyNum("Effect")); - uiResist = (ushort)(Args.m_VarsLocal.GetKeyNum("Resist")); - iDuration = (int)(Args.m_VarsLocal.GetKeyNum("Duration")); - - HUE_TYPE iColor = (HUE_TYPE)Args.m_VarsLocal.GetKeyNum("EffectColor"); - dword dwRender = (dword)Args.m_VarsLocal.GetKeyNum("EffectRender"); + spell = (SPELL_TYPE)(pScriptArgs->m_iN1); + iSkillLevel = (int)(pScriptArgs->m_iN2); // remember that effect/duration is calculated before triggers + DAMAGE_TYPE iDmgType = (DAMAGE_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("DamageType"))); + ITEMID_TYPE iEffectID = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("CreateObject1"))); + fExplode = pScriptArgs->m_VarsLocal.GetKeyNum("EffectExplode") > 0 ? true : false; + iSound = (SOUND_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("Sound")); + iEffect = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("Effect")); + uiResist = (ushort)(pScriptArgs->m_VarsLocal.GetKeyNum("Resist")); + iDuration = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("Duration")); + + HUE_TYPE iColor = (HUE_TYPE)pScriptArgs->m_VarsLocal.GetKeyNum("EffectColor"); + dword dwRender = (dword)pScriptArgs->m_VarsLocal.GetKeyNum("EffectRender"); if ( iEffectID > ITEMID_QTY ) iEffectID = pSpellDef->m_idEffect; diff --git a/src/game/chars/CCharStat.cpp b/src/game/chars/CCharStat.cpp index 7e7df141c..13c6cc0ba 100644 --- a/src/game/chars/CCharStat.cpp +++ b/src/game/chars/CCharStat.cpp @@ -3,7 +3,7 @@ #include "../../common/resource/sections/CSkillClassDef.h" #include "../../common/CException.h" #include "../../common/CExpression.h" -#include "../../common/CScriptTriggerArgs.h" +#include "../../common/CScriptParserBufs.h" #include "../items/CItem.h" #include "../triggers.h" #include "../CServer.h" @@ -33,15 +33,15 @@ void CChar::Stat_SetMod( STAT_TYPE i, int iVal ) { if ( i >= STAT_STR && i <= STAT_DEX ) { - CScriptTriggerArgs args; - args.m_iN1 = i + 8LL; // shift by 8 to indicate modSTR, modINT, modDEX - args.m_iN2 = iStatVal; - args.m_iN3 = iVal; - if ( OnTrigger(CTRIG_StatChange, this, &args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = i + 8LL; // shift by 8 to indicate modSTR, modINT, modDEX + pScriptArgs->m_iN2 = iStatVal; + pScriptArgs->m_iN3 = iVal; + if ( OnTrigger(CTRIG_StatChange, pScriptArgs, this) == TRIGRET_RET_TRUE ) return; // do not restore argn1 to i, bad things will happen! leave i untouched. (matex) - iVal = (int)(args.m_iN3); + iVal = (int)(pScriptArgs->m_iN3); } } @@ -89,14 +89,14 @@ void CChar::Stat_SetMaxMod( STAT_TYPE i, int iVal ) { if ( (i >= STAT_STR) && (i <= STAT_DEX) ) { - CScriptTriggerArgs args; - args.m_iN1 = i + 12LL; // shift by 12 to indicate modMaxHits, modMaxMana, modMaxStam - args.m_iN2 = iStatVal; - args.m_iN3 = iVal; - if ( OnTrigger(CTRIG_StatChange, this, &args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = i + 12LL; // shift by 12 to indicate modMaxHits, modMaxMana, modMaxStam + pScriptArgs->m_iN2 = iStatVal; + pScriptArgs->m_iN3 = iVal; + if ( OnTrigger(CTRIG_StatChange, pScriptArgs, this) == TRIGRET_RET_TRUE ) return; // do not restore argn1 to i, bad things will happen! leave i untouched. (matex) - iVal = (int)(args.m_iN3); + iVal = (int)(pScriptArgs->m_iN3); } } @@ -241,14 +241,14 @@ void CChar::Stat_SetMax( STAT_TYPE i, ushort uiVal ) { if ( i > STAT_NONE && i < STAT_QTY ) // only STR, DEX, INT, FOOD fire MaxHits, MaxMana, MaxStam, MaxFood for @StatChange { - CScriptTriggerArgs args; - args.m_iN1 = i + 4LL; // shift by 4 to indicate MaxHits, etc.. - args.m_iN2 = Stat_GetMax(i); - args.m_iN3 = uiVal; - if ( OnTrigger(CTRIG_StatChange, this, &args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = i + 4LL; // shift by 4 to indicate MaxHits, etc.. + pScriptArgs->m_iN2 = Stat_GetMax(i); + pScriptArgs->m_iN3 = uiVal; + if ( OnTrigger(CTRIG_StatChange, pScriptArgs, this) == TRIGRET_RET_TRUE ) return; // do not restore argn1 to i, bad things will happen! leave it untouched. (matex) - uiVal = (ushort)(args.m_iN3); + uiVal = (ushort)(pScriptArgs->m_iN3); } } m_Stat[i].m_max = uiVal; @@ -348,16 +348,16 @@ void CChar::Stat_SetBase( STAT_TYPE i, ushort uiVal ) // Only Str, Dex, Int, Food fire @StatChange here if (i >= STAT_STR && i <= STAT_FOOD) { - CScriptTriggerArgs args; - args.m_iN1 = i; - args.m_iN2 = uiStatVal; - args.m_iN3 = uiVal; - if (OnTrigger(CTRIG_StatChange, this, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = i; + pScriptArgs->m_iN2 = uiStatVal; + pScriptArgs->m_iN3 = uiVal; + if (OnTrigger(CTRIG_StatChange, pScriptArgs, this ) == TRIGRET_RET_TRUE) return; // do not restore argn1 to i, bad things will happen! leave i untouched. (matex) - int64 iPrevVal = args.m_iN3; + int64 iPrevVal = pScriptArgs->m_iN3; int64 iVal = std::clamp(iPrevVal, int64(-UINT16_MAX), int64(UINT16_MAX)); if (iVal != iPrevVal) { @@ -369,15 +369,15 @@ void CChar::Stat_SetBase( STAT_TYPE i, ushort uiVal ) // MaxFood cannot depend on something, otherwise if the Stat depends on STR, INT, DEX, fire MaxHits, MaxMana, MaxStam if (i != STAT_FOOD && m_Stat[i].m_max < 1) { - args.m_iN1 = i + 4LL; // Shift by 4 to indicate MaxHits, MaxMana, MaxStam - args.m_iN2 = uiStatVal; - args.m_iN3 = uiVal; - if (OnTrigger(CTRIG_StatChange, this, &args) == TRIGRET_RET_TRUE) + pScriptArgs->m_iN1 = i + 4LL; // Shift by 4 to indicate MaxHits, MaxMana, MaxStam + pScriptArgs->m_iN2 = uiStatVal; + pScriptArgs->m_iN3 = uiVal; + if (OnTrigger(CTRIG_StatChange, pScriptArgs, this ) == TRIGRET_RET_TRUE) return; // do not restore argn1 to i, bad things will happen! leave i untouched. (matex) - iPrevVal = args.m_iN3; + iPrevVal = pScriptArgs->m_iN3; iVal = std::clamp(iPrevVal, int64(-UINT16_MAX), int64(UINT16_MAX)); if (iVal != iPrevVal) { @@ -536,18 +536,18 @@ bool CChar::Stats_Regen() if (IsTrigUsed(TRIGGER_REGENSTAT)) { - CScriptTriggerArgs Args; - Args.m_VarsLocal.SetNum("StatID", i, true); - Args.m_VarsLocal.SetNum("Value", iMod, true); - Args.m_VarsLocal.SetNum("StatLimit", uiStatLimit, true); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_VarsLocal.SetNum("StatID", i, true); + pScriptArgs->m_VarsLocal.SetNum("Value", iMod, true); + pScriptArgs->m_VarsLocal.SetNum("StatLimit", uiStatLimit, true); if (i == STAT_DEX || i == STAT_INT) - Args.m_VarsLocal.SetNum("FocusValue", iFocusGain); + pScriptArgs->m_VarsLocal.SetNum("FocusValue", iFocusGain); if (i == STAT_FOOD) - Args.m_VarsLocal.SetNum("HitsHungerLoss", iHitsHungerLoss); + pScriptArgs->m_VarsLocal.SetNum("HitsHungerLoss", iHitsHungerLoss); - if (OnTrigger(CTRIG_RegenStat, this, &Args) == TRIGRET_RET_TRUE) + if (OnTrigger(CTRIG_RegenStat, pScriptArgs, this ) == TRIGRET_RET_TRUE) { // Setting the last regen time to 0 will make this trigger be called over and over and over, without a delay, which // can suck quite a bit cpu. @@ -555,22 +555,22 @@ bool CChar::Stats_Regen() continue; } - i = (STAT_TYPE)(Args.m_VarsLocal.GetKeyNum("StatID")); + i = (STAT_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("StatID")); if (i < STAT_STR) i = STAT_STR; else if (i > STAT_FOOD) i = STAT_FOOD; - iMod = (int)(Args.m_VarsLocal.GetKeyNum("Value")); - uiStatLimit = (ushort)(Args.m_VarsLocal.GetKeyNum("StatLimit")); + iMod = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("Value")); + uiStatLimit = (ushort)(pScriptArgs->m_VarsLocal.GetKeyNum("StatLimit")); if (i == STAT_DEX || i == STAT_INT) { - iFocusGain = (int)(Args.m_VarsLocal.GetKeyNum("FocusValue")); + iFocusGain = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("FocusValue")); iMod += iFocusGain; } if (i == STAT_FOOD) - iHitsHungerLoss = (int)(Args.m_VarsLocal.GetKeyNum("HitsHungerLoss")); + iHitsHungerLoss = (int)(pScriptArgs->m_VarsLocal.GetKeyNum("HitsHungerLoss")); } if (iMod == 0) continue; @@ -673,12 +673,12 @@ void CChar::SetKarma(short iNewKarma, CChar* pNPC) if (IsTrigUsed(TRIGGER_KARMACHANGE)) { - CScriptTriggerArgs Args(iKarmaChange, iOldKarma); - Args.m_pO1 = pNPC; - TRIGRET_TYPE retType = OnTrigger(CTRIG_KarmaChange, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iKarmaChange, iOldKarma, 0, pNPC); + TRIGRET_TYPE retType = OnTrigger(CTRIG_KarmaChange, pScriptArgs, this); if (retType == TRIGRET_RET_TRUE) return; - iKarmaChange = (short)Args.m_iN1; + iKarmaChange = (short)pScriptArgs->m_iN1; iNewKarma = (short)(maximum(g_Cfg.m_iMinKarma, minimum(g_Cfg.m_iMaxKarma, iOldKarma + iKarmaChange))); } @@ -711,12 +711,12 @@ void CChar::SetFame(ushort uiNewFame, CChar* pNPC) if (IsTrigUsed(TRIGGER_FAMECHANGE)) { - CScriptTriggerArgs Args(iFameChange, iOldFame); - Args.m_pO1 = pNPC; - TRIGRET_TYPE retType = OnTrigger(CTRIG_FameChange, this, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iFameChange, iOldFame, 0, pNPC); + TRIGRET_TYPE retType = OnTrigger(CTRIG_FameChange, pScriptArgs, this); if (retType == TRIGRET_RET_TRUE) return; - iFameChange = (short)Args.m_iN1; + iFameChange = (short)pScriptArgs->m_iN1; uiNewFame = (short)(maximum(0, minimum(g_Cfg.m_iMaxFame, iOldFame + iFameChange))); } diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index 53d6ab8d6..2839c5786 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -1,6 +1,7 @@ // CChar is either an NPC or a Player. #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CLog.h" #include "../components/CCPropsItemEquippable.h" #include "../components/CCPropsItemWeapon.h" @@ -208,7 +209,7 @@ CItem *CChar::LayerFind( LAYER_TYPE layer ) const return nullptr; } -TRIGRET_TYPE CChar::OnCharTrigForLayerLoop( CScript &s, CTextConsole *pSrc, CScriptTriggerArgs *pArgs, CSString *pResult, LAYER_TYPE layer ) +TRIGRET_TYPE CChar::OnCharTrigForLayerLoop( CScript &s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole *pSrc, CSString *pResult, LAYER_TYPE layer ) { ADDTOCALLSTACK("CChar::OnCharTrigForLayerLoop"); const CScriptLineContext StartContext = s.GetContext(); @@ -219,7 +220,7 @@ TRIGRET_TYPE CChar::OnCharTrigForLayerLoop( CScript &s, CTextConsole *pSrc, CScr CItem* pItem = static_cast(pObjRec); if ( pItem->GetEquipLayer() == layer ) { - TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if ( iRet == TRIGRET_BREAK ) { EndContext = StartContext; @@ -237,7 +238,7 @@ TRIGRET_TYPE CChar::OnCharTrigForLayerLoop( CScript &s, CTextConsole *pSrc, CScr if ( EndContext.m_iOffset <= StartContext.m_iOffset ) { // just skip to the end. - TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult); + TRIGRET_TYPE iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); if ( iRet != TRIGRET_ENDIF ) return iRet; } @@ -1115,6 +1116,7 @@ bool CChar::CanSeeInContainer( const CItemContainer *pContItem ) const return true; } +// TODO: restore some const safety... remove const_cast and make this function non-const bool CChar::CanSee( const CObjBaseTemplate *pObj ) const //true = client can see the invisble target { @@ -1238,12 +1240,12 @@ bool CChar::CanSee( const CObjBaseTemplate *pObj ) const { if ( IsTrigUsed( TRIGGER_SEEHIDDEN ) ) { - CScriptTriggerArgs Args; - Args.m_iN1 = (plevelMe <= plevelChar); + CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgs->m_iN1 = (plevelMe <= plevelChar); CChar *pChar2 = const_cast< CChar* >( pChar ); CChar *this2 = const_cast< CChar* >( this ); - this2->OnTrigger( CTRIG_SeeHidden, pChar2, &Args ); - return ( Args.m_iN1 != 1 ); + this2->OnTrigger( CTRIG_SeeHidden, pArgs, pChar2 ); + return ( pArgs->m_iN1 != 1 ); } } //Here we analyse how GM can see other player/GM when they are hide. diff --git a/src/game/chars/CCharUse.cpp b/src/game/chars/CCharUse.cpp index bdbdf3964..c60270147 100644 --- a/src/game/chars/CCharUse.cpp +++ b/src/game/chars/CCharUse.cpp @@ -1,6 +1,7 @@ // CChar is either an NPC or a Player. #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../clients/CClient.h" #include "../items/CItem.h" #include "../items/CItemCorpse.h" @@ -75,11 +76,11 @@ void CChar::Use_CarveCorpse( CItemCorpse * pCorpse, CItem * pItemCarving ) word iResourceQty = 0; size_t iResourceTotalQty = pCorpseDef->m_BaseResources.size(); - CScriptTriggerArgs Args(iResourceTotalQty,0,pItemCarving); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iResourceTotalQty, 0, 0, pItemCarving); for (size_t i = 0; i < iResourceTotalQty; ++i) { - const CResourceID& rid = pCorpseDef->m_BaseResources[i].GetResourceID(); if (rid.GetResType() != RES_ITEMDEF) continue; @@ -90,15 +91,15 @@ void CChar::Use_CarveCorpse( CItemCorpse * pCorpse, CItem * pItemCarving ) tchar* pszTmp = Str_GetTemp(); snprintf(pszTmp, Str_TempLength(), "resource.%u.ID", (int)i); - Args.m_VarsLocal.SetNum(pszTmp, (int64)id); + pScriptArgs->m_VarsLocal.SetNum(pszTmp, (int64)id); iResourceQty = (word)pCorpseDef->m_BaseResources[i].GetResQty(); snprintf(pszTmp, Str_TempLength(), "resource.%u.amount", (int)i); - Args.m_VarsLocal.SetNum(pszTmp, iResourceQty); + pScriptArgs->m_VarsLocal.SetNum(pszTmp, iResourceQty); } if (IsTrigUsed(TRIGGER_CARVECORPSE) || IsTrigUsed(TRIGGER_ITEMCARVECORPSE)) { - switch (static_cast(pCorpse)->OnTrigger(ITRIG_CarveCorpse, this, &Args)) + switch (static_cast(pCorpse)->OnTrigger(ITRIG_CarveCorpse, pScriptArgs, this)) { case TRIGRET_RET_TRUE: return; default: break; @@ -119,12 +120,12 @@ void CChar::Use_CarveCorpse( CItemCorpse * pCorpse, CItem * pItemCarving ) tchar* pszTmp = Str_GetTemp(); snprintf(pszTmp, Str_TempLength(), "resource.%u.ID", (int)i); - ITEMID_TYPE id = (ITEMID_TYPE)ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum(pszTmp)); + ITEMID_TYPE id = (ITEMID_TYPE)ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum(pszTmp)); if (id == ITEMID_NOTHING) break; snprintf(pszTmp, Str_TempLength(), "resource.%u.amount", (int)i); - iResourceQty =(word)Args.m_VarsLocal.GetKeyNum(pszTmp); + iResourceQty =(word)pScriptArgs->m_VarsLocal.GetKeyNum(pszTmp); ++ iItems; CItem *pPart = CItem::CreateTemplate(id, nullptr, this); @@ -1002,13 +1003,15 @@ void CChar::Use_Drink( CItem * pItem ) if (IsTrigUsed(TRIGGER_DRINK)) { - CScriptTriggerArgs args(dwDelay, wConsume); - args.m_pO1 = pItem; - args.m_VarsLocal.SetNumNew("BottleId", idbottle); - TRIGRET_TYPE iRet = OnTrigger(CTRIG_Drink, this, &args); - idbottle = (ITEMID_TYPE)args.m_VarsLocal.GetKeyNum("BottleId"); - dwDelay = (dword)(args.m_iN1 > 0 ? args.m_iN1 : 1); //0 causes stays memory infinitely. - wConsume = (word)args.m_iN2; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(dwDelay, wConsume, 0, pItem); + pScriptArgs->m_VarsLocal.SetNumNew("BottleId", idbottle); + + TRIGRET_TYPE iRet = OnTrigger(CTRIG_Drink, pScriptArgs, this); + + idbottle = (ITEMID_TYPE)pScriptArgs->m_VarsLocal.GetKeyNum("BottleId"); + dwDelay = (dword)(pScriptArgs->m_iN1 > 0 ? pScriptArgs->m_iN1 : 1); //0 causes stays memory infinitely. + wConsume = (word)pScriptArgs->m_iN2; wBottleAmount = wConsume; if (iRet == TRIGRET_RET_TRUE) @@ -1239,14 +1242,14 @@ bool CChar::FollowersUpdate(CChar * pCharPet, short iPetFollowerSlots, bool fChe // Arguments should be read only. Otherwise we have to call this trigger also if fCheckOnly == true and // everyone scripts have to be changed to recognize this scenario. - CScriptTriggerArgs Args; - Args.m_iN1 = (iPetFollowerSlots >= 0) ? 0 : 1; - Args.m_iN2 = abs(iPetFollowerSlots); - //Args.m_iN3 = fCheckOnly; - if (OnTrigger(CTRIG_FollowersUpdate, pCharPet, &Args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = (iPetFollowerSlots >= 0) ? 0 : 1; + pScriptArgs->m_iN2 = abs(iPetFollowerSlots); + //pScriptArgs->m_iN3 = fCheckOnly; + if (OnTrigger(CTRIG_FollowersUpdate, pScriptArgs, pCharPet) == TRIGRET_RET_TRUE) return false; - //iPetFollowerSlots = n64_narrow_n16(Args.m_iN2) * ((Args.m_iN1 == 1) ? -1 : 1); + //iPetFollowerSlots = n64_narrow_n16(pScriptArgs->m_iN2) * ((pScriptArgs->m_iN1 == 1) ? -1 : 1); } const short iMaxFollower = n64_narrow_n16(GetDefNum("MAXFOLLOWER", true)); @@ -1577,7 +1580,7 @@ int CChar::Do_Use_Item(CItem *pItem, bool fLink) if (m_pNPC && (IsTrigUsed(TRIGGER_DCLICK) || IsTrigUsed(TRIGGER_ITEMDCLICK))) // for players, DClick was called before this function { - if (pItem->OnTrigger(ITRIG_DCLICK, this) == TRIGRET_RET_TRUE) + if (pItem->OnTrigger(ITRIG_DCLICK, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE) return false; } diff --git a/src/game/clients/CAccount.cpp b/src/game/clients/CAccount.cpp index 6e292947e..f89be63b5 100644 --- a/src/game/clients/CAccount.cpp +++ b/src/game/clients/CAccount.cpp @@ -3,6 +3,7 @@ #include "../../common/CLog.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/CClientIterator.h" #include "../chars/CChar.h" #include "../CServer.h" @@ -213,10 +214,10 @@ bool CAccounts::Account_Delete( CAccount * pAccount ) ADDTOCALLSTACK("CAccounts::Account_Delete"); ASSERT(pAccount != nullptr); - CScriptTriggerArgs Args; - Args.Init(pAccount->GetName()); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pAccount->GetName()); enum TRIGRET_TYPE tr = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onaccount_delete", &g_Serv, &Args, nullptr, &tr); + g_Serv.r_Call("f_onaccount_delete", pScriptArgs, &g_Serv, nullptr, &tr); if ( tr == TRIGRET_RET_TRUE ) { return false; @@ -232,12 +233,12 @@ void CAccounts::Account_Add( CAccount * pAccount ) ADDTOCALLSTACK("CAccounts::Account_Add"); ASSERT(pAccount != nullptr); if ( !g_Serv.IsLoading() ) - { - CScriptTriggerArgs Args; - Args.Init(pAccount->GetName()); + { + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pAccount->GetName()); //Accounts are 'created' in server startup so we don't fire the function. TRIGRET_TYPE tRet = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onaccount_create", &g_Serv, &Args, nullptr, &tRet); + g_Serv.r_Call("f_onaccount_create", pScriptArgs, &g_Serv, nullptr, &tRet); if ( tRet == TRIGRET_RET_TRUE ) { g_Log.Event(LOGL_ERROR|LOGM_INIT, "Account '%s': Creation blocked via script\n", pAccount->GetName()); @@ -397,20 +398,20 @@ void CAccount::SetBlockStatus(bool fNewStatus) { if (IsPriv(PRIV_BLOCKED) && fNewStatus == false) { - CScriptTriggerArgs Args; - Args.Init(GetName()); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(GetName()); TRIGRET_TYPE iRet = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onaccount_unblock", &g_Serv, &Args, nullptr, &iRet); + g_Serv.r_Call("f_onaccount_unblock", pScriptArgs, &g_Serv, nullptr, &iRet); if (iRet == TRIGRET_RET_TRUE) return; ClearPrivFlags(PRIV_BLOCKED); } else if (!IsPriv(PRIV_BLOCKED) && fNewStatus == true) { - CScriptTriggerArgs Args; - Args.Init(GetName()); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(GetName()); TRIGRET_TYPE iRet = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onaccount_block", &g_Serv, &Args, nullptr, &iRet); + g_Serv.r_Call("f_onaccount_block", pScriptArgs, &g_Serv, nullptr, &iRet); if (iRet == TRIGRET_RET_TRUE) return; SetPrivFlags(PRIV_BLOCKED); @@ -513,13 +514,13 @@ bool CAccounts::Account_OnCmd( tchar * pszArgs, CTextConsole * pSrc ) { CSString cmdArgs; if (ppCmd[4] && ppCmd[4][0]) - cmdArgs.Format("%s %s %s", ppCmd[2], ppCmd[3], ppCmd[4]); + cmdArgs.Format("%s %s %s", ppCmd[2], ppCmd[3], ppCmd[4]); else if (ppCmd[3] && ppCmd[3][0]) - cmdArgs.Format("%s %s", ppCmd[2], ppCmd[3]); + cmdArgs.Format("%s %s", ppCmd[2], ppCmd[3]); else if (ppCmd[2] && ppCmd[2][0]) - cmdArgs.Format("%s", ppCmd[2]); + cmdArgs.Format("%s", ppCmd[2]); - CScript script( ppCmd[1], cmdArgs.GetBuffer() ); + CScript script( ppCmd[1], cmdArgs.GetBuffer() ); return pAccount->r_Verb( script, pSrc ); } @@ -944,11 +945,11 @@ bool CAccount::CheckPassword( lpctstr pszPassword ) return false; } - CScriptTriggerArgs Args; - Args.m_VarsLocal.SetStrNew("Account",GetName()); - Args.m_VarsLocal.SetStrNew("Password",pszPassword); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_VarsLocal.SetStrNew("Account",GetName()); + pScriptArgs->m_VarsLocal.SetStrNew("Password",pszPassword); TRIGRET_TYPE tr = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onaccount_connect", &g_Serv, &Args, nullptr, &tr); + g_Serv.r_Call("f_onaccount_connect", pScriptArgs, &g_Serv, nullptr, &tr); if ( tr == TRIGRET_RET_TRUE ) return false; if ( tr == TRIGRET_RET_HALFBAKED) @@ -994,12 +995,12 @@ bool CAccount::SetPassword( lpctstr pszPassword, bool isMD5Hash ) //Accounts are 'created' in server startup so we don't fire the function. if ( !g_Serv.IsLoading() ) { - CScriptTriggerArgs Args; - Args.Init(GetName()); - Args.m_VarsLocal.SetStrNew("password",pszPassword); - Args.m_VarsLocal.SetStrNew("oldPassword",m_sCurPassword.GetBuffer()); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(GetName()); + pScriptArgs->m_VarsLocal.SetStrNew("password",pszPassword); + pScriptArgs->m_VarsLocal.SetStrNew("oldPassword",m_sCurPassword.GetBuffer()); TRIGRET_TYPE tRet = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onaccount_pwchange", &g_Serv, &Args, nullptr, &tRet); + g_Serv.r_Call("f_onaccount_pwchange", pScriptArgs, &g_Serv, nullptr, &tRet); if ( tRet == TRIGRET_RET_TRUE ) { return false; @@ -1657,8 +1658,9 @@ bool CAccount::r_Verb( CScript &s, CTextConsole * pSrc ) { // RES_FUNCTION call CSString sVal; - CScriptTriggerArgs Args( s.GetArgRaw() ); - fLoad = r_Call( ptcKey, pSrc, &Args, &sVal ); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(s.GetArgRaw()); + fLoad = r_Call( ptcKey, pScriptArgs, pSrc, &sVal ); } return fLoad; } diff --git a/src/game/clients/CClient.cpp b/src/game/clients/CClient.cpp index 6b398f771..3aa0ef42d 100644 --- a/src/game/clients/CClient.cpp +++ b/src/game/clients/CClient.cpp @@ -3,6 +3,7 @@ #include "../../common/CLog.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CUOClientVersion.h" #include "../../network/CClientIterator.h" #include "../../network/CNetworkManager.h" @@ -190,10 +191,13 @@ void CClient::CharDisconnect() if ( IsTrigUsed(TRIGGER_LOGOUT) ) { - CScriptTriggerArgs Args(iLingerTime, fCanInstaLogOut); - m_pChar->OnTrigger(CTRIG_LogOut, m_pChar, &Args); - iLingerTime = (int)(Args.m_iN1); - fCanInstaLogOut = (Args.m_iN2 != 0); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iLingerTime, fCanInstaLogOut, 0, nullptr); + + m_pChar->OnTrigger(CTRIG_LogOut, pScriptArgs, m_pChar); + + iLingerTime = (int)(pScriptArgs->m_iN1); + fCanInstaLogOut = (pScriptArgs->m_iN2 != 0); } if ( iLingerTime <= 0 ) diff --git a/src/game/clients/CClient.h b/src/game/clients/CClient.h index 1a74d0957..b58944460 100644 --- a/src/game/clients/CClient.h +++ b/src/game/clients/CClient.h @@ -44,6 +44,8 @@ enum CC_TYPE }; +// TODO: just split CDialogResponseArgs into two objects (CScriptTriggerArgs and a struct for other data?) +// Or just make CScriptTriggerArgs a member of CDialogResponseArgs... Favor composition over inheritance! class CDialogResponseArgs : public CScriptTriggerArgs { // The scriptable return from a gump dialog. @@ -55,7 +57,8 @@ class CDialogResponseArgs : public CScriptTriggerArgs const word m_ID; CSString const m_sText; - TResponseString( word id, lpctstr pszText ) : m_ID( id ), m_sText( pszText ) + TResponseString( word id, lpctstr pszText ) + : m_ID( id ), m_sText( pszText ) { } TResponseString(const TResponseString& copy) = delete; @@ -352,7 +355,7 @@ class CClient : public CSObjListRec, public CScriptObj, public CChatChanMember, bool Event_ExceededNetworkQuota(uchar uiType, int64 iBytes, int64 iQuota); TRIGRET_TYPE Menu_OnSelect( const CResourceID& rid, int iSelect, CObjBase * pObj ); - TRIGRET_TYPE Dialog_OnButton( const CResourceID& rid, dword dwButtonID, CObjBase * pObj, CDialogResponseArgs * pArgs ); + TRIGRET_TYPE Dialog_OnButton( const CResourceID& rid, dword dwButtonID, CObjBase * pObj, std::shared_ptr pArgs ); bool Login_Relay( uint iServer ); // Relay player to a certain IP byte Login_ServerList( const char * pszAccount, const char * pszPassword ); // Initial login (Login on "loginserver", new format) diff --git a/src/game/clients/CClientDialog.cpp b/src/game/clients/CClientDialog.cpp index 67b859367..fae8d9424 100644 --- a/src/game/clients/CClientDialog.cpp +++ b/src/game/clients/CClientDialog.cpp @@ -2,6 +2,7 @@ #include "../../common/resource/sections/CDialogDef.h" #include "../../common/resource/CResourceLock.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CLog.h" #include "../../network/receive.h" #include "../../network/send.h" @@ -137,7 +138,7 @@ bool CClient::addGumpDialogProps( const CUID& uid ) return true; } -TRIGRET_TYPE CClient::Dialog_OnButton( const CResourceID& rid, dword dwButtonID, CObjBase * pObj, CDialogResponseArgs * pArgs ) +TRIGRET_TYPE CClient::Dialog_OnButton(const CResourceID& rid, dword dwButtonID, CObjBase * pObj, std::shared_ptr pArgs ) { ADDTOCALLSTACK("CClient::Dialog_OnButton"); // one of the gump dialog buttons was pressed. @@ -183,10 +184,10 @@ TRIGRET_TYPE CClient::Dialog_OnButton( const CResourceID& rid, dword dwButtonID, CResourceLock prebutton; if (g_Cfg.ResourceLock(prebutton, CResourceID(RES_DIALOG, rid.GetResIndex(), RES_DIALOG_PREBUTTON))) - stopPrebutton = pObj->OnTriggerRun(prebutton, TRIGRUN_SECTION_TRUE, m_pChar, pArgs, nullptr); + stopPrebutton = pObj->OnTriggerRun(prebutton, TRIGRUN_SECTION_TRUE, pArgs, m_pChar, nullptr); if (stopPrebutton != TRIGRET_RET_TRUE) - return pObj->OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, m_pChar, pArgs); + return pObj->OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, pArgs, m_pChar); } return TRIGRET_ENDIF; @@ -254,7 +255,7 @@ TRIGRET_TYPE CClient::Menu_OnSelect( const CResourceID& rid, int iSelect, CObjBa if (strnicmp(ptcStr, "@CANCEL", 7 ) ) continue; - return pObj->OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, m_pChar, nullptr ); + return pObj->OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, CScriptTriggerArgsPtr{}, m_pChar); } } else @@ -271,7 +272,7 @@ TRIGRET_TYPE CClient::Menu_OnSelect( const CResourceID& rid, int iSelect, CObjBa if ( i > iSelect ) break; - return pObj->OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, m_pChar, nullptr ); + return pObj->OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, CScriptTriggerArgsPtr{}, m_pChar); } } @@ -321,9 +322,9 @@ bool CMenuItem::ParseLine( tchar * pszArgs, CScriptObj * pObjBase, CTextConsole } if ( pObjBase != nullptr ) - pObjBase->ParseScriptText( pszArgs, pSrc ); + pObjBase->ParseScriptText( pszArgs, CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, pSrc ); else - g_Serv.ParseScriptText( pszArgs, pSrc ); + g_Serv.ParseScriptText( pszArgs, CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, pSrc ); // Parsing @color if ( *pszArgs == '@' ) @@ -378,7 +379,7 @@ void CClient::Menu_Setup( CResourceID rid, CObjBase * pObj ) DEBUG_ERR(("Error getting the menu title.\n")); return; } - pObj->ParseScriptText( s.GetKeyBuffer(), m_pChar ); + pObj->ParseScriptText( s.GetKeyBuffer(), CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, m_pChar ); CMenuItem item[MAX_MENU_ITEMS]; item[0].m_sText = s.GetKey(); diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index d0721529c..32d9938fb 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -1,6 +1,7 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/CClientIterator.h" #include "../../network/receive.h" #include "../../network/send.h" @@ -53,7 +54,7 @@ void CClient::Event_ChatButton(const nachar* pszName) // Client's chat button wa if ( IsTrigUsed(TRIGGER_USERCHATBUTTON) ) { - if (m_pChar && m_pChar->OnTrigger(CTRIG_UserChatButton, m_pChar) == TRIGRET_RET_TRUE) + if (m_pChar && m_pChar->OnTrigger(CTRIG_UserChatButton, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE) return; } GetChar()->SetTriggerActive("UserChatButton"); // dirty fix for SA Classic clients with injection moving a lot when using chat button, we set 'active trigger' to this, so we check it back on the packet to limit the amount of steps to do. @@ -417,8 +418,9 @@ void CClient::Event_Item_Drop( CUID uidItem, CPointMap pt, CUID uidOn, uchar gri CObjBase *pOldCont = pItem->GetContainer(); if (( IsTrigUsed(TRIGGER_DROPON_ITEM) ) || ( IsTrigUsed(TRIGGER_ITEMDROPON_ITEM) )) { - CScriptTriggerArgs Args( pObjOn ); - if ( pItem->OnTrigger( ITRIG_DROPON_ITEM, m_pChar, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pObjOn; + if ( pItem->OnTrigger( ITRIG_DROPON_ITEM, pScriptArgs, m_pChar ) == TRIGRET_RET_TRUE ) { Event_Item_Drop_Fail( pItem ); return; @@ -429,11 +431,12 @@ void CClient::Event_Item_Drop( CUID uidItem, CPointMap pt, CUID uidOn, uchar gri return; CItem * pItemOn = dynamic_cast ( pObjOn ); - if (( pItemOn ) && (( IsTrigUsed(TRIGGER_DROPON_SELF) ) || ( IsTrigUsed(TRIGGER_ITEMDROPON_SELF) ))) + if (pItemOn && (IsTrigUsed(TRIGGER_DROPON_SELF) || IsTrigUsed(TRIGGER_ITEMDROPON_SELF))) { CItem* pPrevCont = dynamic_cast(pItem->GetContainer()); - CScriptTriggerArgs Args( pItem ); - if ( pItemOn->OnTrigger( ITRIG_DROPON_SELF, m_pChar, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pItem; + if ( pItemOn->OnTrigger( ITRIG_DROPON_SELF, pScriptArgs, m_pChar ) == TRIGRET_RET_TRUE ) { CItem* pCont = dynamic_cast(pItem->GetContainer()); if (pPrevCont == pCont) @@ -612,7 +615,7 @@ void CClient::Event_Skill_Use( SKILL_TYPE skill ) // Skill is clicked on the ski if ( IsTrigUsed(TRIGGER_SKILLSELECT) ) { - if ( m_pChar->Skill_OnCharTrigger( skill, CTRIG_SkillSelect ) == TRIGRET_RET_TRUE ) + if ( m_pChar->Skill_OnCharTrigger( skill, CTRIG_SkillSelect, CScriptTriggerArgsPtr{} ) == TRIGRET_RET_TRUE ) { m_pChar->Skill_Fail( true ); // clean up current skill. return; @@ -621,7 +624,7 @@ void CClient::Event_Skill_Use( SKILL_TYPE skill ) // Skill is clicked on the ski if ( IsTrigUsed(TRIGGER_SELECT) ) { - if ( m_pChar->Skill_OnTrigger( skill, SKTRIG_SELECT ) == TRIGRET_RET_TRUE ) + if ( m_pChar->Skill_OnTrigger( skill, SKTRIG_SELECT, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) { m_pChar->Skill_Fail( true ); // clean up current skill. return; @@ -807,7 +810,7 @@ bool CClient::Event_CheckWalkBuffer(byte rawdir) DEBUG_WARN(("%s (%x): Fast Walk ?\n", GetName(), GetSocketID())); if ( IsTrigUsed(TRIGGER_USEREXWALKLIMIT) ) { - if ( m_pChar->OnTrigger(CTRIG_UserExWalkLimit, m_pChar) != TRIGRET_RET_TRUE ) + if ( m_pChar->OnTrigger(CTRIG_UserExWalkLimit, CScriptTriggerArgsPtr{}, m_pChar) != TRIGRET_RET_TRUE ) return false; } } @@ -819,12 +822,13 @@ bool CClient::Event_ExceededNetworkQuota(uchar uiType, int64 iBytes, int64 iQuot { ADDTOCALLSTACK("CClient::Event_ExceededNetworkQuota"); - CScriptTriggerArgs Args(uiType, iBytes, iQuota); - Args.m_VarsLocal.SetStrNew("Account", GetName()); - Args.m_VarsLocal.SetStrNew("IP", GetPeer().GetAddrStr()); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(uiType, iBytes, iQuota, nullptr); + pScriptArgs->m_VarsLocal.SetStrNew("Account", GetName()); + pScriptArgs->m_VarsLocal.SetStrNew("IP", GetPeer().GetAddrStr()); TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; - g_Serv.r_Call("f_onclient_exceed_network_quota", this, &Args, nullptr, &tRet); + g_Serv.r_Call("f_onclient_exceed_network_quota", pScriptArgs, this, nullptr, &tRet); if (tRet == TRIGRET_RET_FALSE) { @@ -972,9 +976,9 @@ void CClient::Event_CombatAbilitySelect(dword dwAbility) if ( IsTrigUsed(TRIGGER_USERSPECIALMOVE) ) { - CScriptTriggerArgs Args; - Args.m_iN1 = dwAbility; - m_pChar->OnTrigger(CTRIG_UserSpecialMove, m_pChar, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = dwAbility; + m_pChar->OnTrigger(CTRIG_UserSpecialMove, pScriptArgs, m_pChar); } } @@ -987,9 +991,10 @@ void CClient::Event_VirtueSelect(dword dwVirtue, CChar *pCharTarg) if ( IsTrigUsed(TRIGGER_USERVIRTUE) ) { - CScriptTriggerArgs Args(pCharTarg); - Args.m_iN1 = dwVirtue; - m_pChar->OnTrigger(CTRIG_UserVirtue, m_pChar, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pCharTarg; + pScriptArgs->m_iN1 = dwVirtue; + m_pChar->OnTrigger(CTRIG_UserVirtue, pScriptArgs, m_pChar ); } } @@ -1005,18 +1010,18 @@ void CClient::Event_CombatMode( bool fWar ) // Only for switching to combat mode if ( IsTrigUsed(TRIGGER_USERWARMODE) ) { - CScriptTriggerArgs Args; - Args.m_iN1 = m_pChar->IsStatFlag(STATF_WAR) ? 1 : 0; - Args.m_iN2 = 1; - Args.m_iN3 = 0; - if (m_pChar->OnTrigger(CTRIG_UserWarmode, m_pChar, &Args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = m_pChar->IsStatFlag(STATF_WAR) ? 1 : 0; + pScriptArgs->m_iN2 = 1; + pScriptArgs->m_iN3 = 0; + if (m_pChar->OnTrigger(CTRIG_UserWarmode, pScriptArgs, m_pChar) == TRIGRET_RET_TRUE) return; - if ( Args.m_iN2 == 0 ) + if ( pScriptArgs->m_iN2 == 0 ) fCleanSkill = false; - if ( Args.m_iN3 != 0 && Args.m_iN3 < 3) - fWar = (Args.m_iN3 == 1 ? false : true); + if ( pScriptArgs->m_iN3 != 0 && pScriptArgs->m_iN3 < 3) + fWar = (pScriptArgs->m_iN3 == 1 ? false : true); } m_pChar->StatFlag_Mod( STATF_WAR, fWar ); @@ -1071,18 +1076,19 @@ bool CClient::Event_Command(lpctstr pszCommand, TALKMODE_TYPE mode) // filter on commands is active - so trigger it if ( !g_Cfg.m_sCommandTrigger.IsEmpty() ) { - CScriptTriggerArgs Args(pszCommand); - Args.m_iN1 = fAllowCommand; - Args.m_iN2 = fAllowSay; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pszCommand); + pScriptArgs->m_iN1 = fAllowCommand; + pScriptArgs->m_iN2 = fAllowSay; enum TRIGRET_TYPE tr; // Call the filtering function - if ( m_pChar->r_Call(g_Cfg.m_sCommandTrigger, m_pChar, &Args, nullptr, &tr) ) + if ( m_pChar->r_Call(g_Cfg.m_sCommandTrigger, pScriptArgs, m_pChar, nullptr, &tr) ) if ( tr == TRIGRET_RET_TRUE ) - return (Args.m_iN2 != 0); + return (pScriptArgs->m_iN2 != 0); - fAllowCommand = ( Args.m_iN1 != 0 ); - fAllowSay = ( Args.m_iN2 != 0 ); + fAllowCommand = ( pScriptArgs->m_iN1 != 0 ); + fAllowSay = ( pScriptArgs->m_iN2 != 0 ); } if ( !fAllowCommand && !fAllowSay ) @@ -1274,17 +1280,18 @@ void CClient::Event_VendorBuy(CChar* pVendor, const VendorItem* items, uint uiIt continue; //We need to continue for loop for other items not break. pItem = dynamic_cast (items[i].m_serial.ItemFind()); - word amount = items[i].m_vcAmount; + word wAmount = items[i].m_vcAmount; if (pItem == nullptr) continue; if ((IsTrigUsed(TRIGGER_BUY)) || (IsTrigUsed(TRIGGER_ITEMBUY))) { - int64 iItemCost = int64(amount) * items[i].m_price; - CScriptTriggerArgs Args( amount, iItemCost, pVendor ); - Args.m_VarsLocal.SetNum( "TOTALCOST", iCostTotal); - if (pItem->OnTrigger(ITRIG_Buy, this->GetChar(), &Args) == TRIGRET_RET_TRUE) + const int64 iItemCost = int64(wAmount) * items[i].m_price; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(wAmount, iItemCost, 0, pVendor); + pScriptArgs->m_VarsLocal.SetNum( "TOTALCOST", iCostTotal); + if (pItem->OnTrigger(ITRIG_Buy, pScriptArgs, this->GetChar()) == TRIGRET_RET_TRUE) { iCostTotal -= iItemCost; //If we are blocking the transaction we should not pay for it!. uiItemBlocked++; @@ -1294,13 +1301,13 @@ void CClient::Event_VendorBuy(CChar* pVendor, const VendorItem* items, uint uiIt if (!fPlayerVendor) //NPC vendors { - pItem->SetAmount(pItem->GetAmount() - amount); + pItem->SetAmount(pItem->GetAmount() - wAmount); switch (pItem->GetType()) { case IT_FIGURINE: { - for ( int f = 0; f < amount; ++f ) + for ( int f = 0; f < wAmount; ++f ) m_pChar->Use_Figurine(pItem); goto do_consume; } @@ -1320,9 +1327,9 @@ void CClient::Event_VendorBuy(CChar* pVendor, const VendorItem* items, uint uiIt break; } - if ((amount > 1) && (!pItem->Item_GetDef()->IsStackableType())) + if ((wAmount > 1) && (!pItem->Item_GetDef()->IsStackableType())) { - while (amount--) + while (wAmount--) { CItem * pItemNew = CItem::CreateDupeItem(pItem); pItemNew->SetAmount(1); @@ -1337,7 +1344,7 @@ void CClient::Event_VendorBuy(CChar* pVendor, const VendorItem* items, uint uiIt else { CItem * pItemNew = CItem::CreateDupeItem(pItem); - pItemNew->SetAmount(amount); + pItemNew->SetAmount(wAmount); pItemNew->m_TagDefs.SetNum("NOSAVE", 0, true); if (!pPack->CanContainerHold(pItemNew, m_pChar) || (!m_pChar->CanCarry(pItemNew))) m_pChar->ItemDrop(pItemNew, m_pChar->GetTopPoint()); @@ -1347,7 +1354,7 @@ void CClient::Event_VendorBuy(CChar* pVendor, const VendorItem* items, uint uiIt } else //Player vendors { - if ( pItem->GetAmount() <= amount ) //Buy the whole item + if ( pItem->GetAmount() <= wAmount ) //Buy the whole item { if ((!pPack->CanContainerHold(pItem, m_pChar)) || (!m_pChar->CanCarry(pItem))) m_pChar->ItemDrop(pItem, m_pChar->GetTopPoint()); @@ -1358,11 +1365,11 @@ void CClient::Event_VendorBuy(CChar* pVendor, const VendorItem* items, uint uiIt } else { - pItem->SetAmount(pItem->GetAmount() - amount); + pItem->SetAmount(pItem->GetAmount() - wAmount); CItem *pItemNew = CItem::CreateDupeItem(pItem); pItemNew->m_TagDefs.SetNum("NOSAVE", 0, true); - pItemNew->SetAmount(amount); + pItemNew->SetAmount(wAmount); if ((!pPack->CanContainerHold(pItemNew, m_pChar)) || (!m_pChar->CanCarry(pItemNew))) m_pChar->ItemDrop(pItemNew, m_pChar->GetTopPoint()); else @@ -1471,13 +1478,13 @@ void CClient::Event_VendorSell(CChar* pVendor, const VendorItem* items, uint uiI if ( pItemSell == nullptr ) continue; - word amount = items[i].m_vcAmount; + word wAmount = items[i].m_vcAmount; // Now how much did i say i wanted to sell ? dword dwPrice = 0; - if ( pItem->GetAmount() < amount ) // Selling more than i have ? + if ( pItem->GetAmount() < wAmount ) // Selling more than i have ? { - amount = pItem->GetAmount(); + wAmount = pItem->GetAmount(); } // If OVERRIDE.VALUE is define on the script and this NPC buy this item at a specific price, we use this price in priority @@ -1485,18 +1492,19 @@ void CClient::Event_VendorSell(CChar* pVendor, const VendorItem* items, uint uiI if (pItemSell->GetKey("OVERRIDE.VALUE", true)) { //Get the price on NPC template - dwPrice = pItemSell->GetVendorPrice(iConvertFactor,1) * amount; //Check the value of item on NPC template or itemdef + dwPrice = pItemSell->GetVendorPrice(iConvertFactor,1) * wAmount; //Check the value of item on NPC template or itemdef } else - { + { //Get the price/Value of the real item in the backpack - dwPrice = pItem->GetVendorPrice(iConvertFactor,1) * amount; //Check the value of the item on the player + dwPrice = pItem->GetVendorPrice(iConvertFactor,1) * wAmount; //Check the value of the item on the player } if (( IsTrigUsed(TRIGGER_SELL) ) || ( IsTrigUsed(TRIGGER_ITEMSELL) )) { - CScriptTriggerArgs Args( amount, dwPrice, pVendor ); - if ( pItem->OnTrigger( ITRIG_Sell, this->GetChar(), &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(wAmount, dwPrice, 0, pVendor); + if ( pItem->OnTrigger( ITRIG_Sell, pScriptArgs, this->GetChar() ) == TRIGRET_RET_TRUE ) continue; } @@ -1513,7 +1521,7 @@ void CClient::Event_VendorSell(CChar* pVendor, const VendorItem* items, uint uiI // Take the items from player. // Put items in vendor inventory. - if ( amount >= pItem->GetAmount()) + if ( wAmount >= pItem->GetAmount()) { pItem->RemoveFromView(); if ( pVendor->IsStatFlag(STATF_PET) && pContExtra ) @@ -1529,13 +1537,13 @@ void CClient::Event_VendorSell(CChar* pVendor, const VendorItem* items, uint uiI if ( pVendor->IsStatFlag(STATF_PET) && pContExtra ) { CItem * pItemNew = CItem::CreateDupeItem(pItem); - pItemNew->SetAmount(amount); + pItemNew->SetAmount(wAmount); pContExtra->ContentAdd(pItemNew); } - pItem->SetAmountUpdate( pItem->GetAmount() - amount ); + pItem->SetAmountUpdate( pItem->GetAmount() - wAmount ); if (IsSetOF(OF_VendorStockLimit)) - pItemSell->ConsumeAmount(amount); + pItemSell->ConsumeAmount(wAmount); } } @@ -1580,7 +1588,7 @@ void CClient::Event_Profile( byte fWriteMode, CUID uid, lpctstr pszProfile, int if ( IsTrigUsed(TRIGGER_PROFILE) ) { - if ( pChar->OnTrigger(CTRIG_Profile, m_pChar) == TRIGRET_RET_TRUE ) + if ( pChar->OnTrigger(CTRIG_Profile, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) return; } @@ -1624,7 +1632,7 @@ void CClient::Event_MailMsg( CUID uid1, CUID uid2 ) if ( IsTrigUsed(TRIGGER_USERMAILBAG) ) { - if (pChar->OnTrigger(CTRIG_UserMailBag, m_pChar, nullptr) == TRIGRET_RET_TRUE) + if (pChar->OnTrigger(CTRIG_UserMailBag, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE) return; } @@ -1649,7 +1657,7 @@ void CClient::Event_ToolTip( CUID uid ) if (( IsTrigUsed(TRIGGER_TOOLTIP) ) || (( IsTrigUsed(TRIGGER_ITEMTOOLTIP) )&&(pObj->IsItem()))) { - if ( pObj->OnTrigger("@ToolTip", this) == TRIGRET_RET_TRUE ) // CTRIG_ToolTip, ITRIG_ToolTip + if ( pObj->OnTrigger("@ToolTip", CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE ) // CTRIG_ToolTip, ITRIG_ToolTip return; } @@ -2211,10 +2219,10 @@ bool CClient::Event_SetName( CUID uid, const char * pszCharName ) if ( IsTrigUsed(TRIGGER_RENAME) ) { - CScriptTriggerArgs args; - args.m_pO1 = pChar; - args.m_s1 = pszCharName; - if ( m_pChar->OnTrigger(CTRIG_Rename, this, &args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pChar; + pScriptArgs->m_s1 = pszCharName; + if ( m_pChar->OnTrigger(CTRIG_Rename, pScriptArgs, this) == TRIGRET_RET_TRUE ) return false; } if (pChar->IsOwnedBy(m_pChar)) @@ -2344,7 +2352,7 @@ bool CClient::Event_DoubleClick( CUID uid, bool fMacro, bool fTestTouch, bool fS CChar * pChar = static_cast(pObj); if ( IsTrigUsed(TRIGGER_DCLICK) || IsTrigUsed(TRIGGER_CHARDCLICK) ) { - if ( pChar->OnTrigger(CTRIG_DClick, m_pChar) == TRIGRET_RET_TRUE ) + if ( pChar->OnTrigger(CTRIG_DClick, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) return true; } @@ -2412,9 +2420,10 @@ void CClient::Event_SingleClick( CUID uid ) if ( IsTrigUsed(TRIGGER_CLICK) || (IsTrigUsed(TRIGGER_ITEMCLICK) && pObj->IsItem()) || (IsTrigUsed(TRIGGER_CHARCLICK) && pObj->IsChar()) ) { - CScriptTriggerArgs Args(this); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = this; // The "@Click" trigger str should be the same between items and chars... - if ( pObj->OnTrigger(CChar::sm_szTrigName[CTRIG_Click], m_pChar, &Args) == TRIGRET_RET_TRUE ) // CTRIG_Click, ITRIG_Click + if ( pObj->OnTrigger(CChar::sm_szTrigName[CTRIG_Click], pScriptArgs, m_pChar) == TRIGRET_RET_TRUE ) // CTRIG_Click, ITRIG_Click return; } @@ -2558,8 +2567,8 @@ void CClient::Event_AOSPopupMenuRequest( dword uid ) //construct packet after a } m_pPopupPacket = new PacketDisplayPopup(this, uObj); - CScriptTriggerArgs Args; - bool fPreparePacket = false; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + bool fPreparePacket = false; CItem *pItem = uObj.ItemFind(); CChar *pChar = uObj.CharFind(); @@ -2567,8 +2576,8 @@ void CClient::Event_AOSPopupMenuRequest( dword uid ) //construct packet after a { if ( IsTrigUsed(TRIGGER_CONTEXTMENUREQUEST) || IsTrigUsed(TRIGGER_ITEMCONTEXTMENUREQUEST) ) { - Args.m_iN1 = 1; - pItem->OnTrigger(ITRIG_ContextMenuRequest, GetChar(), &Args); + pScriptArgs->m_iN1 = 1; + pItem->OnTrigger(ITRIG_ContextMenuRequest, pScriptArgs, GetChar()); fPreparePacket = true; // there's no hardcoded stuff for items } else @@ -2582,8 +2591,8 @@ void CClient::Event_AOSPopupMenuRequest( dword uid ) //construct packet after a { if ( IsTrigUsed(TRIGGER_CONTEXTMENUREQUEST) ) { - Args.m_iN1 = 1; - TRIGRET_TYPE iRet = pChar->OnTrigger(CTRIG_ContextMenuRequest, GetChar(), &Args); + pScriptArgs->m_iN1 = 1; + TRIGRET_TYPE iRet = pChar->OnTrigger(CTRIG_ContextMenuRequest, pScriptArgs, GetChar()); if ( iRet == TRIGRET_RET_TRUE ) fPreparePacket = true; } @@ -2592,7 +2601,7 @@ void CClient::Event_AOSPopupMenuRequest( dword uid ) //construct packet after a { delete m_pPopupPacket; m_pPopupPacket = nullptr; - return; + return; } if ( pChar && !fPreparePacket ) @@ -2714,10 +2723,10 @@ void CClient::Event_AOSPopupMenuRequest( dword uid ) //construct packet after a m_pPopupPacket->addOption(POPUP_TRADE_OPEN, 1077728, POPUPFLAG_COLOR, 0xFFFF); } - if ( (Args.m_iN1 != 1) && (IsTrigUsed(TRIGGER_CONTEXTMENUREQUEST)) ) + if ( (pScriptArgs->m_iN1 != 1) && (IsTrigUsed(TRIGGER_CONTEXTMENUREQUEST)) ) { - Args.m_iN1 = 2; - pChar->OnTrigger(CTRIG_ContextMenuRequest, GetChar(), &Args); + pScriptArgs->m_iN1 = 2; + pChar->OnTrigger(CTRIG_ContextMenuRequest, pScriptArgs, GetChar()); } } @@ -2746,14 +2755,14 @@ void CClient::Event_AOSPopupMenuSelect(dword uid, word EntryTag) //do something if ( !IsSetOF(OF_NoContextMenuLOS) && !m_pChar->CanSeeLOS(pObj) ) return; - CScriptTriggerArgs Args; - CItem *pItem = uObj.ItemFind(); - if ( pItem ) + CItem *pItem = uObj.ItemFind(); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + if ( pItem ) { if ( IsTrigUsed(TRIGGER_CONTEXTMENUSELECT) || IsTrigUsed(TRIGGER_ITEMCONTEXTMENUSELECT) ) { - Args.m_iN1 = EntryTag; - pItem->OnTrigger(ITRIG_ContextMenuSelect, GetChar(), &Args); + pScriptArgs->m_iN1 = EntryTag; + pItem->OnTrigger(ITRIG_ContextMenuSelect, pScriptArgs, GetChar()); } return; // there's no hardcoded stuff for items } @@ -2764,8 +2773,8 @@ void CClient::Event_AOSPopupMenuSelect(dword uid, word EntryTag) //do something if ( IsTrigUsed(TRIGGER_CONTEXTMENUSELECT) ) { - Args.m_iN1 = EntryTag; - if ( pChar->OnTrigger(CTRIG_ContextMenuSelect, GetChar(), &Args) == TRIGRET_RET_TRUE ) + pScriptArgs->m_iN1 = EntryTag; + if ( pChar->OnTrigger(CTRIG_ContextMenuSelect, pScriptArgs, GetChar()) == TRIGRET_RET_TRUE ) return; } @@ -2909,17 +2918,18 @@ void CClient::Event_AOSPopupMenuSelect(dword uid, word EntryTag) //do something void CClient::Event_BugReport( const tchar * pszText, int len, BUGREPORT_TYPE type, CLanguageID lang ) { ADDTOCALLSTACK("CClient::Event_BugReport"); - UnreferencedParameter(len); + UnreferencedParameter(len); if ( !m_pChar ) return; if ( IsTrigUsed(TRIGGER_USERBUGREPORT) ) { - CScriptTriggerArgs Args(type); - Args.m_s1 = pszText; - Args.m_VarsLocal.SetStr("LANG", false, lang.GetStr()); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = type; + pScriptArgs->m_s1 = pszText; + pScriptArgs->m_VarsLocal.SetStr("LANG", false, lang.GetStr()); - m_pChar->OnTrigger(CTRIG_UserBugReport, m_pChar, &Args); + m_pChar->OnTrigger(CTRIG_UserBugReport, pScriptArgs, m_pChar); } } @@ -2931,8 +2941,9 @@ void CClient::Event_UseToolbar(byte bType, dword dwArg) if ( IsTrigUsed(TRIGGER_USERKRTOOLBAR) ) { - CScriptTriggerArgs Args( bType, dwArg ); - if ( m_pChar->OnTrigger( CTRIG_UserKRToolbar, m_pChar, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(bType, dwArg, 0, nullptr); + if ( m_pChar->OnTrigger( CTRIG_UserKRToolbar, pScriptArgs, m_pChar ) == TRIGRET_RET_TRUE ) return; } @@ -2978,22 +2989,23 @@ void CClient::Event_ExtCmd( EXTCMD_TYPE type, tchar *pszName ) byte bDoorAutoDist = 1; if ( IsTrigUsed(TRIGGER_USEREXTCMD) ) - { - CScriptTriggerArgs Args(pszName); - Args.m_iN1 = type; + { + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pszName); + pScriptArgs->m_iN1 = type; if (type == EXTCMD_DOOR_AUTO) - Args.m_VarsLocal.SetNumNew("DoorAutoDist", bDoorAutoDist); + pScriptArgs->m_VarsLocal.SetNumNew("DoorAutoDist", bDoorAutoDist); - if ( m_pChar->OnTrigger(CTRIG_UserExtCmd, m_pChar, &Args) == TRIGRET_RET_TRUE ) + if ( m_pChar->OnTrigger(CTRIG_UserExtCmd, pScriptArgs, m_pChar) == TRIGRET_RET_TRUE ) return; if (type == EXTCMD_DOOR_AUTO) { - bDoorAutoDist = (byte)std::clamp(Args.m_VarsLocal.GetKeyNum("DoorAutoDist"), (int64)0, (int64)UO_MAP_VIEW_SIGHT); + bDoorAutoDist = (byte)std::clamp(pScriptArgs->m_VarsLocal.GetKeyNum("DoorAutoDist"), (int64)0, (int64)UO_MAP_VIEW_SIGHT); } - Str_CopyLimitNull(pszName, Args.m_s1, MAX_TALK_BUFFER); + Str_CopyLimitNull(pszName, pScriptArgs->m_s1, MAX_TALK_BUFFER); } tchar *ppArgs[2]; @@ -3117,9 +3129,10 @@ void CClient::Event_ExtCmd( EXTCMD_TYPE type, tchar *pszName ) return; int iVirtueID = ppArgs[0][0] - '0'; // 0x1=Honor, 0x2=Sacrifice, 0x3=Valor - CScriptTriggerArgs Args(m_pChar); - Args.m_iN1 = iVirtueID; - m_pChar->OnTrigger(CTRIG_UserVirtueInvoke, m_pChar, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = m_pChar; + pScriptArgs->m_iN1 = iVirtueID; + m_pChar->OnTrigger(CTRIG_UserVirtueInvoke, pScriptArgs, m_pChar); return; } @@ -3141,28 +3154,29 @@ bool CClient::xPacketFilter( const byte * pData, uint iLen ) EXC_TRY("packet filter"); if ( iLen > 0 && g_Serv.m_PacketFilter[pData[0]][0] ) { - CScriptTriggerArgs Args(pData[0]); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = pData[0]; enum TRIGRET_TYPE trigReturn; tchar idx[12]; - Args.m_s1 = GetPeerStr(); - Args.m_pO1 = this; // Yay for ARGO.SENDPACKET - Args.m_VarsLocal.SetNum("CONNECTIONTYPE", GetConnectType()); + pScriptArgs->m_s1 = GetPeerStr(); + pScriptArgs->m_pO1 = this; // Yay for ARGO.SENDPACKET + pScriptArgs->m_VarsLocal.SetNum("CONNECTIONTYPE", GetConnectType()); uint bytes = iLen; uint bytestr = minimum(bytes, SCRIPT_MAX_LINE_LEN); tchar *zBuf = Str_GetTemp(); - Args.m_VarsLocal.SetNum("NUM", bytes); + pScriptArgs->m_VarsLocal.SetNum("NUM", bytes); memcpy(zBuf, &(pData[0]), bytestr); zBuf[bytestr] = 0; - Args.m_VarsLocal.SetStr("STR", true, zBuf); + pScriptArgs->m_VarsLocal.SetStr("STR", true, zBuf); if ( m_pAccount ) { - Args.m_VarsLocal.SetStr("ACCOUNT", false, m_pAccount->GetName()); + pScriptArgs->m_VarsLocal.SetStr("ACCOUNT", false, m_pAccount->GetName()); if ( m_pChar ) { - Args.m_VarsLocal.SetNum("CHAR", m_pChar->GetUID().GetObjUID()); + pScriptArgs->m_VarsLocal.SetNum("CHAR", m_pChar->GetUID().GetObjUID()); } } @@ -3170,11 +3184,11 @@ bool CClient::xPacketFilter( const byte * pData, uint iLen ) for ( uint i = 0; i < bytes; ++i ) { snprintf(idx, sizeof(idx), "%u", i); - Args.m_VarsLocal.SetNum(idx, (int)(pData[i])); + pScriptArgs->m_VarsLocal.SetNum(idx, (int)(pData[i])); } // Call the filtering function - if ( g_Serv.r_Call(g_Serv.m_PacketFilter[pData[0]], &g_Serv, &Args, nullptr, &trigReturn) ) + if ( g_Serv.r_Call(g_Serv.m_PacketFilter[pData[0]], pScriptArgs, &g_Serv, nullptr, &trigReturn) ) if ( trigReturn == TRIGRET_RET_TRUE ) return true; // do not cry about errors } @@ -3190,28 +3204,29 @@ bool CClient::xOutPacketFilter( const byte * pData, uint iLen ) EXC_TRY("Outgoing packet filter"); if ( iLen > 0 && g_Serv.m_OutPacketFilter[pData[0]][0] ) { - CScriptTriggerArgs Args(pData[0]); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = pData[0]; enum TRIGRET_TYPE trigReturn; tchar idx[12]; - Args.m_s1 = GetPeerStr(); - Args.m_pO1 = this; - Args.m_VarsLocal.SetNum("CONNECTIONTYPE", GetConnectType()); + pScriptArgs->m_s1 = GetPeerStr(); + pScriptArgs->m_pO1 = this; + pScriptArgs->m_VarsLocal.SetNum("CONNECTIONTYPE", GetConnectType()); size_t bytes = iLen; size_t bytestr = minimum(bytes, SCRIPT_MAX_LINE_LEN); tchar *zBuf = Str_GetTemp(); - Args.m_VarsLocal.SetNum("NUM", bytes); + pScriptArgs->m_VarsLocal.SetNum("NUM", bytes); memcpy(zBuf, &(pData[0]), bytestr); zBuf[bytestr] = 0; - Args.m_VarsLocal.SetStr("STR", true, zBuf); + pScriptArgs->m_VarsLocal.SetStr("STR", true, zBuf); if ( m_pAccount ) { - Args.m_VarsLocal.SetStr("ACCOUNT", false, m_pAccount->GetName()); + pScriptArgs->m_VarsLocal.SetStr("ACCOUNT", false, m_pAccount->GetName()); if ( m_pChar ) { - Args.m_VarsLocal.SetNum("CHAR", m_pChar->GetUID().GetObjUID()); + pScriptArgs->m_VarsLocal.SetNum("CHAR", m_pChar->GetUID().GetObjUID()); } } @@ -3219,11 +3234,11 @@ bool CClient::xOutPacketFilter( const byte * pData, uint iLen ) for ( size_t i = 0; i < bytes; ++i ) { snprintf(idx, sizeof(idx), "%" PRIuSIZE_T, i); - Args.m_VarsLocal.SetNum(idx, (int)(pData[i])); + pScriptArgs->m_VarsLocal.SetNum(idx, (int)(pData[i])); } // Call the filtering function - if ( g_Serv.r_Call(g_Serv.m_OutPacketFilter[pData[0]], &g_Serv, &Args, nullptr, &trigReturn) ) + if ( g_Serv.r_Call(g_Serv.m_OutPacketFilter[pData[0]], pScriptArgs, &g_Serv, nullptr, &trigReturn) ) if ( trigReturn == TRIGRET_RET_TRUE ) return true; } diff --git a/src/game/clients/CClientLog.cpp b/src/game/clients/CClientLog.cpp index f034b0e24..c698d7bec 100644 --- a/src/game/clients/CClientLog.cpp +++ b/src/game/clients/CClientLog.cpp @@ -6,6 +6,7 @@ #include "../../common/CLog.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/CIPHistoryManager.h" #include "../../network/CNetworkManager.h" #include "../../network/send.h" @@ -477,12 +478,14 @@ bool CClient::OnRxAxis( const byte * pData, uint iLen ) } if (GetPeer().IsValidAddr()) { - CScriptTriggerArgs Args; - Args.m_VarsLocal.SetStrNew("Account",GetName()); - Args.m_VarsLocal.SetStrNew("IP",GetPeer().GetAddrStr()); - TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; - r_Call("f_axis_preload", this, &Args, nullptr, &tRet); - if ( tRet == TRIGRET_RET_FALSE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_VarsLocal.SetStrNew("Account",GetName()); + pScriptArgs->m_VarsLocal.SetStrNew("IP",GetPeer().GetAddrStr()); + + TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; + r_Call("f_axis_preload", pScriptArgs, this, nullptr, &tRet); + + if ( tRet == TRIGRET_RET_FALSE ) return false; if ( tRet == TRIGRET_RET_TRUE ) { diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index adfafa2e8..2d172f94d 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -5,6 +5,7 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/send.h" #include "../chars/CChar.h" #include "../chars/CCharNPC.h" @@ -573,21 +574,22 @@ void CClient::addArrowQuest( int x, int y, int id ) const { ADDTOCALLSTACK("CClient::addArrowQuest"); - CScriptTriggerArgs args(x, y); - if (this->GetNetState()->isClientVersionNumber(MINCLIVER_HS) || this->GetNetState()->isClientEnhanced()) - args.m_iN3 = id; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(x, y, 0, nullptr); + if (GetNetState()->isClientVersionNumber(MINCLIVER_HS) || GetNetState()->isClientEnhanced()) + pScriptArgs->m_iN3 = id; else - args.m_iN3 = 0; + pScriptArgs->m_iN3 = 0; if (x > 0) { if (IsTrigUsed(TRIGGER_ARROWQUEST_ADD)) - m_pChar->OnTrigger(CTRIG_ArrowQuest_Add, m_pChar, &args); + m_pChar->OnTrigger(CTRIG_ArrowQuest_Add, pScriptArgs, m_pChar); } else { if (IsTrigUsed(TRIGGER_ARROWQUEST_CLOSE)) - m_pChar->OnTrigger(CTRIG_ArrowQuest_Close, m_pChar, &args); + m_pChar->OnTrigger(CTRIG_ArrowQuest_Close, pScriptArgs, m_pChar); } new PacketArrowQuest(this, x, y, id); @@ -858,14 +860,15 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ int iClilocId = Exp_GetVal( pszText ); //pszText holds the cliloc number, we can't use ppArgs[0] because if the string name exists it will contain the speaker name along with the cliloc number. int iAffixType = Exp_GetVal( ppArgs[1] ); CSString CArgs; + for (int i = 3; i < iQty; ++i ) { - if ( CArgs.GetLength() ) + if ( CArgs.GetLength() ) CArgs += "\t"; CArgs += ( !strcmp(ppArgs[i], "NULL") ? " " : ppArgs[i] ); } - addBarkLocalizedEx( iClilocId, pSrc, (HUE_TYPE)(Args[0]), mode, (FONT_TYPE)(Args[1]), (AFFIX_TYPE)(iAffixType), ppArgs[2], CArgs.GetBuffer()); + addBarkLocalizedEx( iClilocId, pSrc, (HUE_TYPE)(Args[0]), mode, (FONT_TYPE)(Args[1]), (AFFIX_TYPE)(iAffixType), ppArgs[2], CArgs.GetBuffer()); break; } @@ -877,12 +880,12 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ CSString CArgs; for ( int i = 1; i < iQty; ++i ) { - if ( CArgs.GetLength() ) + if ( CArgs.GetLength() ) CArgs += "\t"; CArgs += ( !strcmp(ppArgs[i], "NULL") ? " " : ppArgs[i] ); } - addBarkLocalized( iClilocId, pSrc, (HUE_TYPE)(Args[0]), mode, (FONT_TYPE)(Args[1]), CArgs.GetBuffer()); + addBarkLocalized( iClilocId, pSrc, (HUE_TYPE)(Args[0]), mode, (FONT_TYPE)(Args[1]), CArgs.GetBuffer()); break; } @@ -1291,21 +1294,22 @@ void CClient::addItemName( CItem * pItem ) if (( IsTrigUsed(TRIGGER_AFTERCLICK) ) || ( IsTrigUsed(TRIGGER_ITEMAFTERCLICK) )) { - CScriptTriggerArgs Args( this ); - Args.m_VarsLocal.SetStrNew("ClickMsgText", &szName[0]); - Args.m_VarsLocal.SetNumNew("ClickMsgHue", (int64)(wHue)); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(0, 0, 0, this); + pScriptArgs->m_VarsLocal.SetStrNew("ClickMsgText", &szName[0]); + pScriptArgs->m_VarsLocal.SetNumNew("ClickMsgHue", (int64)(wHue)); - TRIGRET_TYPE ret = pItem->OnTrigger( "@AfterClick", m_pChar, &Args ); // CTRIG_AfterClick, ITRIG_AfterClick + TRIGRET_TYPE ret = pItem->OnTrigger( "@AfterClick", pScriptArgs, m_pChar ); // CTRIG_AfterClick, ITRIG_AfterClick if ( ret == TRIGRET_RET_TRUE ) return; - lpctstr pNewStr = Args.m_VarsLocal.GetKeyStr("ClickMsgText"); + lpctstr pNewStr = pScriptArgs->m_VarsLocal.GetKeyStr("ClickMsgText"); if ( pNewStr != nullptr ) Str_CopyLimitNull(szName, pNewStr, ARRAY_COUNT(szName)); - wHue = (HUE_TYPE)(Args.m_VarsLocal.GetKeyNum("ClickMsgHue")); + wHue = (HUE_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("ClickMsgHue")); } addObjMessage( szName, pItem, wHue, TALKMODE_ITEM ); @@ -1415,21 +1419,23 @@ void CClient::addCharName( const CChar * pChar ) // Singleclick text for a chara if ( IsTrigUsed(TRIGGER_AFTERCLICK) ) { - CScriptTriggerArgs Args( this ); - Args.m_VarsLocal.SetStrNew("ClickMsgText", pszTemp); - Args.m_VarsLocal.SetNumNew("ClickMsgHue", (int64)(wHue)); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(0, 0, 0, this); + pScriptArgs->m_VarsLocal.SetStrNew("ClickMsgText", pszTemp); + pScriptArgs->m_VarsLocal.SetNumNew("ClickMsgHue", (int64)(wHue)); - TRIGRET_TYPE ret = const_cast(pChar)->OnTrigger( "@AfterClick", m_pChar, &Args ); // CTRIG_AfterClick, ITRIG_AfterClick + // TODO: const correctness... + TRIGRET_TYPE ret = const_cast(pChar)->OnTrigger( "@AfterClick", pScriptArgs, m_pChar ); // CTRIG_AfterClick, ITRIG_AfterClick if ( ret == TRIGRET_RET_TRUE ) return; - lpctstr pNewStr = Args.m_VarsLocal.GetKeyStr("ClickMsgText"); + lpctstr pNewStr = pScriptArgs->m_VarsLocal.GetKeyStr("ClickMsgText"); if ( pNewStr != nullptr ) Str_CopyLimitNull(pszTemp, pNewStr, Str_TempLength()); - wHue = (HUE_TYPE)(Args.m_VarsLocal.GetKeyNum("ClickMsgHue")); + wHue = (HUE_TYPE)(pScriptArgs->m_VarsLocal.GetKeyNum("ClickMsgHue")); } addObjMessage( pszTemp, pChar, wHue, TALKMODE_ITEM ); @@ -1632,9 +1638,9 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou { if ( IsTrigUsed(TRIGGER_TARGON_CANCEL) ) { - CScriptTriggerArgs Args; - Args.m_s1 = m_Targ_Text; - if (pCharThis->OnTrigger( CTRIG_Targon_Cancel, pCharThis, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_s1 = m_Targ_Text; + if (pCharThis->OnTrigger( CTRIG_Targon_Cancel, pScriptArgs, pCharThis ) == TRIGRET_RET_TRUE ) fSuppressCancelMessage = true; } } break; @@ -1643,7 +1649,7 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou CItem * pItemUse = m_Targ_UID.ItemFind(); if (pItemUse && (IsTrigUsed(TRIGGER_TARGON_CANCEL) || IsTrigUsed(TRIGGER_ITEMTARGON_CANCEL))) { - if ( pItemUse->OnTrigger( ITRIG_TARGON_CANCEL, pCharThis) == TRIGRET_RET_TRUE ) + if ( pItemUse->OnTrigger( ITRIG_TARGON_CANCEL, CScriptTriggerArgsPtr{}, pCharThis) == TRIGRET_RET_TRUE ) fSuppressCancelMessage = true; } } break; @@ -1653,11 +1659,12 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou const CSpellDef* pSpellDef = g_Cfg.GetSpellDef(m_tmSkillMagery.m_iSpell); if (pSpellDef) { - CScriptTriggerArgs Args(m_tmSkillMagery.m_iSpell, 0, m_Targ_Prv_UID.ObjFind()); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(m_tmSkillMagery.m_iSpell, 0, 0, m_Targ_Prv_UID.ObjFind()); if ( IsTrigUsed(TRIGGER_SPELLTARGETCANCEL) ) { - if (pCharThis->OnTrigger( CTRIG_SpellTargetCancel, this, &Args ) == TRIGRET_RET_TRUE ) + if (pCharThis->OnTrigger( CTRIG_SpellTargetCancel, pScriptArgs, this ) == TRIGRET_RET_TRUE ) { fSuppressCancelMessage = true; break; @@ -1666,7 +1673,7 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou if ( IsTrigUsed(TRIGGER_TARGETCANCEL) ) { - if (pCharThis->Spell_OnTrigger( m_tmSkillMagery.m_iSpell, SPTRIG_TARGETCANCEL, pCharThis, &Args ) == TRIGRET_RET_TRUE ) + if (pCharThis->Spell_OnTrigger( m_tmSkillMagery.m_iSpell, SPTRIG_TARGETCANCEL, pScriptArgs, pCharThis ) == TRIGRET_RET_TRUE ) fSuppressCancelMessage = true; } } @@ -1700,7 +1707,7 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou { if ( IsTrigUsed(TRIGGER_SKILLTARGETCANCEL) ) { - if (pCharThis->Skill_OnCharTrigger(action, CTRIG_SkillTargetCancel) == TRIGRET_RET_TRUE ) + if (pCharThis->Skill_OnCharTrigger(action, CTRIG_SkillTargetCancel, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) { fSuppressCancelMessage = true; break; @@ -1708,7 +1715,7 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou } if ( IsTrigUsed(TRIGGER_TARGETCANCEL) ) { - if (pCharThis->Skill_OnTrigger(action, SKTRIG_TARGETCANCEL) == TRIGRET_RET_TRUE ) + if (pCharThis->Skill_OnTrigger(action, SKTRIG_TARGETCANCEL, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) fSuppressCancelMessage = true; } } @@ -1906,8 +1913,9 @@ void CClient::addSkillWindow(SKILL_TYPE skill, bool fFromInfo) const // Opens th if ( IsTrigUsed(TRIGGER_USERSKILLS) ) { - CScriptTriggerArgs Args(fAllSkills? -1 : skill, fFromInfo); - if (m_pChar->OnTrigger(CTRIG_UserSkills, pChar, &Args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init((fAllSkills? -1 : skill), fFromInfo, 0, nullptr); + if (m_pChar->OnTrigger(CTRIG_UserSkills, pScriptArgs, pChar) == TRIGRET_RET_TRUE) return; } @@ -2182,9 +2190,9 @@ void CClient::addStatusWindow( CObjBase *pObj, bool fRequested ) // Opens the st if ( IsTrigUsed(TRIGGER_USERSTATS) ) { - CScriptTriggerArgs Args(0, 0, pObj); - Args.m_iN3 = fRequested; - if ( m_pChar->OnTrigger(CTRIG_UserStats, dynamic_cast(pObj), &Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(0, 0, fRequested, pObj); + if ( m_pChar->OnTrigger(CTRIG_UserStats, pScriptArgs, dynamic_cast(pObj)) == TRIGRET_RET_TRUE ) return; } @@ -2273,8 +2281,9 @@ void CClient::addSpellbookOpen( CItem * pBook ) if ( IsTrigUsed(TRIGGER_SPELLBOOK) ) { - CScriptTriggerArgs Args( 0, 0, pBook ); - if ( m_pChar->OnTrigger( CTRIG_SpellBook, m_pChar, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init( 0, 0, 0, pBook ); + if ( m_pChar->OnTrigger( CTRIG_SpellBook, pScriptArgs, m_pChar ) == TRIGRET_RET_TRUE ) return; } @@ -2660,7 +2669,7 @@ void CClient::addCharPaperdoll( CChar * pChar ) if (IsTrigUsed(TRIGGER_SENDPAPERDOLL)) { - pChar->OnTrigger(CTRIG_SendPaperdoll, m_pChar); + pChar->OnTrigger(CTRIG_SendPaperdoll, CScriptTriggerArgsPtr{}, m_pChar); } new PacketPaperdoll(this, pChar); @@ -2790,15 +2799,16 @@ byte CClient::Setup_Start( CChar * pChar ) // Send character startup stuff to pl bool fQuickLogIn = pChar->LayerFind(LAYER_FLAG_ClientLinger); if ( IsTrigUsed(TRIGGER_LOGIN) ) { - CScriptTriggerArgs Args( fNoMessages, fQuickLogIn ); - if ( pChar->OnTrigger( CTRIG_LogIn, pChar, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(fNoMessages, fQuickLogIn, 0, nullptr); + if ( pChar->OnTrigger( CTRIG_LogIn, pScriptArgs, pChar ) == TRIGRET_RET_TRUE ) { m_pChar->ClientDetach(); pChar->SetDisconnected(); return PacketLoginError::Blocked; } - fNoMessages = (Args.m_iN1 != 0); - fQuickLogIn = (Args.m_iN2 != 0); + fNoMessages = (pScriptArgs->m_iN1 != 0); + fQuickLogIn = (pScriptArgs->m_iN2 != 0); } tchar *z = Str_GetTemp(); @@ -3108,12 +3118,14 @@ byte CClient::LogIn( CAccount * pAccount, CSString & sMsg ) // Do the scripts allow to login this account? pAccount->m_Last_IP.SetAddrIP(GetPeer().GetAddrIP()); - CScriptTriggerArgs Args; - Args.Init(pAccount->GetName()); - Args.m_iN1 = GetConnectType(); - Args.m_pO1 = this; - TRIGRET_TYPE tr = TRIGRET_RET_DEFAULT; - g_Serv.r_Call("f_onaccount_login", &g_Serv, &Args, nullptr, &tr); + + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pAccount->GetName()); + pScriptArgs->m_iN1 = GetConnectType(); + pScriptArgs->m_pO1 = this; + + TRIGRET_TYPE tr = TRIGRET_RET_DEFAULT; + g_Serv.r_Call("f_onaccount_login", pScriptArgs, &g_Serv, nullptr, &tr); if ( tr == TRIGRET_RET_TRUE ) { sMsg = g_Cfg.GetDefaultMsg( DEFMSG_MSG_ACC_DENIED ); diff --git a/src/game/clients/CClientMsg_AOSTooltip.cpp b/src/game/clients/CClientMsg_AOSTooltip.cpp index 9b513f8eb..ae41d9701 100644 --- a/src/game/clients/CClientMsg_AOSTooltip.cpp +++ b/src/game/clients/CClientMsg_AOSTooltip.cpp @@ -1,4 +1,5 @@ #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../chars/CChar.h" #include "../chars/CCharNPC.h" #include "../clients/CClientTooltip.h" @@ -96,9 +97,10 @@ bool CClient::addAOSTooltip(CObjBase * pObj, bool fRequested, bool fShop) if (IsTrigUsed(TRIGGER_CLIENTTOOLTIP) || (pItem && IsTrigUsed(TRIGGER_ITEMCLIENTTOOLTIP)) || (pChar && IsTrigUsed(TRIGGER_CHARCLIENTTOOLTIP))) { - CScriptTriggerArgs args(pObj); - args.m_iN1 = fRequested; - iRet = pObj->OnTrigger("@ClientTooltip", this->GetChar(), &args); //ITRIG_CLIENTTOOLTIP , CTRIG_ClientTooltip + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pObj; + pScriptArgs->m_iN1 = fRequested; + iRet = pObj->OnTrigger("@ClientTooltip", pScriptArgs, this->GetChar()); //ITRIG_CLIENTTOOLTIP , CTRIG_ClientTooltip } if (iRet != TRIGRET_RET_TRUE) @@ -117,9 +119,10 @@ bool CClient::addAOSTooltip(CObjBase * pObj, bool fRequested, bool fShop) if (IsTrigUsed(TRIGGER_CLIENTTOOLTIP_AFTERDEFAULT) || (pItem && IsTrigUsed(TRIGGER_ITEMCLIENTTOOLTIP_AFTERDEFAULT)) || (pChar && IsTrigUsed(TRIGGER_CHARCLIENTTOOLTIP_AFTERDEFAULT))) { - CScriptTriggerArgs args(pObj); - args.m_iN1 = fRequested; - iRet = pObj->OnTrigger("@ClientTooltip_AfterDefault", this->GetChar(), &args); //Save to return on iRet to make sure return value doesn't stuck the boolean. + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pObj; + pScriptArgs->m_iN1 = fRequested; + iRet = pObj->OnTrigger("@ClientTooltip_AfterDefault", pScriptArgs, this->GetChar()); //Save to return on iRet to make sure return value doesn't stuck the boolean. } } diff --git a/src/game/clients/CClientTarg.cpp b/src/game/clients/CClientTarg.cpp index f0b3f4b32..e7956fd36 100644 --- a/src/game/clients/CClientTarg.cpp +++ b/src/game/clients/CClientTarg.cpp @@ -4,6 +4,7 @@ #include "../../common/resource/sections/CItemTypeDef.h" #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CLog.h" #include "../chars/CChar.h" #include "../items/CItemCorpse.h" @@ -88,18 +89,19 @@ bool CClient::OnTarg_Obj_Set( CObjBase * pObj ) bool CClient::OnTarg_Obj_Function( CObjBase * pObj, const CPointMap & pt, ITEMID_TYPE id ) { ADDTOCALLSTACK("CClient::OnTarg_Obj_Function"); - m_Targ_p = pt; - lpctstr pSpace = strchr( m_Targ_Text, ' ' ); + m_Targ_p = pt; + lpctstr pSpace = strchr( m_Targ_Text, ' ' ); if ( !pSpace ) pSpace = strchr( m_Targ_Text, '\t' ); if ( pSpace ) GETNONWHITESPACE( pSpace ); - CScriptTriggerArgs Args( pSpace ? pSpace : "" ); - Args.m_VarsLocal.SetNum( "ID", id, true ); - Args.m_pO1 = pObj; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pSpace ? pSpace : ""); + pScriptArgs->m_VarsLocal.SetNum( "ID", id, true ); + pScriptArgs->m_pO1 = pObj; CSString sVal; - m_pChar->r_Call( static_cast(m_Targ_Text), this, &Args, &sVal ); + m_pChar->r_Call(m_Targ_Text.GetBuffer(), pScriptArgs, this, &sVal ); return true; } @@ -1714,8 +1716,9 @@ bool CClient::OnTarg_Use_Item( CObjBase * pObjTarg, CPointMap & pt, ITEMID_TYPE if (( IsTrigUsed(CItem::sm_szTrigName[trigtype]) ) || ( IsTrigUsed(CChar::sm_szTrigName[(CTRIG_itemAfterClick - 1) + trigtype]) )) //ITRIG_TARGON_GROUND, ITRIG_TARGON_CHAR, ITRIG_TARGON_ITEM { - CScriptTriggerArgs Args( id, 0, pObjTarg ); - if ( pItemUse->OnTrigger( trigtype, m_pChar, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(id, 0, 0, pObjTarg); + if ( pItemUse->OnTrigger( trigtype, pScriptArgs, m_pChar ) == TRIGRET_RET_TRUE ) return true; } @@ -2464,8 +2467,7 @@ bool CClient::OnTarg_Party_Add( CChar * pChar ) if ( IsTrigUsed(TRIGGER_PARTYINVITE) ) { - CScriptTriggerArgs args; - if ( pChar->OnTrigger(CTRIG_PartyInvite, m_pChar, &args) == TRIGRET_RET_TRUE ) + if ( pChar->OnTrigger(CTRIG_PartyInvite, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) return false; } diff --git a/src/game/clients/CClientUse.cpp b/src/game/clients/CClientUse.cpp index 4f6914afa..90d82006c 100644 --- a/src/game/clients/CClientUse.cpp +++ b/src/game/clients/CClientUse.cpp @@ -2,6 +2,7 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CLog.h" #include "../../network/send.h" #include "../chars/CChar.h" @@ -103,7 +104,7 @@ bool CClient::Cmd_Use_Item( CItem *pItem, bool fTestTouch, bool fScript ) if ( IsTrigUsed(TRIGGER_DCLICK) || IsTrigUsed(TRIGGER_ITEMDCLICK) ) { - if ( pItem->OnTrigger(ITRIG_DCLICK, m_pChar) == TRIGRET_RET_TRUE ) + if ( pItem->OnTrigger(ITRIG_DCLICK, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) return true; } @@ -644,16 +645,16 @@ bool CClient::Skill_Menu(SKILL_TYPE skill, lpctstr skillmenu, ITEMID_TYPE itemus // Default menu is d_craft_menu // Open in page 0, args is skill used. // LPCTSTR dSkillMenu = "d_CraftingMenu"; - CScriptTriggerArgs Args; - Args.m_VarsLocal.SetStrNew("SkillMenu", skillmenu); - Args.m_VarsLocal.SetNumNew("Skill", skill); - Args.m_VarsLocal.SetNumNew("ItemUsed", itemused); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_VarsLocal.SetStrNew("SkillMenu", skillmenu); + pScriptArgs->m_VarsLocal.SetNumNew("Skill", skill); + pScriptArgs->m_VarsLocal.SetNumNew("ItemUsed", itemused); if (IsTrigUsed(TRIGGER_SKILLMENU)) { - if (m_pChar->OnTrigger(CTRIG_SkillMenu, m_pChar, &Args) == TRIGRET_RET_TRUE ) + if (m_pChar->OnTrigger(CTRIG_SkillMenu, pScriptArgs, m_pChar) == TRIGRET_RET_TRUE ) return true; - skillmenu = Args.m_VarsLocal.GetKeyStr("Skillmenu", false); + skillmenu = pScriptArgs->m_VarsLocal.GetKeyStr("Skillmenu", false); } lpctstr SkillUsed = g_Cfg.GetSkillKey(skill); @@ -793,7 +794,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte if ( strcmpi(s.GetArgStr(), "@Cancel") ) continue; - if ( m_pChar->OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, m_pChar, nullptr) == TRIGRET_RET_TRUE ) + if ( m_pChar->OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) return 0; break; @@ -811,8 +812,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte bool fSkip = false; // skip this if we lack resources or skill. bool fSkipNeedCleanup = false; int iOnCount = 0; - int iShowCount = 0; - CScriptTriggerArgs Args; + int iShowCount = 0; while ( s.ReadKeyParse() ) { @@ -889,7 +889,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte // Check for a skill / non-consumables required. if ( s.IsKey("TEST") ) { - m_pChar->ParseScriptText(s.GetArgRaw(), m_pChar); + m_pChar->ParseScriptText(s.GetArgRaw(), CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, m_pChar); CResourceQtyArray skills(s.GetArgStr()); if ( !skills.IsResourceMatchAll(m_pChar) ) { @@ -900,7 +900,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte if ( s.IsKey("TESTIF") ) { - m_pChar->ParseScriptText(s.GetArgRaw(), m_pChar); + m_pChar->ParseScriptText(s.GetArgRaw(), CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, m_pChar); if ( !s.GetArgVal() ) { fSkipNeedCleanup = true; @@ -912,7 +912,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte if ( iOnCount == iSelect ) { // Execute command from script - TRIGRET_TYPE tRet = m_pChar->OnTriggerRunVal(s, TRIGRUN_SINGLE_EXEC, m_pChar, &Args); + TRIGRET_TYPE tRet = m_pChar->OnTriggerRunVal(s, TRIGRUN_SINGLE_EXEC, CScriptTriggerArgsPtr{}, m_pChar); if ( tRet != TRIGRET_RET_DEFAULT ) return (tRet == TRIGRET_RET_TRUE) ? 0 : 1; @@ -1012,9 +1012,10 @@ bool CClient::Cmd_Skill_Magery( SPELL_TYPE iSpell, CObjBase *pSrc ) case SPELL_Polymorph: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) - { - CScriptTriggerArgs args("sm_polymorph"); - if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) + { + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init("sm_polymorph"); + if ( m_pChar->OnTrigger("@SkillMenu", pScriptArgs, m_pChar) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_polymorph")); @@ -1023,9 +1024,10 @@ bool CClient::Cmd_Skill_Magery( SPELL_TYPE iSpell, CObjBase *pSrc ) case SPELL_Summon: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) - { - CScriptTriggerArgs args("sm_summon"); - if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) + { + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init("sm_summon"); + if ( m_pChar->OnTrigger("@SkillMenu", pScriptArgs, m_pChar) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_summon")); @@ -1034,9 +1036,10 @@ bool CClient::Cmd_Skill_Magery( SPELL_TYPE iSpell, CObjBase *pSrc ) case SPELL_Summon_Familiar: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) - { - CScriptTriggerArgs args("sm_summon_familiar"); - if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) + { + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init("sm_summon_familiar"); + if ( m_pChar->OnTrigger("@SkillMenu", pScriptArgs, m_pChar) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_summon_familiar")); @@ -1321,8 +1324,9 @@ bool CClient::Cmd_SecureTrade( CChar *pChar, CItem *pItem ) if ( pItem && (IsTrigUsed(TRIGGER_DROPON_CHAR) || IsTrigUsed(TRIGGER_ITEMDROPON_CHAR)) ) { - CScriptTriggerArgs Args(pChar); - if ( pItem->OnTrigger(ITRIG_DROPON_CHAR, m_pChar, &Args) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pChar; + if ( pItem->OnTrigger(ITRIG_DROPON_CHAR, pScriptArgs, m_pChar) == TRIGRET_RET_TRUE ) return false; } @@ -1356,8 +1360,9 @@ bool CClient::Cmd_SecureTrade( CChar *pChar, CItem *pItem ) { if ( IsTrigUsed(TRIGGER_DROPON_TRADE) ) { - CScriptTriggerArgs Args1(pChar); - if ( pItem->OnTrigger(ITRIG_DROPON_TRADE, this, &Args1) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs1 = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs1->m_pO1 = pChar; + if ( pItem->OnTrigger(ITRIG_DROPON_TRADE, pScriptArgs1, this) == TRIGRET_RET_TRUE ) return false; } CItemContainer *pCont = dynamic_cast(pItemCont); @@ -1370,15 +1375,18 @@ bool CClient::Cmd_SecureTrade( CChar *pChar, CItem *pItem ) // Open new trade window if ( IsTrigUsed(TRIGGER_TRADECREATE) ) { - CScriptTriggerArgs Args(pItem); - if ( (m_pChar->OnTrigger(CTRIG_TradeCreate, pChar, &Args) == TRIGRET_RET_TRUE) || (pChar->OnTrigger(CTRIG_TradeCreate, m_pChar, &Args) == TRIGRET_RET_TRUE) ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pItem; + if ( (m_pChar->OnTrigger(CTRIG_TradeCreate, pScriptArgs, pChar) == TRIGRET_RET_TRUE) + || (pChar->OnTrigger(CTRIG_TradeCreate, pScriptArgs, m_pChar) == TRIGRET_RET_TRUE) ) return false; } if ( IsTrigUsed(TRIGGER_DROPON_TRADE) && pItem ) { - CScriptTriggerArgs Args1(pChar); - if ( pItem->OnTrigger(ITRIG_DROPON_TRADE, this, &Args1) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pChar; + if ( pItem->OnTrigger(ITRIG_DROPON_TRADE, pScriptArgs, this) == TRIGRET_RET_TRUE ) return false; } @@ -1437,8 +1445,9 @@ bool CClient::Cmd_SecureTrade( CChar *pChar, CItem *pItem ) { if ( IsTrigUsed(TRIGGER_DROPON_TRADE) ) { - CScriptTriggerArgs Args1(pChar); - if ( pItem->OnTrigger(ITRIG_DROPON_TRADE, this, &Args1) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pChar; + if ( pItem->OnTrigger(ITRIG_DROPON_TRADE, pScriptArgs, this) == TRIGRET_RET_TRUE ) { pCont1->Delete(); pCont2->Delete(); diff --git a/src/game/clients/CParty.cpp b/src/game/clients/CParty.cpp index 047eeece0..689b6198e 100644 --- a/src/game/clients/CParty.cpp +++ b/src/game/clients/CParty.cpp @@ -1,6 +1,7 @@ #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CLog.h" #include "../../common/CScriptObj.h" #include "../../network/send.h" @@ -254,13 +255,13 @@ bool CPartyDef::MessageEvent( CUID uidDst, CUID uidSrc, const nachar *pText, int if ( !m_pSpeechFunction.IsEmpty() ) { TRIGRET_TYPE tr = TRIGRET_RET_FALSE; - CScriptTriggerArgs Args; - Args.m_iN1 = uidSrc.GetObjUID(); - Args.m_iN2 = uidDst.GetObjUID(); - Args.m_s1 = szText; - Args.m_s1_buf_vec = szText; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = uidSrc.GetObjUID(); + pScriptArgs->m_iN2 = uidDst.GetObjUID(); + pScriptArgs->m_s1 = szText; + pScriptArgs->m_s1_buf_vec = szText; - if ( r_Call(m_pSpeechFunction, &g_Serv, &Args, nullptr, &tr) ) + if ( r_Call(m_pSpeechFunction, pScriptArgs, &g_Serv, nullptr, &tr) ) { if ( tr == TRIGRET_RET_TRUE ) return false; @@ -319,13 +320,12 @@ bool CPartyDef::RemoveMember( CUID uidRemove, CUID uidCommand ) CChar *pSrc = uidCommand.CharFind(); if ( pSrc && IsTrigUsed(TRIGGER_PARTYREMOVE) ) { - CScriptTriggerArgs args; - if ( pCharRemove->OnTrigger(CTRIG_PartyRemove, pSrc, &args) == TRIGRET_RET_TRUE ) + if ( pCharRemove->OnTrigger(CTRIG_PartyRemove, CScriptTriggerArgsPtr{}, pSrc) == TRIGRET_RET_TRUE ) return false; } if ( IsTrigUsed(TRIGGER_PARTYLEAVE) ) { - if ( pCharRemove->OnTrigger(CTRIG_PartyLeave, pCharRemove, nullptr) == TRIGRET_RET_TRUE ) + if ( pCharRemove->OnTrigger(CTRIG_PartyLeave, CScriptTriggerArgsPtr{}, pCharRemove) == TRIGRET_RET_TRUE ) return false; } @@ -360,8 +360,7 @@ bool CPartyDef::Disband( CUID uidMaster ) CChar *pMaster = GetMaster().CharFind(); if ( pMaster && IsTrigUsed(TRIGGER_PARTYDISBAND) ) { - CScriptTriggerArgs args; - if ( pMaster->OnTrigger(CTRIG_PartyDisband, pMaster, &args) == TRIGRET_RET_TRUE ) + if ( pMaster->OnTrigger(CTRIG_PartyDisband, CScriptTriggerArgsPtr{}, pMaster) == TRIGRET_RET_TRUE ) return false; } @@ -378,9 +377,9 @@ bool CPartyDef::Disband( CUID uidMaster ) if ( IsTrigUsed(TRIGGER_PARTYREMOVE) ) { - CScriptTriggerArgs args; - args.m_iN1 = 1; - pChar->OnTrigger(CTRIG_PartyRemove, pSrc, &args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = 1; + pChar->OnTrigger(CTRIG_PartyRemove, pScriptArgs, pSrc); } SendRemoveList(pChar, true); @@ -456,9 +455,8 @@ bool CPartyDef::AcceptEvent( CChar *pCharAccept, CUID uidInviter, bool bForced, } if (IsTrigUsed(TRIGGER_PARTYADD)) - { - CScriptTriggerArgs Args; - if ( pCharAccept->OnTrigger(CTRIG_PartyAdd, pCharInviter, &Args) == TRIGRET_RET_TRUE ) + { + if ( pCharAccept->OnTrigger(CTRIG_PartyAdd, CScriptTriggerArgsPtr{}, pCharInviter) == TRIGRET_RET_TRUE ) return false; } diff --git a/src/game/components/CCChampion.cpp b/src/game/components/CCChampion.cpp index 98266c28a..d87539769 100644 --- a/src/game/components/CCChampion.cpp +++ b/src/game/components/CCChampion.cpp @@ -14,6 +14,7 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CLog.h" #include "../../common/CScript.h" #include "../chars/CChar.h" @@ -171,8 +172,7 @@ void CCChampion::Start(CChar *pChar) if (pChar && IsTrigUsed(TRIGGER_START)) { // TODO: add source? - // DONE - if (OnTrigger(ITRIG_Start, pChar, nullptr) == TRIGRET_RET_TRUE) + if (OnTrigger(ITRIG_Start, CScriptTriggerArgsPtr{}, pChar) == TRIGRET_RET_TRUE) return; } @@ -188,7 +188,7 @@ void CCChampion::Stop(CChar* pChar) { if (IsTrigUsed(TRIGGER_STOP)) { - if (OnTrigger(ITRIG_STOP, pChar, nullptr) == TRIGRET_RET_TRUE) + if (OnTrigger(ITRIG_STOP, CScriptTriggerArgsPtr{}, pChar) == TRIGRET_RET_TRUE) return; } } @@ -219,11 +219,10 @@ void CCChampion::Complete() Stop(); // TODO: add new trigger - // DONE // TODO: Add attacker list in trigger. if (IsTrigUsed(TRIGGER_COMPLETE)) { - OnTrigger(ITRIG_COMPLETE, &g_Serv, nullptr); + OnTrigger(ITRIG_COMPLETE, CScriptTriggerArgsPtr{}, &g_Serv); } // TODO: add rewards, titles, etc? } @@ -401,8 +400,9 @@ void CCChampion::AddWhiteCandle(const CUID& uid) { if (IsTrigUsed(TRIGGER_ADDWHITECANDLE)) { - CScriptTriggerArgs args(pCandle); - if (OnTrigger(ITRIG_ADDWHITECANDLE, &g_Serv, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pCandle; + if (OnTrigger(ITRIG_ADDWHITECANDLE, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE) { pCandle->Delete(); return; @@ -522,8 +522,9 @@ void CCChampion::AddRedCandle(const CUID& uid) { if (IsTrigUsed(TRIGGER_ADDREDCANDLE)) { - CScriptTriggerArgs args(pCandle); - if (OnTrigger(ITRIG_ADDREDCANDLE, &g_Serv, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pCandle; + if (OnTrigger(ITRIG_ADDREDCANDLE, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE) { pCandle->Delete(); return; @@ -559,8 +560,9 @@ void CCChampion::SetLevel(byte iLevel) // DONE if (IsTrigUsed(TRIGGER_LEVEL)) { - CScriptTriggerArgs args(_iLevel, iLevelMonsters, _iCandlesNextLevel); - OnTrigger(ITRIG_LEVEL, &g_Serv, &args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(_iLevel, iLevelMonsters, _iCandlesNextLevel, nullptr); + OnTrigger(ITRIG_LEVEL, pScriptArgs, &g_Serv); } if (_iLevel >= _iLevelMax) // Start boss fight when level maxed. @@ -673,9 +675,10 @@ void CCChampion::DelWhiteCandle(CANDLEDELREASON_TYPE reason) // DONE if (IsTrigUsed(TRIGGER_DELWHITECANDLE)) { - CScriptTriggerArgs args(reason); - args.m_pO1 = pCandle; - if (OnTrigger(ITRIG_DELWHITECANDLE, &g_Serv, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = reason; + pScriptArgs->m_pO1 = pCandle; + if (OnTrigger(ITRIG_DELWHITECANDLE, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE) return; } @@ -699,9 +702,10 @@ void CCChampion::DelRedCandle(CANDLEDELREASON_TYPE reason) // DONE if (IsTrigUsed(TRIGGER_DELREDCANDLE)) { - CScriptTriggerArgs args(reason); - args.m_pO1 = pCandle; - if (OnTrigger(ITRIG_DELREDCANDLE, &g_Serv, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = reason; + pScriptArgs->m_pO1 = pCandle; + if (OnTrigger(ITRIG_DELREDCANDLE, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE) return; } @@ -1191,7 +1195,7 @@ bool CCChampion::r_Verb(CScript & s, CTextConsole * pSrc) } -TRIGRET_TYPE CCChampion::OnTrigger(ITRIG_TYPE trig, CTextConsole* pSrc, CScriptTriggerArgs* pArgs) +TRIGRET_TYPE CCChampion::OnTrigger(ITRIG_TYPE trig, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc) { lpctstr pszTrigName = CItem::sm_szTrigName[trig]; @@ -1205,12 +1209,12 @@ TRIGRET_TYPE CCChampion::OnTrigger(ITRIG_TYPE trig, CTextConsole* pSrc, CScriptT CResourceLock s; if (pResourceLink->ResourceLock(s)) { - iRet = GetLink()->OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = GetLink()->OnTriggerScript(s, pszTrigName, pArgs, pSrc); } } if (iRet == TRIGRET_RET_DEFAULT) { - iRet = GetLink()->OnTrigger(trig, pSrc, pArgs); + iRet = GetLink()->OnTrigger(trig, pArgs, pSrc); } return iRet; } diff --git a/src/game/components/CCChampion.h b/src/game/components/CCChampion.h index 9a4982570..dd1cf4cb5 100644 --- a/src/game/components/CCChampion.h +++ b/src/game/components/CCChampion.h @@ -232,7 +232,7 @@ class CCChampion : public CComponent /** * @brief Deleting last added White Candle. */ - void DelWhiteCandle(CANDLEDELREASON_TYPE reason = CANDLEDELREASON_CLEAR); + void DelWhiteCandle(CANDLEDELREASON_TYPE iReason = CANDLEDELREASON_CLEAR); /** * @brief Deleting last added Red Candle.d. */ @@ -304,7 +304,7 @@ class CCChampion : public CComponent virtual bool r_LoadVal(CScript & s) override; virtual bool r_Verb(CScript & s, CTextConsole * pSrc) override; // Execute command from script virtual void Copy(const CComponent *target) override; - TRIGRET_TYPE OnTrigger(ITRIG_TYPE trig, CTextConsole *pSrc, CScriptTriggerArgs *args); + TRIGRET_TYPE OnTrigger(ITRIG_TYPE trig, CScriptTriggerArgsPtr args, CTextConsole *pSrc); /************************************************************************ * CItem related section. diff --git a/src/game/components/CCMultiMovable.cpp b/src/game/components/CCMultiMovable.cpp index 95c8efc01..6753308ee 100644 --- a/src/game/components/CCMultiMovable.cpp +++ b/src/game/components/CCMultiMovable.cpp @@ -1,5 +1,6 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/CClientIterator.h" #include "../../network/send.h" #include "../clients/CClient.h" @@ -445,8 +446,9 @@ bool CCMultiMovable::MoveToRegion(CRegionWorld * pRegionOld, CRegionWorld *pRegi if (IsTrigUsed(TRIGGER_REGIONLEAVE)) { - CScriptTriggerArgs Args(pRegionOld); - if (pMulti->OnTrigger(ITRIG_RegionLeave, pMulti->GetCaptain(), &Args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pRegionOld; + if (pMulti->OnTrigger(ITRIG_RegionLeave, pScriptArgs, pMulti->GetCaptain()) == TRIGRET_RET_TRUE) { return false; } @@ -464,8 +466,9 @@ bool CCMultiMovable::MoveToRegion(CRegionWorld * pRegionOld, CRegionWorld *pRegi if (IsTrigUsed(TRIGGER_REGIONENTER)) { - CScriptTriggerArgs Args(pRegionNew); - if (pMulti->OnTrigger(ITRIG_RegionEnter, pMulti->GetCaptain(), &Args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pRegionNew; + if (pMulti->OnTrigger(ITRIG_RegionEnter, pScriptArgs, pMulti->GetCaptain()) == TRIGRET_RET_TRUE) { return false; } @@ -624,8 +627,9 @@ bool CCMultiMovable::Face(DIR_TYPE dir) if (IsTrigUsed(TRIGGER_SHIP_TURN)) { - CScriptTriggerArgs Args(dir, sm_FaceDir[iFaceOffset]); - pItem->OnTrigger(ITRIG_Ship_Turn, &g_Serv, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(dir, sm_FaceDir[iFaceOffset], 0, nullptr); + pItem->OnTrigger(ITRIG_Ship_Turn, pScriptArgs, &g_Serv); } } else if (pObj->IsChar()) @@ -857,8 +861,9 @@ bool CCMultiMovable::Move(DIR_TYPE dir, int distance) if (IsTrigUsed(TRIGGER_SHIP_MOVE)) { - CScriptTriggerArgs Args(dir, fStopped); - pItemThis->OnTrigger(ITRIG_Ship_Move, &g_Serv, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(dir, fStopped, 0, nullptr); + pItemThis->OnTrigger(ITRIG_Ship_Move, pScriptArgs, &g_Serv); } return true; @@ -909,8 +914,9 @@ void CCMultiMovable::Stop() if (IsTrigUsed(TRIGGER_SHIP_STOP)) { - CScriptTriggerArgs Args(pItemThis); - pItemThis->OnTrigger(ITRIG_Ship_Stop, &g_Serv, &Args); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pItemThis; + pItemThis->OnTrigger(ITRIG_Ship_Stop, pScriptArgs, &g_Serv); } _pCaptain = nullptr; @@ -1260,7 +1266,7 @@ bool CCMultiMovable::r_Verb(CScript & s, CTextConsole * pSrc) // Execute command tchar szText[MAX_TALK_BUFFER]; Str_CopyLimitNull(szText, pszSpeak, MAX_TALK_BUFFER); - pChar->ParseScriptText(szText, &g_Serv); + pChar->ParseScriptText(szText, CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, &g_Serv); pTiller->Speak(szText, HUE_TEXT_DEF, TALKMODE_SAY, FONT_NORMAL); } return true; diff --git a/src/game/components/CCPropsChar.cpp b/src/game/components/CCPropsChar.cpp index 7c34a1664..723febb19 100644 --- a/src/game/components/CCPropsChar.cpp +++ b/src/game/components/CCPropsChar.cpp @@ -62,6 +62,8 @@ bool CCPropsChar::IgnoreElementalProperty(PropertyIndex_t iPropIndex) // static case PROPCH_RESFIRE: case PROPCH_RESFIREMAX: return true; + default: + break; } return false; } diff --git a/src/game/components/CCSpawn.cpp b/src/game/components/CCSpawn.cpp index 12ddc0cda..9ee8d2661 100644 --- a/src/game/components/CCSpawn.cpp +++ b/src/game/components/CCSpawn.cpp @@ -1,6 +1,7 @@ #include "../../common/resource/sections/CRandGroupDef.h" #include "../../common/sphere_library/CSRand.h" #include "../../common/CLog.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CException.h" #include "../../common/CExpression.h" #include "../chars/CChar.h" @@ -309,12 +310,13 @@ void CCSpawn::GenerateItem() if (IsTrigUsed(TRIGGER_PRESPAWN)) { - CScriptTriggerArgs args(rid.GetResIndex()); - if (pSpawnItem->OnTrigger(ITRIG_PreSpawn, &g_Serv, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(rid.GetResIndex(), 0, 0, nullptr); + if (pSpawnItem->OnTrigger(ITRIG_PreSpawn, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE) { return; } - rid = CResourceIDBase(RES_ITEMDEF, (int)args.m_iN1); + rid = CResourceIDBase(RES_ITEMDEF, (int)pScriptArgs->m_iN1); } const ITEMID_TYPE id = (ITEMID_TYPE)(rid.GetResIndex()); CItem *pItem = CItem::CreateTemplate(id); @@ -339,9 +341,9 @@ void CCSpawn::GenerateItem() //pItem->SetDecayTime(g_Cfg.m_iDecay_Item); // it will decay eventually to be replaced later if (IsTrigUsed(TRIGGER_SPAWN)) { - CScriptTriggerArgs args; - args.m_pO1 = pItem; - if (pSpawnItem->OnTrigger(ITRIG_Spawn, &g_Serv, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pItem; + if (pSpawnItem->OnTrigger(ITRIG_Spawn, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE) { pItem->Delete(); return; @@ -383,12 +385,13 @@ CChar* CCSpawn::GenerateChar(CResourceIDBase rid) } if (IsTrigUsed(TRIGGER_PRESPAWN)) { - CScriptTriggerArgs args(rid.GetResIndex()); - if (pSpawnItem->OnTrigger(ITRIG_PreSpawn, &g_Serv, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = rid.GetResIndex(); + if (pSpawnItem->OnTrigger(ITRIG_PreSpawn, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE) { return nullptr; } - rid = CResourceIDBase(RES_CHARDEF, (int)args.m_iN1); + rid = CResourceIDBase(RES_CHARDEF, (int)pScriptArgs->m_iN1); } RES_TYPE iRidType = rid.GetResType(); @@ -413,9 +416,9 @@ CChar* CCSpawn::GenerateChar(CResourceIDBase rid) if (IsTrigUsed(TRIGGER_SPAWN)) { - CScriptTriggerArgs args; - args.m_pO1 = pChar; - if (pSpawnItem->OnTrigger(ITRIG_Spawn, &g_Serv, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pChar; + if (pSpawnItem->OnTrigger(ITRIG_Spawn, pScriptArgs, &g_Serv) == TRIGRET_RET_TRUE) { pChar->Delete(); return nullptr; @@ -566,11 +569,11 @@ void CCSpawn::DelObj(const CUID& uid) if (IsTrigUsed(TRIGGER_DELOBJ)) { - CScriptTriggerArgs args; - args.m_pO1 = pSpawnItem; - args.m_iN1 = pSpawnItem->_GetTimerAdjusted() / MSECS_PER_SEC; - pSpawnItem->OnTrigger(ITRIG_DELOBJ, &g_Serv, &args); - pSpawnItem->_SetTimeoutS(args.m_iN1); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pSpawnItem; + pScriptArgs->m_iN1 = pSpawnItem->_GetTimerAdjusted() / MSECS_PER_SEC; + pSpawnItem->OnTrigger(ITRIG_DELOBJ, pScriptArgs, &g_Serv); + pSpawnItem->_SetTimeoutS(pScriptArgs->m_iN1); } pSpawnItem->UpdatePropertyFlag(); @@ -643,12 +646,12 @@ void CCSpawn::AddObj(const CUID& uid) if (IsTrigUsed(TRIGGER_ADDOBJ)) { - CScriptTriggerArgs args; - args.m_pO1 = pSpawnedObj; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pSpawnedObj; const int64 iTimer= pSpawnItem->_GetTimerAdjusted(); - args.m_iN1 = (iTimer < 0) ? -1 : iTimer/MSECS_PER_SEC; - pSpawnItem->OnTrigger(ITRIG_ADDOBJ, &g_Serv, &args); - pSpawnItem->_SetTimeoutS(args.m_iN1); + pScriptArgs->m_iN1 = (iTimer < 0) ? -1 : iTimer/MSECS_PER_SEC; + pSpawnItem->OnTrigger(ITRIG_ADDOBJ, pScriptArgs, &g_Serv); + pSpawnItem->_SetTimeoutS(pScriptArgs->m_iN1); } pSpawnItem->UpdatePropertyFlag(); } diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 2f21b4740..52eb6b799 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -2,6 +2,7 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../network/CClientIterator.h" #include "../../network/send.h" #include "../components/CCChampion.h" @@ -243,7 +244,7 @@ bool CItem::NotifyDelete() ADDTOCALLSTACK("CItem::NotifyDelete"); if ((IsTrigUsed(TRIGGER_DESTROY)) || (IsTrigUsed(TRIGGER_ITEMDESTROY))) { - if (CItem::OnTrigger(ITRIG_DESTROY, &g_Serv) == TRIGRET_RET_TRUE) + if (CItem::OnTrigger(ITRIG_DESTROY, CScriptTriggerArgsPtr{}, &g_Serv) == TRIGRET_RET_TRUE) return false; } @@ -452,7 +453,7 @@ CItem * CItem::GenerateScript( CChar * pSrc) CResourceLock s; if ( pItemDef->ResourceLock(s)) { - OnTrigger(ITRIG_Create, pSrc ? static_cast(pSrc) : static_cast(&g_Serv), nullptr); + OnTrigger(ITRIG_Create, CScriptTriggerArgsPtr{}, pSrc ? static_cast(pSrc) : static_cast(&g_Serv)); } return this; } @@ -650,15 +651,15 @@ CItem * CItem::ReadTemplate( CResourceLock & s, CObjBase * pCont ) // static continue; { lptstr ptcFunctionName = s.GetArgRaw(); - std::unique_ptr pScriptArgs; - // Locate arguments for the called function + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + // Locate arguments for the called function tchar* ptcArgs = strchr(ptcFunctionName, ' '); if (ptcArgs) { *ptcArgs = 0; ++ptcArgs; GETNONWHITESPACE(ptcArgs); - pScriptArgs = std::make_unique(ptcArgs); + pScriptArgs->Init(ptcArgs); } // use pCont is exist, if not use g_Serv @@ -666,11 +667,11 @@ CItem * CItem::ReadTemplate( CResourceLock & s, CObjBase * pCont ) // static { CObjBaseTemplate* pContObjBaseT = pCont->GetTopLevelObj(); ASSERT(pContObjBaseT); - pItem->r_Call(ptcFunctionName, dynamic_cast(pContObjBaseT), pScriptArgs.get()); + pItem->r_Call(ptcFunctionName, pScriptArgs, dynamic_cast(pContObjBaseT)); } else { - pItem->r_Call(ptcFunctionName, &g_Serv, pScriptArgs.get()); + pItem->r_Call(ptcFunctionName, pScriptArgs, &g_Serv); } if (pItem->IsDeleted()) @@ -1624,19 +1625,19 @@ bool CItem::MoveToCheck( const CPointMap & pt, CChar * pCharMover ) TRIGRET_TYPE ttResult = TRIGRET_RET_DEFAULT; if (IsTrigUsed(TRIGGER_DROPON_GROUND) || IsTrigUsed(TRIGGER_ITEMDROPON_GROUND)) { - CScriptTriggerArgs args; - args.m_iN1 = iDecayTime / MSECS_PER_TENTH; // ARGN1 = Decay time for the dropped item (in tenths of second) - //args.m_iN2 = 0; - args.m_s1 = ptNewPlace.WriteUsed(); - ttResult = OnTrigger(ITRIG_DROPON_GROUND, pCharMover, &args); + CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgs->m_iN1 = iDecayTime / MSECS_PER_TENTH; // ARGN1 = Decay time for the dropped item (in tenths of second) + //args->m_iN2 = 0; + pArgs->m_s1 = ptNewPlace.WriteUsed(); + ttResult = OnTrigger(ITRIG_DROPON_GROUND, pArgs, pCharMover); if (IsDeleted()) return false; - iDecayTime = args.m_iN1 * MSECS_PER_TENTH; + iDecayTime = pArgs->m_iN1 * MSECS_PER_TENTH; // Warning: here we ignore the read-onlyness of CSString's buffer only because we know that CPointMap constructor won't write past the end, but only replace some characters with '\0'. It's not worth it to build another string just for that. - tchar* ptcArgs = const_cast(args.m_s1.GetBuffer()); + tchar* ptcArgs = const_cast(pArgs->m_s1.GetBuffer()); const CPointMap ptChanged(ptcArgs); if (!ptChanged.IsValidPoint()) g_Log.EventError("Trying to override item drop P with an invalid P. Using the original one.\n"); @@ -3719,13 +3720,16 @@ void CItem::SetTriggerActive(lpctstr trig) _iRunningTriggerId = -1; } -TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) +TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) { ADDTOCALLSTACK("CItem::OnTrigger"); if (IsTriggerActive(pszTrigName)) //This should protect any item trigger from infinite loop return TRIGRET_RET_ABORTED; + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + if ( !pSrc ) pSrc = &g_Serv; @@ -3760,7 +3764,7 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript EXC_SET_BLOCK("chardef"); const CUID uidOldAct = pChar->m_Act_UID; pChar->m_Act_UID = GetUID(); - iRet = pChar->OnTrigger(ptcCharTrigName, pSrc, pArgs); + iRet = pChar->OnTrigger(ptcCharTrigName, pScriptArgs, pSrc); pChar->m_Act_UID = uidOldAct; if (iRet == TRIGRET_RET_TRUE) goto stopandret; // Block further action. @@ -3785,7 +3789,7 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript if (!pLink->ResourceLock(s)) continue; - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) goto stopandret; @@ -3809,7 +3813,7 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript CResourceLock s; if ( !pLink->ResourceLock(s) ) continue; - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) goto stopandret; } @@ -3824,7 +3828,7 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript CResourceLock s; if ( !pLink->ResourceLock(s) ) continue; - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) goto stopandret; } @@ -3838,7 +3842,7 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript { const CChar* pChar = pSrc->GetChar(); if ( pChar ) - g_Log.EventError( "0%x '%s' has unhandled [TYPEDEF %d] for 0%x '%s'\n", (dword) GetUID(), GetName(), GetType(), (dword) pChar->GetUID(), pChar->GetName()); + g_Log.EventError( "0%x '%s' has unhandled [TYPEDEF %d] for 0%x '%s'\n", (dword) GetUID(), GetName(), GetType(), (dword) pChar->GetUID(), pChar->GetName()); else g_Log.EventError( "0%x '%s' has unhandled [TYPEDEF %d]\n", (dword) GetUID(), GetName(), GetType() ); SetType(Item_GetDef()->GetType()); @@ -3851,7 +3855,7 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript CResourceLock s; if ( pResourceLink->ResourceLock(s)) { - iRet = CScriptObj::OnTriggerScript( s, pszTrigName, pSrc, pArgs ); + iRet = CScriptObj::OnTriggerScript( s, pszTrigName, pScriptArgs, pSrc ); if ( iRet == TRIGRET_RET_TRUE ) goto stopandret; } @@ -3869,7 +3873,7 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript { CResourceLock s; if (pResourceLink->ResourceLock(s)) - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); } // If i'm running the @Create trigger, i jumped here first, but i need to go back and try to run the trigger from the other sources @@ -3884,15 +3888,15 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript EXC_CATCH; EXC_DEBUG_START; - g_Log.EventDebug("trigger '%s' action '%d' char '0%x' [0%x]\n", pszTrigName, iAction, (pSrc && pSrc->GetChar()) ? (dword)pSrc->GetChar()->GetUID() : 0, (dword)GetUID()); + g_Log.EventDebug("trigger '%s' action '%d' char '0%x' [0%x]\n", pszTrigName, iAction, ((pSrc && pSrc->GetChar()) ? (dword)pSrc->GetChar()->GetUID() : 0), (dword)GetUID()); EXC_DEBUG_END; return iRet; } -TRIGRET_TYPE CItem::OnTrigger( ITRIG_TYPE trigger, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) +TRIGRET_TYPE CItem::OnTrigger( ITRIG_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) { ASSERT((trigger >= 0) && (trigger < ITRIG_QTY)); - return OnTrigger( CItem::sm_szTrigName[trigger], pSrc, pArgs ); + return OnTrigger( CItem::sm_szTrigName[trigger], pArgs, pSrc ); } // Item type specific stuff. @@ -5579,21 +5583,22 @@ bool CItem::SetMagicLock( CChar * pCharSrc, int iSkillLevel ) return true; } -bool CItem::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, CItem * pSourceItem, bool bReflecting, int64 iDuration) +bool CItem::OnSpellEffect(SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, CItem * pSourceItem, bool fReflecting, int64 iDuration) { ADDTOCALLSTACK("CItem::OnSpellEffect"); - UnreferencedParameter(bReflecting); // items are not affected by Magic Reflection + UnreferencedParameter(fReflecting); // items are not affected by Magic Reflection UnreferencedParameter(iDuration); // A spell is cast on this item. // ARGS: // iSkillLevel = 0-1000 = difficulty. may be slightly larger . how advanced is this spell (might be from a wand) const CSpellDef * pSpellDef = g_Cfg.GetSpellDef(spell); - CScriptTriggerArgs Args( spell, iSkillLevel, pSourceItem ); + CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgs->Init(spell, iSkillLevel, 0, pSourceItem); if ( IsTrigUsed(TRIGGER_SPELLEFFECT) || IsTrigUsed(TRIGGER_ITEMSPELL) ) { - switch ( OnTrigger(ITRIG_SPELLEFFECT, pCharSrc, &Args) ) + switch ( OnTrigger(ITRIG_SPELLEFFECT, pArgs, pCharSrc) ) { case TRIGRET_RET_TRUE: return false; @@ -5608,7 +5613,7 @@ bool CItem::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, if ( IsTrigUsed(TRIGGER_EFFECT) ) { - switch (Spell_OnTrigger(spell, SPTRIG_EFFECT, pCharSrc, &Args)) + switch (Spell_OnTrigger(spell, SPTRIG_EFFECT, pArgs, pCharSrc)) { case TRIGRET_RET_TRUE: return false; @@ -5621,8 +5626,8 @@ bool CItem::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, } } - spell = (SPELL_TYPE)(Args.m_iN1); - iSkillLevel = (int)(Args.m_iN2); + spell = (SPELL_TYPE)(pArgs->m_iN1); + iSkillLevel = (int)(pArgs->m_iN2); pSpellDef = g_Cfg.GetSpellDef( spell ); ASSERT(pSpellDef); @@ -5742,8 +5747,8 @@ bool CItem::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, if ( (iEffectID > ITEMID_NOTHING) && (iEffectID < ITEMID_QTY) ) { bool fExplode = (pSpellDef->IsSpellType(SPELLFLAG_FX_BOLT) && !pSpellDef->IsSpellType(SPELLFLAG_GOOD)); // bolt (chasing) spells have explode = 1 by default (if not good spell) - dword dwColor = (dword)(Args.m_VarsLocal.GetKeyNum("EffectColor")); - dword dwRender = (dword)(Args.m_VarsLocal.GetKeyNum("EffectRender")); + dword dwColor = (dword)(pArgs->m_VarsLocal.GetKeyNum("EffectColor")); + dword dwRender = (dword)(pArgs->m_VarsLocal.GetKeyNum("EffectRender")); if ( pSpellDef->IsSpellType(SPELLFLAG_FX_BOLT) ) Effect(EFFECT_BOLT, iEffectID, pCharSrc, 5, 1, fExplode, dwColor, dwRender); @@ -5816,8 +5821,9 @@ int CItem::OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uType ) if ( IsTrigUsed(TRIGGER_DAMAGE) || IsTrigUsed(TRIGGER_ITEMDAMAGE) ) { - CScriptTriggerArgs Args(iDmg, (int)(uType)); - if ( OnTrigger( ITRIG_DAMAGE, pSrc, &Args ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pArgs->Init(iDmg, (int)uType, 0, nullptr); + if ( OnTrigger( ITRIG_DAMAGE, pArgs, pSrc ) == TRIGRET_RET_TRUE ) return 0; } @@ -6225,7 +6231,8 @@ bool CItem::_OnTick() if (( IsTrigUsed(TRIGGER_TIMER) ) || ( IsTrigUsed(TRIGGER_ITEMTIMER) )) { - iRet = OnTrigger( ITRIG_TIMER, &g_Serv ); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + iRet = OnTrigger( ITRIG_TIMER, pScriptArgs, &g_Serv ); if (iRet == TRIGRET_RET_TRUE) { return true; diff --git a/src/game/items/CItem.h b/src/game/items/CItem.h index 4b71a2d3b..89a84654c 100644 --- a/src/game/items/CItem.h +++ b/src/game/items/CItem.h @@ -821,8 +821,8 @@ protected: virtual void _SetTimeout(int64 iMsecs) override final; */ void SetTriggerActive(lpctstr trig = nullptr); - virtual TRIGRET_TYPE OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) override; - TRIGRET_TYPE OnTrigger( ITRIG_TYPE trigger, CTextConsole * pSrc, CScriptTriggerArgs * pArgs = nullptr ); + virtual TRIGRET_TYPE OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) override; + TRIGRET_TYPE OnTrigger( ITRIG_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ); // Item type specific stuff. inline bool IsType(IT_TYPE type) const noexcept { @@ -883,7 +883,7 @@ protected: virtual void _SetTimeout(int64 iMsecs) override final; bool IsBookSystem() const; void OnExplosion(); - virtual bool OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, CItem * pSourceItem, bool bReflecting = false, int64 iDuration = 0) override; + virtual bool OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, CItem * pSourceItem, bool fReflecting = false, int64 iDuration = 0) override; int OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uType = DAMAGE_HIT_BLUNT ); int Armor_GetRepairPercent() const; diff --git a/src/game/items/CItemContainer.cpp b/src/game/items/CItemContainer.cpp index f913255af..6b9f27101 100644 --- a/src/game/items/CItemContainer.cpp +++ b/src/game/items/CItemContainer.cpp @@ -1,6 +1,7 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CLog.h" #include "../../network/send.h" #include "../chars/CChar.h" @@ -157,29 +158,34 @@ void CItemContainer::Trade_Status( bool bCheck ) if ( IsTrigUsed(TRIGGER_TRADEACCEPTED) || IsTrigUsed(TRIGGER_CHARTRADEACCEPTED) ) { - CScriptTriggerArgs Args1(pChar1); + CScriptTriggerArgsPtr pScriptArgsPlayer1 = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgsPlayer1->Init(pChar1); + + CScriptTriggerArgsPtr pScriptArgsPlayer2 = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgsPlayer2->Init(pChar2); + ushort i = 1; for (CSObjContRec* pObjRec : *pPartner) { CItem* pItem = static_cast(pObjRec); - Args1.m_VarObjs.Insert(i, pItem, true); + pScriptArgsPlayer1->m_VarObjs.Insert(i, pItem, true); ++i; } - Args1.m_iN1 = --i; + pScriptArgsPlayer1->m_iN1 = --i; - CScriptTriggerArgs Args2(pChar2); i = 1; for (CSObjContRec * pObjRec : *this) { CItem* pItem = static_cast(pObjRec); - Args2.m_VarObjs.Insert(i, pItem, true); + pScriptArgsPlayer2->m_VarObjs.Insert(i, pItem, true); ++i; } - Args2.m_iN1 = --i; + pScriptArgsPlayer2->m_iN1 = --i; - Args1.m_iN2 = Args2.m_iN1; - Args2.m_iN2 = Args1.m_iN1; - if ( (pChar1->OnTrigger(CTRIG_TradeAccepted, pChar2, &Args1) == TRIGRET_RET_TRUE) || (pChar2->OnTrigger(CTRIG_TradeAccepted, pChar1, &Args2) == TRIGRET_RET_TRUE) ) + pScriptArgsPlayer1->m_iN2 = pScriptArgsPlayer2->m_iN1; + pScriptArgsPlayer2->m_iN2 = pScriptArgsPlayer1->m_iN1; + if ((pChar1->OnTrigger(CTRIG_TradeAccepted, pScriptArgsPlayer1, pChar2) == TRIGRET_RET_TRUE) + || (pChar2->OnTrigger(CTRIG_TradeAccepted, pScriptArgsPlayer2, pChar1) == TRIGRET_RET_TRUE) ) return; } @@ -314,11 +320,15 @@ bool CItemContainer::Trade_Delete() if ( IsTrigUsed(TRIGGER_TRADECLOSE) ) { - CChar *pChar2 = dynamic_cast(pPartner->GetParent()); - CScriptTriggerArgs Args(pChar2); - pChar->OnTrigger(CTRIG_TradeClose, pChar, &Args); - CScriptTriggerArgs Args2(pChar); - pChar2->OnTrigger(CTRIG_TradeClose, pChar, &Args2); + CScriptTriggerArgsPtr pScriptArgsPlayer1 = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgsPlayer1->Init(pChar); + + CChar *pChar2 = dynamic_cast(pPartner->GetParent()); + CScriptTriggerArgsPtr pScriptArgsPlayer2 = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgsPlayer2->Init(pChar2); + + pChar->OnTrigger(CTRIG_TradeClose, pScriptArgsPlayer1, pChar); + pChar2->OnTrigger(CTRIG_TradeClose, pScriptArgsPlayer2, pChar); } m_uidLink.InitUID(); // unlink. diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index 64753406c..37bd14ff2 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -2,6 +2,7 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/sphereproto.h" #include "../chars/CChar.h" #include "../clients/CClient.h" @@ -406,7 +407,7 @@ void CItemMulti::Multi_Setup(CChar *pChar, dword dwKeyCode) } } } - pChar->r_Call("f_multi_setup", pChar, nullptr, nullptr, nullptr); + pChar->r_Call("f_multi_setup", CScriptTriggerArgsPtr{}, pChar, nullptr, nullptr); } bool CItemMulti::Multi_IsPartOf(const CItem * pItem) const @@ -1203,21 +1204,23 @@ void CItemMulti::Redeed(bool fDisplayMsg, bool fMoveToBank, CUID uidRedeedingCha pMulti->DeleteAddon(GetUID()); } } - CScriptTriggerArgs args(pDeed); - args.m_iN1 = itDeed; - args.m_iN2 = 1; // Transfer / Redeed all items to the moving crate. - args.m_iN3 = fMoveToBank; // Transfer the Moving Crate to the owner's bank. + + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = pDeed; + pScriptArgs->m_iN1 = itDeed; + pScriptArgs->m_iN2 = 1; // Transfer / Redeed all items to the moving crate. + pScriptArgs->m_iN3 = fMoveToBank; // Transfer the Moving Crate to the owner's bank. if (IsTrigUsed(TRIGGER_REDEED)) { - tRet = OnTrigger(ITRIG_Redeed, uidRedeedingChar.CharFind(), &args); - if (args.m_iN2 == 0) + tRet = OnTrigger(ITRIG_Redeed, pScriptArgs, uidRedeedingChar.CharFind()); + if (pScriptArgs->m_iN2 == 0) { fMoveToBank = false; } else { fTransferAll = true; - fMoveToBank = args.m_iN3 ? true : false; + fMoveToBank = pScriptArgs->m_iN3 ? true : false; } } RemoveAllComponents(); @@ -3245,13 +3248,14 @@ CItem *CItemMulti::Multi_Create(CChar *pChar, const CItemBase * pItemDef, CPoint return nullptr; } } - CScriptTriggerArgs args; - args.m_VarsLocal.SetStrNew("check_blockradius", "-1, -1, 1, 1"); // Values are West, Norht, East, South - args.m_VarsLocal.SetStrNew("check_multiradius", "0, -5, 0, 5"); - args.m_VarsLocal.SetStrNew("id", g_Cfg.ResourceGetName(CResourceID(RES_ITEMDEF, pItemDef->GetID()))); - args.m_VarsLocal.SetStrNew("p", pt.WriteUsed()); + + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_VarsLocal.SetStrNew("check_blockradius", "-1, -1, 1, 1"); // Values are West, Norht, East, South + pScriptArgs->m_VarsLocal.SetStrNew("check_multiradius", "0, -5, 0, 5"); + pScriptArgs->m_VarsLocal.SetStrNew("id", g_Cfg.ResourceGetName(CResourceID(RES_ITEMDEF, pItemDef->GetID()))); + pScriptArgs->m_VarsLocal.SetStrNew("p", pt.WriteUsed()); TRIGRET_TYPE tRet; - pChar->r_Call("f_multi_onplacement_check", pChar, &args, nullptr, &tRet); + pChar->r_Call("f_multi_onplacement_check", pScriptArgs, pChar, nullptr, &tRet); if (tRet == TRIGRET_RET_TRUE) { return nullptr; @@ -3270,10 +3274,10 @@ CItem *CItemMulti::Multi_Create(CChar *pChar, const CItemBase * pItemDef, CPoint if (!pChar->IsPriv(PRIV_GM) && tRet != TRIGRET_RET_HALFBAKED) { CRect rectBlockRadius; - rectBlockRadius.Read(args.m_VarsLocal.GetKeyStr("check_blockradius")); + rectBlockRadius.Read(pScriptArgs->m_VarsLocal.GetKeyStr("check_blockradius")); CRect rectMultiRadius; - rectMultiRadius.Read(args.m_VarsLocal.GetKeyStr("check_multiradius")); + rectMultiRadius.Read(pScriptArgs->m_VarsLocal.GetKeyStr("check_multiradius")); if (!pDeed->IsAttr(ATTR_MAGIC)) { @@ -3602,29 +3606,28 @@ void CMultiStorage::AddHouse(const CUID& uidHouse, HOUSE_PRIV ePriv) { return; } + const CItemMulti *pMulti = static_cast(uidHouse.ItemFind()); - CScriptTriggerArgs args; TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; - args.m_iN1 = pMulti->GetMultiCount(); - args.m_iN2 = ePriv; - if (ePriv == HOUSE_PRIV::HP_OWNER) - { - args.m_iN3 = 1; - } + + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = pMulti->GetMultiCount(); + pScriptArgs->m_iN2 = ePriv; + pScriptArgs->m_iN3 = (ePriv == HOUSE_PRIV::HP_OWNER) ? 1 : 0; if (IsTrigUsed(TRIGGER_ADDMULTI)) { CChar *pChar = _uidSrc.CharFind(); if (pChar) { - tRet = pChar->OnTrigger(CTRIG_AddMulti, pChar, &args); + tRet = pChar->OnTrigger(CTRIG_AddMulti, pScriptArgs, pChar); } } if (tRet != TRIGRET_RET_TRUE) { - if (args.m_iN3 == 1) + if (pScriptArgs->m_iN3 == 1) { - _iHousesTotal += static_cast(args.m_iN1); + _iHousesTotal += static_cast(pScriptArgs->m_iN1); } _lHouses[uidHouse] = ePriv; } @@ -3642,26 +3645,24 @@ void CMultiStorage::DelHouse(const CUID& uidHouse) { CItemMulti *pMulti = static_cast(uidHouse.ItemFind()); HOUSE_PRIV ePriv = GetPriv( uidHouse ); - CScriptTriggerArgs args; - args.m_iN1 = pMulti->GetMultiCount(); - args.m_iN2 = ePriv; - args.m_pO1 = pMulti; - if (ePriv == HOUSE_PRIV::HP_OWNER) - { - args.m_iN3 = 1; - } + + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = pMulti->GetMultiCount(); + pScriptArgs->m_iN2 = ePriv; + pScriptArgs->m_pO1 = pMulti; + pScriptArgs->m_iN3 = (ePriv == HOUSE_PRIV::HP_OWNER) ? 1 : 0; if (IsTrigUsed(TRIGGER_DELMULTI)) { CChar* pChar = _uidSrc.CharFind(); if (pChar) { - pChar->OnTrigger(CTRIG_DelMulti, pChar, &args); + pChar->OnTrigger(CTRIG_DelMulti, pScriptArgs, pChar); } } - if (args.m_iN3 == 1) + if (pScriptArgs->m_iN3 == 1) { - _iHousesTotal -= static_cast(args.m_iN1); + _iHousesTotal -= static_cast(pScriptArgs->m_iN1); } _lHouses.erase(uidHouse); return; @@ -3779,29 +3780,28 @@ void CMultiStorage::AddShip(const CUID& uidShip, HOUSE_PRIV ePriv) { return; } + const CItemShip* pShip = static_cast(uidShip.ItemFind()); - CScriptTriggerArgs args; TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; - args.m_iN1 = pShip->GetMultiCount(); - args.m_iN2 = ePriv; - if (ePriv == HOUSE_PRIV::HP_OWNER) - { - args.m_iN3 = 1; - } + + CScriptTriggerArgsPtr pScriptArgs; + pScriptArgs->m_iN1 = pShip->GetMultiCount(); + pScriptArgs->m_iN2 = ePriv; + pScriptArgs->m_iN3 = (ePriv == HOUSE_PRIV::HP_OWNER) ? 1 : 0; if (IsTrigUsed(TRIGGER_ADDMULTI)) { CChar* pChar = _uidSrc.CharFind(); if (pChar) { - tRet = pChar->OnTrigger(CTRIG_AddMulti, pChar, &args); + tRet = pChar->OnTrigger(CTRIG_AddMulti, pScriptArgs, pChar); } } if (tRet != TRIGRET_RET_TRUE) { - if (args.m_iN3 == 1) + if (pScriptArgs->m_iN3 == 1) { - _iShipsTotal += static_cast(args.m_iN1); + _iShipsTotal += static_cast(pScriptArgs->m_iN1); } _lShips[uidShip] = ePriv; } @@ -3819,13 +3819,14 @@ void CMultiStorage::DelShip(const CUID& uidShip) { CItemMulti* pMulti = static_cast(uidShip.ItemFind()); HOUSE_PRIV ePriv = GetPriv(uidShip); - CScriptTriggerArgs args; - args.m_iN1 = pMulti->GetMultiCount(); - args.m_iN2 = ePriv; - args.m_pO1 = pMulti; + + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = pMulti->GetMultiCount(); + pScriptArgs->m_iN2 = ePriv; + pScriptArgs->m_pO1 = pMulti; if (ePriv == HOUSE_PRIV::HP_OWNER) { - args.m_iN3 = 1; + pScriptArgs->m_iN3 = 1; } if (IsTrigUsed(TRIGGER_DELMULTI)) @@ -3833,12 +3834,12 @@ void CMultiStorage::DelShip(const CUID& uidShip) CChar* pChar = _uidSrc.CharFind(); if (pChar) { - pChar->OnTrigger(CTRIG_DelMulti, pChar, &args); + pChar->OnTrigger(CTRIG_DelMulti, pScriptArgs, pChar); } } - if (args.m_iN3 == 1) + if (pScriptArgs->m_iN3 == 1) { - _iShipsTotal -= static_cast(args.m_iN1); + _iShipsTotal -= static_cast(pScriptArgs->m_iN1); } _lShips.erase(uidShip); return; diff --git a/src/game/items/CItemMultiCustom.cpp b/src/game/items/CItemMultiCustom.cpp index d8e0986d8..a983b3759 100644 --- a/src/game/items/CItemMultiCustom.cpp +++ b/src/game/items/CItemMultiCustom.cpp @@ -6,6 +6,7 @@ #include "../../common/CLog.h" #include "../../common/CException.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../../common/CUOInstall.h" #include "../../network/send.h" #include "../chars/CChar.h" @@ -120,31 +121,31 @@ void CItemMultiCustom::BeginCustomize(CClient* pClientSrc, bool continueCustomiz if (IsTrigUsed(TRIGGER_HOUSEDESIGNBEGIN)) { - CScriptTriggerArgs args; - args.m_pO1 = this; - args.m_iN1 = 1; // Redeed AddOns - args.m_iN2 = 0; // Transfer Lockdowns and Secured containers to Moving Crate. - args.m_iN3 = 2; // Eject everyone from house. - if (pChar->OnTrigger(CTRIG_HouseDesignBegin, pChar, &args) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = this; + pScriptArgs->m_iN1 = 1; // Redeed AddOns + pScriptArgs->m_iN2 = 0; // Transfer Lockdowns and Secured containers to Moving Crate. + pScriptArgs->m_iN3 = 2; // Eject everyone from house. + if (pChar->OnTrigger(CTRIG_HouseDesignBegin, pScriptArgs, pChar) == TRIGRET_RET_TRUE) { EndCustomize(true); return; } - if (args.m_iN1 == 1) + if (pScriptArgs->m_iN1 == 1) { RedeedAddons(); } - if (args.m_iN2 == 1) + if (pScriptArgs->m_iN2 == 1) { TransferSecuredToMovingCrate(); TransferLockdownsToMovingCrate(); } - if (args.m_iN3 == 1) + if (pScriptArgs->m_iN3 == 1) { EjectAll(pChar->GetUID()); } - else if (args.m_iN3 == 2) + else if (pScriptArgs->m_iN3 == 2) { EjectAll(); } @@ -207,9 +208,9 @@ void CItemMultiCustom::EndCustomize(bool fForce) { if (IsTrigUsed(TRIGGER_HOUSEDESIGNEXIT)) { - CScriptTriggerArgs Args(this); - Args.m_iN1 = fForce; - if (pChar->OnTrigger(CTRIG_HouseDesignExit, pChar, &Args) == TRIGRET_RET_TRUE && !fForce) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = fForce; + if (pChar->OnTrigger(CTRIG_HouseDesignExit, pScriptArgs, pChar) == TRIGRET_RET_TRUE && !fForce) { BeginCustomize(pClient); return; @@ -279,8 +280,8 @@ void CItemMultiCustom::CommitChanges(CClient * pClientSrc) CChar* pCharClient = pClientSrc ? pClientSrc->GetChar() : nullptr; if (pCharClient) { + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); const bool fSendFullTrigger = IsTrigUsed(TRIGGER_HOUSEDESIGNCOMMITITEM); - CScriptTriggerArgs Args; short iMaxZ = 0; for (auto it = m_designWorking.m_vectorComponents.begin(); it != m_designWorking.m_vectorComponents.end();) @@ -288,15 +289,15 @@ void CItemMultiCustom::CommitChanges(CClient * pClientSrc) const CMultiComponent* pComp = *it; if (fSendFullTrigger) { - Args.Clear(); - Args.m_VarsLocal.SetNum("ID", pComp->m_item.m_wTileID); - Args.m_VarsLocal.SetNum("P.X", pComp->m_item.m_dx); - Args.m_VarsLocal.SetNum("P.Y", pComp->m_item.m_dy); - Args.m_VarsLocal.SetNum("P.Z", pComp->m_item.m_dz); - Args.m_VarsLocal.SetNum("VISIBLE", pComp->m_item.m_visible); - Args.m_pO1 = this; - - const TRIGRET_TYPE iRet = pCharClient->OnTrigger(CTRIG_HouseDesignCommitItem, pCharClient, &Args); + pScriptArgs->Clear(); + pScriptArgs->m_VarsLocal.SetNum("ID", pComp->m_item.m_wTileID); + pScriptArgs->m_VarsLocal.SetNum("P.X", pComp->m_item.m_dx); + pScriptArgs->m_VarsLocal.SetNum("P.Y", pComp->m_item.m_dy); + pScriptArgs->m_VarsLocal.SetNum("P.Z", pComp->m_item.m_dz); + pScriptArgs->m_VarsLocal.SetNum("VISIBLE", pComp->m_item.m_visible); + pScriptArgs->m_pO1 = this; + + const TRIGRET_TYPE iRet = pCharClient->OnTrigger(CTRIG_HouseDesignCommitItem, pScriptArgs, pCharClient); if (iRet == TRIGRET_RET_FALSE) { it = m_designWorking.m_vectorComponents.erase(it); @@ -312,16 +313,16 @@ void CItemMultiCustom::CommitChanges(CClient * pClientSrc) } if (IsTrigUsed(TRIGGER_HOUSEDESIGNCOMMIT)) { - Args.Clear(); - Args.m_iN1 = m_designMain.m_vectorComponents.size(); - Args.m_iN2 = m_designWorking.m_vectorComponents.size(); - Args.m_iN3 = m_designWorking.m_iRevision; - Args.m_pO1 = this; - Args.m_VarsLocal.SetNum("FIXTURES.OLD", GetFixtureCount(&m_designMain)); - Args.m_VarsLocal.SetNum("FIXTURES.NEW", GetFixtureCount(&m_designWorking)); - Args.m_VarsLocal.SetNum("MAXZ", iMaxZ); + pScriptArgs->Clear(); + pScriptArgs->m_iN1 = m_designMain.m_vectorComponents.size(); + pScriptArgs->m_iN2 = m_designWorking.m_vectorComponents.size(); + pScriptArgs->m_iN3 = m_designWorking.m_iRevision; + pScriptArgs->m_pO1 = this; + pScriptArgs->m_VarsLocal.SetNum("FIXTURES.OLD", GetFixtureCount(&m_designMain)); + pScriptArgs->m_VarsLocal.SetNum("FIXTURES.NEW", GetFixtureCount(&m_designWorking)); + pScriptArgs->m_VarsLocal.SetNum("MAXZ", iMaxZ); - if (pCharClient->OnTrigger(CTRIG_HouseDesignCommit, pCharClient, &Args) == TRIGRET_RET_TRUE) + if (pCharClient->OnTrigger(CTRIG_HouseDesignCommit, pScriptArgs, pCharClient) == TRIGRET_RET_TRUE) return; } } diff --git a/src/game/items/CItemPlant.cpp b/src/game/items/CItemPlant.cpp index 235f95e83..f7e252bb7 100644 --- a/src/game/items/CItemPlant.cpp +++ b/src/game/items/CItemPlant.cpp @@ -1,7 +1,6 @@ - -#include "../../common/CScriptTriggerArgs.h" #include "../../common/resource/CResourceID.h" #include "../../common/CExpression.h" +#include "../../common/CScriptParserBufs.h" #include "../chars/CChar.h" #include "../triggers.h" #include "../CServer.h" @@ -36,15 +35,19 @@ bool CItem::Plant_Use(CChar *pChar) if (!Can(CAN_I_SCRIPTEDMORE)) iFruitIDOverride = (ITEMID_TYPE)m_itCrop.m_ridFruitOverride.GetResIndex(); word iAmount = std::max(m_itCrop.m_ridAmount, (word)1); + if (IsTrigUsed(TRIGGER_RESOURCETEST)) { - CScriptTriggerArgs args(iGrowID, iFruitID, iFruitIDOverride); - TRIGRET_TYPE iRet = OnTrigger(ITRIG_ResourceTest, pChar, &args); - iGrowID = (ITEMID_TYPE)(ResGetIndex((dword)args.m_iN1)); - iFruitID = (ITEMID_TYPE)(ResGetIndex((dword)args.m_iN2)); - iFruitIDOverride = (ITEMID_TYPE)(ResGetIndex((dword)args.m_iN3)); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iGrowID, iFruitID, iFruitIDOverride, nullptr); + TRIGRET_TYPE iRet = OnTrigger(ITRIG_ResourceTest, pScriptArgs, pChar); + if (iRet == TRIGRET_RET_TRUE) return true; + + iGrowID = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_iN1)); + iFruitID = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_iN2)); + iFruitIDOverride = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_iN3)); } if (iGrowID != ITEMID_NOTHING) // If we set an override, we can reap this at every stage @@ -67,10 +70,12 @@ bool CItem::Plant_Use(CChar *pChar) { if (IsTrigUsed(TRIGGER_RESOURCEGATHER)) { - CScriptTriggerArgs args(iAmount); - args.m_pO1 = pItemFruit; - TRIGRET_TYPE iRet = OnTrigger(ITRIG_ResourceGather, pChar, &args); - iAmount = (word)(args.m_iN1 > 0 ? args.m_iN1 : 1); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iAmount, 0, 0, pItemFruit); + + TRIGRET_TYPE iRet = OnTrigger(ITRIG_ResourceGather, pScriptArgs, pChar); + + iAmount = (word)((pScriptArgs->m_iN1 > 0) ? pScriptArgs->m_iN1 : 1); if (iRet == TRIGRET_RET_TRUE) { pItemFruit->Delete(true); @@ -163,7 +168,7 @@ bool CItem::Plant_OnTick() bool CItem::Plant_SetID(ITEMID_TYPE id) { bool iRet = SetID(id); - OnTrigger(ITRIG_Create, &g_Serv, nullptr); + OnTrigger(ITRIG_Create, CScriptTriggerArgsPtr{}, &g_Serv); return iRet; } diff --git a/src/game/items/CItemStone.cpp b/src/game/items/CItemStone.cpp index 52eaf30ad..5f1bad3e4 100644 --- a/src/game/items/CItemStone.cpp +++ b/src/game/items/CItemStone.cpp @@ -2,7 +2,7 @@ #include "../../common/CException.h" #include "../../common/CExpression.h" #include "../../common/CLog.h" -#include "../../common/CScriptTriggerArgs.h" +#include "../../common/CScriptParserBufs.h" #include "../chars/CChar.h" #include "../CServer.h" #include "../CWorld.h" @@ -1333,11 +1333,12 @@ bool CItemStone::IsAlliedWith( const CItemStone * pStone) const if ( pStone == nullptr ) return false; - CScriptTriggerArgs Args; - Args.m_pO1 = const_cast(pStone); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = const_cast(pStone); enum TRIGRET_TYPE tr = TRIGRET_RET_DEFAULT; - if ( const_cast(this)->r_Call("f_stonesys_internal_isalliedwith", &g_Serv, &Args, nullptr, &tr) ) + // TODO: no const_cast please... we'll have to remove const from this method + if ( const_cast(this)->r_Call("f_stonesys_internal_isalliedwith", pScriptArgs, &g_Serv, nullptr, &tr) ) { if ( tr == TRIGRET_RET_FALSE ) return false; @@ -1370,11 +1371,11 @@ bool CItemStone::IsAtWarWith( const CItemStone * pEnemyStone ) const if ( pEnemyStone == nullptr ) return false; - CScriptTriggerArgs Args; - Args.m_pO1 = const_cast(pEnemyStone); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_pO1 = const_cast(pEnemyStone); enum TRIGRET_TYPE tr = TRIGRET_RET_DEFAULT; - if ( const_cast(this)->r_Call("f_stonesys_internal_isatwarwith", &g_Serv, &Args, nullptr, &tr) ) + if ( const_cast(this)->r_Call("f_stonesys_internal_isatwarwith", pScriptArgs, &g_Serv, nullptr, &tr) ) { if ( tr == TRIGRET_RET_FALSE ) return false; diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index ccb0410ca..24cba818a 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -323,7 +323,7 @@ int Sphere_InitServer( int argc, char *argv[] ) // Trigger server start - g_Serv.r_Call("f_onserver_start", &g_Serv, nullptr); + g_Serv.r_Call("f_onserver_start", CScriptTriggerArgsPtr{}, &g_Serv); return g_Serv.GetExitFlag(); EXC_CATCH; @@ -337,7 +337,7 @@ int Sphere_InitServer( int argc, char *argv[] ) void Sphere_ExitServer() { // Trigger server quit - g_Serv.r_Call("f_onserver_exit", &g_Serv, nullptr); + g_Serv.r_Call("f_onserver_exit", CScriptTriggerArgsPtr{}, &g_Serv); g_Serv.SetServerMode(SERVMODE_Exiting); diff --git a/src/network/CIPHistoryManager.cpp b/src/network/CIPHistoryManager.cpp index 4c037776a..bf0f48bcb 100644 --- a/src/network/CIPHistoryManager.cpp +++ b/src/network/CIPHistoryManager.cpp @@ -1,4 +1,4 @@ -#include "../common/CScriptTriggerArgs.h" +#include "../common/CScriptParserBufs.h" #include "../game/CServer.h" #include "../game/CServerConfig.h" #include "../game/CWorldGameTime.h" @@ -34,10 +34,11 @@ void HistoryIP::setBlocked(bool isBlocked, int64 timeoutSeconds) ADDTOCALLSTACK("HistoryIP:setBlocked"); if (isBlocked == true) { - CScriptTriggerArgs args(m_ip.GetAddrStr()); - args.m_iN1 = timeoutSeconds; - g_Serv.r_Call("f_onserver_blockip", &g_Serv, &args); - timeoutSeconds = args.m_iN1; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(m_ip.GetAddrStr()); + pScriptArgs->m_iN1 = timeoutSeconds; + g_Serv.r_Call("f_onserver_blockip", pScriptArgs, &g_Serv ); + timeoutSeconds = pScriptArgs->m_iN1; } m_fBlocked = isBlocked; diff --git a/src/network/CNetworkManager.cpp b/src/network/CNetworkManager.cpp index ca558b289..b6d539492 100644 --- a/src/network/CNetworkManager.cpp +++ b/src/network/CNetworkManager.cpp @@ -1,3 +1,4 @@ +#include "../common/CScriptParserBufs.h" #include "../game/clients/CClient.h" #include "../game/CServer.h" #include "../game/CServerConfig.h" @@ -180,16 +181,18 @@ void CNetworkManager::acceptNewConnection(void) // Call this special scripted function. - CScriptTriggerArgs fargs_ex(client_addr.GetAddrStr()); - fargs_ex.m_VarsLocal.SetNumNew("TIME_CUR_CONNECTED_MS", ip.m_iTimeLastConnectedMs); - fargs_ex.m_VarsLocal.SetNumNew("TIME_LAST_CONNECTED_MS", iIpPrevConnectionTime); - fargs_ex.m_VarsLocal.SetNumNew("PINGS", ip.m_iPings); - fargs_ex.m_VarsLocal.SetNumNew("CONNECTION_REQUESTS", ip.m_iConnectionRequests); - fargs_ex.m_VarsLocal.SetNumNew("ALIVE_CONNECTIONS", ip.m_iAliveSuccessfulConnections); - fargs_ex.m_VarsLocal.SetNumNew("PENDING_CONNECTING", ip.m_iPendingConnectionRequests); - fargs_ex.m_VarsLocal.SetNumNew("BAN_TIMEOUT", 5ll * 60); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(client_addr.GetAddrStr()); + pScriptArgs->m_VarsLocal.SetNumNew("TIME_CUR_CONNECTED_MS", ip.m_iTimeLastConnectedMs); + pScriptArgs->m_VarsLocal.SetNumNew("TIME_LAST_CONNECTED_MS", iIpPrevConnectionTime); + pScriptArgs->m_VarsLocal.SetNumNew("PINGS", ip.m_iPings); + pScriptArgs->m_VarsLocal.SetNumNew("CONNECTION_REQUESTS", ip.m_iConnectionRequests); + pScriptArgs->m_VarsLocal.SetNumNew("ALIVE_CONNECTIONS", ip.m_iAliveSuccessfulConnections); + pScriptArgs->m_VarsLocal.SetNumNew("PENDING_CONNECTING", ip.m_iPendingConnectionRequests); + pScriptArgs->m_VarsLocal.SetNumNew("BAN_TIMEOUT", 5ll * 60); + TRIGRET_TYPE fret = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onserver_connectreq_ex", &g_Serv, &fargs_ex, nullptr, &fret); + g_Serv.r_Call("f_onserver_connectreq_ex", pScriptArgs, &g_Serv, nullptr, &fret); if (fret == -1) { // RETURN -1: do not block. @@ -208,7 +211,7 @@ void CNetworkManager::acceptNewConnection(void) { // RETURN 2 (TRIGRET_RET_DEFAULT) or other: block IP CLOSESOCKET(h); - ip.setBlocked(true, fargs_ex.m_VarsLocal.GetKeyNum("BAN_TIMEOUT")); + ip.setBlocked(true, pScriptArgs->m_VarsLocal.GetKeyNum("BAN_TIMEOUT")); g_Log.Event(LOGM_CLIENTS_LOG | LOGL_ERROR, "Outcome (default): requested kick + IP block allowed by script 'f_onserver_connectreq_ex'.\n"); } @@ -269,11 +272,13 @@ void CNetworkManager::acceptNewConnection(void) */ // Call this special scripted function. - CScriptTriggerArgs fargs_acquired(client_addr.GetAddrStr()); - fargs_acquired.m_iN1 = iIpPrevConnectionTime; - fargs_acquired.m_iN2 = ip.m_iTimeLastConnectedMs; // Current connection time. + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(client_addr.GetAddrStr()); + pScriptArgs->m_iN1 = iIpPrevConnectionTime; + pScriptArgs->m_iN2 = ip.m_iTimeLastConnectedMs; // Current connection time. + TRIGRET_TYPE fret = TRIGRET_RET_FALSE; - g_Serv.r_Call("f_onserver_connection_acquired", &g_Serv, &fargs_acquired, nullptr, &fret); + g_Serv.r_Call("f_onserver_connection_acquired", pScriptArgs, &g_Serv, nullptr, &fret); // select an empty slot EXC_SET_BLOCK("detecting slot"); diff --git a/src/network/receive.cpp b/src/network/receive.cpp index d4fed1129..216f91382 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -1,6 +1,7 @@ #include "../common/resource/sections/CDialogDef.h" #include "../common/CExpression.h" +#include "../common/CScriptParserBufs.h" #include "../common/CLog.h" #include "../common/CUOClientVersion.h" #include "../game/uo_files/uofiles_enums_creid.h" @@ -200,22 +201,22 @@ bool PacketCreate::doCreate(CNetState* net, lpctstr charname, bool fFemale, RACE ASSERT(pChar != nullptr); TRIGRET_TYPE tr = TRIGRET_RET_DEFAULT; - CScriptTriggerArgs createArgs; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); // RW - createArgs.m_iN1 = uiFlags; - createArgs.m_iN2 = prProf; - createArgs.m_iN3 = rtRace; + pScriptArgs->m_iN1 = uiFlags; + pScriptArgs->m_iN2 = prProf; + pScriptArgs->m_iN3 = rtRace; // R - createArgs.m_s1 = account->GetName(); - createArgs.m_pO1 = client; + pScriptArgs->m_s1 = account->GetName(); + pScriptArgs->m_pO1 = client; - client->r_Call("f_onchar_create_init", &g_Serv, &createArgs, nullptr, &tr); + client->r_Call("f_onchar_create_init", pScriptArgs, &g_Serv, nullptr, &tr); if (tr == TRIGRET_RET_TRUE) goto block_creation; - //uiFlags = (uint)createArgs.m_iN1; // unused at this point - prProf = (PROFESSION_TYPE)createArgs.m_iN2; - rtRace = (RACE_TYPE)createArgs.m_iN3; + //uiFlags = (uint)pScriptArgs->.m_iN1; // unused at this point + prProf = (PROFESSION_TYPE)pScriptArgs->m_iN2; + rtRace = (RACE_TYPE)pScriptArgs->m_iN3; // Creating the pChar pChar->InitPlayer(client, charname, fFemale, rtRace, wStr, wDex, wInt, @@ -223,9 +224,9 @@ bool PacketCreate::doCreate(CNetState* net, lpctstr charname, bool fFemale, RACE wSkinHue, idHair, wHairHue, idBeard, wBeardHue, wShirtHue, wPantsHue, idFace, iStartLoc); // Calling the function after the char creation, it can't be done before or the function won't have SRC. - // The createArgs are Read-Only for this function. + // The pScriptArgs-> are Read-Only for this function. tr = TRIGRET_RET_DEFAULT; - client->r_Call("f_onchar_create", pChar, &createArgs, nullptr, &tr); + client->r_Call("f_onchar_create", pScriptArgs, pChar, nullptr, &tr); if ( tr == TRIGRET_RET_TRUE ) { @@ -2240,12 +2241,13 @@ bool PacketGumpDialogRet::onReceive(CNetState* net) client->m_mapOpenedGumps.erase(itGumpFound); // package up the gump response info. - CDialogResponseArgs resp; + // TODO: just split CDialogResponseArgs into two objects (CScriptTriggerArgs and a struct for other data?) + // Or just make CScriptTriggerArgs a member of CDialogResponseArgs... Favor composition over inheritance! + auto resp = std::make_shared(); // store the returned checked boxes' ids for possible later use for (uint i = 0; i < checkCount; ++i) - resp.m_CheckArray.push_back(readInt32()); - + resp->m_CheckArray.push_back(readInt32()); dword textCount = readInt32(); textCount = minimum(textCount, THREAD_STRING_LENGTH); @@ -2263,7 +2265,7 @@ bool PacketGumpDialogRet::onReceive(CNetState* net) if ((fix = strchr(text, '\t')) != nullptr) *fix = ' '; - resp.AddText(id, text); + resp->AddText(id, text); } if (net->isClientKR()) @@ -2287,7 +2289,7 @@ bool PacketGumpDialogRet::onReceive(CNetState* net) // // Call the scripted response. Lose all the checks and text. // - client->Dialog_OnButton( ridContext, button, object, &resp ); + client->Dialog_OnButton( ridContext, button, object, resp ); return true; } @@ -2739,18 +2741,18 @@ bool PacketArrowClick::onReceive(CNetState* net) if (character == nullptr) return false; - bool rightClick = readBool(); + const bool fRightClick = readBool(); if ( IsTrigUsed(TRIGGER_USERQUESTARROWCLICK) ) { - CScriptTriggerArgs Args; - Args.m_iN1 = (rightClick == true? 1 : 0); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = (fRightClick == true? 1 : 0); #ifdef _ALPHASPHERE - Args.m_iN2 = character->GetKeyNum("ARROWQUEST_X", true); - Args.m_iN3 = character->GetKeyNum("ARROWQUEST_Y", true); + pScriptArgs->m_iN2 = character->GetKeyNum("ARROWQUEST_X", true); + pScriptArgs->m_iN3 = character->GetKeyNum("ARROWQUEST_Y", true); #endif - if (character->OnTrigger(CTRIG_UserQuestArrowClick, character, &Args) == TRIGRET_RET_TRUE) + if (character->OnTrigger(CTRIG_UserQuestArrowClick, pScriptArgs, character) == TRIGRET_RET_TRUE) return true; } @@ -3235,8 +3237,9 @@ bool PacketBandageMacro::onReceive(CNetState* net) //Should we simulate the dclick? client->m_Targ_UID = bandage->GetUID(); - CScriptTriggerArgs extArgs(1); // Signal we're from the macro - if (bandage->OnTrigger( ITRIG_DCLICK, character, &extArgs ) == TRIGRET_RET_TRUE) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = 1; // Signal we're from the macro + if (bandage->OnTrigger( ITRIG_DCLICK, pScriptArgs, character) == TRIGRET_RET_TRUE) { return true; } @@ -3327,7 +3330,7 @@ bool PacketGargoyleFly::onReceive(CNetState* net) if ( IsTrigUsed(TRIGGER_TOGGLEFLYING) ) { - if ( character->OnTrigger(CTRIG_ToggleFlying, character, nullptr) == TRIGRET_RET_TRUE ) + if ( character->OnTrigger(CTRIG_ToggleFlying, CScriptTriggerArgsPtr{}, character) == TRIGRET_RET_TRUE ) return false; } @@ -4053,7 +4056,6 @@ bool PacketSpecialMove::onReceive(CNetState* net) dword ability = readInt32(); client->Event_CombatAbilitySelect(ability); - return true; } @@ -4148,7 +4150,7 @@ bool PacketGuildButton::onReceive(CNetState* net) return false; if ( IsTrigUsed(TRIGGER_USERGUILDBUTTON) ) - character->OnTrigger(CTRIG_UserGuildButton, character, nullptr); + character->OnTrigger(CTRIG_UserGuildButton, CScriptTriggerArgsPtr{}, character); return true; } @@ -4175,7 +4177,7 @@ bool PacketQuestButton::onReceive(CNetState* net) return false; if ( IsTrigUsed(TRIGGER_USERQUESTBUTTON) ) - character->OnTrigger(CTRIG_UserQuestButton, character, nullptr); + character->OnTrigger(CTRIG_UserQuestButton, CScriptTriggerArgsPtr{}, character); return true; } @@ -4738,7 +4740,7 @@ bool PacketUltimaStoreButton::onReceive(CNetState *net) return false; if (IsTrigUsed(TRIGGER_USERULTIMASTOREBUTTON)) - character->OnTrigger(CTRIG_UserUltimaStoreButton, character, nullptr); + character->OnTrigger(CTRIG_UserUltimaStoreButton, CScriptTriggerArgsPtr{}, character); return true; } From f5f4e2c033addcfa9ab9737a86c975f919c7ef25 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 17 May 2025 07:38:23 +0200 Subject: [PATCH 013/112] sstring.cpp: added a commented Str_CopyLimitNull implementation, to be benchmarked --- src/common/CUID.h | 21 ++++--- src/common/sphere_library/sstring.cpp | 91 +++++++++++++++++++++++---- src/common/sphere_library/sstring.h | 8 +-- 3 files changed, 95 insertions(+), 25 deletions(-) diff --git a/src/common/CUID.h b/src/common/CUID.h index fcd40e6aa..770486eaf 100644 --- a/src/common/CUID.h +++ b/src/common/CUID.h @@ -33,25 +33,25 @@ class CUID // A unique system serial id. 4 bytes long dword m_dwInternalVal; public: - inline void InitUID() noexcept { + inline constexpr void InitUID() noexcept { m_dwInternalVal = UID_UNUSED; } // Use ClearUID only if the CUID is not used as a pure UID, but it can assume other kind of values. // Example: m_itFigurine.m_UID, m_itKey.m_UIDLock -> a MORE1/MORE2 == 0 is considered legit, also for many many item types MORE* isn't a UID. - inline void ClearUID() noexcept { + inline constexpr void ClearUID() noexcept { m_dwInternalVal = UID_PLAIN_CLEAR; } - inline CUID() noexcept + inline constexpr CUID() noexcept { InitUID(); } - inline CUID(const CUID& uid) noexcept + inline constexpr CUID(const CUID& uid) noexcept { SetPrivateUID(uid.GetPrivateUID()); } - inline explicit CUID(dword dwPrivateUID) noexcept + inline constexpr explicit CUID(dword dwPrivateUID) noexcept { // TODO: directly setting the private UID can led to unexpected results... // it's better to use SetObjUID in order to "filter" the raw value passed. @@ -81,13 +81,13 @@ class CUID // A unique system serial id. 4 bytes long return IsChar(m_dwInternalVal); } - inline bool IsObjDisconnected() const noexcept // Called very frequently + inline constexpr bool IsObjDisconnected() const noexcept // Called very frequently { // Not in the game world for some reason. return ((m_dwInternalVal & (UID_F_RESOURCE | UID_O_DISCONNECT)) == UID_O_DISCONNECT); } - bool IsObjTopLevel() const noexcept + constexpr bool IsObjTopLevel() const noexcept { // on the ground in the world. // might be static in client ? @@ -101,10 +101,10 @@ class CUID // A unique system serial id. 4 bytes long void RemoveObjFlags(dword dwFlags) noexcept; // Internal UID, which also has special flags not understood by the client. - inline void SetPrivateUID(dword dwVal) noexcept { + inline constexpr void SetPrivateUID(dword dwVal) noexcept { m_dwInternalVal = dwVal; } - inline dword GetPrivateUID() const noexcept { + inline constexpr dword GetPrivateUID() const noexcept { return m_dwInternalVal; } @@ -155,4 +155,7 @@ class CUID // A unique system serial id. 4 bytes long }; +static constexpr CUID kUIDUninitialized(UID_UNUSED); +static constexpr CUID kUIDEmpty(UID_PLAIN_CLEAR); + #endif // _INC_CUID_H diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index 8674cb196..ba36c499c 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -61,7 +61,7 @@ bool cstr_to_num( ) noexcept { static_assert(std::is_integral_v<_IntType>, "Only integers supported"); - if (!str || !out || base == 1 || base > 16) + if (!str || !out || base == 1 || base > 16) [[unlikely]] return false; // 1) Skip leading spaces @@ -284,7 +284,7 @@ static constexpr tchar DIGITS[] = "0123456789abcdef"; template tchar* Str_FromInt_Fast(_IntType val, lptstr_restrict out_buf, size_t buf_length, uint base) noexcept { - if (!out_buf || buf_length == 0 || base == 0 || base > 16) + if (!out_buf || buf_length == 0 || base == 0 || base > 16) [[unlikely]] return nullptr; const bool fHex = (base == 16); @@ -524,7 +524,7 @@ int StrncpyCharBytesWritten(int iBytesToWrite, size_t uiBufSize, bool fPrintErro return (uiBufSize > 1) ? int(uiBufSize - 1) : 0; // Bytes written, excluding the string terminator. } -bool IsStrEmpty( const tchar * pszTest ) +bool IsStrEmpty( const tchar * pszTest ) noexcept { if ( !pszTest || !*pszTest ) return true; @@ -538,7 +538,7 @@ bool IsStrEmpty( const tchar * pszTest ) return true; } -bool IsStrNumericDec( const tchar * pszTest ) +bool IsStrNumericDec( const tchar * pszTest ) noexcept { if ( !pszTest || !*pszTest ) return false; @@ -554,7 +554,7 @@ bool IsStrNumericDec( const tchar * pszTest ) } -bool IsStrNumeric( const tchar * pszTest ) +bool IsStrNumeric( const tchar * pszTest ) noexcept { if ( !pszTest || !*pszTest ) return false; @@ -575,7 +575,7 @@ bool IsStrNumeric( const tchar * pszTest ) return true; } -bool IsSimpleNumberString( lpctstr_restrict pszTest ) +bool IsSimpleNumberString( lpctstr_restrict pszTest ) noexcept { // is this a string or a simple numeric expression ? // string = 1 2 3, sdf, sdf sdf sdf, 123d, 123 d, @@ -638,10 +638,10 @@ bool IsSimpleNumberString( lpctstr_restrict pszTest ) // strncpy doesn't null-terminate if it truncates the copy, and if uiMaxlen is > than the source string length, the remaining space is filled with '\0' size_t Str_CopyLimit(lptstr_restrict pDst, lpctstr_restrict pSrc, const size_t uiMaxSize) noexcept { - if (uiMaxSize == 0) + if (uiMaxSize == 0) [[unlikely]] return 0; - if (pSrc[0] == '\0') + if (pSrc[0] == '\0') [[unlikely]] { pDst[0] = '\0'; @@ -660,11 +660,11 @@ size_t Str_CopyLimit(lptstr_restrict pDst, lpctstr_restrict pSrc, const size_t u size_t Str_CopyLimitNull(lptstr_restrict pDst, lpctstr_restrict pSrc, size_t uiMaxSize) noexcept { - if (uiMaxSize == 0) + if (uiMaxSize == 0) [[unlikely]] { return 0; } - if (pSrc[0] == '\0') + if (pSrc[0] == '\0') [[unlikely]] { pDst[0] = '\0'; return 0; @@ -686,6 +686,70 @@ size_t Str_CopyLimitNull(lptstr_restrict pDst, lpctstr_restrict pSrc, size_t uiM return len; // bytes copied in pDst string (not counting the string terminator) } +/* +// Copy up to max_len-1 chars from src to dst, NUL-terminate. +// Returns number of chars written (excluding the terminator). +size_t Str_CopyLimitNull_ShortStr(char* dst, const char* src, size_t max_len) +{ + if (max_len == 0) [[unlikely]] + return 0; + + char* const start = dst; + size_t remaining = max_len - 1; + + // Use 64-bit words on modern 64-bit targets + using word_t = std::conditional_t= 8, uint64_t, uint32_t>; + constexpr ushort W = static_cast(sizeof(word_t)); + + // Magic constants for zero detection + // lomagic (0x0101010101010101ULL) subtracts 1 from each byte. + // himagic (0x8080808080808080ULL) is used to mask out the high bit of each byte. + // The expression ((w - lomagic) & ~w & himagic) evaluates to non-zero if any byte in the word was zero. + constexpr word_t lomagic = (W == 8 ? 0x0101010101010101ULL : 0x01010101U); + constexpr word_t himagic = (W == 8 ? 0x8080808080808080ULL : 0x80808080U); + + // Word-wise loop (assumes unaligned loads/stores are fast) + while (remaining >= W) + { + // Direct unaligned load/store, acceptable for short strings + word_t w = *reinterpret_cast(src); + *reinterpret_cast(dst) = w; + + // Detect any zero byte + if (((w - lomagic) & ~w & himagic) != 0) + { + // Scan this word byte-by-byte for the exact NUL + for (size_t i = 0; i < W; ++i) + { + char c = src[i]; + dst[i] = c; + if (c == '\0') + return (dst + i) - start; + } + // Should never get here + ASSERT(false); + } + src += W; + dst += W; + remaining -= W; + } + + // Tail bytes + while (remaining-- > 0) + { + const char c = *src++; + *dst++ = c; + if (c == '\0') + return dst - start - 1; + } + + // Buffer full – append NUL + *dst = '\0'; + return dst - start; +} +*/ + +// Acceptable overhead for out use cases (non-performance critical). size_t Str_CopyLen(lptstr_restrict pDst, lpctstr_restrict pSrc) noexcept { strcpy(pDst, pSrc); @@ -1016,7 +1080,8 @@ tchar * Str_GetUnQuoted(lptstr_restrict pStr) noexcept int Str_TrimEndWhitespace(tchar * pStr, int len) noexcept { - ASSERT(len >= 0); + if (!pStr) [[unlikely]] + return -1; while (len > 0 && IsWhitespace(pStr[len - 1])) { --len; } @@ -1026,6 +1091,8 @@ int Str_TrimEndWhitespace(tchar * pStr, int len) noexcept tchar * Str_TrimWhitespace(tchar * pStr) noexcept { + if (!pStr) [[unlikely]] + return nullptr; GETNONWHITESPACE(pStr); Str_TrimEndWhitespace(pStr, (int)strlen(pStr)); return pStr; @@ -1033,7 +1100,7 @@ tchar * Str_TrimWhitespace(tchar * pStr) noexcept void Str_EatEndWhitespace(const tchar* const pStrBegin, tchar*& pStrEnd) noexcept { - if (pStrBegin == pStrEnd) + if (pStrBegin == pStrEnd) [[unlikely]] return; tchar* ptcPrev = pStrEnd - 1; diff --git a/src/common/sphere_library/sstring.h b/src/common/sphere_library/sstring.h index ed96c179b..39cb2fa2e 100644 --- a/src/common/sphere_library/sstring.h +++ b/src/common/sphere_library/sstring.h @@ -256,10 +256,10 @@ void Str_SkipEnclosedAngularBrackets(tchar*& ptcLine) noexcept; ///@{ //TODOC -bool IsSimpleNumberString( const tchar * pszTest ); -bool IsStrNumericDec( const tchar * pszTest ); -bool IsStrNumeric( const tchar * pszTest ); -bool IsStrEmpty( const tchar * pszTest ); +bool IsSimpleNumberString( const tchar * pszTest ) noexcept; +bool IsStrNumericDec( const tchar * pszTest ) noexcept; +bool IsStrNumeric( const tchar * pszTest ) noexcept; +bool IsStrEmpty( const tchar * pszTest ) noexcept; // strncpy does not always return the actual amount of bytes written. this doesn't count the string terminator. int StrncpyCharBytesWritten(int iBytesToWrite, size_t uiBufSize, bool fPrintError = true); From 70362b06c1ab3413ecfbb0e19b1ca061c554a23c Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 19 May 2025 16:53:50 +0200 Subject: [PATCH 014/112] Script parsing code relocation and optimization. - Changed: Moved all the script grammar parsing code to CExpression. Each Sphere thread has its copy of CExpression, which now is mostly multi-thread capable. - Changed into CExpression: Made CScriptSubExprState array, used for parsing subexpressions, a member of CExpression, instead of allocating it multiple times and passing it around through the functions. Stack-allocate CScriptExprContext struct and pass it around the functions. It's sufficiently small and way faster than heap allocating it each time, or taking it from a heap-allocated pool. - Changed: Moved DEFMSGs, LISTs, VARs into a new class meant to be a thread-safe holder class for global data, instead of storing them on CExpression, named CExprGlobals. - More info about sobjpool.h, added two commits ago: It's a library to create a pool of heap pre-allocated objects. These can be reused by borrowing one from the pool. It's useful for objects with expensive constructors and/or objects that are very frequently allocated/deallocated, to avoid the cost of heap allocation/deallocation and construction/destruction. - Changed: sstacks.h class names, added another helper stack container class. - Added: class GuardedAccess into basic_threading.h. It offers methods to retrieve LockedReader/LockedWriter objects which also hold a mutex lock. There are also methods which will lock the mutex only if MT_ENGINES macro is enabled, so that the core won't incur in useless mutex locking overhead when unneeded (the multi-threaded world/script handling and networking isn't complete).These are mtEngineLockedReader and mtEngineLockedWriter. It's possible also to retrieve an non-locked/unguarded access. --- .clang-tidy | 2 + src/common/CCacheableScriptFile.cpp | 20 +- src/common/CExpression.cpp | 1102 ++++++++++++++---- src/common/CExpression.h | 188 ++- src/common/CFloatMath.cpp | 8 +- src/common/CLog.cpp | 8 +- src/common/CSVFile.cpp | 8 +- src/common/CScript.cpp | 14 +- src/common/CScriptContexts.cpp | 4 +- src/common/CScriptObj.cpp | 732 ++---------- src/common/CScriptObj.h | 15 +- src/common/CScriptParserBufs.cpp | 27 +- src/common/CScriptParserBufs.h | 31 +- src/common/ListDefContMap.cpp | 6 +- src/common/ListDefContMap.h | 6 +- src/common/basic_threading.h | 202 +++- src/common/common.h | 7 +- src/common/resource/CResourceDef.cpp | 13 +- src/common/resource/CResourceLock.cpp | 6 +- src/common/resource/CResourceScript.cpp | 2 +- src/common/resource/CValueDefs.cpp | 2 +- src/common/resource/sections/CDialogDef.cpp | 7 +- src/common/resource/sections/CWebPageDef.cpp | 44 +- src/common/sphere_library/CSFile.cpp | 26 +- src/common/sphere_library/CSFileText.cpp | 24 +- src/common/sphere_library/sstringobjs.cpp | 18 + src/common/sphere_library/sstringobjs.h | 7 + src/game/CSector.cpp | 4 +- src/game/CServer.cpp | 17 +- src/game/CServerConfig.cpp | 78 +- src/game/CTimedFunction.cpp | 4 +- src/game/CTimedObject.cpp | 8 +- src/game/CWorld.cpp | 8 +- src/game/chars/CChar.cpp | 16 +- src/game/chars/CChar.h | 4 +- src/game/chars/CCharAct.cpp | 2 +- src/game/chars/CCharAttacker.cpp | 2 +- src/game/chars/CCharSkill.cpp | 22 +- src/game/chars/CCharStatus.cpp | 75 +- src/game/chars/CStoneMember.cpp | 6 +- src/game/clients/CClientDialog.cpp | 12 +- src/game/clients/CClientEvent.cpp | 9 +- src/game/clients/CClientMsg.cpp | 93 +- src/game/clients/CClientUse.cpp | 6 +- src/game/components/CCMultiMovable.cpp | 5 +- src/game/components/CCSpawn.cpp | 4 +- src/game/items/CItem.cpp | 8 +- src/game/items/CItemStone.cpp | 58 +- src/game/spheresvr.cpp | 3 +- src/game/uo_files/uofiles_enums.h | 2 +- src/sphere/threads.cpp | 9 +- src/sphere/threads.h | 6 +- src/tables/classnames.tbl | 1 + 53 files changed, 1680 insertions(+), 1311 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index a7ed7d8ee..88cf52568 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -52,6 +52,8 @@ Checks: > ##-readability-braces-around-statements, ##-readability-magic-numbers + #-format security + # Turn all the warnings from the checks above into errors. #WarningsAsErrors: "*" diff --git a/src/common/CCacheableScriptFile.cpp b/src/common/CCacheableScriptFile.cpp index 2f6f949f8..709d31d79 100644 --- a/src/common/CCacheableScriptFile.cpp +++ b/src/common/CCacheableScriptFile.cpp @@ -186,7 +186,7 @@ bool CCacheableScriptFile::_Open(lpctstr ptcFilename, uint uiModeFlags) bool CCacheableScriptFile::Open(lpctstr ptcFilename, uint uiModeFlags) { ADDTOCALLSTACK("CCacheableScriptFile::Open"); - MT_UNIQUE_LOCK_RETURN(CCacheableScriptFile::_Open(ptcFilename, uiModeFlags)); + MT_UNIQUE_LOCK_RETURN(this, CCacheableScriptFile::_Open(ptcFilename, uiModeFlags)); } void CCacheableScriptFile::_Close() @@ -205,7 +205,7 @@ void CCacheableScriptFile::_Close() void CCacheableScriptFile::Close() { ADDTOCALLSTACK("CCacheableScriptFile::Close"); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); _Close(); } @@ -222,7 +222,7 @@ bool CCacheableScriptFile::IsFileOpen() const { ADDTOCALLSTACK("CCacheableScriptFile::IsFileOpen"); - MT_SHARED_LOCK_SET; + MT_SHARED_LOCK_SET(this); if ( _useDefaultFile() ) MT_RETURN(CSFileText::_IsFileOpen()); @@ -240,7 +240,7 @@ bool CCacheableScriptFile::_IsEOF() const bool CCacheableScriptFile::IsEOF() const { //ADDTOCALLSTACK("CCacheableScriptFile::IsEOF"); - MT_SHARED_LOCK_RETURN(_IsEOF()); + MT_SHARED_LOCK_RETURN(this, _IsEOF()); } tchar * CCacheableScriptFile::_ReadString(tchar *pBuffer, int sizemax) @@ -280,7 +280,7 @@ tchar * CCacheableScriptFile::_ReadString(tchar *pBuffer, int sizemax) tchar * CCacheableScriptFile::ReadString(tchar *pBuffer, int sizemax) { //ADDTOCALLSTACK_DEBUG("CCacheableScriptFile::ReadString"); - MT_UNIQUE_LOCK_RETURN(CCacheableScriptFile::_ReadString(pBuffer, sizemax)); + MT_UNIQUE_LOCK_RETURN(this, CCacheableScriptFile::_ReadString(pBuffer, sizemax)); } void CCacheableScriptFile::_dupeFrom(CCacheableScriptFile *other) @@ -295,7 +295,7 @@ void CCacheableScriptFile::_dupeFrom(CCacheableScriptFile *other) } void CCacheableScriptFile::dupeFrom(CCacheableScriptFile *other) { - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); _dupeFrom(other); } @@ -307,7 +307,7 @@ bool CCacheableScriptFile::_HasCache() const } bool CCacheableScriptFile::HasCache() const { - MT_SHARED_LOCK_RETURN(_HasCache()); + MT_SHARED_LOCK_RETURN(this, _HasCache()); } bool CCacheableScriptFile::_useDefaultFile() const @@ -318,7 +318,7 @@ bool CCacheableScriptFile::_useDefaultFile() const * bool CCacheableScriptFile::useDefaultFile() const { - MT_SHARED_LOCK_RETURN(_useDefaultFile()); + MT_SHARED_LOCK_RETURN(this, _useDefaultFile()); }*/ int CCacheableScriptFile::_Seek(int iOffset, int iOrigin) @@ -342,7 +342,7 @@ int CCacheableScriptFile::_Seek(int iOffset, int iOrigin) int CCacheableScriptFile::Seek(int iOffset, int iOrigin) { ADDTOCALLSTACK("CCacheableScriptFile::Seek"); - MT_UNIQUE_LOCK_RETURN(CCacheableScriptFile::_Seek(iOffset, iOrigin)); + MT_UNIQUE_LOCK_RETURN(this, CCacheableScriptFile::_Seek(iOffset, iOrigin)); } int CCacheableScriptFile::_GetPosition() const @@ -356,5 +356,5 @@ int CCacheableScriptFile::_GetPosition() const int CCacheableScriptFile::GetPosition() const { ADDTOCALLSTACK("CCacheableScriptFile::GetPosition"); - MT_UNIQUE_LOCK_RETURN(CCacheableScriptFile::_GetPosition()); + MT_UNIQUE_LOCK_RETURN(this, CCacheableScriptFile::_GetPosition()); } diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index 12f4c841e..13027ef7c 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -8,18 +8,99 @@ #include #include -tchar CExpression::sm_szMessages[DEFMSG_QTY][DEFMSG_MAX_LEN] = + +///////////////////////////////////////////////////////////////////////// +// - CExprGlobals + +tchar CExprGlobals::sm_szDefMessages[DEFMSG_QTY][m_kiDefmsgMaxLen] = { #define MSG(a,b) b, #include "../tables/defmessages.tbl" }; -lpctstr const CExpression::sm_szMsgNames[DEFMSG_QTY] = +lpctstr const CExprGlobals::sm_szDefMsgNames[DEFMSG_QTY] = { #define MSG(a,b) #a, #include "../tables/defmessages.tbl" }; +void CExprGlobals::UpdateDefMsgDependentData() +{ + // Method to be re-evaluated. + // At the moment, it's not actually useful, if g_Cfg.GetDefaultMsg returns the pointer to the string + // CExprGlobals::sm_szDefMessages. The memory location does never change, but we might want to modify this behavior, + // or use this function for other global data which is cached in this class. + + //std::lock_guard _lock_me(MT_CMUTEX); + auto gwriter = g_ExprGlobals.mtEngineLockedWriter(); + auto& vardefs = gwriter->m_VarDefs; + + // TODO: get rid of this associative system... what about using a plain simple map/hash map? + + m_SkillTitles_Ninjitsu = std::array + {{ + { "", INT32_MIN }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_NEOPHYTE], (int)(vardefs.GetKeyNum("SKILLTITLE_NEOPHYTE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_NOVICE], (int)(vardefs.GetKeyNum("SKILLTITLE_NOVICE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_APPRENTICE], (int)(vardefs.GetKeyNum("SKILLTITLE_APPRENTICE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_JOURNEYMAN], (int)(vardefs.GetKeyNum("SKILLTITLE_JOURNEYMAN")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_EXPERT], (int)(vardefs.GetKeyNum("SKILLTITLE_EXPERT")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_ADEPT], (int)(vardefs.GetKeyNum("SKILLTITLE_ADEPT")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_MASTER], (int)(vardefs.GetKeyNum("SKILLTITLE_MASTER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_GRANDMASTER], (int)(vardefs.GetKeyNum("SKILLTITLE_GRANDMASTER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_ELDER_NINJITSU], (int)(vardefs.GetKeyNum("SKILLTITLE_ELDER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_LEGENDARY_NINJITSU], (int)(vardefs.GetKeyNum("SKILLTITLE_LEGENDARY")) }, + { nullptr, INT32_MAX } + }}; + + m_SkillTitles_Bushido = std::array + {{ + { "", INT32_MIN }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_NEOPHYTE], (int)(vardefs.GetKeyNum("SKILLTITLE_NEOPHYTE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_NOVICE], (int)(vardefs.GetKeyNum("SKILLTITLE_NOVICE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_APPRENTICE], (int)(vardefs.GetKeyNum("SKILLTITLE_APPRENTICE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_JOURNEYMAN], (int)(vardefs.GetKeyNum("SKILLTITLE_JOURNEYMAN")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_EXPERT], (int)(vardefs.GetKeyNum("SKILLTITLE_EXPERT")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_ADEPT], (int)(vardefs.GetKeyNum("SKILLTITLE_ADEPT")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_MASTER], (int)(vardefs.GetKeyNum("SKILLTITLE_MASTER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_GRANDMASTER], (int)(vardefs.GetKeyNum("SKILLTITLE_GRANDMASTER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_ELDER_BUSHIDO], (int)(vardefs.GetKeyNum("SKILLTITLE_ELDER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_LEGENDARY_BUSHIDO], (int)(vardefs.GetKeyNum("SKILLTITLE_LEGENDARY")) }, + { nullptr, INT32_MAX } + }}; + + m_SkillTitles_Generic = std::array + {{ + { "", INT32_MIN }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_NEOPHYTE], (int)(vardefs.GetKeyNum("SKILLTITLE_NEOPHYTE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_NOVICE], (int)(vardefs.GetKeyNum("SKILLTITLE_NOVICE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_APPRENTICE], (int)(vardefs.GetKeyNum("SKILLTITLE_APPRENTICE")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_JOURNEYMAN], (int)(vardefs.GetKeyNum("SKILLTITLE_JOURNEYMAN")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_EXPERT], (int)(vardefs.GetKeyNum("SKILLTITLE_EXPERT")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_ADEPT], (int)(vardefs.GetKeyNum("SKILLTITLE_ADEPT")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_MASTER], (int)(vardefs.GetKeyNum("SKILLTITLE_MASTER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_GRANDMASTER], (int)(vardefs.GetKeyNum("SKILLTITLE_GRANDMASTER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_ELDER], (int)(vardefs.GetKeyNum("SKILLTITLE_ELDER")) }, + { sm_szDefMessages[DEFMSG_SKILLTITLE_LEGENDARY], (int)(vardefs.GetKeyNum("SKILLTITLE_LEGENDARY")) }, + { nullptr, INT32_MAX } + }}; +} + +lpctstr CExprGlobals::SkillTitle(SKILL_TYPE skill, uint uiVal) const +{ + // TODO: CValStr::FindName is hack-ish as hell, please use another way of storing and getting this stuff, like maps. + switch (skill) + { + case SKILL_NINJITSU: + return m_SkillTitles_Ninjitsu[0].FindName(uiVal); + case SKILL_BUSHIDO: + return m_SkillTitles_Bushido[0].FindName(uiVal); + default: + break; + } + return m_SkillTitles_Generic[0].FindName(uiVal); +} + ///////////////////////////////////////////////////////////////////////// // - String expressions parsers (separate and evaluate arguments) @@ -426,14 +507,14 @@ int Str_ParseCmdsAdv(tchar * pszCmdLine, tchar ** ppCmd, int iMax, const tchar * bool IsValidResourceDef( lpctstr ptcTest ) { - return (nullptr != g_Exp.m_VarResDefs.CheckParseKey( ptcTest )); + return (nullptr != g_ExprGlobals.mtEngineLockedReader()->m_VarResDefs.CheckParseKey( ptcTest )); } bool IsValidGameObjDef( lpctstr ptcTest ) { if (!IsSimpleNumberString(ptcTest)) { - const CVarDefCont * pVarBase = g_Exp.m_VarResDefs.CheckParseKey( ptcTest ); + const CVarDefCont * pVarBase = g_ExprGlobals.mtEngineLockedReader()->m_VarResDefs.CheckParseKey( ptcTest ); if ( pVarBase == nullptr ) return false; @@ -539,32 +620,45 @@ CExpression::CExpression() noexcept : { } -llong CExpression::GetSingle( lpctstr & pszArgs ) +CExpression& CExpression::GetExprParser() // static +{ + auto thread = static_cast(ThreadHolder::get().current()); + return *(thread->m_pExpr.get()); + /* + // If we need to use it at startup (or deep shutdown?) when there's no AbstractSphereThread instance. + static CExpression expr_thread_unsafe; + return !thread + ? expr_thread_unsafe + : *(thread->m_pExpr.get()); + */ +} + +llong CExpression::GetSingle(lpctstr & refStrExpr ) { ADDTOCALLSTACK("CExpression::GetSingle"); // Parse just a single expression without any operators or ranges. - ASSERT(pszArgs); - GETNONWHITESPACE( pszArgs ); + ASSERT(refStrExpr); + GETNONWHITESPACE( refStrExpr ); - lpctstr ptcStartingString = pszArgs; - if (pszArgs[0]=='.') - ++pszArgs; + lpctstr ptcStartingString = refStrExpr; + if (refStrExpr[0]=='.') + ++refStrExpr; - if ( pszArgs[0] == '0' ) // leading '0' = hex value. + if ( refStrExpr[0] == '0' ) // leading '0' = hex value. { // A hex value. - if ( pszArgs[1] == '.' ) // leading 0. means it really is decimal. + if ( refStrExpr[1] == '.' ) // leading 0. means it really is decimal. { - pszArgs += 2; + refStrExpr += 2; goto try_dec; } - lpctstr pStart = pszArgs; + lpctstr pStart = refStrExpr; llong val = 0; ushort ndigits = 0; while (true) { - tchar ch = *pszArgs; + tchar ch = *refStrExpr; if ( IsDigit(ch) ) ch -= '0'; else @@ -574,7 +668,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) { if ( ch == '.' && pStart[0] != '0' ) // ok i'm confused. it must be decimal. { - pszArgs = pStart; + refStrExpr = pStart; goto try_dec; } break; @@ -584,7 +678,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) } val *= 0x10; val += ch; - ++ pszArgs; + ++ refStrExpr; } if (ndigits <= 8) return (llong)(int)val; @@ -603,53 +697,53 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) return iVal.value(); } */ - else if ( pszArgs[0] == '.' || IsDigit(pszArgs[0]) ) + else if ( refStrExpr[0] == '.' || IsDigit(refStrExpr[0]) ) { // A decimal number try_dec: llong iVal = 0; - for ( ; ; ++pszArgs ) + for ( ; ; ++refStrExpr ) { - if ( *pszArgs == '.' ) + if ( *refStrExpr == '.' ) continue; // just skip this. - if ( ! IsDigit(*pszArgs) ) + if ( ! IsDigit(*refStrExpr) ) break; iVal *= 10; - iVal += (llong)(*pszArgs) - '0'; + iVal += (llong)(*refStrExpr) - '0'; } return iVal; } - else if ( ! _ISCSYMF(pszArgs[0]) ) +else if ( ! _ISCSYMF(refStrExpr[0]) ) { //#pragma region maths // MSVC specific // some sort of math op ? - switch ( pszArgs[0] ) + switch ( refStrExpr[0] ) { case '{': - ++pszArgs; - return GetRangeNumber( pszArgs ); + ++refStrExpr; + return GetRangeNumber( refStrExpr ); case '[': case '(': // Parse out a sub expression. - ++pszArgs; - return GetVal( pszArgs ); + ++refStrExpr; + return GetVal( refStrExpr ); case '+': - ++pszArgs; + ++refStrExpr; break; case '-': - ++pszArgs; - return -GetSingle( pszArgs ); + ++refStrExpr; + return -GetSingle( refStrExpr ); case '~': // Bitwise not. - ++pszArgs; - return ~GetSingle( pszArgs ); + ++refStrExpr; + return ~GetSingle( refStrExpr ); case '!': // boolean not. - ++pszArgs; - if ( pszArgs[0] == '=' ) // odd condition such as (!=x) which is always true of course. + ++refStrExpr; + if ( refStrExpr[0] == '=' ) // odd condition such as (!=x) which is always true of course. { - ++pszArgs; // so just skip it. and compare it to 0 - return GetSingle( pszArgs ); + ++refStrExpr; // so just skip it. and compare it to 0 + return GetSingle( refStrExpr ); } - return !GetSingle( pszArgs ); + return !GetSingle( refStrExpr ); case ';': // seperate field. case ',': // seperate field. case '\0': @@ -662,15 +756,15 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) { // Symbol or intrinsinc function ? - INTRINSIC_TYPE iIntrinsic = (INTRINSIC_TYPE) FindTableHeadSorted( pszArgs, sm_IntrinsicFunctions, ARRAY_COUNT(sm_IntrinsicFunctions)-1 ); + INTRINSIC_TYPE iIntrinsic = (INTRINSIC_TYPE) FindTableHeadSorted( refStrExpr, sm_IntrinsicFunctions, ARRAY_COUNT(sm_IntrinsicFunctions)-1 ); if ( iIntrinsic >= 0 ) { size_t iLen = strlen(sm_IntrinsicFunctions[iIntrinsic]); - if ( strchr("( ", pszArgs[iLen]) ) + if ( strchr("( ", refStrExpr[iLen]) ) { - pszArgs += (iLen + 1); + refStrExpr += (iLen + 1); tchar * pszArgsNext; - Str_Parse( const_cast(pszArgs), &(pszArgsNext), ")" ); + Str_Parse( const_cast(refStrExpr), &(pszArgsNext), ")" ); tchar * ppCmd[5]; llong iResult; @@ -680,10 +774,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) { case INTRINSIC_ID: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = ResGetIndex((dword)GetVal(pszArgs)); + iResult = ResGetIndex((dword)GetVal(refStrExpr)); } else { @@ -695,7 +789,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_MAX: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 2, "," ); if ( iCount < 2 ) iResult = 0; else @@ -707,7 +801,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_MIN: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 2, "," ); if ( iCount < 2 ) iResult = 0; else @@ -722,9 +816,9 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) iCount = 0; iResult = 0; - if ( *pszArgs ) + if ( *refStrExpr ) { - llong iArgument = GetVal(pszArgs); + llong iArgument = GetVal(refStrExpr); if ( iArgument <= 0 ) { DEBUG_ERR(( "Exp_GetVal: (x)Log(%lld) is %s\n", iArgument, (!iArgument ? "infinite" : "undefined") )); @@ -733,21 +827,21 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) { iCount = 1; - if ( strchr(pszArgs, ',') ) + if ( strchr(refStrExpr, ',') ) { ++iCount; - SKIP_ARGSEP(pszArgs); - if ( !strcmpi(pszArgs, "e") ) + SKIP_ARGSEP(refStrExpr); + if ( !strcmpi(refStrExpr, "e") ) { iResult = (llong)log( (double)iArgument ); } - else if ( !strcmpi(pszArgs, "pi") ) + else if ( !strcmpi(refStrExpr, "pi") ) { iResult = (llong)(log( (double)iArgument ) / log( M_PI ) ); } else { - llong iBase = GetVal(pszArgs); + llong iBase = GetVal(refStrExpr); if ( iBase <= 0 ) { DEBUG_ERR(( "Exp_GetVal: (%lld)Log(%lld) is %s\n", iBase, iArgument, (!iBase ? "infinite" : "undefined") )); @@ -766,10 +860,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_NAPIERPOW: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = (llong)exp( (double)GetVal( pszArgs ) ); + iResult = (llong)exp( (double)GetVal( refStrExpr ) ); } else { @@ -784,9 +878,9 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) iCount = 0; iResult = 0; - if ( *pszArgs ) + if ( *refStrExpr ) { - llong iTosquare = GetVal(pszArgs); + llong iTosquare = GetVal(refStrExpr); if (iTosquare >= 0) { @@ -806,10 +900,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_SIN: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = (llong)sin( (double)GetVal( pszArgs ) ); + iResult = (llong)sin( (double)GetVal( refStrExpr ) ); } else { @@ -821,10 +915,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_ARCSIN: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = (llong)asin( (double)GetVal( pszArgs ) ); + iResult = (llong)asin( (double)GetVal( refStrExpr ) ); } else { @@ -836,10 +930,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_COS: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = (llong)cos( (double)GetVal( pszArgs ) ); + iResult = (llong)cos( (double)GetVal( refStrExpr ) ); } else { @@ -851,10 +945,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_ARCCOS: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = (llong)acos( (double)GetVal( pszArgs ) ); + iResult = (llong)acos( (double)GetVal( refStrExpr ) ); } else { @@ -866,10 +960,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_TAN: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = (llong)tan( (double)GetVal( pszArgs ) ); + iResult = (llong)tan( (double)GetVal( refStrExpr ) ); } else { @@ -881,10 +975,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_ARCTAN: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = (llong)atan( (double)GetVal( pszArgs ) ); + iResult = (llong)atan( (double)GetVal( refStrExpr ) ); } else { @@ -896,7 +990,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_StrIndexOf: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 3, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 3, "," ); if ( iCount < 2 ) iResult = -1; else @@ -905,7 +999,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_STRMATCH: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 2, "," ); if ( iCount < 2 ) iResult = 0; else @@ -914,7 +1008,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_STRREGEX: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 2, "," ); if ( iCount < 2 ) iResult = 0; else @@ -930,7 +1024,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_RANDBELL: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 2, "," ); if ( iCount < 2 ) iResult = 0; else @@ -939,10 +1033,10 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_STRASCII: { - if ( *pszArgs ) + if ( *refStrExpr ) { iCount = 1; - iResult = pszArgs[0]; + iResult = refStrExpr[0]; } else { @@ -953,7 +1047,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_RAND: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 2, "," ); if ( iCount <= 0 ) iResult = 0; else @@ -971,7 +1065,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_STRCMP: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 2, "," ); if ( iCount < 2 ) iResult = 1; else @@ -980,7 +1074,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_STRCMPI: { - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 2, "," ); if ( iCount < 2 ) iResult = 1; else @@ -990,26 +1084,26 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_STRLEN: { iCount = 1; - iResult = strlen(pszArgs); + iResult = strlen(refStrExpr); } break; case INTRINSIC_ISOBSCENE: { iCount = 1; - iResult = g_Cfg.IsObscene( pszArgs ); + iResult = g_Cfg.IsObscene( refStrExpr ); } break; case INTRINSIC_ISNUMBER: { iCount = 1; - SKIP_NONNUM( pszArgs ); - iResult = IsStrNumeric( pszArgs ); + SKIP_NONNUM( refStrExpr ); + iResult = IsStrNumeric( refStrExpr ); } break; case INTRINSIC_QVAL: { // Here is handled the intrinsic QVAL form: QVAL(VALUE1,VALUE2,LESSTHAN,EQUAL,GREATERTHAN) - iCount = Str_ParseCmds( const_cast(pszArgs), ppCmd, 5, "," ); + iCount = Str_ParseCmds( const_cast(refStrExpr), ppCmd, 5, "," ); if (iCount < 3) { iResult = 0; @@ -1036,7 +1130,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) case INTRINSIC_ABS: { iCount = 1; - iResult = llabs(GetVal(pszArgs)); + iResult = llabs(GetVal(refStrExpr)); } break; default: @@ -1045,7 +1139,7 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) break; } - pszArgs = pszArgsNext; + refStrExpr = pszArgsNext; if ( !iCount ) { @@ -1058,13 +1152,18 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) } // Must be a symbol of some sort ? - lpctstr ptcArgsOriginal = pszArgs; + + //[[maybe_unused]] auto _ = g_ExprGlobals.mtEngineGetLockShared(); + //auto globals_reader = g_ExprGlobals.unsafeReader(); + auto reader = g_ExprGlobals.mtEngineLockedReader(); + + lpctstr ptcArgsOriginal = refStrExpr; llong llVal; - if ( m_VarGlobals.GetParseVal_Advance( pszArgs, &llVal ) ) // VAR. + if ( reader->m_VarGlobals.GetParseVal_Advance( refStrExpr, &llVal ) ) // VAR. return llVal; - if ( m_VarResDefs.GetParseVal( ptcArgsOriginal, &llVal ) ) // RESDEF. + if ( reader->m_VarResDefs.GetParseVal( ptcArgsOriginal, &llVal ) ) // RESDEF. return llVal; - if ( m_VarDefs.GetParseVal( ptcArgsOriginal, &llVal ) ) // DEF. + if ( reader->m_VarDefs.GetParseVal( ptcArgsOriginal, &llVal ) ) // DEF. return llVal; } //#pragma endregion intrinsics // MSVC specific @@ -1084,14 +1183,14 @@ llong CExpression::GetSingle( lpctstr & pszArgs ) return 0; } -llong CExpression::GetValMath( llong llVal, lpctstr & pExpr ) +llong CExpression::GetValMath(llong llVal, lpctstr & refStrExpr ) { ADDTOCALLSTACK("CExpression::GetValMath"); - GETNONWHITESPACE(pExpr); + GETNONWHITESPACE(refStrExpr); // Look for math type operator and eventually apply it to the second operand (which we evaluate here if a valid operator is found). llong llValSecond; - switch ( pExpr[0] ) + switch ( refStrExpr[0] ) { case '\0': break; @@ -1099,59 +1198,59 @@ llong CExpression::GetValMath( llong llVal, lpctstr & pExpr ) case ')': // expression end markers. case '}': case ']': - ++pExpr; // consume this. + ++refStrExpr; // consume this. break; case '+': - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal += llValSecond; break; case '-': - llValSecond = GetVal(pExpr); + llValSecond = GetVal(refStrExpr); //++pExpr; No need to consume the negative sign, we need to keep it! llVal += llValSecond; // a subtraction is an addiction with a negative number. break; case '*': - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal *= llValSecond; break; case '|': - ++pExpr; - if ( pExpr[0] == '|' ) // boolean ? + ++refStrExpr; + if ( refStrExpr[0] == '|' ) // boolean ? { - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal = (llValSecond || llVal); } else // bitwise { - llValSecond = GetVal(pExpr); + llValSecond = GetVal(refStrExpr); llVal |= llValSecond; } break; case '&': - ++pExpr; - if ( pExpr[0] == '&' ) // boolean ? + ++refStrExpr; + if ( refStrExpr[0] == '&' ) // boolean ? { - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal = (llValSecond && llVal); // tricky stuff here. logical ops must come first or possibly not get processed. } else // bitwise { - llValSecond = GetVal(pExpr); + llValSecond = GetVal(refStrExpr); llVal &= llValSecond; } break; case '/': - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); if (!llValSecond) { g_Log.EventError("Evaluating math: Divide by 0\n"); @@ -1161,8 +1260,8 @@ llong CExpression::GetValMath( llong llVal, lpctstr & pExpr ) break; case '%': - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); if (!llValSecond) { g_Log.EventError("Evaluating math: Modulo 0\n"); @@ -1172,72 +1271,72 @@ llong CExpression::GetValMath( llong llVal, lpctstr & pExpr ) break; case '^': - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal ^= llValSecond; break; case '>': // boolean - ++pExpr; - if ( pExpr[0] == '=' ) // boolean ? + ++refStrExpr; + if ( refStrExpr[0] == '=' ) // boolean ? { - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal = ( llVal >= llValSecond ); } - else if ( pExpr[0] == '>' ) // shift + else if ( refStrExpr[0] == '>' ) // shift { - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal >>= llValSecond; } else { - llValSecond = GetVal(pExpr); + llValSecond = GetVal(refStrExpr); llVal = (llVal > llValSecond); } break; case '<': // boolean - ++pExpr; - if ( pExpr[0] == '=' ) // boolean ? + ++refStrExpr; + if ( refStrExpr[0] == '=' ) // boolean ? { - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal = ( llVal <= llValSecond ); } - else if ( pExpr[0] == '<' ) // shift + else if ( refStrExpr[0] == '<' ) // shift { - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal <<= llValSecond; } else { - llValSecond = GetVal(pExpr); + llValSecond = GetVal(refStrExpr); llVal = (llVal < llValSecond); } break; case '!': - ++pExpr; - if ( pExpr[0] != '=' ) + ++refStrExpr; + if ( refStrExpr[0] != '=' ) break; // boolean ! is handled as a single expresion. - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal = ( llVal != llValSecond ); break; case '=': // boolean - while ( pExpr[0] == '=' ) - ++pExpr; - llValSecond = GetVal(pExpr); + while ( refStrExpr[0] == '=' ) + ++refStrExpr; + llValSecond = GetVal(refStrExpr); llVal = ( llVal == llValSecond ); break; case '@': - ++pExpr; - llValSecond = GetVal(pExpr); + ++refStrExpr; + llValSecond = GetVal(refStrExpr); if (llVal < 0) { llVal = cexpression_power(llVal, llValSecond); @@ -1255,7 +1354,7 @@ llong CExpression::GetValMath( llong llVal, lpctstr & pExpr ) return llVal; } -llong CExpression::GetVal( lpctstr & pExpr ) +llong CExpression::GetVal(lpctstr & refStrExpr ) { // This function moves the pointer forward, so you can retrieve the value only once! @@ -1281,58 +1380,58 @@ llong CExpression::GetVal( lpctstr & pExpr ) // { animal_colors 1 no_colors 1 } // weighted range // { red_colors 1 {34 39} 1 } // same (red_colors expands to a range) - if ( pExpr == nullptr ) + if ( refStrExpr == nullptr ) return 0; if (_iGetVal_Reentrant >= 128 ) { - g_Log.EventError( "Deadlock detected while parsing '%s'. Fix the error in your scripts.\n", pExpr ); + g_Log.EventError( "Deadlock detected while parsing '%s'. Fix the error in your scripts.\n", refStrExpr ); return 0; } ++_iGetVal_Reentrant; // Get the first operand value: it may be a number or an expression - llong llVal = GetSingle(pExpr); + llong llVal = GetSingle(refStrExpr); // Check if there is an operator (mathematical or logical), in that case apply it to the second operand (which we evaluate again with GetSingle). - llVal = GetValMath(llVal, pExpr); + llVal = GetValMath(llVal, refStrExpr); --_iGetVal_Reentrant; return llVal; } -int CExpression::GetRangeVals(lpctstr & pExpr, int64 * piVals, int iMaxQty, bool bNoWarn) +int CExpression::GetRangeVals(lpctstr & refStrExpr, int64 * piVals, int iMaxQty, bool fNoWarn) { ADDTOCALLSTACK("CExpression::GetRangeVals"); // Get a list of values. - if (pExpr == nullptr) + if (refStrExpr == nullptr) return 0; ASSERT(piVals); int iQty = 0; - while (pExpr[0] != '\0') + while (refStrExpr[0] != '\0') { - if (pExpr[0] == ';') // seperate field - is this used anymore? + if (refStrExpr[0] == ';') // seperate field - is this used anymore? return iQty; - if (pExpr[0] == ',') - ++pExpr; + if (refStrExpr[0] == ',') + ++refStrExpr; - piVals[iQty] = GetSingle(pExpr); + piVals[iQty] = GetSingle(refStrExpr); if (++iQty >= iMaxQty) return iQty; - GETNONWHITESPACE(pExpr); + GETNONWHITESPACE(refStrExpr); // Look for math type operator. - switch (pExpr[0]) + switch (refStrExpr[0]) { case ')': // expression end markers. case '}': case ']': - ++pExpr; // consume this and end. + ++refStrExpr; // consume this and end. return iQty; case '+': @@ -1344,45 +1443,45 @@ int CExpression::GetRangeVals(lpctstr & pExpr, int64 * piVals, int iMaxQty, bool case '>': case '|': case '&': - piVals[iQty - 1] = GetValMath(piVals[iQty - 1], pExpr); + piVals[iQty - 1] = GetValMath(piVals[iQty - 1], refStrExpr); return iQty; } } - if (!bNoWarn) + if (!fNoWarn) g_Log.EventError("Range isn't closed by a '}' character\n"); return iQty; } -int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataArray &psSubExprData, int iMaxQty) // static +int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) { ADDTOCALLSTACK("CExpression::GetConditionalSubexpressions"); // Get the start and end pointers for each logical subexpression (delimited by brackets or by logical operators || and &&) inside a conditional statement (IF/ELIF/ELSEIF and QVAL). // Parse from left to start (like it was always done in Sphere). // Start and end pointers are inclusive (pointed values are valid chars, the end pointer doesn't necessarily point to '\0'). - if (pExpr == nullptr) + if (refStrExpr == nullptr) return 0; - //ASSERT(pSubexprPos); - //memset((void*)&pSubexprPos, 0, ARRAY_COUNT(pSubexprPos)); - int iSubexprQty = 0; // number of subexpressions - using SType = CScriptSubExprData::Type; - while (pExpr[0] != '\0') + using SType = CScriptSubExprState::Type; + memset(m_parsingSubexprsStates, 0, sizeof(m_parsingSubexprsStates)); + uint uiSubexprQty = 0; // number of subexpressions + + while (refStrExpr[0] != '\0') { - if (++iSubexprQty >= iMaxQty) + if (++uiSubexprQty >= sm_uiMaxConditionalSubexprs) { - g_Log.EventWarn("Exceeded maximum allowed number of subexpressions (%d). Parsing halted.\n", iMaxQty); - return iSubexprQty; + g_Log.EventWarn("Exceeded maximum allowed number of subexpressions (%u). Parsing halted.\n", sm_uiMaxConditionalSubexprs); + return uiSubexprQty; } - GETNONWHITESPACE(pExpr); - CScriptSubExprData& sCurSubexpr = psSubExprData[iSubexprQty - 1]; - tchar ch = pExpr[0]; + GETNONWHITESPACE(refStrExpr); + CScriptSubExprState& sCurSubexpr = m_parsingSubexprsStates[uiSubexprQty - 1]; + tchar ch = refStrExpr[0]; // Init the data for the current subexpression and set the position of the first character of the subexpression. - sCurSubexpr = {pExpr, nullptr, SType::None, 0}; + sCurSubexpr = {refStrExpr, nullptr, SType::None, 0}; // -- What's an expression and what's a subexpression. // An expression can contain a single statement, a single operation (enclosed, or not, by curly brackets), like: IF or IF == 1. @@ -1406,10 +1505,10 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA { // Remember that i'm interested only in the special case of subexpressions preceded by '!'. // If it's inside the subexpression, it will already be handled correctly. - ptcTopLevelNegation = pExpr; - ++pExpr; - GETNONWHITESPACE(pExpr); - ch = *pExpr; + ptcTopLevelNegation = refStrExpr; + ++refStrExpr; + GETNONWHITESPACE(refStrExpr); + ch = *refStrExpr; } // Helper lambda functions for the next section. @@ -1470,8 +1569,8 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA // This ensures that we begin every parsing loop without any open curly bracket. // Start of a expression within curved brackets? - lptstr ptcCurSubexprStart = pExpr; - lptstr ptcTopBracket = (ch == '(') ? pExpr : nullptr; + lptstr ptcCurSubexprStart = refStrExpr; + lptstr ptcTopBracket = (ch == '(') ? refStrExpr : nullptr; // -- Done with preliminar expression analysis. Now look for subexpressions. lptstr ptcLastClosingBracket = nullptr; // Needs to be preserved in the subexpression parsing. @@ -1486,14 +1585,14 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA // We could have encountered one of the situations below and already found the end of the subexpression, or we could need to find it here. if (sCurSubexpr.ptcEnd == nullptr) { - sCurSubexpr.ptcEnd = pExpr; + sCurSubexpr.ptcEnd = refStrExpr; if (ptcTopBracket && ptcLastClosingBracket) { lptstr ptcLineLastClosingBracket = findLastClosingBracket(ptcCurSubexprStart); // ptcLastClosingBracket: the last closing bracket found while parsing the subexpression (might not be at the end of the line). // ptcExprLastClosingBracket: the last closing bracket ')', if any, of the string. The function used does NOT check if that's a valid closing bracket // (eg. if in the string for every opening bracket there is a closing bracket). - if (iSubexprQty == 1) + if (uiSubexprQty == 1) { if (nullptr == ptcLineLastClosingBracket) { @@ -1505,7 +1604,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA // I'm here because the whole expression is enclosed by parentheses // + 1 because i want to point to the character after the ')', even if it's the string terminator. sCurSubexpr.ptcEnd = ptcLastClosingBracket + 1; - sCurSubexpr.uiType |= CScriptSubExprData::TopParenthesizedExpr; + sCurSubexpr.uiType |= CScriptSubExprState::TopParenthesizedExpr; } else { @@ -1520,11 +1619,11 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA else if (ch == '(') { - if (ptcCurSubexprStart == pExpr) + if (ptcCurSubexprStart == refStrExpr) { // Start of a subexpression delimited by brackets (it can be preceded by an operator like '!', handled before). // Now i want only to see where's the matching closing bracket. - sCurSubexpr.ptcStart = pExpr; + sCurSubexpr.ptcStart = refStrExpr; } else { @@ -1548,36 +1647,36 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA } // Just skip what's enclosed in the subexpression. - ptcLastClosingBracket = skipBracketedSubexpression(pExpr); + ptcLastClosingBracket = skipBracketedSubexpression(refStrExpr); if (ptcLastClosingBracket != nullptr) - pExpr = ptcLastClosingBracket; + refStrExpr = ptcLastClosingBracket; else { g_Log.EventError("Expression started with '(' but isn't closed by a ')' character.\n"); - sCurSubexpr.ptcEnd = pExpr - 1; // Position of the char just before the last ')' of the bracketed subexpression -> this eats away the last closing bracket - return iSubexprQty; + sCurSubexpr.ptcEnd = refStrExpr - 1; // Position of the char just before the last ')' of the bracketed subexpression -> this eats away the last closing bracket + return uiSubexprQty; } // Okay, i've eaten the expression in brackets, now fall through the rest of the loop and continue. } - else if ((ch == '|') && (pExpr[1] == '|')) + else if ((ch == '|') && (refStrExpr[1] == '|')) { // Logical two-way OR operator: || if (sCurSubexpr.ptcEnd == nullptr) - sCurSubexpr.ptcEnd = pExpr; + sCurSubexpr.ptcEnd = refStrExpr; sCurSubexpr.uiType = SType::Or | (sCurSubexpr.uiType & ~SType::None); - pExpr += 2u; // Skip the second char of the operator + refStrExpr += 2u; // Skip the second char of the operator break; // End of subexpr... } - else if ((ch == '&') && (pExpr[1] == '&')) + else if ((ch == '&') && (refStrExpr[1] == '&')) { // Logical two-way AND operator: && if (sCurSubexpr.ptcEnd == nullptr) - sCurSubexpr.ptcEnd = pExpr; + sCurSubexpr.ptcEnd = refStrExpr; sCurSubexpr.uiType = SType::And | (sCurSubexpr.uiType & ~SType::None); - pExpr += 2u; // Skip the second char of the operator + refStrExpr += 2u; // Skip the second char of the operator break; // End of subexpr... } @@ -1588,14 +1687,16 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA if (ch == '<') { // This can be: <, <= or the start of a bracketed expression < > - if (pExpr[1] == '=') + if (refStrExpr[1] == '=') { sCurSubexpr.uiType = SType::BinaryNonLogical | (sCurSubexpr.uiType & ~SType::None); - pExpr += 1u; + refStrExpr += 1u; } else { - const ushort prevSubexprType = ((iSubexprQty == 1) ? (ushort)SType::None : psSubExprData[iSubexprQty - 2].uiType); + const ushort prevSubexprType = ((uiSubexprQty == 1) + ? (ushort)SType::None + : m_parsingSubexprsStates[uiSubexprQty - 2].uiType); if ((prevSubexprType & SType::None)) { // This subexpr is not preceded by a two-way operator, so probably i'm an operator: skip me. @@ -1606,14 +1707,14 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA else { // This subexpr is preceded by a two-way operator, so probably i'm not another operator, rather a < > expression. - lptstr pExprSkipped = pExpr; + lptstr pExprSkipped = refStrExpr; Str_SkipEnclosedAngularBrackets(pExprSkipped); - if (pExpr != pExprSkipped) + if (refStrExpr != pExprSkipped) { // I actually have something enclosed in angular brackets. // The function above moves the pointer after the last closing bracket '>', but we want to point here to it, not the character after. - pExpr = pExprSkipped; - ch = *pExpr; + refStrExpr = pExprSkipped; + ch = *refStrExpr; continue; // This allows us to skip the "ch = *(++pExpr);" below, we don't want to advance further the pointer. } } @@ -1622,10 +1723,10 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA } else if (ch == '>') { - if (pExpr[1] == '=') + if (refStrExpr[1] == '=') { sCurSubexpr.uiType = SType::BinaryNonLogical | (sCurSubexpr.uiType & ~SType::None); - pExpr += 1u; + refStrExpr += 1u; } else { @@ -1635,15 +1736,15 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA // End of arithmetic subexpression parsing. } - ch = *(++pExpr); + ch = *(++refStrExpr); } // End of the subexpression while loop } // End of the main while loop // Now that we found the subexpressions, prepare them for their evaluation. lptstr ptcStart, ptcEnd; - for (int i = 0; i < iSubexprQty; ++i) + for (uint i = 0; i < uiSubexprQty; ++i) { - CScriptSubExprData& sCurSubexpr = psSubExprData[i]; + CScriptSubExprState& sCurSubexpr = m_parsingSubexprsStates[i]; ptcStart = sCurSubexpr.ptcStart; ptcEnd = sCurSubexpr.ptcEnd; @@ -1677,7 +1778,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataA sCurSubexpr.ptcEnd = ptcEnd; } - return iSubexprQty; + return uiSubexprQty; } @@ -1768,7 +1869,7 @@ static int GetRangeArgsPos(lpctstr & pExpr, lpctstr (&pArgPos)[kiRangeMaxArgs][2 return iQty; } -int64 CExpression::GetRangeNumber(lpctstr & pExpr) +int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) { ADDTOCALLSTACK("CExpression::GetRangeNumber"); @@ -1780,7 +1881,7 @@ int64 CExpression::GetRangeNumber(lpctstr & pExpr) // of the elements and then only the element which was randomly chosen. lpctstr pElementsStart[kiRangeMaxArgs][2] {}; - int iQty = GetRangeArgsPos( pExpr, pElementsStart, false ); // number of arguments (not of value-weight couples) + int iQty = GetRangeArgsPos( refStrExpr, pElementsStart, false ); // number of arguments (not of value-weight couples) if (iQty == 0) return 0; @@ -1883,7 +1984,7 @@ int64 CExpression::GetRangeNumber(lpctstr & pExpr) return GetSingle(pToParseCasted); } -CSString CExpression::GetRangeString(lpctstr & pExpr) +CSString CExpression::GetRangeString(lpctstr & refStrExpr) { ADDTOCALLSTACK("CExpression::GetRangeString"); @@ -1894,7 +1995,7 @@ CSString CExpression::GetRangeString(lpctstr & pExpr) // of the elements and then only the element which was randomly chosen. lpctstr pElementsStart[kiRangeMaxArgs][2]{}; - int iQty = GetRangeArgsPos( pExpr, pElementsStart, true ); // number of arguments (not of value-weight couples) + int iQty = GetRangeArgsPos( refStrExpr, pElementsStart, true ); // number of arguments (not of value-weight couples) if (iQty <= 0) return {}; @@ -1955,3 +2056,588 @@ CSString CExpression::GetRangeString(lpctstr & pExpr) const int iToParseLen = int(pElementsStart[i][1] - pElementsStart[i][0]); return CSString(pElementsStart[i][0], iToParseLen); } + +bool CExpression::_Evaluate_Conditional_EvalSingle( + CScriptSubExprState& refSubExprState, CScriptExprContext& refExprContext, + CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) +{ + ADDTOCALLSTACK("CExpression::_Evaluate_Conditional_EvalSingle"); + + ASSERT(refSubExprState.ptcStart); + ASSERT(refSubExprState.ptcEnd); + ASSERT(refExprContext._pScriptObjI != nullptr); + + using SType = CScriptSubExprState::Type; + bool fVal; + lptstr ptcSubexpr; + + // Evaluate the subexpression body + if (refExprContext._iEvaluate_Conditional_Reentrant >= 16) + { + g_Log.EventError("Exceeding the limit of 16 subexpressions. Further parsing is halted.\n"); + return false; + } + ++ refExprContext._iEvaluate_Conditional_Reentrant; + + // Is this conditional expression is fully enclosed by brackets ? + const bool fFullyEnclosed = (refSubExprState.uiType & SType::TopParenthesizedExpr); + + // Length to copy: include the last valid char (i'm not copying the subsequent char, which can be another char or '\0' + ASSERT(refSubExprState.ptcEnd >= refSubExprState.ptcStart); + size_t len = std::min(Str_TempLength() - 1U, size_t(refSubExprState.ptcEnd - refSubExprState.ptcStart)); + if (len == 0) + { + g_Log.EventError("Empty subexpression. Defaulting its value to false.\n"); + return false; + } + + lptstr ptcParsingStart = refSubExprState.ptcStart; + if (fFullyEnclosed) + { + -- len; // Exclude the closing bracket ')'. + ASSERT(len > 0); + + // In this case, we need to start parsing after the opening parenthesis '('; if we start before it and the subexpr is marked with MaybeNestedSubexpr, + // Evaluate_Conditional will again return the same subexpression fully enclosed by parenthesis, and we'll have a deadlock. + // Remember that sdata.uiNonAssociativeOffset is the distance between the open bracket '(' and the non-associative operator (negation operator '!'). + // The string might start with said non-associative operator. + ptcParsingStart += 1; + len -= 1; + } + + ASSERT(len < Str_TempLength()); + ptcSubexpr = Str_GetTemp(); + memcpy(ptcSubexpr, ptcParsingStart, len); + ptcSubexpr[len] = '\0'; + + const bool fNested = (refSubExprState.uiType & SType::MaybeNestedSubexpr); + if (fNested) + { + // Probably this subexpression has other conditional subexpressions inside. + fVal = Evaluate_Conditional(ptcSubexpr, refExprContext, pScriptArgs, pSrc); + } + else + { + // If an expression is enclosed by parentheses, ParseScriptText needs to read both the open and the closed one, we cannot + // pass the string starting with the character after the '('. + ParseScriptText(ptcSubexpr, refExprContext, pScriptArgs, pSrc, 0); + fVal = bool(Exp_GetLLVal(ptcSubexpr)); + } + + -- refExprContext._iEvaluate_Conditional_Reentrant; + + + // Apply non-associative operators preceding the subexpression + if (refSubExprState.uiNonAssociativeOffset) + { + ptcSubexpr = refSubExprState.ptcStart - refSubExprState.uiNonAssociativeOffset; + ASSERT(!IsWhitespace(*ptcSubexpr)); + while (const tchar chOperator = *ptcSubexpr) + { + if (chOperator == '!') + fVal = !fVal; + else if (IsWhitespace(chOperator)) + ; // Allowed, skip it + else + break; + ++ptcSubexpr; + } + } + + return fVal; +} + +bool CExpression::Evaluate_Conditional(lptstr ptcExpr, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) +{ + ADDTOCALLSTACK("CExpression::Evaluate_Conditional"); + ASSERT(refExprContext._pScriptObjI != nullptr); + + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + lptstr ptcExprDbg = ptcExpr; + const int iQty = GetConditionalSubexpressions(ptcExprDbg); // number of arguments + + if (iQty == 0) + return 0; + + using SType = CScriptSubExprState::Type; + + if (iQty == 1) + { + // We don't have subexpressions, but only a simple expression. + CScriptSubExprState& sCur = m_parsingSubexprsStates[0]; + ASSERT((sCur.uiType & SType::None) || (sCur.uiType & SType::BinaryNonLogical)); + + const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, refExprContext, pScriptArgs, pSrc); + return fVal; + } + + // We have some subexpressions, connected between them by logical operators. + + bool fWholeExprVal = false; + for (int i = 0; i < iQty; ++i) + { + CScriptSubExprState& sCur = m_parsingSubexprsStates[i]; + ASSERT(sCur.uiType != SType::Unknown); + + if (i == 0) + { + fWholeExprVal = _Evaluate_Conditional_EvalSingle(sCur, refExprContext, pScriptArgs, pSrc); + continue; + } + + CScriptSubExprState& sPrev = m_parsingSubexprsStates[i - 1]; + if (sPrev.uiType & SType::Or) + { + if (fWholeExprVal) + return true; + + const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, refExprContext, pScriptArgs, pSrc); + fWholeExprVal = fWholeExprVal || fVal; + } + else if (sPrev.uiType & SType::And) + { + if (!fWholeExprVal) + return false; + + const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, refExprContext, pScriptArgs, pSrc); + fWholeExprVal = (i == 1) ? fVal : (fWholeExprVal && fVal); + } + + + if (sCur.uiType & SType::None) + { + ASSERT(i == iQty - 1); // It should be the last subexpression + ASSERT((sPrev.uiType & SType::Or) || (sPrev.uiType & SType::And)); + } + + } + + return fWholeExprVal; +} + +static void Evaluate_QvalConditional_ParseArg(tchar* ptcSrc, tchar** ptcDest, lpctstr ptcSep) +{ + ASSERT(ptcSep && *ptcSep); + + // Check if we are encountering a a nested QVAL? + tchar* ptcBracketPos = nullptr; + tchar* ptcSepPos = nullptr; + for (tchar* ptcLine = ptcSrc; *ptcLine != '\0';) + { + const tchar ch = *ptcLine; + if ((ch != '<') && (ch != *ptcSep)) + { + ++ptcLine; + continue; + } + + if ((ch == '<') && !ptcBracketPos) + { + tchar* ptcTest = ptcLine + 1; + GETNONWHITESPACE(ptcTest); + if (!strnicmp("QVAL", ptcTest, 4)) + { + ptcBracketPos = ptcLine; + ptcLine = ptcTest + 3; + } + } + else if ((ch == *ptcSep) && !ptcSepPos) + { + ptcSepPos = ptcLine; + } + + if (!ptcBracketPos || !ptcSepPos) + { + ++ptcLine; + continue; + } + + if (ptcSepPos < ptcBracketPos) + { + // The separator we have found is before the nested QVAL. + ptcSrc = ptcSepPos; + break; + } + + // Found a nested QVAL. Skip it, otherwise we'll catch the wrong separator + Str_SkipEnclosedAngularBrackets(ptcBracketPos); + if (ptcBracketPos <= ptcLine) + ++ptcLine; + else + ptcSrc = ptcLine = ptcBracketPos; + + ptcBracketPos = ptcSepPos = nullptr; + } + + Str_Parse(ptcSrc, ptcDest, ptcSep); +} + +bool CExpression::Evaluate_QvalConditional( + lpctstr ptcKey, CSString& refStrVal, + CScriptExprContext& pContext, + CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) +{ + // Do a switch ? type statement + ADDTOCALLSTACK("CExpression::Evaluate_QvalConditional"); + ASSERT(pContext._pScriptObjI != nullptr); + + // Do NOT work on the original arguments, it WILL fuck up the original string! + tchar* ptcArgs = Str_GetTemp(); + Str_CopyLimitNull(ptcArgs, ptcKey, Str_TempLength()); + + // We only partially evaluated the QVAL parameters (it's a special case), so we need to parse the expressions (still have angular brackets at this stage) + tchar* ppCmds[3]; + ppCmds[0] = ptcArgs; + + // Get the condition + Evaluate_QvalConditional_ParseArg(ppCmds[0], &(ppCmds[1]), "?"); + + // Get the first and second retvals + Evaluate_QvalConditional_ParseArg(ppCmds[1], &(ppCmds[2]), ":"); + + // Complete evaluation of the condition + // (do that in another string, since it may overwrite the arguments, which are written later in the same string). + tchar* ptcTemp = Str_GetTemp(); + Str_CopyLimitNull(ptcTemp, ppCmds[0], Str_TempLength()); + ParseScriptText(ptcTemp, pContext, pScriptArgs, pSrc, 0); + const bool fCondition = Exp_GetLLVal(ptcTemp); + + // Get the retval we want + // (we might as well work on the transformed original string, since at this point we don't care if we corrupt other arguments) + ptcTemp = ppCmds[(fCondition ? 1 : 2)]; + ParseScriptText(ptcTemp, pContext, pScriptArgs, pSrc, 0); + + refStrVal = ptcTemp; + if (refStrVal.IsEmpty()) + refStrVal.Clear(); + return true; +} + +// TODO: fully move out the QVAL evaluation from here? +int CExpression::ParseScriptText( + tchar * ptcResponse, + CScriptExprContext& pContext, + CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, + int iFlags) +{ + ADDTOCALLSTACK("CScriptObj::ParseScriptText"); + //ASSERT(ptcResponse[0] != ' '); // Not needed: i remove whitespaces and invalid characters here. + ASSERT(pContext._pScriptObjI != nullptr); + + // Take in a line of text that may have fields that can be replaced with operators here. + // ARGS: + // iFlags & 1: Use HTML-compatible delimiters (%). Inside those, angular brackets are allowed to do nested evaluations. + // iFlags & 2: Don't allow recusive bracket count. + // iFlags & 4: Just parsing a nested QVAL. + // NOTE: + // html will have opening + // RETURN: + // iFlags & 4: Position of the ending bracket/delimiter of a QVAL statement. + // Otherwise: New length of the string. + + // Recursion control variables. + // _iParseScriptText_Reentrant = 0; + // _fParseScriptText_Brackets = false; // Am i evaluating a statement? (Am i inside < > brackets of a statement i am currently evaluating?) + + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + + const bool fNoRecurseBrackets = ((iFlags & 2) != 0); + + // General purpose variables. + const bool fHTML = ((iFlags & 1) != 0); + + // If we are parsing a string from a HTML file, we are using '%' as a delimiter for Sphere expressions, since < > are reserved characters in HTML. + const tchar chBegin = fHTML ? '%' : '<'; + const tchar chEnd = fHTML ? '%' : '>'; + + // Variables used to handle the QVAL special case and do lazy evaluation, instead of fully evaluating the whole string on the first pass. + // As an aftertought QVAL parsing could have been moved into a separate function, but it's intricate enough and it's working, so let's leave as it is...' + enum class QvalStatus { None, Condition, Returns, End } eQval = QvalStatus::None; + int iQvalOpenBrackets = 0; + + size_t uiSubstitutionBegin = 0; + int i = 0; + EXC_TRY("ParseScriptText Main Loop"); + for ( i = 0; ptcResponse[i] != '\0'; ++i) + { + const tchar ch = ptcResponse[i]; + + // Are we looking for the current statement start? + if ( !pContext._fParseScriptText_Brackets) // not in brackets + { + if ( ch == chBegin ) // found the start ! + { + const tchar chNext = ptcResponse[i + 1]; + if ((chNext != '<') && !IsAlnum(chNext)) + continue; // Ignore this, it might be a operator like <= + if ((chBegin == '<') && (chNext == '<')) + { + // Is a << operator? I want a whitespace after the operator. + if ((ptcResponse[i + 2] != '\0') && (ptcResponse[i + 3] != '\0') && IsWhitespace(ptcResponse[i + 2])) + { + lpctstr ptcOpTest = &(ptcResponse[4]); + if (*ptcOpTest != '\0') + { + GETNONWHITESPACE(ptcOpTest); + if (*ptcOpTest != '\0') // There's more text to parse + { + // I guess i have sufficient proof: skip, it's a << operator + i += 2; // Skip < and the whitespace + continue; + } + } + } + } + + // Set the statement start + ASSERT(i >= 0); + uiSubstitutionBegin = (size_t)i; + pContext._fParseScriptText_Brackets = true; + + // Set-up to process special statements: is it a QVAL? + const bool fIsQval = !strnicmp(ptcResponse + i + 1, "QVAL", 4); + if (fIsQval) + { + ++iQvalOpenBrackets; + eQval = QvalStatus::Condition; + + i += 4; + } + } + + continue; + } + + // Are we inside a QVAL and are we searching where its condition end? + if ((ch == '?') && (eQval == QvalStatus::Condition)) + { + // Now we keep the bracket count to find the closing bracket for the QVAL statement. + eQval = QvalStatus::Returns; + continue; + } + + // Handle possibly recursive angular brackets (i'm already inside an open bracket) + if (pContext._fParseScriptText_Brackets && (ch == '<')) + { + const tchar chNext = ptcResponse[i + 1]; + if (chNext == '<') + { + // Nested angular brackets? like: <> + lptstr ptcTestNested = ptcResponse + i; + lpctstr ptcTestOrig = ptcTestNested; + Str_SkipEnclosedAngularBrackets(ptcTestNested); + // If i have matching closing brackets, so it must be nested angular brackets. + if (ptcTestNested == ptcTestOrig) + { + // Otherwise, it might be the << operator. + + // This shouldn't be necessary... but + /* + // Is a << operator? I want a whitespace after the operator. + if ((ptcResponse[i + 2] != '\0') && (ptcResponse[i + 3] != '\0') && IsWhitespace(ptcResponse[i + 2])) + { + lpctstr ptcOpTest = &(ptcResponse[4]); + if (*ptcOpTest != '\0') + { + GETNONWHITESPACE(ptcOpTest); + if (*ptcOpTest != '\0') // There's more text to parse + { + // I guess i have sufficient proof: skip, it's a << operator + i += 2; // Skip < and the whitespace + pContext._fParseScriptText_Brackets = false; + continue; + } + } + } + // Print an error! I thought it was a << operator but it is not! What's happening here ?! + */ + + ++i; + continue; + } + } + + // Detect nested QVALs + if (eQval != QvalStatus::None) + { + const bool fIsQval = !strnicmp(ptcResponse + i + 1, "QVAL", 4); + if (fIsQval) + { + // Nested QVAL... Needs to be evaluated separately, but we only want to know where it ends. + ASSERT(pContext._fParseScriptText_Brackets == true); + ++ pContext._iParseScriptText_Reentrant; + pContext._fParseScriptText_Brackets = false; + + tchar* ptcRecurseParse = ptcResponse + i; + const int iLen = ParseScriptText(ptcRecurseParse, pContext, pScriptArgs, pSrc, 4); + + pContext._fParseScriptText_Brackets = true; + -- pContext._iParseScriptText_Reentrant; + + i += iLen; + continue; + } + + // At this point, we shouldn't face nested QVALs. + + // I'm inside a QVAL. I can be parsing the condition or the return values. + if (eQval == QvalStatus::Returns) // I'm after its condition (so after '?'), thus i'm parsing the return values. + ++iQvalOpenBrackets; + + // Halt here the evaluation of the stuff inside this open bracket, since i don't want to know what's inside. + continue; + } + + if (pContext._iParseScriptText_Reentrant > 32 ) + { + EXC_SET_BLOCK("recursive brackets limit"); + ASSERT_ALWAYS(pContext._iParseScriptText_Reentrant < 32); + } + + ASSERT(pContext._fParseScriptText_Brackets == true); + ++pContext._iParseScriptText_Reentrant; + pContext._fParseScriptText_Brackets = false; + + // Parse what's inside the open bracket + tchar* ptcRecurseParse = ptcResponse + i; + const int iLen = ParseScriptText(ptcRecurseParse, pContext, pScriptArgs, pSrc, 2 ); + + pContext._fParseScriptText_Brackets = true; + --pContext._iParseScriptText_Reentrant; + + i += iLen; + continue; + } + + // At this point i'm sure that ahead we won't find other open angular brackets, we may find their closing one or just plain text. + if ( ch == chEnd ) + { + // Closing bracket found: should we evaluate what's inside the brackets? + if (eQval != QvalStatus::None) + { + // Special handling for QVAL + if (eQval == QvalStatus::Returns) + { + // I'm after the '?' symbol in QVAL. We are searching for the closing bracket. + --iQvalOpenBrackets; + + if (iQvalOpenBrackets == 0) + { + // End of the QVAL statement. + if (iFlags & 04) + { + // I was just checking for the QVAL statement end. + ASSERT(pContext._fParseScriptText_Brackets == true); + pContext._fParseScriptText_Brackets = false; + return i; + } + + // Proceed, so we can execute it (do not 'continue'). + eQval = QvalStatus::End; + } + else + { + // Still inside QVAL, just go ahead. + continue; + } + } + else + { + // I'm before the '?' symbol in QVAL and i'm still searching for it, so we know when the conditional expression ends + continue; // Ignore brackets, i want only the ? symbol. + } + } + + + // If i'm here it means that finally i'm at the end of the statement inside brackets. + pContext._fParseScriptText_Brackets = false; // Close the statement. + + if ((eQval == QvalStatus::End) && (iQvalOpenBrackets != 0)) + { + // I had an incomplete QVAL statement. + g_Log.EventError("QVAL parameters after '?' have unmatched '%c'.\n", ((iQvalOpenBrackets < 0) ? '<' : '>')); + } + + // Complete the evaluation of our string + //-- Write to our temporary sVal the evaluated script + EXC_SET_BLOCK("writeval"); + + ptcResponse[i] = '\0'; // Needed for r_WriteVal + lpctstr ptcKey = ptcResponse + uiSubstitutionBegin + 1; // move past the opening bracket + + CSString sVal; + bool fRes; + if (eQval != QvalStatus::None) + { + // Separate evaluation for QVAL. I may need additional script context for it (pScriptArgs isn't available in r_WriteVal). + EXC_SET_BLOCK("writeval qval"); + ptcKey += 4; // Skip the letters QVAL and pass only the arguments + fRes = Evaluate_QvalConditional(ptcKey, sVal, pContext, pScriptArgs, pSrc); + eQval = QvalStatus::None; + } + else + { + // Standard evaluation for everything else + EXC_SET_BLOCK("writeval generic"); + ASSERT(pContext._pScriptObjI); + fRes = pContext._pScriptObjI->r_WriteVal(ptcKey, sVal, pSrc); + if (fRes == false) + { + EXC_SET_BLOCK("writeval args"); + // write the value of functions or triggers variables/objects like ARGO, ARGN1/2/3, LOCALs... + if ((pScriptArgs != nullptr) && pScriptArgs->r_WriteVal(ptcKey, sVal, pSrc)) + fRes = true; + } + } + + + if ( fRes == false ) + { + DEBUG_ERR(( "Can't resolve <%s>.\n", ptcKey )); + // Just in case this really is a <= operator ? + ptcResponse[i] = chEnd; // it's the char we overwrote with '\0' + } + + if (fHTML && sVal.IsEmpty()) + { + sVal = " "; + } + + //-- In the output string, substitute the raw substring with its parsed value + EXC_SET_BLOCK("mem shifting"); + + const size_t uiWriteValLen = sVal.GetLength(); + + // Make room for the obtained value, moving to left (if it's shorter than the scripted statement) or right (if longer) the string characters after it. + tchar* ptcDest = ptcResponse + uiSubstitutionBegin + uiWriteValLen; // + iWriteValLen because we need to leave the space for the replacing keyword + const tchar * const ptcLeftover = ptcResponse + i + 1; // End of the statement we just evaluated + const size_t uiLeftoverLen = strlen(ptcLeftover) + 1; + memmove(ptcDest, ptcLeftover, uiLeftoverLen); + + // Insert the obtained value in the room we created. + ptcDest = ptcResponse + uiSubstitutionBegin; + memcpy(ptcDest, sVal.GetBuffer(), uiWriteValLen); + + // This can be negative. + i = (int)(uiSubstitutionBegin + uiWriteValLen) - 1; + + if (fNoRecurseBrackets) // just do this one then bail out. + { + pContext._fParseScriptText_Brackets = false; + return i; + } + } + } + EXC_CATCH; + + EXC_DEBUG_START; + g_Log.EventDebug("response '%s' source addr '0%p' flags '%d' args '%p'\n", ptcResponse, static_cast(pSrc), iFlags, static_cast(pScriptArgs.get())); + EXC_DEBUG_END; + + pContext._fParseScriptText_Brackets = false; + return i; +} diff --git a/src/common/CExpression.h b/src/common/CExpression.h index 461405dd2..64cd492eb 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -10,8 +10,11 @@ #ifndef _INC_CEXPRSSION_H #define _INC_CEXPRSSION_H +#include "sphere_library/CSAssoc.h" +#include "CScriptParserBufs.h" #include "CVarDefMap.h" #include "ListDefContMap.h" +#include #undef ISWHITESPACE @@ -54,10 +57,16 @@ inline bool IsWhitespace(const T ch) noexcept { #define EXPRESSION_MAX_KEY_LEN SCRIPT_MAX_SECTION_LEN #define VARDEF_FLOAT_MAXBUFFERSIZE 82 -struct CScriptSubExprData; -static constexpr size_t uiMaxConditionalSubexprs = 32; -using CScriptSubExprDataArray = CScriptSubExprData[uiMaxConditionalSubexprs]; +struct CScriptExprContext +{ + CScriptObj *_pScriptObjI; + + // Recursion counters and state variables + short _iEvaluate_Conditional_Reentrant{}; + short _iParseScriptText_Reentrant{}; + bool _fParseScriptText_Brackets{}; +}; enum DEFMSG_TYPE { @@ -127,51 +136,110 @@ static lpctstr constexpr sm_IntrinsicFunctions[INTRINSIC_QTY+1] = }; -extern class CExpression +enum SKILL_TYPE : int; + +struct CExprGlobals { - short _iGetVal_Reentrant; +#ifdef MT_ENGINES + MT_CMUTEX_DEF; +#endif + static const char *m_sClassName; -public: - static const char *m_sClassName; CVarDefMap m_VarResDefs; // Defined variables in sorted order (RESDEF/RESDEF0). - CVarDefMap m_VarDefs; // Defined variables in sorted order (DEF/DEF0). - CVarDefMap m_VarGlobals; // Global variables (VAR/VAR0) - CListDefMap m_ListGlobals; // Global lists - CListDefMap m_ListInternals; // Internal lists + CVarDefMap m_VarDefs; // Defined variables in sorted order (DEF/DEF0). + CVarDefMap m_VarGlobals; // Global variables (VAR/VAR0) + CListDefMap m_ListGlobals; // Global lists + CListDefMap m_ListInternals; // Internal lists + +private: + friend class CServerConfig; + friend class CScriptObj; // For DEFMSG.* + + // Defined default messages (DEFMESSAGEs) + static constexpr ushort m_kiDefmsgMaxLen = 128; + static tchar sm_szDefMessages[DEFMSG_QTY][m_kiDefmsgMaxLen]; // like: "You put %s to %s" + static lpctstr const sm_szDefMsgNames[DEFMSG_QTY]; // like: "put_it" - // defined default messages -#define DEFMSG_MAX_LEN 128 - static tchar sm_szMessages[DEFMSG_QTY][DEFMSG_MAX_LEN]; // like: "You put %s to %s" - static lpctstr const sm_szMsgNames[DEFMSG_QTY]; // like: "put_it" + static constexpr ushort m_kiSkillTitlesQty = 12; + std::array m_SkillTitles_Ninjitsu; + std::array m_SkillTitles_Bushido; + std::array m_SkillTitles_Generic; public: + void UpdateDefMsgDependentData(); + + lpctstr SkillTitle(SKILL_TYPE skill, uint uiVal) const; +}; + +extern sl::GuardedAccess g_ExprGlobals; // Declared in spheresvr.cpp + + +// Main SphereScript parsing utilities +class CExpression +{ + struct CScriptSubExprState + { + lptstr ptcStart, ptcEnd; + enum Type : ushort + { + Unknown = 0, + // Powers of two + MaybeNestedSubexpr = 0x1 << 0, // 001 + TopParenthesizedExpr = 0x1 << 1, // 002 + None = 0x1 << 2, // 004 + BinaryNonLogical = 0x1 << 3, // 008 + And = 0x1 << 4, // 010 + Or = 0x1 << 5 // 020 + }; + ushort uiType; + ushort uiNonAssociativeOffset; // How much bytes/characters before the start is (if any) the first non-associative operator preceding the subexpression. + }; + + static constexpr size_t sm_uiMaxConditionalSubexprs = 32; + CScriptSubExprState m_parsingSubexprsStates[sm_uiMaxConditionalSubexprs]; + + short _iGetVal_Reentrant; + +public: + static const char *m_sClassName; + // Evaluate using the stuff we know. - llong GetSingle(lpctstr & pExpr); + llong GetSingle(lpctstr & refStrExpr); - llong GetValMath(llong llVal, lpctstr & pExpr); - llong GetVal(lpctstr & pExpr); + llong GetValMath(llong llVal, lpctstr & refStrExpr); + llong GetVal(lpctstr & refStrExpr); - int GetRangeVals(lpctstr& pExpr, int64* piVals, int iMaxQty, bool bNoWarn = false); - int64 GetRangeNumber(lpctstr& pExpr); // Evaluate a { } range - CSString GetRangeString(lpctstr& pExpr); // STRRANDRANGE + int GetRangeVals(lpctstr& refStrExpr, int64* piVals, int iMaxQty, bool fNoWarn = false); + int64 GetRangeNumber(lpctstr& refStrExpr); // Evaluate a { } range + CSString GetRangeString(lpctstr& refStrExpr); // STRRANDRANGE - static int GetConditionalSubexpressions(lptstr& pExpr, CScriptSubExprDataArray& psSubExprData, int iMaxQty); + int GetConditionalSubexpressions(lptstr& refStrExpr); // Strict G++ Prototyping produces an error when not casting char*& to const char*& // So this is a rather lazy and const-UNsafe workaround - inline llong GetSingle(lptstr &pArgs) { - return GetSingle(const_cast(pArgs)); + inline llong GetSingle(lptstr &refArgs) { + return GetSingle(const_cast(refArgs)); } - inline llong GetVal(lptstr& pArgs) { - return GetVal(const_cast(pArgs)); + inline llong GetVal(lptstr& refArgs) { + return GetVal(const_cast(refArgs)); } - inline int GetRangeVals(lptstr &pExpr, int64 * piVals, int iMaxQty, bool bNoWarn = false) { - return GetRangeVals(const_cast(pExpr), piVals, iMaxQty, bNoWarn); + inline int GetRangeVals(lptstr &refStrExpr, int64 * piVals, int iMaxQty, bool fNoWarn = false) { + return GetRangeVals(const_cast(refStrExpr), piVals, iMaxQty, fNoWarn); } - inline int64 GetRangeNumber(lptstr &pArgs) { - return GetRangeNumber(const_cast(pArgs)); + inline int64 GetRangeNumber(lptstr &refStrArgs) { + return GetRangeNumber(const_cast(refStrArgs)); } + // Arguments inside conditional statements: IF, ELIF, ELSEIF + bool _Evaluate_Conditional_EvalSingle(CScriptSubExprState& refSubExprState, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + bool Evaluate_Conditional(lptstr ptcExpression, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + + bool Evaluate_QvalConditional(lpctstr ptcKey, CSString& refStrVal, CScriptExprContext& refContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + + /* + * @brief Do the first-level parsing of a script line and eventually replace requested values got by r_WriteVal. + */ + int ParseScriptText( tchar * pszResponse, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, int iFlags = 0 ); public: CExpression() noexcept; @@ -179,7 +247,11 @@ extern class CExpression CExpression(const CExpression& copy) = delete; CExpression& operator=(const CExpression& other) = delete; -} g_Exp; + + // One per thread + [[nodiscard]] + static CExpression& GetExprParser(); +}; uint GetIdentifierString( tchar * szTag, lpctstr pszArgs ); @@ -260,33 +332,33 @@ int Calc_GetLog2( uint iVal ); int Calc_GetSCurve( int iValDiff, int iVariance ); int Calc_GetBellCurve( int iValDiff, int iVariance ); -#define Exp_GetSingle( pa ) static_cast (g_Exp.GetSingle( pa )) -#define Exp_GetUSingle( pa ) static_cast (g_Exp.GetSingle( pa )) -#define Exp_GetLLSingle( pa ) g_Exp.GetSingle( pa ) -#define Exp_GetDWSingle( pa ) static_cast (g_Exp.GetSingle( pa )) - -#define Exp_GetRange( pa ) static_cast (g_Exp.GetRangeNumber( pa )) -#define Exp_GetLLRange( pa ) g_Exp.GetRangeNumber( pa ) - -#define Exp_GetCVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetUCVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetSVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetUSVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetUVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetLLVal( pa ) g_Exp.GetVal( pa ) -#define Exp_GetULLVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetBVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetWVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetDWVal( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_Get8Val( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_Get16Val( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_Get32Val( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_Get64Val( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetU8Val( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetU16Val( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetU32Val( pa ) static_cast (g_Exp.GetVal( pa )) -#define Exp_GetU64Val( pa ) static_cast (g_Exp.GetVal( pa )) +#define Exp_GetSingle( pa ) static_cast (CExpression::GetExprParser().GetSingle( pa )) +#define Exp_GetUSingle( pa ) static_cast (CExpression::GetExprParser().GetSingle( pa )) +#define Exp_GetLLSingle( pa ) CExpression::GetExprParser().GetSingle( pa ) +#define Exp_GetDWSingle( pa ) static_cast (CExpression::GetExprParser().GetSingle( pa )) + +#define Exp_GetRange( pa ) static_cast (CExpression::GetExprParser().GetRangeNumber( pa )) +#define Exp_GetLLRange( pa ) CExpression::GetExprParser().GetRangeNumber( pa ) + +#define Exp_GetCVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetUCVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetSVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetUSVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetUVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetLLVal( pa ) CExpression::GetExprParser().GetVal( pa ) +#define Exp_GetULLVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetBVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetWVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetDWVal( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_Get8Val( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_Get16Val( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_Get32Val( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_Get64Val( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetU8Val( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetU16Val( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetU32Val( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) +#define Exp_GetU64Val( pa ) static_cast (CExpression::GetExprParser().GetVal( pa )) #if INTPTR_MAX == INT32_MAX #define Exp_GetSTVal Exp_GetU32Val diff --git a/src/common/CFloatMath.cpp b/src/common/CFloatMath.cpp index 8b0238ce6..e6fd9198e 100644 --- a/src/common/CFloatMath.cpp +++ b/src/common/CFloatMath.cpp @@ -672,12 +672,14 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) } } } + + auto gReader = g_ExprGlobals.mtEngineLockedReader(); llong llVal; - if ( g_Exp.m_VarGlobals.GetParseVal( pArgs, &llVal ) ) + if ( gReader->m_VarGlobals.GetParseVal( pArgs, &llVal ) ) return (int)llVal; - if ( g_Exp.m_VarResDefs.GetParseVal( pArgs, &llVal ) ) + if ( gReader->m_VarResDefs.GetParseVal( pArgs, &llVal ) ) return (int)llVal; - if ( g_Exp.m_VarDefs.GetParseVal( pArgs, &llVal ) ) + if ( gReader->m_VarDefs.GetParseVal( pArgs, &llVal ) ) return (int)llVal; return 0; } diff --git a/src/common/CLog.cpp b/src/common/CLog.cpp index 364dadae5..b4b024963 100644 --- a/src/common/CLog.cpp +++ b/src/common/CLog.cpp @@ -107,7 +107,7 @@ const CScript * CLog::_SetScriptContext( const CScript * pScriptContext ) const CScript * CLog::SetScriptContext(const CScript * pScriptContext) { - MT_UNIQUE_LOCK_RETURN(CLog::_SetScriptContext(pScriptContext)); + MT_UNIQUE_LOCK_RETURN(this, CLog::_SetScriptContext(pScriptContext)); } const CScriptObj * CLog::_SetObjectContext( const CScriptObj * pObjectContext ) @@ -119,7 +119,7 @@ const CScriptObj * CLog::_SetObjectContext( const CScriptObj * pObjectContext ) const CScriptObj * CLog::SetObjectContext(const CScriptObj * pObjectContext) { - MT_UNIQUE_LOCK_RETURN(CLog::_SetObjectContext(pObjectContext)); + MT_UNIQUE_LOCK_RETURN(this, CLog::_SetObjectContext(pObjectContext)); } bool CLog::SetFilePath( lpctstr pszName ) @@ -216,7 +216,7 @@ bool CLog::_OpenLog( lpctstr pszBaseDirName ) // name set previously. bool CLog::OpenLog(lpctstr pszBaseDirName) // name set previously. { ADDTOCALLSTACK("CLog::OpenLog"); - MT_UNIQUE_LOCK_RETURN(CLog::_OpenLog(pszBaseDirName)); + MT_UNIQUE_LOCK_RETURN(this, CLog::_OpenLog(pszBaseDirName)); } int CLog::EventStr( dword dwMask, lpctstr pszMsg, ConsoleTextColor iLogColor) noexcept @@ -310,7 +310,7 @@ int CLog::EventStr( dword dwMask, lpctstr pszMsg, ConsoleTextColor iLogColor) no // Print to log file. if ( !(dwMask & LOGF_CONSOLE_ONLY) ) { - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); if ( datetime.GetDay() != m_dateStamp.GetDay()) { diff --git a/src/common/CSVFile.cpp b/src/common/CSVFile.cpp index da0f6c041..4e03adeca 100644 --- a/src/common/CSVFile.cpp +++ b/src/common/CSVFile.cpp @@ -24,12 +24,12 @@ CSVFile::~CSVFile() int CSVFile::GetColumnCount() const { ADDTOCALLSTACK("CSVFile::GetColumnCount"); - MT_SHARED_LOCK_RETURN(_iColumnCount); + MT_SHARED_LOCK_RETURN(this, _iColumnCount); } int CSVFile::GetCurrentRow() const { ADDTOCALLSTACK("CSVFile::GetCurrentRow"); - MT_SHARED_LOCK_RETURN(_iCurrentRow); + MT_SHARED_LOCK_RETURN(this, _iCurrentRow); } bool CSVFile::_Open(lpctstr ptcFilename, uint uiModeFlags) @@ -89,7 +89,7 @@ bool CSVFile::_Open(lpctstr ptcFilename, uint uiModeFlags) bool CSVFile::Open(lpctstr ptcFilename, uint uiModeFlags) { ADDTOCALLSTACK("CSVFile::Open"); - MT_UNIQUE_LOCK_RETURN(CSVFile::_Open(ptcFilename, uiModeFlags)); + MT_UNIQUE_LOCK_RETURN(this, CSVFile::_Open(ptcFilename, uiModeFlags)); } int CSVFile::_ReadRowContent(tchar ** ppOutput, int rowIndex, int columns) @@ -140,6 +140,6 @@ bool CSVFile::_ReadNextRowContent(CSVRowData& target) bool CSVFile::ReadNextRowContent(CSVRowData& target) { ADDTOCALLSTACK("CSVFile::ReadNextRowContent"); - MT_UNIQUE_LOCK_RETURN(CSVFile::_ReadNextRowContent(target)); + MT_UNIQUE_LOCK_RETURN(this, CSVFile::_ReadNextRowContent(target)); } diff --git a/src/common/CScript.cpp b/src/common/CScript.cpp index e83ed9086..67d6d26ca 100644 --- a/src/common/CScript.cpp +++ b/src/common/CScript.cpp @@ -321,7 +321,7 @@ tchar * CScriptKeyAlloc::_GetKeyBufferRaw( size_t iLen ) tchar * CScriptKeyAlloc::GetKeyBufferRaw( size_t iLen ) { ADDTOCALLSTACK("CScriptKeyAlloc::GetKeyBufferRaw"); - MT_UNIQUE_LOCK_RETURN(CScriptKeyAlloc::_GetKeyBufferRaw(iLen)); + MT_UNIQUE_LOCK_RETURN(this, CScriptKeyAlloc::_GetKeyBufferRaw(iLen)); } */ @@ -514,7 +514,7 @@ bool CScript::_Open( lpctstr ptcFilename, uint uiFlags ) bool CScript::Open( lpctstr ptcFilename, uint uiFlags ) { ADDTOCALLSTACK("CScript::Open"); - MT_UNIQUE_LOCK_RETURN(CScript::_Open(ptcFilename, uiFlags)); + MT_UNIQUE_LOCK_RETURN(this, CScript::_Open(ptcFilename, uiFlags)); } bool CScript::_ReadTextLine( bool fRemoveBlanks ) // Read a line from the opened script file @@ -542,7 +542,7 @@ bool CScript::_ReadTextLine( bool fRemoveBlanks ) // Read a line from the opened } bool CScript::ReadTextLine( bool fRemoveBlanks ) // Read a line from the opened script file { - MT_UNIQUE_LOCK_RETURN(CScript::_ReadTextLine(fRemoveBlanks)); + MT_UNIQUE_LOCK_RETURN(this, CScript::_ReadTextLine(fRemoveBlanks)); } bool CScript::FindTextHeader( lpctstr pszName ) // Find a section in the current script @@ -585,7 +585,7 @@ int CScript::_Seek( int iOffset, int iOrigin ) int CScript::Seek( int iOffset, int iOrigin ) { ADDTOCALLSTACK("CScript::Seek"); - MT_UNIQUE_LOCK_RETURN(CScript::_Seek(iOffset, iOrigin)); + MT_UNIQUE_LOCK_RETURN(this, CScript::_Seek(iOffset, iOrigin)); } bool CScript::FindNextSection() @@ -821,7 +821,7 @@ bool CScript::_SeekContext(const CScriptLineContext &LineContext ) } bool CScript::SeekContext( CScriptLineContext const& LineContext ) { - MT_UNIQUE_LOCK_RETURN(CScript::_SeekContext(LineContext)); + MT_UNIQUE_LOCK_RETURN(this, CScript::_SeekContext(LineContext)); } CScriptLineContext CScript::_GetContext() const @@ -837,7 +837,7 @@ CScriptLineContext CScript::_GetContext() const CScriptLineContext CScript::GetContext() const { - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); /* CScriptLineContext LineContext; LineContext.m_iLineNum = m_iLineNum; @@ -869,7 +869,7 @@ bool CScript::WriteSection( lpctstr ptcSection, ... ) offset += Str_CopyLimitNull(tsHeader.buffer() + offset, tsSectionFormatted.buffer(), tsHeader.capacity() - 2); offset += Str_ConcatLimitNull(tsHeader.buffer() + offset, "]\n", tsHeader.capacity() - offset); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); _Write(tsHeader.buffer(), int(offset)); return true; diff --git a/src/common/CScriptContexts.cpp b/src/common/CScriptContexts.cpp index 65916366e..23809dd33 100644 --- a/src/common/CScriptContexts.cpp +++ b/src/common/CScriptContexts.cpp @@ -36,14 +36,14 @@ void CScriptFileContext::_OpenScript( const CScript * pScriptContext ) void CScriptFileContext::OpenScript( const CScript * pScriptContext ) { ADDTOCALLSTACK("CScriptFileContext::OpenScript"); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); _OpenScript(pScriptContext); } void CScriptFileContext::Close() { ADDTOCALLSTACK("CScriptFileContext::Close"); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); _Close(); } void CScriptFileContext::_Close() diff --git a/src/common/CScriptObj.cpp b/src/common/CScriptObj.cpp index 31f634e92..de81b835b 100644 --- a/src/common/CScriptObj.cpp +++ b/src/common/CScriptObj.cpp @@ -354,29 +354,33 @@ bool CScriptObj::r_LoadVal( CScript & s ) const bool fZero = (index == SSC_VAR0); bool fQuoted = false; lpctstr ptcArg = s.GetArgStr(&fQuoted); - g_Exp.m_VarGlobals.SetStr(ptcKey + (fZero ? 5 : 4), fQuoted, ptcArg, fZero); + g_ExprGlobals.mtEngineLockedWriter()->m_VarGlobals.SetStr(ptcKey + (fZero ? 5 : 4), fQuoted, ptcArg, fZero); return true; } - case SSC_LIST: - if ( !g_Exp.m_ListGlobals.r_LoadVal(ptcKey + 5, s) ) + case SSC_LIST: + if ( ! g_ExprGlobals.mtEngineLockedWriter()->m_ListGlobals.r_LoadVal(ptcKey + 5, s) ) DEBUG_ERR(("Unable to process command '%s %s'\n", ptcKey, s.GetArgRaw())); return true; case SSC_DEFMSG: + { ptcKey += 7; - for ( long l = 0; l < DEFMSG_QTY; ++l ) + // TODO: we really should be using a sorted vector or an hash map... + auto rw = g_ExprGlobals.mtEngineLockedWriter(); + for ( int l = 0; l < DEFMSG_QTY; ++l ) { - if ( !strcmpi(ptcKey, g_Exp.sm_szMsgNames[l]) ) + if ( !strcmpi(ptcKey, rw->sm_szDefMsgNames[l]) ) { bool fQuoted = false; - tchar * args = s.GetArgStr(&fQuoted); - Str_CopyLimitNull(g_Exp.sm_szMessages[l], args, sizeof(g_Exp.sm_szMessages[0])); + tchar * args = s.GetArgStr(&fQuoted); + Str_CopyLimitNull(rw->sm_szDefMessages[l], args, CExprGlobals::m_kiDefmsgMaxLen); return true; } } g_Log.Event(LOGM_INIT|LOGL_ERROR, "Setting not used message override named '%s'\n", ptcKey); return false; + } } return true; EXC_CATCH; @@ -648,35 +652,41 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc case SSC_VAR: // "VAR." = get/set a system wide variable. { - const CVarDefCont * pVar = g_Exp.m_VarGlobals.GetKey(ptcKey); - sVal = CVarDefCont::GetValStrZeroed(pVar, fZero); + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + const CVarDefCont * pVar = gReader->m_VarGlobals.GetKey(ptcKey); + sVal = CVarDefCont::GetValStrZeroed(pVar, fZero); } return true; - case SSC_DEFLIST: - g_Exp.m_ListInternals.r_Write(pSrc, ptcKey, sVal); + case SSC_DEFLIST: + g_ExprGlobals.mtEngineLockedReader()->m_ListInternals.r_Write(pSrc, ptcKey, sVal); return true; - case SSC_LIST: - if (!g_Exp.m_ListGlobals.r_Write(pSrc, ptcKey, sVal)) + case SSC_LIST: + { + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + if (! gReader->m_ListGlobals.r_Write(pSrc, ptcKey, sVal)) sVal = "-1"; + } return true; case SSC_DEF0: fZero = true; FALLTHROUGH; case SSC_DEF: - { - const CVarDefCont * pVar = g_Exp.m_VarDefs.GetKey(ptcKey); - if ( pVar ) - sVal = pVar->GetValStr(); - else if ( fZero ) - sVal.SetValFalse(); - } - return true; + { + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + const CVarDefCont * pVar = gReader->m_VarDefs.GetKey(ptcKey); + if ( pVar ) + sVal = pVar->GetValStr(); + else if ( fZero ) + sVal.SetValFalse(); + } + return true; case SSC_RESDEF0: fZero = true; FALLTHROUGH; case SSC_RESDEF: { - const CVarDefCont * pVar = g_Exp.m_VarResDefs.GetKey(ptcKey); + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + const CVarDefCont * pVar = gReader->m_VarResDefs.GetKey(ptcKey); if ( pVar ) sVal = pVar->GetValStr(); else if ( fZero ) @@ -776,7 +786,7 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc return true; case SSC_STRRANDRANGE: - sVal = g_Exp.GetRangeString(ptcKey); + sVal = CExpression::GetExprParser().GetRangeString(ptcKey); return true; case SSC_StrPos: @@ -1279,7 +1289,7 @@ bool CScriptObj::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command f { ptcKey = s.GetArgStr(); SKIP_SEPARATORS(ptcKey); - g_Exp.m_VarGlobals.ClearKeys(ptcKey); + g_ExprGlobals.mtEngineLockedWriter()->m_VarGlobals.ClearKeys(ptcKey); return true; } @@ -1492,594 +1502,6 @@ bool CScriptObj::r_Load( CScript & s ) return true; } -// TODO: move to CExpression -bool CScriptObj::_Evaluate_Conditional_EvalSingle(CScriptSubExprData& sdata, CScriptTriggerArgsPtr pScriptArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc) -{ - ADDTOCALLSTACK("CScriptObj::_Evaluate_Conditional_EvalSingle"); - ASSERT(sdata.ptcStart); - ASSERT(sdata.ptcEnd); - using SType = CScriptSubExprData::Type; - bool fVal; - lptstr ptcSubexpr; - - // Evaluate the subexpression body - if (pContext->_iEvaluate_Conditional_Reentrant >= 16) - { - g_Log.EventError("Exceeding the limit of 16 subexpressions. Further parsing is halted.\n"); - return false; - } - ++ pContext->_iEvaluate_Conditional_Reentrant; - - // Is this conditional expression is fully enclosed by brackets ? - const bool fFullyEnclosed = (sdata.uiType & SType::TopParenthesizedExpr); - - // Length to copy: include the last valid char (i'm not copying the subsequent char, which can be another char or '\0' - ASSERT(sdata.ptcEnd >= sdata.ptcStart); - size_t len = std::min(Str_TempLength() - 1U, size_t(sdata.ptcEnd - sdata.ptcStart)); - if (len == 0) - { - g_Log.EventError("Empty subexpression. Defaulting its value to false.\n"); - return false; - } - - lptstr ptcParsingStart = sdata.ptcStart; - if (fFullyEnclosed) - { - -- len; // Exclude the closing bracket ')'. - ASSERT(len > 0); - - // In this case, we need to start parsing after the opening parenthesis '('; if we start before it and the subexpr is marked with MaybeNestedSubexpr, - // Evaluate_Conditional will again return the same subexpression fully enclosed by parenthesis, and we'll have a deadlock. - // Remember that sdata.uiNonAssociativeOffset is the distance between the open bracket '(' and the non-associative operator (negation operator '!'). - // The string might start with said non-associative operator. - ptcParsingStart += 1; - len -= 1; - } - - ASSERT(len < Str_TempLength()); - ptcSubexpr = Str_GetTemp(); - memcpy(ptcSubexpr, ptcParsingStart, len); - ptcSubexpr[len] = '\0'; - - const bool fNested = (sdata.uiType & SType::MaybeNestedSubexpr); - if (fNested) - { - // Probably this subexpression has other conditional subexpressions inside. - fVal = Evaluate_Conditional(ptcSubexpr, pScriptArgs, pContext, pSrc); - } - else - { - // If an expression is enclosed by parentheses, ParseScriptText needs to read both the open and the closed one, we cannot - // pass the string starting with the character after the '('. - ParseScriptText(ptcSubexpr, pScriptArgs, pContext, pSrc, 0); - fVal = bool(Exp_GetLLVal(ptcSubexpr)); - } - - -- pContext->_iEvaluate_Conditional_Reentrant; - - - // Apply non-associative operators preceding the subexpression - if (sdata.uiNonAssociativeOffset) - { - ptcSubexpr = sdata.ptcStart - sdata.uiNonAssociativeOffset; - ASSERT(!IsWhitespace(*ptcSubexpr)); - while (const tchar chOperator = *ptcSubexpr) - { - if (chOperator == '!') - fVal = !fVal; - else if (IsWhitespace(chOperator)) - ; // Allowed, skip it - else - break; - ++ptcSubexpr; - } - } - - return fVal; -} - -// TODO: move to CExpression, then we can even move CScriptExprContext as a CExpression member -bool CScriptObj::Evaluate_Conditional(lptstr ptcExpr, CScriptTriggerArgsPtr pScriptArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc) -{ - ADDTOCALLSTACK("CScriptObj::Evaluate_Conditional"); - //g_Log.EventDebug("\nEvaluating conditional expression: \"%s\"\n", ptcExpr); - - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - if (!pContext) - pContext = CScriptParserBufs::GetCScriptExprContextDataPtr(); - - CScriptSubExprData psCScriptSubExprData[uiMaxConditionalSubexprs]{}; - lptstr ptcExprDbg = ptcExpr; - const int iQty = CExpression::GetConditionalSubexpressions(ptcExprDbg, psCScriptSubExprData, ARRAY_COUNT(psCScriptSubExprData)); // number of arguments - - /*g_Log.EventDebug("---Qty: %d\n", iQty); - for (int i = 0; i < iQty; ++i) - g_Log.EventDebug("---Subexpr %d: \"%.*s\"\n", i, (psCScriptSubExprData[i].ptcEnd - psCScriptSubExprData[i].ptcStart), psCScriptSubExprData[i].ptcStart); - */ - - if (iQty == 0) - return 0; - - using SType = CScriptSubExprData::Type; - - if (iQty == 1) - { - // We don't have subexpressions, but only a simple expression. - CScriptSubExprData& sCur = psCScriptSubExprData[0]; - ASSERT((sCur.uiType & SType::None) || (sCur.uiType & SType::BinaryNonLogical)); - - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pScriptArgs, pContext, pSrc); - return fVal; - } - - // We have some subexpressions, connected between them by logical operators. - - bool fWholeExprVal = false; - for (int i = 0; i < iQty; ++i) - { - CScriptSubExprData& sCur = psCScriptSubExprData[i]; - ASSERT(sCur.uiType != SType::Unknown); - - if (i == 0) - { - fWholeExprVal = _Evaluate_Conditional_EvalSingle(sCur, pScriptArgs, pContext, pSrc); - continue; - } - - CScriptSubExprData& sPrev = psCScriptSubExprData[i - 1]; - if (sPrev.uiType & SType::Or) - { - if (fWholeExprVal) - return true; - - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pScriptArgs, pContext, pSrc); - fWholeExprVal = fWholeExprVal || fVal; - } - else if (sPrev.uiType & SType::And) - { - if (!fWholeExprVal) - return false; - - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, pScriptArgs, pContext, pSrc); - fWholeExprVal = (i == 1) ? fVal : (fWholeExprVal && fVal); - } - - - if (sCur.uiType & SType::None) - { - ASSERT(i == iQty - 1); // It should be the last subexpression - ASSERT((sPrev.uiType & SType::Or) || (sPrev.uiType & SType::And)); - } - - } - - return fWholeExprVal; -} - -// TODO: move to CExpression -static void Evaluate_QvalConditional_ParseArg(tchar* ptcSrc, tchar** ptcDest, lpctstr ptcSep) -{ - ASSERT(ptcSep && *ptcSep); - - // Check if we are encountering a a nested QVAL? - tchar* ptcBracketPos = nullptr; - tchar* ptcSepPos = nullptr; - for (tchar* ptcLine = ptcSrc; *ptcLine != '\0';) - { - const tchar ch = *ptcLine; - if ((ch != '<') && (ch != *ptcSep)) - { - ++ptcLine; - continue; - } - - if ((ch == '<') && !ptcBracketPos) - { - tchar* ptcTest = ptcLine + 1; - GETNONWHITESPACE(ptcTest); - if (!strnicmp("QVAL", ptcTest, 4)) - { - ptcBracketPos = ptcLine; - ptcLine = ptcTest + 3; - } - } - else if ((ch == *ptcSep) && !ptcSepPos) - { - ptcSepPos = ptcLine; - } - - if (!ptcBracketPos || !ptcSepPos) - { - ++ptcLine; - continue; - } - - if (ptcSepPos < ptcBracketPos) - { - // The separator we have found is before the nested QVAL. - ptcSrc = ptcSepPos; - break; - } - - // Found a nested QVAL. Skip it, otherwise we'll catch the wrong separator - Str_SkipEnclosedAngularBrackets(ptcBracketPos); - if (ptcBracketPos <= ptcLine) - ++ptcLine; - else - ptcSrc = ptcLine = ptcBracketPos; - - ptcBracketPos = ptcSepPos = nullptr; - } - - Str_Parse(ptcSrc, ptcDest, ptcSep); -} - -// TODO: move to CExpression -bool CScriptObj::Evaluate_QvalConditional(lpctstr ptcKey, CSString& sVal, CScriptTriggerArgsPtr pScriptArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc) -{ - ADDTOCALLSTACK("CScriptObj::Evaluate_QvalConditional"); - // Do a switch ? type statement - - // Do NOT work on the original arguments, it WILL fuck up the original string! - tchar* ptcArgs = Str_GetTemp(); - Str_CopyLimitNull(ptcArgs, ptcKey, Str_TempLength()); - - // We only partially evaluated the QVAL parameters (it's a special case), so we need to parse the expressions (still have angular brackets at this stage) - tchar* ppCmds[3]; - ppCmds[0] = ptcArgs; - - // Get the condition - Evaluate_QvalConditional_ParseArg(ppCmds[0], &(ppCmds[1]), "?"); - - // Get the first and second retvals - Evaluate_QvalConditional_ParseArg(ppCmds[1], &(ppCmds[2]), ":"); - - // Complete evaluation of the condition - // (do that in another string, since it may overwrite the arguments, which are written later in the same string). - tchar* ptcTemp = Str_GetTemp(); - Str_CopyLimitNull(ptcTemp, ppCmds[0], Str_TempLength()); - ParseScriptText(ptcTemp, pScriptArgs, pContext, pSrc, 0); - const bool fCondition = Exp_GetLLVal(ptcTemp); - - // Get the retval we want - // (we might as well work on the transformed original string, since at this point we don't care if we corrupt other arguments) - ptcTemp = ppCmds[(fCondition ? 1 : 2)]; - ParseScriptText(ptcTemp, pScriptArgs, pContext, pSrc, 0); - - sVal = ptcTemp; - if (sVal.IsEmpty()) - sVal.Clear(); - return true; -} - -// TODO: move to CExpression -int CScriptObj::ParseScriptText(tchar * ptcResponse, CScriptTriggerArgsPtr pScriptArgs, CScriptExprContextPtr pContext, CTextConsole * pSrc, int iFlags) -{ - ADDTOCALLSTACK("CScriptObj::ParseScriptText"); - //ASSERT(ptcResponse[0] != ' '); // Not needed: i remove whitespaces and invalid characters here. - - // Take in a line of text that may have fields that can be replaced with operators here. - // ARGS: - // iFlags & 1: Use HTML-compatible delimiters (%). Inside those, angular brackets are allowed to do nested evaluations. - // iFlags & 2: Don't allow recusive bracket count. - // iFlags & 4: Just parsing a nested QVAL. - // NOTE: - // html will have opening - // RETURN: - // iFlags & 4: Position of the ending bracket/delimiter of a QVAL statement. - // Otherwise: New length of the string. - - // Recursion control variables. - // _iParseScriptText_Reentrant = 0; - // _fParseScriptText_Brackets = false; // Am i evaluating a statement? (Am i inside < > brackets of a statement i am currently evaluating?) - - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - - if (!pContext) - pContext = CScriptParserBufs::GetCScriptExprContextDataPtr(); - else { - ASSERT(pContext->_fParseScriptText_Brackets == false); - } - - - const bool fNoRecurseBrackets = ((iFlags & 2) != 0); - - // General purpose variables. - const bool fHTML = ((iFlags & 1) != 0); - - // If we are parsing a string from a HTML file, we are using '%' as a delimiter for Sphere expressions, since < > are reserved characters in HTML. - const tchar chBegin = fHTML ? '%' : '<'; - const tchar chEnd = fHTML ? '%' : '>'; - - // Variables used to handle the QVAL special case and do lazy evaluation, instead of fully evaluating the whole string on the first pass. - // As an aftertought QVAL parsing could have been moved into a separate function, but it's intricate enough and it's working, so let's leave as it is...' - enum class QvalStatus { None, Condition, Returns, End } eQval = QvalStatus::None; - int iQvalOpenBrackets = 0; - - size_t uiSubstitutionBegin = 0; - int i = 0; - EXC_TRY("ParseScriptText Main Loop"); - for ( i = 0; ptcResponse[i] != '\0'; ++i) - { - const tchar ch = ptcResponse[i]; - - // Are we looking for the current statement start? - if ( !pContext->_fParseScriptText_Brackets) // not in brackets - { - if ( ch == chBegin ) // found the start ! - { - const tchar chNext = ptcResponse[i + 1]; - if ((chNext != '<') && !IsAlnum(chNext)) - continue; // Ignore this, it might be a operator like <= - if ((chBegin == '<') && (chNext == '<')) - { - // Is a << operator? I want a whitespace after the operator. - if ((ptcResponse[i + 2] != '\0') && (ptcResponse[i + 3] != '\0') && IsWhitespace(ptcResponse[i + 2])) - { - lpctstr ptcOpTest = &(ptcResponse[4]); - if (*ptcOpTest != '\0') - { - GETNONWHITESPACE(ptcOpTest); - if (*ptcOpTest != '\0') // There's more text to parse - { - // I guess i have sufficient proof: skip, it's a << operator - i += 2; // Skip < and the whitespace - continue; - } - } - } - } - - // Set the statement start - ASSERT(i >= 0); - uiSubstitutionBegin = (size_t)i; - pContext->_fParseScriptText_Brackets = true; - - // Set-up to process special statements: is it a QVAL? - const bool fIsQval = !strnicmp(ptcResponse + i + 1, "QVAL", 4); - if (fIsQval) - { - ++iQvalOpenBrackets; - eQval = QvalStatus::Condition; - - i += 4; - } - } - - continue; - } - - // Are we inside a QVAL and are we searching where its condition end? - if ((ch == '?') && (eQval == QvalStatus::Condition)) - { - // Now we keep the bracket count to find the closing bracket for the QVAL statement. - eQval = QvalStatus::Returns; - continue; - } - - // Handle possibly recursive angular brackets (i'm already inside an open bracket) - if (pContext->_fParseScriptText_Brackets && (ch == '<')) - { - const tchar chNext = ptcResponse[i + 1]; - if (chNext == '<') - { - // Nested angular brackets? like: <> - lptstr ptcTestNested = ptcResponse + i; - lpctstr ptcTestOrig = ptcTestNested; - Str_SkipEnclosedAngularBrackets(ptcTestNested); - // If i have matching closing brackets, so it must be nested angular brackets. - if (ptcTestNested == ptcTestOrig) - { - // Otherwise, it might be the << operator. - - // This shouldn't be necessary... but - /* - // Is a << operator? I want a whitespace after the operator. - if ((ptcResponse[i + 2] != '\0') && (ptcResponse[i + 3] != '\0') && IsWhitespace(ptcResponse[i + 2])) - { - lpctstr ptcOpTest = &(ptcResponse[4]); - if (*ptcOpTest != '\0') - { - GETNONWHITESPACE(ptcOpTest); - if (*ptcOpTest != '\0') // There's more text to parse - { - // I guess i have sufficient proof: skip, it's a << operator - i += 2; // Skip < and the whitespace - pContext->_fParseScriptText_Brackets = false; - continue; - } - } - } - // Print an error! I thought it was a << operator but it is not! What's happening here ?! - */ - - ++i; - continue; - } - } - - // Detect nested QVALs - if (eQval != QvalStatus::None) - { - const bool fIsQval = !strnicmp(ptcResponse + i + 1, "QVAL", 4); - if (fIsQval) - { - // Nested QVAL... Needs to be evaluated separately, but we only want to know where it ends. - ASSERT(pContext->_fParseScriptText_Brackets == true); - ++ pContext->_iParseScriptText_Reentrant; - pContext->_fParseScriptText_Brackets = false; - - tchar* ptcRecurseParse = ptcResponse + i; - const int iLen = ParseScriptText(ptcRecurseParse, pScriptArgs, pContext, pSrc, 4); - - pContext->_fParseScriptText_Brackets = true; - -- pContext->_iParseScriptText_Reentrant; - - i += iLen; - continue; - } - - // At this point, we shouldn't face nested QVALs. - - // I'm inside a QVAL. I can be parsing the condition or the return values. - if (eQval == QvalStatus::Returns) // I'm after its condition (so after '?'), thus i'm parsing the return values. - ++iQvalOpenBrackets; - - // Halt here the evaluation of the stuff inside this open bracket, since i don't want to know what's inside. - continue; - } - - if (pContext->_iParseScriptText_Reentrant > 32 ) - { - EXC_SET_BLOCK("recursive brackets limit"); - ASSERT_ALWAYS(pContext->_iParseScriptText_Reentrant < 32); - } - - ASSERT(pContext->_fParseScriptText_Brackets == true); - ++pContext->_iParseScriptText_Reentrant; - pContext->_fParseScriptText_Brackets = false; - - // Parse what's inside the open bracket - tchar* ptcRecurseParse = ptcResponse + i; - const int iLen = ParseScriptText(ptcRecurseParse, pScriptArgs, pContext, pSrc, 2 ); - - pContext->_fParseScriptText_Brackets = true; - --pContext->_iParseScriptText_Reentrant; - - i += iLen; - continue; - } - - // At this point i'm sure that ahead we won't find other open angular brackets, we may find their closing one or just plain text. - if ( ch == chEnd ) - { - // Closing bracket found: should we evaluate what's inside the brackets? - if (eQval != QvalStatus::None) - { - // Special handling for QVAL - if (eQval == QvalStatus::Returns) - { - // I'm after the '?' symbol in QVAL. We are searching for the closing bracket. - --iQvalOpenBrackets; - - if (iQvalOpenBrackets == 0) - { - // End of the QVAL statement. - if (iFlags & 04) - { - // I was just checking for the QVAL statement end. - ASSERT(pContext->_fParseScriptText_Brackets == true); - pContext->_fParseScriptText_Brackets = false; - return i; - } - - // Proceed, so we can execute it (do not 'continue'). - eQval = QvalStatus::End; - } - else - { - // Still inside QVAL, just go ahead. - continue; - } - } - else - { - // I'm before the '?' symbol in QVAL and i'm still searching for it, so we know when the conditional expression ends - continue; // Ignore brackets, i want only the ? symbol. - } - } - - - // If i'm here it means that finally i'm at the end of the statement inside brackets. - pContext->_fParseScriptText_Brackets = false; // Close the statement. - - if ((eQval == QvalStatus::End) && (iQvalOpenBrackets != 0)) - { - // I had an incomplete QVAL statement. - g_Log.EventError("QVAL parameters after '?' have unmatched '%c'.\n", ((iQvalOpenBrackets < 0) ? '<' : '>')); - } - - // Complete the evaluation of our string - //-- Write to our temporary sVal the evaluated script - EXC_SET_BLOCK("writeval"); - - ptcResponse[i] = '\0'; // Needed for r_WriteVal - lpctstr ptcKey = ptcResponse + uiSubstitutionBegin + 1; // move past the opening bracket - - CSString sVal; - bool fRes; - if (eQval != QvalStatus::None) - { - // Separate evaluation for QVAL. I may need additional script context for it (pScriptArgs isn't available in r_WriteVal). - EXC_SET_BLOCK("writeval qval"); - ptcKey += 4; // Skip the letters QVAL and pass only the arguments - fRes = Evaluate_QvalConditional(ptcKey, sVal, pScriptArgs, pContext, pSrc); - eQval = QvalStatus::None; - } - else - { - // Standard evaluation for everything else - EXC_SET_BLOCK("writeval generic"); - fRes = r_WriteVal(ptcKey, sVal, pSrc); - if (fRes == false) - { - EXC_SET_BLOCK("writeval args"); - // write the value of functions or triggers variables/objects like ARGO, ARGN1/2/3, LOCALs... - if ((pScriptArgs != nullptr) && pScriptArgs->r_WriteVal(ptcKey, sVal, pSrc)) - fRes = true; - } - } - - - if ( fRes == false ) - { - DEBUG_ERR(( "Can't resolve <%s>.\n", ptcKey )); - // Just in case this really is a <= operator ? - ptcResponse[i] = chEnd; // it's the char we overwrote with '\0' - } - - if (fHTML && sVal.IsEmpty()) - { - sVal = " "; - } - - //-- In the output string, substitute the raw substring with its parsed value - EXC_SET_BLOCK("mem shifting"); - - const size_t uiWriteValLen = sVal.GetLength(); - - // Make room for the obtained value, moving to left (if it's shorter than the scripted statement) or right (if longer) the string characters after it. - tchar* ptcDest = ptcResponse + uiSubstitutionBegin + uiWriteValLen; // + iWriteValLen because we need to leave the space for the replacing keyword - const tchar * const ptcLeftover = ptcResponse + i + 1; // End of the statement we just evaluated - const size_t uiLeftoverLen = strlen(ptcLeftover) + 1; - memmove(ptcDest, ptcLeftover, uiLeftoverLen); - - // Insert the obtained value in the room we created. - ptcDest = ptcResponse + uiSubstitutionBegin; - memcpy(ptcDest, sVal.GetBuffer(), uiWriteValLen); - - // This can be negative. - i = (int)(uiSubstitutionBegin + uiWriteValLen) - 1; - - if (fNoRecurseBrackets) // just do this one then bail out. - { - pContext->_fParseScriptText_Brackets = false; - return i; - } - } - } - EXC_CATCH; - - EXC_DEBUG_START; - g_Log.EventDebug("response '%s' source addr '0%p' flags '%d' args '%p'\n", ptcResponse, static_cast(pSrc), iFlags, static_cast(pScriptArgs.get())); - EXC_DEBUG_END; - - pContext->_fParseScriptText_Brackets = false; - return i; -} - bool CScriptObj::Execute_Call(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) { ADDTOCALLSTACK("CScriptObj::Execute_Call"); @@ -2410,6 +1832,9 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CScriptTrig CScriptLineContext EndContext(StartContext); int LoopsMade = 0; + CExpression& expr_parser = CExpression::GetExprParser(); + CScriptExprContext context; + if (iType & 8) // WHILE { TemporaryString tsConditionBuf; @@ -2426,9 +1851,8 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CScriptTrig tchar* ptcCond = tsConditionBuf.buffer(); Str_CopyLimitNull(ptcCond, tsOrig.buffer(), tsConditionBuf.capacity()); - - - ParseScriptText(ptcCond, pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); + context = {._pScriptObjI = this}; + expr_parser.ParseScriptText(ptcCond, context, pScriptArgs, pSrc, 0); if (!Exp_GetLLVal(ptcCond)) break; @@ -2452,7 +1876,8 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CScriptTrig } else { - ParseScriptText(s.GetArgStr(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); + context = {._pScriptObjI = this}; + expr_parser.ParseScriptText(s.GetArgStr(), context, pScriptArgs, pSrc, 0); } @@ -2757,7 +2182,10 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, C { if (s.HasArgs()) { - ParseScriptText(s.GetArgRaw(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); + CExpression& expr_parser = CExpression::GetExprParser(); + CScriptExprContext context = {._pScriptObjI = this}; + + expr_parser.ParseScriptText(s.GetArgRaw(), context, pScriptArgs, pSrc, 0); if (iCmd == SK_FORCHARLAYER) iRet = pCharThis->OnCharTrigForLayerLoop(s, pScriptArgs, pSrc, pResult, (LAYER_TYPE)s.GetArgVal()); else @@ -2794,7 +2222,10 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForCont(CScript& s, CScriptTriggerArgsPtr TemporaryString tsOrigValue; tchar* ptcOrigValue = tsOrigValue.buffer(); Str_ConcatLimitNull(ptcOrigValue, ppArgs[0], tsOrigValue.capacity()); - ParseScriptText(ptcOrigValue, pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); + + CExpression& expr_parser = CExpression::GetExprParser(); + CScriptExprContext context = {._pScriptObjI = this}; + expr_parser.ParseScriptText(ptcOrigValue, context, pScriptArgs, pSrc, 0); CUID pCurUid(Exp_GetDWVal(ptcOrigValue)); if (pCurUid.IsValidUID()) @@ -2868,20 +2299,24 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, C return iRet; } + CExpression& expr_parser = CExpression::GetExprParser(); + CScriptExprContext context = {._pScriptObjI = this}; + TemporaryString tsParsedArg0; Str_CopyLimitNull(tsParsedArg0.buffer(), ppArgs[0], tsParsedArg0.capacity()); - if ((ParseScriptText(tsParsedArg0.buffer(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0) <= 0)) + if (expr_parser.ParseScriptText(tsParsedArg0.buffer(), context, pScriptArgs, pSrc, 0) <= 0) { g_Log.EventError("FORCONT[id/type] called on container 0%x with incorrect argument 0.\n", (dword)pObjCont->GetUID()); iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); return iRet; } + context = {._pScriptObjI = this}; TemporaryString tsParsedArg1; if (ppArgs[1] != nullptr) { Str_CopyLimitNull(tsParsedArg1.buffer(), ppArgs[1], tsParsedArg0.capacity()); - if (ParseScriptText(tsParsedArg1.buffer(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0) <= 0) + if (expr_parser.ParseScriptText(tsParsedArg1.buffer(), context, pScriptArgs, pSrc, 0) <= 0) { g_Log.EventError("FORCONT[id/type] called on container 0%x with incorrect argument 1.\n", (dword)pObjCont->GetUID()); iRet = OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); @@ -3068,6 +2503,9 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScript { // Parse out any variables in it. (may act like a verb sometimes?) EXC_SET_BLOCK("parsing"); + CExpression& expr_parser = CExpression::GetExprParser(); + CScriptExprContext context = {._pScriptObjI = this}; + if( strchr(s.GetKey(), '<') ) { EXC_SET_BLOCK("parsing <> in a key"); @@ -3075,13 +2513,13 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScript Str_CopyLimitNull (tsBuf.buffer(), s.GetKey(), tsBuf.capacity()); Str_ConcatLimitNull(tsBuf.buffer(), " ", tsBuf.capacity()); Str_ConcatLimitNull(tsBuf.buffer(), s.GetArgRaw(), tsBuf.capacity()); - ParseScriptText(tsBuf.buffer(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); + expr_parser.ParseScriptText(tsBuf.buffer(), context, pScriptArgs, pSrc, 0); s.ParseKey(tsBuf.buffer()); } else { - ParseScriptText( s.GetArgRaw(), pScriptArgs, CScriptExprContextPtr{}, pSrc, 0); + expr_parser.ParseScriptText( s.GetArgRaw(), context, pScriptArgs, pSrc, 0); } } } @@ -3140,29 +2578,35 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScript case SK_IF: { - EXC_SET_BLOCK("if statement"); - // At this point, we have to parse the conditional expression - const lptstr ptcArg = s.GetArgStr(); - bool fTrigger = Evaluate_Conditional(ptcArg, pScriptArgs, CScriptExprContextPtr{}, pSrc); - bool fBeenTrue = false; - for (;;) - { - iRet = OnTriggerRun( s, fTrigger ? TRIGRUN_SECTION_TRUE : TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult ); - if (( iRet < TRIGRET_ENDIF ) || ( iRet >= TRIGRET_RET_HALFBAKED )) - return clean_return(iRet); - if ( iRet == TRIGRET_ENDIF ) - break; + EXC_SET_BLOCK("if statement"); + // At this point, we have to parse the conditional expression + CExpression& expr_parser = CExpression::GetExprParser(); + CScriptExprContext context = {._pScriptObjI = this}; + + const lptstr ptcArg = s.GetArgStr(); + bool fTrigger = expr_parser.Evaluate_Conditional(ptcArg, context, pScriptArgs, pSrc); + bool fBeenTrue = false; + for (;;) + { + iRet = OnTriggerRun( s, fTrigger ? TRIGRUN_SECTION_TRUE : TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult ); + if (( iRet < TRIGRET_ENDIF ) || ( iRet >= TRIGRET_RET_HALFBAKED )) + return clean_return(iRet); + if ( iRet == TRIGRET_ENDIF ) + break; - fBeenTrue |= fTrigger; - if ( fBeenTrue ) - fTrigger = false; - else if ( iRet == TRIGRET_ELSE ) - fTrigger = true; - else if ( iRet == TRIGRET_ELSEIF ) - fTrigger = Evaluate_Conditional(s.GetArgStr(), pScriptArgs, CScriptExprContextPtr{}, pSrc); - } - } - break; + fBeenTrue |= fTrigger; + if ( fBeenTrue ) + fTrigger = false; + else if ( iRet == TRIGRET_ELSE ) + fTrigger = true; + else if ( iRet == TRIGRET_ELSEIF ) + { + context = {._pScriptObjI = this}; + fTrigger = expr_parser.Evaluate_Conditional(s.GetArgStr(), context, pScriptArgs, pSrc); + } + } + } + break; case SK_BEGIN: // Do this block here. diff --git a/src/common/CScriptObj.h b/src/common/CScriptObj.h index 4f576ffb2..f9192ed9b 100644 --- a/src/common/CScriptObj.h +++ b/src/common/CScriptObj.h @@ -15,10 +15,7 @@ class CSString; class CUID; class CChar; -struct CScriptSubExprData; - -struct CScriptExprContextData; -using CScriptExprContextPtr = std::shared_ptr; +struct CScriptSubExprState; class CScriptTriggerArgs; using CScriptTriggerArgsPtr = std::shared_ptr; @@ -89,11 +86,6 @@ class CScriptObj */ virtual bool r_WriteVal(lpctstr pKey, CSString& sVal, CTextConsole* pSrc = nullptr, bool fNoCallParent = false, bool fNoCallChildren = false); - /* - * @brief Do the first-level parsing of a script line and eventually replace requested values got by r_WriteVal. - */ - int ParseScriptText( tchar * pszResponse, CScriptTriggerArgsPtr pArgs, CScriptExprContextPtr pContext, CTextConsole * pSrc, int iFlags = 0 ); - /* * @brief Execute a script command. * Called when parsing a script section with OnTriggerRun or if issued by a CClient. @@ -143,11 +135,6 @@ class CScriptObj TRIGRET_TYPE OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); // Special statements - bool _Evaluate_Conditional_EvalSingle(CScriptSubExprData& sdata, CScriptTriggerArgsPtr pArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc); - bool Evaluate_Conditional(lptstr ptcExpression, CScriptTriggerArgsPtr pArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc); // IF, ELIF, ELSEIF - - bool Evaluate_QvalConditional(lpctstr ptcKey, CSString& sVal, CScriptTriggerArgsPtr pArgs, CScriptExprContextPtr pContext, CTextConsole* pSrc); - bool Execute_Call(CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); bool Execute_FullTrigger(CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); diff --git a/src/common/CScriptParserBufs.cpp b/src/common/CScriptParserBufs.cpp index 9cc9baea0..523f620dc 100644 --- a/src/common/CScriptParserBufs.cpp +++ b/src/common/CScriptParserBufs.cpp @@ -16,37 +16,14 @@ struct CScriptParserBufsImpl static constexpr bool sm_allow_fallback_objects = true; // We stack allocate this POD struct, so we don't need to keep them in a pool. - //sl::ObjectPool - //m_poolCScriptSubExprData; - - // While it's a POD struct (so trivial/simple to construct), we still need to allocate it on the heap, - // and malloc/new have a computional impact. - // An alternative could be to allocate the buffer on the stack with 'alloca' and pass around the pointer... Is it worth it? - sl::ObjectPool - m_poolCScriptExprContextData; + //sl::ObjectPool + //m_poolCScriptSubExprState; // This is even more expensive to construct, so will definitely benefit a lot from having allocated, cached instances. sl::ObjectPool m_poolScriptTriggerArgs; }; -auto CScriptParserBufs::GetCScriptExprContextDataPtr() -> CScriptExprContextPtr -{ - auto& pool = CScriptParserBufsImpl::Get().m_poolCScriptExprContextData; - auto ptr = pool.acquireShared(); - if (!pool.isFromPool(ptr)) - { - static_assert(CScriptParserBufsImpl::sm_allow_fallback_objects); - g_Log.EventDebug( - "Requesting CScriptExprContext from an exhausted pool (max size: %" PRIuSIZE_T "). Alive new heap-allocated fallback objects: %" PRIuSIZE_T ".\n", - pool.sm_pool_size, pool.getFallbackCount()); - } - - *ptr.get() = {}; - //memset(ptr.get(), 0, sizeof(CScriptExprContextData)); - return ptr; -} - auto CScriptParserBufs::GetCScriptTriggerArgsPtr() -> CScriptTriggerArgsPtr { auto& pool = CScriptParserBufsImpl::Get().m_poolScriptTriggerArgs; diff --git a/src/common/CScriptParserBufs.h b/src/common/CScriptParserBufs.h index 9f3cd7f44..76d8d1ee3 100644 --- a/src/common/CScriptParserBufs.h +++ b/src/common/CScriptParserBufs.h @@ -4,34 +4,6 @@ #include "CScriptTriggerArgs.h" -struct CScriptSubExprData -{ - lptstr ptcStart, ptcEnd; - enum Type : ushort - { - Unknown = 0, - // Powers of two - MaybeNestedSubexpr = 0x1 << 0, // 001 - TopParenthesizedExpr = 0x1 << 1, // 002 - None = 0x1 << 2, // 004 - BinaryNonLogical = 0x1 << 3, // 008 - And = 0x1 << 4, // 010 - Or = 0x1 << 5 // 020 - }; - ushort uiType; - ushort uiNonAssociativeOffset; // How much bytes/characters before the start is (if any) the first non-associative operator preceding the subexpression. -}; -using CScriptSubExprDataPtr = std::unique_ptr; - -struct CScriptExprContextData -{ - // Recursion counters and state variables - short _iEvaluate_Conditional_Reentrant; - short _iParseScriptText_Reentrant; - bool _fParseScriptText_Brackets; -}; -using CScriptExprContextPtr = std::shared_ptr; - using CScriptTriggerArgsPtr = std::shared_ptr; /* class CScriptTriggerArgsPtr : public std::shared_ptr @@ -47,8 +19,7 @@ class CScriptParserBufs ~CScriptParserBufs() noexcept = default; public: - //static CScriptSubExprDataPtr GetCScriptSubExprDataPtr(); - static CScriptExprContextPtr GetCScriptExprContextDataPtr(); + //static CScriptSubExprStatePtr GetCScriptSubExprStatePtr(); static CScriptTriggerArgsPtr GetCScriptTriggerArgsPtr(); }; diff --git a/src/common/ListDefContMap.cpp b/src/common/ListDefContMap.cpp index 7d092eafb..8944b134f 100644 --- a/src/common/ListDefContMap.cpp +++ b/src/common/ListDefContMap.cpp @@ -714,7 +714,7 @@ CListDefCont* CListDefMap::AddList(lpctstr ptcKey) return pListBase; } -void CListDefMap::DumpKeys( CTextConsole * pSrc, lpctstr pszPrefix ) +void CListDefMap::DumpKeys( CTextConsole * pSrc, lpctstr pszPrefix ) const { ADDTOCALLSTACK("CListDefMap::DumpKeys"); // List out all the keys. @@ -967,7 +967,7 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) return false; } -bool CListDefMap::r_Write( CTextConsole *pSrc, lpctstr pszString, CSString& strVal ) +bool CListDefMap::r_Write( CTextConsole *pSrc, lpctstr pszString, CSString& strVal ) const { ADDTOCALLSTACK("CListDefMap::r_Write"); UnreferencedParameter(pSrc); @@ -1037,7 +1037,7 @@ bool CListDefMap::r_Write( CTextConsole *pSrc, lpctstr pszString, CSString& strV } -void CListDefMap::r_WriteSave( CScript& s ) +void CListDefMap::r_WriteSave( CScript& s ) const { ADDTOCALLSTACK("CListDefMap::r_WriteSave"); diff --git a/src/common/ListDefContMap.h b/src/common/ListDefContMap.h index 9df957509..a294da027 100644 --- a/src/common/ListDefContMap.h +++ b/src/common/ListDefContMap.h @@ -210,13 +210,13 @@ class CListDefMap CListDefCont* AddList(lpctstr ptcKey); - void DumpKeys( CTextConsole * pSrc, lpctstr pszPrefix = nullptr ); + void DumpKeys( CTextConsole * pSrc, lpctstr pszPrefix = nullptr ) const; void ClearKeys(lpctstr mask = nullptr); void DeleteKey( lpctstr key ); bool r_LoadVal( lpctstr ptcKey, CScript & s ) NONVIRTUAL; - bool r_Write( CTextConsole *pSrc, lpctstr pszString, CSString& strVal ) NONVIRTUAL; - void r_WriteSave( CScript& s ) NONVIRTUAL; + bool r_Write( CTextConsole *pSrc, lpctstr pszString, CSString& strVal ) const NONVIRTUAL; + void r_WriteSave( CScript& s ) const NONVIRTUAL; }; #endif // _INC_LISTDEFCONTMAP_H diff --git a/src/common/basic_threading.h b/src/common/basic_threading.h index 2f6e3e270..cf2a913bc 100644 --- a/src/common/basic_threading.h +++ b/src/common/basic_threading.h @@ -12,24 +12,28 @@ #include // As per standard, std::shared_mutex has NOT a noexcept contructor, nevertheless its implementations should actually not throw. +// -- Macros for direct mutex lock/unlock and mutex-guarded value returning. + +#define MT_DEFAULT_CMUTEX_TYPE std::shared_mutex + // Name of the Class Mutex -#define MT_CMUTEX _classMutex +#define MT_CMUTEX _classMutex // Use this in the class definition to add the class mutex. -#define MT_CMUTEX_DEF mutable std::shared_mutex MT_CMUTEX +#define MT_CMUTEX_DEF mutable MT_DEFAULT_CMUTEX_TYPE MT_CMUTEX // Read-Only: multiple threads can read the same resource -#define MT_SHARED_LOCK_SET std::shared_lock _shared_lock(this->MT_CMUTEX) +#define MT_SHARED_LOCK_SET(pClass) std::shared_lock _shared_lock((pClass)->MT_CMUTEX) // Locking is already done by MT_SHARED_LOCK_SET. Use it only if you know what you are doing! -#define MT_SHARED_LOCK _shared_lock.lock() +#define MT_SHARED_LOCK _shared_lock.lock() // Unlocking is done automatically then the function ends (when the lock created with MT_SHARED_LOCK_SET goes out of scope). Use it only if you know what you are doing! -#define MT_SHARED_UNLOCK _shared_lock.unlock() +#define MT_SHARED_UNLOCK _shared_lock.unlock() // Read/Write: exclusive access to a thread at a time -#define MT_UNIQUE_LOCK_SET std::unique_lock _unique_lock(this->MT_CMUTEX) +#define MT_UNIQUE_LOCK_SET(pClass) std::unique_lock _unique_lock((pClass)->MT_CMUTEX) // Locking is already done by MT_UNIQUE_LOCK_SET. Use it only if you know what you are doing! -#define MT_UNIQUE_LOCK _unique_lock.lock() +#define MT_UNIQUE_LOCK _unique_lock.lock() // Unlocking is done automatically then the function ends (when the lock created with MT_UNIQUE_LOCK_SET goes out of scope). Use it only if you know what you are doing! -#define MT_UNIQUE_UNLOCK _unique_lock.unlock() +#define MT_UNIQUE_UNLOCK _unique_lock.unlock() // Thread-safe return macro. If we directly do return without using a temporary storage variable, the return value will be stored in the returned register // after the thread lock is destroyed, so after the mutex is unlocked. In this way, we have a variable holding the value of the return expression, but before the mutex unlocking. @@ -40,42 +44,43 @@ // A single macro to lock the class mutex and return the value in a thread-safe way. // Do not use to return references! (It will return the address of the local-scoped, temporary variable _MT_RETURN_val) -#define MT_SHARED_LOCK_RETURN(x) \ +#define MT_SHARED_LOCK_RETURN(pClass, val) \ /* Creating and locking a mutex might THROW std::system_error ! */ \ - {MT_SHARED_LOCK_SET; \ - MT_RETURN(x);} + {MT_SHARED_LOCK_SET(pClass); \ + MT_RETURN(val);} -#define MT_UNIQUE_LOCK_RETURN(x) \ +#define MT_UNIQUE_LOCK_RETURN(pClass, val) \ /* Creating and locking a mutex might THROW std::system_error ! */ \ - {MT_UNIQUE_LOCK_SET; \ - MT_RETURN(x);} + {MT_UNIQUE_LOCK_SET(pClass); \ + MT_RETURN(val);} #if MT_ENGINES == 1 //_DEBUG - #define MT_ENGINE_SHARED_LOCK_SET MT_SHARED_LOCK_SET - #define MT_ENGINE_SHARED_LOCK MT_SHARED_LOCK - #define MT_ENGINE_SHARED_UNLOCK MT_SHARED_UNLOCK - #define MT_ENGINE_UNIQUE_LOCK_SET MT_UNIQUE_LOCK_SET - #define MT_ENGINE_UNIQUE_UNLOCK MT_UNIQUE_UNLOCK + #define MT_ENGINE_SHARED_LOCK_SET(pClass) MT_SHARED_LOCK_SET + #define MT_ENGINE_SHARED_LOCK MT_SHARED_LOCK + #define MT_ENGINE_SHARED_UNLOCK MT_SHARED_UNLOCK + #define MT_ENGINE_UNIQUE_LOCK_SET(pClass) MT_UNIQUE_LOCK_SET + #define MT_ENGINE_UNIQUE_UNLOCK MT_UNIQUE_UNLOCK - #define MT_ENGINE_RETURN(x) MT_RETURN(x) - #define MT_ENGINE_SHARED_LOCK_RETURN(x) MT_SHARED_LOCK_RETURN(x) - #define MT_ENGINE_UNIQUE_LOCK_RETURN(x) MT_UNIQUE_LOCK_RETURN(x) + #define MT_ENGINE_RETURN(x) MT_RETURN(x) + #define MT_ENGINE_SHARED_LOCK_RETURN(x) MT_SHARED_LOCK_RETURN(x) + #define MT_ENGINE_UNIQUE_LOCK_RETURN(x) MT_UNIQUE_LOCK_RETURN(x) #else - #define MT_ENGINE_SHARED_LOCK_SET (void)0 + #define MT_ENGINE_SHARED_LOCK_SET(pClass) (void)0 #define MT_ENGINE_SHARED_LOCK (void)0 #define MT_ENGINE_SHARED_UNLOCK (void)0 - #define MT_ENGINE_UNIQUE_LOCK_SET (void)0 + #define MT_ENGINE_UNIQUE_LOCK_SET(pClass) (void)0 #define MT_ENGINE_UNIQUE_LOCK (void)0 #define MT_ENGINE_UNIQUE_UNLOCK (void)0 - #define MT_ENGINE_RETURN(x) return (x) - #define MT_ENGINE_SHARED_LOCK_RETURN(x) return (x) - #define MT_ENGINE_UNIQUE_LOCK_RETURN(x) return (x) + #define MT_ENGINE_RETURN(x) return (x) + #define MT_ENGINE_SHARED_LOCK_RETURN(x) return (x) + #define MT_ENGINE_UNIQUE_LOCK_RETURN(x) return (x) #endif +// --- // Try acquiring a lock (try multiple times), to be used only for CRITICAL mutexes, that you pray will never fail (for an OS error) to be locked (not because they are already locked). If successiful, call func_, usually a lambda. If fails, program explodes (std::abort). #define RETRY_LOCK_ATTEMPTS_MAX 20 @@ -107,4 +112,147 @@ RETRY_LOCK_FOR_TASK_IMPL_(std::unique_lock, (mutex_name_), (lock_name_), RETRY_LOCK_ATTEMPTS_MAX, (retval_), (func_)) +// --- + +namespace sl +{ + +template +class GuardedAccess +{ +public: + using Mutex = MT_DEFAULT_CMUTEX_TYPE; + using SharedLock = std::shared_lock; + using UniqueLock = std::unique_lock; + + struct NoOpLock + { + NoOpLock(Mutex&) noexcept {} + ~NoOpLock() noexcept = default; + }; + using OptLock = +#if MT_ENGINES + SharedLock +#else + NoOpLock +#endif + ; + + // ---- Construct underlying T in-place + template + explicit GuardedAccess(Args&&... args) + : mutex_(), data_(std::forward(args)...) + {} + + // ----- Reader proxies ----- + class LockedReader + { + public: + LockedReader(const GuardedAccess& owner) + : lock_(owner->pmutex_), ptr_(&owner.data_) + {} + RETURNS_NOTNULL + const T* operator->() const { return ptr_; } + const T& operator* () const { return *ptr_; } + private: + SharedLock lock_; + const T* ptr_; + }; + + class MTEngineLockedReader + { + public: + MTEngineLockedReader(const GuardedAccess& owner) + : +#if MT_ENGINES + locked_(owner) +#else + owner_(owner) +#endif + {} +#if MT_ENGINES + LockedReader operator-> () const { return locked_.operator->; } + LockedReader operator* () const { return locked_.operator*; } +#else + RETURNS_NOTNULL + const T* operator->() const { return &owner_.data_; } + const T& operator* () const { return owner_.data_; } +#endif + private: +#if MT_ENGINES + LockedReader locked_; +#else + const GuardedAccess& owner_; +#endif + }; + + // ----- Writer proxies ----- + class LockedWriter + { + public: + LockedWriter(GuardedAccess& owner) + : lock_(owner->pmutex_), ptr_(&owner.data_) + {} + RETURNS_NOTNULL + T* operator->() { return ptr_; } + T& operator* () { return *ptr_; } + private: + UniqueLock lock_; + T* ptr_; + }; + + class MTEngineLockedWriter + { + public: + MTEngineLockedWriter(GuardedAccess& owner) + : +#if MT_ENGINES + locked_(owner) +#else + owner_(owner) +#endif + {} +#if MT_ENGINES + LockedWriter operator-> () { return locked_.operator->; } + LockedWriter operator* () { return locked_.operator*; } +#else + RETURNS_NOTNULL + T* operator->() { return &owner_.data_; } + T& operator* () { return owner_.data_; } +#endif + private: +#if MT_ENGINES + LockedWriter locked_; +#else + GuardedAccess& owner_; +#endif + }; + + // ---- Unsafe/raw accessors ---- + // No lock taken! You must lock manually. + T& unsafeWriter() { return data_; } + const T& unsafeReader() const { return data_; } + + // ---- Lock grabbing ---- + auto getLockShared() const { return SharedLock(mutex_); } + auto getLockUnique() { return UniqueLock(mutex_); } + + auto mtEngineGetLockShared() const { return OptLock(mutex_); } + auto mtEngineGetLockUnique() { return OptLock(mutex_); } + + // ---- Accessors ---- + auto lockedReader() const { return LockedReader(*this); } + auto lockedWriter() { return LockedWriter(*this); } + + auto mtEngineLockedReader() const { return MTEngineLockedReader(*this); } + auto mtEngineLockedWriter() { return MTEngineLockedWriter(*this); } + +private: + mutable Mutex mutex_; + T data_; +}; + + +} // namespace sl + #endif // _INC_BASIC_THREADING_H diff --git a/src/common/common.h b/src/common/common.h index 3ae802333..098e25ae1 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -34,10 +34,6 @@ #include #include -#include "assertion.h" -#include "basic_threading.h" - - // On Windows, Clang with MSVC runtime defines _MSC_VER! (But also __clang__). #if !defined(_MSC_VER) || defined(__clang__) # define NON_MSVC_COMPILER 1 @@ -101,6 +97,9 @@ #define NOEXCEPT_NODEBUG noexcept #endif +#include "assertion.h" +#include "basic_threading.h" + // use to indicate that a function uses printf-style arguments, allowing GCC // to validate the format string and arguments: // a = 1-based index of format string diff --git a/src/common/resource/CResourceDef.cpp b/src/common/resource/CResourceDef.cpp index f14941743..26320cbaf 100644 --- a/src/common/resource/CResourceDef.cpp +++ b/src/common/resource/CResourceDef.cpp @@ -40,10 +40,12 @@ bool CResourceDef::SetResourceName( lpctstr pszName ) } } - const CVarDefCont * pExistingVarKey = g_Exp.m_VarResDefs.GetKey( pszName ); const dword dwResPrivateUID = GetResourceID().GetPrivateUID(); const int iResIndex = GetResourceID().GetResIndex(); - CVarDefContNum* pVarKeyNum = nullptr; + CVarDefContNum* pVarKeyNum = nullptr; + + auto gwriter = g_ExprGlobals.mtEngineLockedWriter(); + const CVarDefCont * pExistingVarKey = gwriter->m_VarResDefs.GetKey( pszName ); if ( pExistingVarKey ) { const dword dwKeyVal = (dword)pExistingVarKey->GetValNum(); @@ -62,11 +64,11 @@ bool CResourceDef::SetResourceName( lpctstr pszName ) g_Log.EventWarn("DEFNAME=%s: redefinition (0%x!=0%x)\n", pszName, iKeyIndex, iResIndex); - pVarKeyNum = g_Exp.m_VarResDefs.SetNum( pszName, dwResPrivateUID ); + pVarKeyNum = gwriter->m_VarResDefs.SetNum( pszName, dwResPrivateUID ); } else { - pVarKeyNum = g_Exp.m_VarResDefs.SetNumNew( pszName, dwResPrivateUID ); + pVarKeyNum = gwriter->m_VarResDefs.SetNumNew( pszName, dwResPrivateUID ); } if ( pVarKeyNum == nullptr ) @@ -136,7 +138,8 @@ bool CResourceDef::MakeResourceName() size_t uiVar = 1; size_t uiLen = strlen( pbuf ); - for ( const CVarDefCont *pVarDef : g_Exp.m_VarResDefs ) + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + for ( const CVarDefCont *pVarDef : gReader->m_VarResDefs ) { // Is this a similar key? ptcKey = pVarDef->GetKey(); diff --git a/src/common/resource/CResourceLock.cpp b/src/common/resource/CResourceLock.cpp index b99d1a8b8..16e4cdd29 100644 --- a/src/common/resource/CResourceLock.cpp +++ b/src/common/resource/CResourceLock.cpp @@ -33,7 +33,7 @@ bool CResourceLock::_Open(lpctstr ptcUnused, uint uiUnused) bool CResourceLock::Open(lpctstr ptcUnused, uint uiUnused) { ADDTOCALLSTACK("CResourceLock::Open"); - MT_UNIQUE_LOCK_RETURN(CResourceLock::_Open(ptcUnused, uiUnused)); + MT_UNIQUE_LOCK_RETURN(this, CResourceLock::_Open(ptcUnused, uiUnused)); } void CResourceLock::_Close() @@ -60,7 +60,7 @@ void CResourceLock::_Close() void CResourceLock::Close() { ADDTOCALLSTACK("CResourceLock::Close"); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); CResourceLock::_Close(); } @@ -96,7 +96,7 @@ bool CResourceLock::_ReadTextLine( bool fRemoveBlanks ) // Read a line from the bool CResourceLock::ReadTextLine( bool fRemoveBlanks ) // Read a line from the opened script file { //ADDTOCALLSTACK_DEBUG("CResourceLock::ReadTextLine"); - MT_UNIQUE_LOCK_RETURN(CResourceLock::_ReadTextLine(fRemoveBlanks)); + MT_UNIQUE_LOCK_RETURN(this, CResourceLock::_ReadTextLine(fRemoveBlanks)); } int CResourceLock::OpenLock( CResourceScript * pLock, CScriptLineContext context ) diff --git a/src/common/resource/CResourceScript.cpp b/src/common/resource/CResourceScript.cpp index 6069b3bff..88460eabc 100644 --- a/src/common/resource/CResourceScript.cpp +++ b/src/common/resource/CResourceScript.cpp @@ -44,7 +44,7 @@ bool CResourceScript::_CheckForChange() bool CResourceScript::CheckForChange() { ADDTOCALLSTACK("CResourceScript::CheckForChange"); - MT_UNIQUE_LOCK_RETURN(CResourceScript::_CheckForChange()); + MT_UNIQUE_LOCK_RETURN(this, CResourceScript::_CheckForChange()); } void CResourceScript::ReSync() diff --git a/src/common/resource/CValueDefs.cpp b/src/common/resource/CValueDefs.cpp index fe1def35a..e24ec51c6 100644 --- a/src/common/resource/CValueDefs.cpp +++ b/src/common/resource/CValueDefs.cpp @@ -31,7 +31,7 @@ bool CValueRangeDef::Load( tchar * pszDef ) // it can be a range (with format {lo# hi#}) or a single value, even without brackets, // so we don't need a warning if GetRangeVals doesn't find the brackets int64 piVal[2]; - int iQty = g_Exp.GetRangeVals( pszDef, piVal, ARRAY_COUNT(piVal), true); + int iQty = CExpression::GetExprParser().GetRangeVals( pszDef, piVal, ARRAY_COUNT(piVal), true); if ( iQty <= 0 ) return false; diff --git a/src/common/resource/sections/CDialogDef.cpp b/src/common/resource/sections/CDialogDef.cpp index e2900043c..5003b896b 100644 --- a/src/common/resource/sections/CDialogDef.cpp +++ b/src/common/resource/sections/CDialogDef.cpp @@ -553,6 +553,7 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp m_wPage = (word)(iPage); m_fNoDispose = false; + CExpression& expr_parser = CExpression::GetExprParser(); CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); pScriptArgs->Init(iPage, 0, 0, pObjSrc); //DEBUG_ERR(("pScriptArgs->m_s1_buf_vec %s pScriptArgs->m_s1 %s Arguments 0x%x\n",pScriptArgs->m_s1_buf_vec, pScriptArgs->m_s1, Arguments)); @@ -566,7 +567,8 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp { while ( s.ReadKey()) { - m_pObj->ParseScriptText( s.GetKeyBuffer(), CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, pClient->GetChar() ); + CScriptExprContext scpContext{._pScriptObjI = m_pObj}; + expr_parser.ParseScriptText( s.GetKeyBuffer(), scpContext, CScriptTriggerArgsPtr{}, pClient->GetChar() ); m_sText.emplace_back(false) = s.GetKey(); } } @@ -584,7 +586,8 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp // starting x,y location. int64 iSizes[2]; tchar * pszBuf = s.GetKeyBuffer(); - m_pObj->ParseScriptText( pszBuf, CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, pClient->GetChar() ); + CScriptExprContext scpContext{._pScriptObjI = m_pObj}; + expr_parser.ParseScriptText( pszBuf, scpContext, CScriptTriggerArgsPtr{}, pClient->GetChar() ); Str_ParseCmds( pszBuf, iSizes, ARRAY_COUNT(iSizes) ); m_x = (int)(iSizes[0]); diff --git a/src/common/resource/sections/CWebPageDef.cpp b/src/common/resource/sections/CWebPageDef.cpp index 31212a072..b31a90784 100644 --- a/src/common/resource/sections/CWebPageDef.cpp +++ b/src/common/resource/sections/CWebPageDef.cpp @@ -216,10 +216,13 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on if ( pszArgs[0] == '\0' ) pszArgs = "%NAME%%REGION.NAME%\n"; Str_CopyLimitNull( pszTmp2, pszArgs, Str_TempLength() ); - pChar->ParseScriptText( + + CScriptExprContext scpContext{._pScriptObjI = pChar}; + CExpression::GetExprParser().ParseScriptText( Str_MakeFiltered(pszTmp2), - CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + scpContext, CScriptTriggerArgsPtr{}, &g_Serv, 1 ); + pSrc->SysMessage( pszTmp2 ); } } @@ -231,7 +234,7 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on if ( !s.HasArgs() ) return false; - IT_TYPE needtype = ( iHeadKey == WV_GUILDLIST ) ? IT_STONE_GUILD : IT_STONE_TOWN; + const IT_TYPE needtype = ( iHeadKey == WV_GUILDLIST ) ? IT_STONE_GUILD : IT_STONE_TOWN; for ( size_t i = 0; i < g_World.m_Stones.size(); ++i ) { @@ -240,13 +243,15 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on continue; ++sm_iListIndex; - Str_CopyLimitNull(pszTmp2, s.GetArgStr(), Str_TempLength()); - pStone->ParseScriptText( + + CScriptExprContext scpContext{._pScriptObjI = pStone}; + CExpression::GetExprParser().ParseScriptText( Str_MakeFiltered(pszTmp2), - CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + scpContext, CScriptTriggerArgsPtr{}, &g_Serv, 1); - pSrc->SysMessage(pszTmp2); + + pSrc->SysMessage(pszTmp2); } } break; @@ -260,10 +265,13 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on CGMPage* pPage = sptrPage.get(); ++sm_iListIndex; Str_CopyLimitNull( pszTmp2, s.GetArgStr(), Str_TempLength()); - pPage->ParseScriptText( + + CScriptExprContext scpContext{._pScriptObjI = pPage}; + CExpression::GetExprParser().ParseScriptText( Str_MakeFiltered(pszTmp2), - CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + scpContext, CScriptTriggerArgsPtr{}, &g_Serv, 1 ); + pSrc->SysMessage( pszTmp2 ); } } @@ -321,8 +329,8 @@ bool CWebPageDef::WebPageUpdate( bool fNow, lpctstr pszDstName, CTextConsole * p return false; } + CExpression& expr_parser = CExpression::GetExprParser(); bool fScriptMode = false; - while ( FileRead.ReadTextLine( false )) { tchar *pszTmp = Str_GetTemp(); @@ -333,11 +341,14 @@ bool CWebPageDef::WebPageUpdate( bool fNow, lpctstr pszDstName, CTextConsole * p { // Deal with the stuff preceding the scripts. *pszHead = '\0'; - pszHead += 26; - ParseScriptText( pszTmp, - CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + pszHead += 26; + + CScriptExprContext scpContext{._pScriptObjI = this}; + expr_parser.ParseScriptText( + pszTmp, scpContext, CScriptTriggerArgsPtr{}, pSrc, 1 ); - FileOut.SysMessage( pszTmp ); + + FileOut.SysMessage( pszTmp ); fScriptMode = true; } else @@ -375,8 +386,9 @@ bool CWebPageDef::WebPageUpdate( bool fNow, lpctstr pszDstName, CTextConsole * p } // Look for stuff we can displace here. %STUFF% - ParseScriptText( pszHead, - CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, + CScriptExprContext scpContext{._pScriptObjI = this}; + expr_parser.ParseScriptText( pszHead, + scpContext, CScriptTriggerArgsPtr{}, pSrc, 1 ); FileOut.SysMessage( pszHead ); } diff --git a/src/common/sphere_library/CSFile.cpp b/src/common/sphere_library/CSFile.cpp index b652df0ec..3794f943a 100644 --- a/src/common/sphere_library/CSFile.cpp +++ b/src/common/sphere_library/CSFile.cpp @@ -79,7 +79,7 @@ void CSFile::Close() { ADDTOCALLSTACK("CSFile::Close"); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); CSFile::_Close(); } @@ -150,7 +150,7 @@ bool CSFile::_Open( lpctstr ptcFilename, uint uiModeFlags ) bool CSFile::Open( lpctstr ptcFilename, uint uiModeFlags ) { ADDTOCALLSTACK("CSFile::Open"); - MT_UNIQUE_LOCK_RETURN(CSFile::_Open(ptcFilename, uiModeFlags)); + MT_UNIQUE_LOCK_RETURN(this, CSFile::_Open(ptcFilename, uiModeFlags)); } bool CSFile::_IsFileOpen() const @@ -159,7 +159,7 @@ bool CSFile::_IsFileOpen() const } bool CSFile::IsFileOpen() const { - MT_SHARED_LOCK_RETURN(_fileDescriptor != _kInvalidFD); + MT_SHARED_LOCK_RETURN(this, _fileDescriptor != _kInvalidFD); } lpctstr CSFile::_GetFilePath() const @@ -168,7 +168,7 @@ lpctstr CSFile::_GetFilePath() const } lpctstr CSFile::GetFilePath() const { - MT_SHARED_LOCK_RETURN(_strFileName.GetBuffer()); + MT_SHARED_LOCK_RETURN(this, _strFileName.GetBuffer()); } bool CSFile::_SetFilePath( lpctstr pszName ) @@ -191,7 +191,7 @@ bool CSFile::_SetFilePath( lpctstr pszName ) bool CSFile::SetFilePath( lpctstr pszName ) { ADDTOCALLSTACK("CFile::SetFilePath"); - MT_UNIQUE_LOCK_RETURN(CSFile::_SetFilePath(pszName)); + MT_UNIQUE_LOCK_RETURN(this, CSFile::_SetFilePath(pszName)); } @@ -221,7 +221,7 @@ int CSFile::_GetLength() int CSFile::GetLength() { ADDTOCALLSTACK("CSFile::GetLength"); - MT_UNIQUE_LOCK_RETURN(CSFile::_GetLength()); + MT_UNIQUE_LOCK_RETURN(this, CSFile::_GetLength()); } int CSFile::_GetPosition() const @@ -253,13 +253,13 @@ int CSFile::_GetPosition() const int CSFile::GetPosition() const { ADDTOCALLSTACK("CSFile::GetPosition"); - MT_UNIQUE_LOCK_RETURN(CSFile::_GetPosition()); + MT_UNIQUE_LOCK_RETURN(this, CSFile::_GetPosition()); } int CSFile::Read( void * pData, int iLength ) const { ADDTOCALLSTACK("CSFile::Read"); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); #ifdef _WIN32 DWORD ret; @@ -309,7 +309,7 @@ int CSFile::_Seek( int iOffset, int iOrigin ) int CSFile::Seek( int iOffset, int iOrigin ) { ADDTOCALLSTACK("CSFile::Seek"); - MT_UNIQUE_LOCK_RETURN(CSFile::_Seek(iOffset, iOrigin)); + MT_UNIQUE_LOCK_RETURN(this, CSFile::_Seek(iOffset, iOrigin)); } void CSFile::_SeekToBegin() @@ -370,7 +370,7 @@ bool CSFile::_Write( const void * pData, int iLength ) bool CSFile::Write(const void* pData, int iLength) { ADDTOCALLSTACK("CSFile::Write"); - MT_UNIQUE_LOCK_RETURN(CSFile::_Write(pData, iLength)); + MT_UNIQUE_LOCK_RETURN(this, CSFile::_Write(pData, iLength)); } // CSFile:: File name operations. @@ -470,7 +470,7 @@ uint CSFile::_GetFullMode() const } uint CSFile::GetFullMode() const { - MT_SHARED_LOCK_RETURN(_uiMode); + MT_SHARED_LOCK_RETURN(this, _uiMode); } uint CSFile::_GetMode() const @@ -479,7 +479,7 @@ uint CSFile::_GetMode() const } uint CSFile::GetMode() const { - MT_SHARED_LOCK_RETURN(_uiMode & 0x0FFFFFFF); + MT_SHARED_LOCK_RETURN(this, _uiMode & 0x0FFFFFFF); } bool CSFile::_IsWriteMode() const @@ -488,7 +488,7 @@ bool CSFile::_IsWriteMode() const } bool CSFile::IsWriteMode() const { - MT_SHARED_LOCK_RETURN(_uiMode & OF_WRITE); + MT_SHARED_LOCK_RETURN(this, _uiMode & OF_WRITE); } diff --git a/src/common/sphere_library/CSFileText.cpp b/src/common/sphere_library/CSFileText.cpp index 87062c863..4ebc6e641 100644 --- a/src/common/sphere_library/CSFileText.cpp +++ b/src/common/sphere_library/CSFileText.cpp @@ -31,7 +31,7 @@ bool CSFileText::_IsFileOpen() const } bool CSFileText::IsFileOpen() const { - MT_SHARED_LOCK_RETURN(_pStream != nullptr); + MT_SHARED_LOCK_RETURN(this, _pStream != nullptr); } bool CSFileText::_Open(lpctstr ptcFilename, uint uiModeFlags) @@ -63,7 +63,7 @@ bool CSFileText::_Open(lpctstr ptcFilename, uint uiModeFlags) bool CSFileText::Open(lpctstr ptcFilename, uint uiModeFlags) { ADDTOCALLSTACK("CSFileText::Open"); - MT_UNIQUE_LOCK_RETURN(CSFileText::_Open(ptcFilename, uiModeFlags)); + MT_UNIQUE_LOCK_RETURN(this, CSFileText::_Open(ptcFilename, uiModeFlags)); } void CSFileText::_Close() @@ -86,7 +86,7 @@ void CSFileText::_Close() void CSFileText::Close() { ADDTOCALLSTACK("CSFileText::Close"); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); CSFileText::_Close(); } @@ -122,7 +122,7 @@ int CSFileText::Seek( int iOffset, int iOrigin ) // RETURN: // true = success ADDTOCALLSTACK("CSFileText::Seek"); - MT_UNIQUE_LOCK_RETURN(CSFileText::_Seek(iOffset, iOrigin)); + MT_UNIQUE_LOCK_RETURN(this, CSFileText::_Seek(iOffset, iOrigin)); } void CSFileText::_Flush() const @@ -138,7 +138,7 @@ void CSFileText::_Flush() const void CSFileText::Flush() const { ADDTOCALLSTACK("CSFileText::Flush"); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); CSFileText::_Flush(); } @@ -154,7 +154,7 @@ bool CSFileText::_IsEOF() const bool CSFileText::IsEOF() const { //ADDTOCALLSTACK("CSFileText::IsEOF"); - MT_SHARED_LOCK_RETURN(CSFileText::_IsEOF()); + MT_SHARED_LOCK_RETURN(this, CSFileText::_IsEOF()); } @@ -175,7 +175,7 @@ int CSFileText::Printf( lpctstr pFormat, ... ) ADDTOCALLSTACK("CSFileText::Printf"); ASSERT(pFormat); - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); va_list vargs; va_start( vargs, pFormat ); @@ -195,7 +195,7 @@ int CSFileText::Read( void * pBuffer, int sizemax ) const if ( IsEOF() ) return 0; // LINUX will ASSERT if we read past end. - MT_UNIQUE_LOCK_SET; + MT_UNIQUE_LOCK_SET(this); size_t ret = fread( pBuffer, 1, sizemax, _pStream ); if (ret > INT_MAX) { @@ -220,7 +220,7 @@ tchar * CSFileText::_ReadString( tchar * pBuffer, int sizemax ) tchar * CSFileText::ReadString( tchar * pBuffer, int sizemax ) { ADDTOCALLSTACK("CSFileText::ReadString"); - MT_UNIQUE_LOCK_RETURN(CSFileText::_ReadString(pBuffer, sizemax)); + MT_UNIQUE_LOCK_RETURN(this, CSFileText::_ReadString(pBuffer, sizemax)); } int CSFileText::_VPrintf( lpctstr pFormat, va_list args ) @@ -239,7 +239,7 @@ int CSFileText::VPrintf(lpctstr pFormat, va_list args) ADDTOCALLSTACK("CSFileText::VPrintf"); ASSERT(pFormat); - MT_UNIQUE_LOCK_RETURN(CSFileText::_VPrintf(pFormat, args)); + MT_UNIQUE_LOCK_RETURN(this, CSFileText::_VPrintf(pFormat, args)); } bool CSFileText::_Write( const void * pData, int iLen ) @@ -269,7 +269,7 @@ bool CSFileText::Write(const void* pData, int iLen) { // RETURN: 1 = success else fail. ADDTOCALLSTACK("CSFileText::Write"); - MT_UNIQUE_LOCK_RETURN(CSFileText::_Write(pData, iLen)); + MT_UNIQUE_LOCK_RETURN(this, CSFileText::_Write(pData, iLen)); } bool CSFileText::_WriteString( lpctstr pStr ) @@ -284,7 +284,7 @@ bool CSFileText::_WriteString( lpctstr pStr ) bool CSFileText::WriteString(lpctstr pStr) { ADDTOCALLSTACK("CSFileText::WriteString"); - MT_UNIQUE_LOCK_RETURN(CSFileText::_WriteString(pStr)); + MT_UNIQUE_LOCK_RETURN(this, CSFileText::_WriteString(pStr)); } // CSFileText:: Mode operations. diff --git a/src/common/sphere_library/sstringobjs.cpp b/src/common/sphere_library/sstringobjs.cpp index 1f31f68b2..d7714bbe0 100644 --- a/src/common/sphere_library/sstringobjs.cpp +++ b/src/common/sphere_library/sstringobjs.cpp @@ -54,6 +54,24 @@ tchar* Str_GetTemp() noexcept return AbstractSphereThread::Strings::allocateBuffer(); } +[[nodiscard]] +tchar* Str_CopyToTemp(lpctstr pSrc) noexcept +{ + lptstr pDest = Str_GetTemp(); + Str_CopyLimitNull(pDest, pSrc, Str_TempLength()); + return pDest; +} + +[[nodiscard]] +lpctstr Str_mtEngineGetSafeTemp(lpctstr pSrc) noexcept +{ +#if MT_ENGINES + return Str_CopyToTemp(pSrc); +#else + return pSrc; +#endif +} + /* * AbstractString diff --git a/src/common/sphere_library/sstringobjs.h b/src/common/sphere_library/sstringobjs.h index 86e591715..b14c8ad67 100644 --- a/src/common/sphere_library/sstringobjs.h +++ b/src/common/sphere_library/sstringobjs.h @@ -23,6 +23,13 @@ size_t Str_TempLength() noexcept [[nodiscard]] tchar* Str_GetTemp() noexcept; +[[nodiscard]] +tchar* Str_CopyToTemp(lpctstr pSrc) noexcept; + +[[nodiscard]] +lpctstr Str_mtEngineGetSafeTemp(lpctstr pSrc) noexcept; + + //-- // Base abstract class for strings, provides basic information of what should be available diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index 14b9dbd50..0e170d1fe 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -216,7 +216,7 @@ void CSector::_GoSleep() void CSector::GoSleep() { ADDTOCALLSTACK("CSector::GoSleep"); - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); CSector::_GoSleep(); } @@ -287,7 +287,7 @@ void CSector::_GoAwake() void CSector::GoAwake() { ADDTOCALLSTACK("CSector::GoAwake"); - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); CSector::_GoAwake(); } diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index ec87619f7..ed00675f7 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -1815,7 +1815,7 @@ bool CServer::r_Verb( CScript &s, CTextConsole * pSrc ) { ptcKey = s.GetArgStr(); SKIP_SEPARATORS(ptcKey); - g_Exp.m_VarGlobals.ClearKeys(ptcKey); + g_ExprGlobals.mtEngineLockedWriter()->m_VarGlobals.ClearKeys(ptcKey); return true; } } @@ -2231,15 +2231,15 @@ bool CServer::r_Verb( CScript &s, CTextConsole * pSrc ) case SV_VARLIST: if ( ! strcmpi( s.GetArgStr(), "log" ) ) pSrc = &g_Serv; - g_Exp.m_VarGlobals.DumpKeys(pSrc, "VAR."); + g_ExprGlobals.mtEngineLockedReader()->m_VarGlobals.DumpKeys(pSrc, "VAR."); break; case SV_PRINTLISTS: if ( ! strcmpi( s.GetArgStr(), "log" ) ) pSrc = &g_Serv; - g_Exp.m_ListGlobals.DumpKeys(pSrc, "LIST."); + g_ExprGlobals.mtEngineLockedReader()->m_ListGlobals.DumpKeys(pSrc, "LIST."); break; case SV_CLEARLISTS: - g_Exp.m_ListGlobals.ClearKeys( s.GetArgStr()) ; + g_ExprGlobals.mtEngineLockedWriter()->m_ListGlobals.ClearKeys( s.GetArgStr()) ; break; default: return CScriptObj::r_Verb(s, pSrc); @@ -2385,9 +2385,10 @@ bool CServer::CommandLinePostLoad( int argc, tchar * argv[] ) return false; } - ssize_t count = ssize_t(g_Exp.m_VarDefs.GetCount()); + auto r = g_ExprGlobals.mtEngineLockedReader(); + ssize_t count = ssize_t(r->m_VarDefs.GetCount()); ssize_t i = 0; - for ( const CVarDefCont * pCont : g_Exp.m_VarDefs ) + for ( const CVarDefCont * pCont : r->m_VarDefs ) { if ( ( i % 0x1ff ) == 0 ) PrintPercent( i, count ); @@ -2404,9 +2405,9 @@ bool CServer::CommandLinePostLoad( int argc, tchar * argv[] ) return false; } - count = ssize_t(g_Exp.m_VarResDefs.GetCount()); + count = ssize_t(r->m_VarResDefs.GetCount()); i = 0; - for ( const CVarDefCont * pCont : g_Exp.m_VarResDefs ) + for ( const CVarDefCont * pCont : r->m_VarResDefs ) { if ( ( i % 0x1ff ) == 0 ) PrintPercent( i, count ); diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 5d7ffda4f..9acd51753 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -3061,7 +3061,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) if (( restype == RES_WORLDSCRIPT ) || ( restype == RES_WS )) { const lpctstr pszDef = pScript->GetArgStr(); - CVarDefCont * pVarBase = g_Exp.m_VarResDefs.GetKey( pszDef ); + CVarDefCont * pVarBase = g_ExprGlobals.mtEngineLockedReader()->m_VarResDefs.GetKey( pszDef ); pVarNum = nullptr; if ( pVarBase ) pVarNum = dynamic_cast ( pVarBase ); @@ -3121,9 +3121,10 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) if ( m_ResourceList.ContainsKey( const_cast(pszSection) )) { // Add to DEFLIST - CListDefCont* pListBase = g_Exp.m_ListInternals.GetKey(pszSection); + auto rw = g_ExprGlobals.mtEngineLockedWriter(); + CListDefCont* pListBase = rw->m_ListInternals.GetKey(pszSection); if ( !pListBase ) - pListBase = g_Exp.m_ListInternals.AddList(pszSection); + pListBase = rw->m_ListInternals.AddList(pszSection); if ( pListBase ) pListBase->r_LoadVal(pScript->GetArgStr()); @@ -3183,12 +3184,13 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) if ( fNewStyleDef ) { // search for this. + auto gread = g_ExprGlobals.mtEngineLockedReader(); size_t l; for ( l = 0; l < DEFMSG_QTY; ++l ) { - if ( !strcmpi(ptcKey, g_Exp.sm_szMsgNames[l]) ) + if ( !strcmpi(ptcKey, gread->sm_szDefMsgNames[l]) ) { - Str_CopyLimitNull(g_Exp.sm_szMessages[l], pScript->GetArgStr(), sizeof(g_Exp.sm_szMessages[l])); + Str_CopyLimitNull(gread->sm_szDefMessages[l], pScript->GetArgStr(), sizeof(CExprGlobals::sm_szDefMessages[l])); break; } } @@ -3198,20 +3200,23 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) } else { - g_Exp.m_VarDefs.SetStr(ptcKey, false, pScript->GetArgStr(), false); + g_ExprGlobals.mtEngineLockedWriter()->m_VarDefs.SetStr(ptcKey, false, pScript->GetArgStr(), false); } } + return true; case RES_RESDEFNAME: + { // just get a block of resource aliases (like a classic DEF). + auto gwrite = g_ExprGlobals.mtEngineLockedWriter(); while (pScript->ReadKeyParse()) { const lpctstr ptcKey = pScript->GetKey(); - g_Exp.m_VarResDefs.SetStr(ptcKey, false, pScript->GetArgStr(), false); + gwrite->m_VarResDefs.SetStr(ptcKey, false, pScript->GetArgStr(), false); } return true; - + } case RES_RESOURCELIST: { while ( pScript->ReadKey() ) @@ -3857,12 +3862,12 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) ptcKey = ptcKey + 4; lpctstr ptcArg = pScript->GetArgStr( &fQuoted ); - g_Exp.m_VarGlobals.SetStr( ptcKey, fQuoted, ptcArg ); + g_ExprGlobals.mtEngineLockedWriter()->m_VarGlobals.SetStr( ptcKey, fQuoted, ptcArg ); } return true; case RES_WORLDLISTS: { - CListDefCont* pListBase = g_Exp.m_ListGlobals.AddList(pScript->GetArgStr()); + CListDefCont* pListBase = g_ExprGlobals.mtEngineLockedWriter()->m_ListGlobals.AddList(pScript->GetArgStr()); if ( !pListBase ) { @@ -4170,7 +4175,7 @@ CResourceID CServerConfig::ResourceGetNewID( RES_TYPE restype, lpctstr pszName, } - CVarDefCont * pVarBase = g_Exp.m_VarResDefs.GetKey( pszName ); + CVarDefCont * pVarBase = g_ExprGlobals.mtEngineLockedReader()->m_VarResDefs.GetKey( pszName ); if ( pVarBase ) { // An existing VarDef with the same name ? @@ -4340,7 +4345,7 @@ CResourceID CServerConfig::ResourceGetNewID( RES_TYPE restype, lpctstr pszName, int iRandIndex = iIndex + g_Rand.GetVal(iHashRange); rid = CResourceID(restype, iRandIndex, wPage); - const bool fCheckPage = (pszName && (g_Exp.m_VarResDefs.GetKeyNum(pszName) != 0)); + const bool fCheckPage = (pszName && (g_ExprGlobals.mtEngineLockedReader()->m_VarResDefs.GetKeyNum(pszName) != 0)); while (true) { if (fCheckPage) @@ -4365,7 +4370,7 @@ CResourceID CServerConfig::ResourceGetNewID( RES_TYPE restype, lpctstr pszName, if ( pszName ) { - CVarDefContNum* pVarTemp = g_Exp.m_VarResDefs.SetNum( pszName, rid.GetPrivateUID() ); + CVarDefContNum* pVarTemp = g_ExprGlobals.mtEngineLockedWriter()->m_VarResDefs.SetNum( pszName, rid.GetPrivateUID() ); ASSERT(pVarTemp); *ppVarNum = pVarTemp; } @@ -4859,8 +4864,11 @@ bool CServerConfig::Load( bool fResync ) g_Serv.PrintPercent( (size_t)(j + 1), count); } + g_ExprGlobals.mtEngineLockedWriter()->UpdateDefMsgDependentData(); + // Now that we have parsed every script, we can end the configuration of some resources... - // ROOMs have to inherit stuff from the parent AREADEF + + // ROOMs have to inherit stuff from the parent AREADEF for (CRegion* pCurRegion : m_RegionDefs) { const RES_TYPE resType = pCurRegion->GetResourceID().GetResType(); @@ -5002,26 +5010,47 @@ bool CServerConfig::Load( bool fResync ) lpctstr CServerConfig::GetDefaultMsg(int lKeyNum) { - ADDTOCALLSTACK("CServerConfig::GetDefaultMsg"); + ADDTOCALLSTACK("CServerConfig::GetDefaultMsg(int)"); if (( lKeyNum < 0 ) || ( lKeyNum >= DEFMSG_QTY )) { g_Log.EventError("Defmessage %d out of range [0..%d]\n", lKeyNum, DEFMSG_QTY-1); return ""; } - return g_Exp.sm_szMessages[lKeyNum]; + +#if MT_ENGINES + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + lpctstr ptcRet = Str_mtEngineGetSafeTemp(CExprGlobals::sm_szDefMessages[lKeyNum]); + return ptcRet; +#else + return Str_mtEngineGetSafeTemp(CExprGlobals::sm_szDefMessages[lKeyNum]); +#endif } lpctstr CServerConfig::GetDefaultMsg(lpctstr ptcKey) { - ADDTOCALLSTACK("CServerConfig::GetDefaultMsg"); - for (int i = 0; i < DEFMSG_QTY; ++i ) - { - if ( !strcmpi(ptcKey, g_Exp.sm_szMsgNames[i]) ) - return g_Exp.sm_szMessages[i]; + ADDTOCALLSTACK("CServerConfig::GetDefaultMsg(lpctstr)"); + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + uint i; + for (i = 0; i < DEFMSG_QTY; ++i ) + { + if ( !strcmpi(ptcKey, gReader->sm_szDefMsgNames[i]) ) + break; } - g_Log.EventError("Defmessage \"%s\" non existent\n", ptcKey); - return ""; + + if (i == DEFMSG_QTY) + { + g_Log.EventError("Defmessage \"%s\" non existent\n", ptcKey); + return ""; + } + +#if MT_ENGINES + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + lpctstr ptcRet = Str_mtEngineGetSafeTemp(CExprGlobals::sm_szDefMessages[i]); + return ptcRet; +#else + return Str_mtEngineGetSafeTemp(CExprGlobals::sm_szDefMessages[i]); +#endif } bool CServerConfig::GenerateDefname(tchar *pObjectName, size_t iInputLength, lpctstr pPrefix, TemporaryString *pOutput, bool bCheckConflict, CVarDefMap* vDefnames) @@ -5078,10 +5107,11 @@ bool CServerConfig::GenerateDefname(tchar *pObjectName, size_t iInputLength, lpc size_t iEnd = iOut; int iAttempts = 1; + auto gReader = g_ExprGlobals.mtEngineLockedReader(); for (;;) { bool isValid = true; - if (g_Exp.m_VarResDefs.GetKey(buf) != nullptr) + if (gReader->m_VarResDefs.GetKey(buf) != nullptr) { // check loaded defnames isValid = false; diff --git a/src/game/CTimedFunction.cpp b/src/game/CTimedFunction.cpp index b81415b2e..cb89e434a 100644 --- a/src/game/CTimedFunction.cpp +++ b/src/game/CTimedFunction.cpp @@ -85,12 +85,12 @@ bool CTimedFunction::OnTick() // virtual CUID uid; CScript s; { - MT_ENGINE_SHARED_LOCK_SET; + MT_ENGINE_SHARED_LOCK_SET(this); uid.SetPrivateUID(_uidAttached); s.ParseKey(_ptcCommand); } - delete this; // This has to be the last function call to ever access this object! + delete this; // This has to be the last function/statement call to ever access this object! // From now on, this object does NOT exist anymore! return _ExecTimedFunction(std::move(uid), std::move(s)); diff --git a/src/game/CTimedObject.cpp b/src/game/CTimedObject.cpp index 4cbd77785..14c830925 100644 --- a/src/game/CTimedObject.cpp +++ b/src/game/CTimedObject.cpp @@ -92,7 +92,7 @@ void CTimedObject::_SetTimeout(int64 iDelayInMsecs) void CTimedObject::SetTimeout(int64 iDelayInMsecs) { ADDTOCALLSTACK_DEBUG("CTimedObject::SetTimeout"); - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); _SetTimeout(iDelayInMsecs); } @@ -175,13 +175,13 @@ int64 CTimedObject::GetTimerSAdjusted() const noexcept void CTimedObject::GoSleep() { - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); _GoSleep(); } void CTimedObject::GoAwake() { - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); _GoAwake(); // Call virtuals! } @@ -192,7 +192,7 @@ PROFILE_TYPE CTimedObject::GetProfileType() const noexcept void CTimedObject::ClearTimeoutRaw() noexcept { - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); CTimedObject::_ClearTimeoutRaw(); } diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index 194279234..ae08f2862 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -846,9 +846,11 @@ bool CWorld::SaveStage() // Save world state in stages. _Ticker._TimedFunctions.r_Write(m_FileData); m_FileData.WriteSection("GLOBALS"); - g_Exp.m_VarGlobals.r_WritePrefix(m_FileData, nullptr); - - g_Exp.m_ListGlobals.r_WriteSave(m_FileData); + { + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + gReader->m_VarGlobals.r_WritePrefix(m_FileData, nullptr); + gReader->m_ListGlobals.r_WriteSave(m_FileData); + } const size_t iQty = g_Cfg.m_RegionDefs.size(); for ( size_t i = 0; i < iQty; ++i ) diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index 4f3242532..ca485d6a8 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -809,7 +809,7 @@ bool CChar::_IsStatFlag(uint64 uiStatFlag) const noexcept #if MT_ENGINES bool CChar::IsStatFlag( uint64 uiStatFlag) const noexcept { - MT_ENGINE_SHARED_LOCK_SET; + MT_ENGINE_SHARED_LOCK_SET(this); return (_uiStatFlag & uiStatFlag); } #endif @@ -822,7 +822,7 @@ void CChar::_StatFlag_Set( uint64 uiStatFlag) noexcept */ void CChar::StatFlag_Set(uint64 uiStatFlag) noexcept { - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); _uiStatFlag |= uiStatFlag; } @@ -834,7 +834,7 @@ void CChar::_StatFlag_Clear(uint64 uiStatFlag) noexcept */ void CChar::StatFlag_Clear(uint64 uiStatFlag) noexcept { - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); _uiStatFlag &= ~uiStatFlag; } @@ -849,7 +849,7 @@ void CChar::_StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept */ void CChar::StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept { -// MT_ENGINE_UNIQUE_LOCK_SET; +// MT_ENGINE_UNIQUE_LOCK_SET(this); // _StatFlag_Mod(uiStatFlag, fMod); if (fMod) _uiStatFlag |= uiStatFlag; @@ -897,7 +897,7 @@ void CChar::SetVisualRange(byte newSight) { CClient* pClient; { - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); // max value is 18 on classic clients prior 7.0.55.27 version and 24 on enhanced clients and latest classic clients m_iVisualRange = minimum(newSight, g_Cfg.m_iMapViewSizeMax); pClient = GetClientActive(); @@ -1527,16 +1527,16 @@ height_t CChar::GetHeight() const return tmpHeight; // This is SLOW (since this method is called very frequently)! Move those defs value to CharDef! + auto gReader = g_ExprGlobals.mtEngineLockedReader(); const uint uiDispID = (uint)pCharDef->GetDispID(); - char heightDef[20]{"height_"}; Str_FromUI(uint(uiDispID), heightDef + 7, sizeof(heightDef) - 7, 16); - tmpHeight = (height_t)(g_Exp.m_VarDefs.GetKeyNum(heightDef)); + tmpHeight = (height_t)(gReader->m_VarDefs.GetKeyNum(heightDef)); if ( tmpHeight ) //set by a defname ([DEFNAME charheight] height_0a) return tmpHeight; Str_FromUI(uint(uiDispID), heightDef + 7, sizeof(heightDef) - 7, 10); - tmpHeight = (height_t)(g_Exp.m_VarDefs.GetKeyNum(heightDef)); + tmpHeight = (height_t)(gReader->m_VarDefs.GetKeyNum(heightDef)); if ( tmpHeight ) //set by a defname ([DEFNAME charheight] height_10) return tmpHeight; diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index ecc8637b9..8bec31f76 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -927,8 +927,8 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; SOUND_TYPE Skill_GetSound( SKILL_TYPE skill); int Skill_Stage( SKTRIG_TYPE stage ); - TRIGRET_TYPE Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr pArgs); //pArgs.m_iN1 will be rewritten with skill - TRIGRET_TYPE Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr pArgs); //pArgs.m_iN1 will be rewritten with skill + TRIGRET_TYPE Skill_OnTrigger(SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr pScriptArgs); //pScriptArgs.m_iN1 will be rewritten with skill + TRIGRET_TYPE Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr pScriptArgs); //pArgs.m_iN1 will be rewritten with skill bool Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ); bool Skill_Tracking( CUID uidTarg, DIR_TYPE & dirPrv, int iDistMax = INT16_MAX ); diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index ef361f0e5..7ed21e89c 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -3925,7 +3925,7 @@ ITEMID_TYPE CChar::Horse_GetMountItemID() const { tchar* ptcMountID = Str_GetTemp(); snprintf(ptcMountID, Str_TempLength(), "mount_0x%x", GetDispID()); - lpctstr ptcMemoryID = g_Exp.m_VarDefs.GetKeyStr(ptcMountID); // get the mount item defname from the mount_0x** defname + lpctstr ptcMemoryID = g_ExprGlobals.mtEngineLockedReader()->m_VarDefs.GetKeyStr(ptcMountID); // get the mount item defname from the mount_0x** defname CResourceID memoryRid = g_Cfg.ResourceGetID(RES_ITEMDEF, ptcMemoryID); return (ITEMID_TYPE)(memoryRid.GetResIndex()); // get the ID of the memory (mount item) diff --git a/src/game/chars/CCharAttacker.cpp b/src/game/chars/CCharAttacker.cpp index 37846249b..ca1d48ec5 100644 --- a/src/game/chars/CCharAttacker.cpp +++ b/src/game/chars/CCharAttacker.cpp @@ -67,7 +67,7 @@ bool CChar::Attacker_Add(CChar * pChar, int iThreat) if (m_EmoteHueOverride != 0) //Set EMOTECOLOROVERRIDE to ATTACKERS emoteHue = m_EmoteHueOverride; else - emoteHue = (HUE_TYPE)(g_Exp.m_VarDefs.GetKeyNum("EMOTE_DEF_COLOR")); + emoteHue = (HUE_TYPE)(g_ExprGlobals.mtEngineLockedReader()->m_VarDefs.GetKeyNum("EMOTE_DEF_COLOR")); snprintf(z, Str_TempLength(), g_Cfg.GetDefaultMsg(DEFMSG_COMBAT_ATTACKO), GetName(), pChar->GetName()); UpdateObjMessage(z, nullptr, pClient, emoteHue, TALKMODE_EMOTE); } diff --git a/src/game/chars/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index bff953940..2bda0ce4e 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -3848,18 +3848,21 @@ void CChar::Skill_Fail( bool fCancel ) } -TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr pArgs ) +TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr pScriptArgs ) { ADDTOCALLSTACK("CChar::Skill_OnTrigger"); if ( !IsSkillBase(skill) ) return TRIGRET_RET_DEFAULT; + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + if ( !(stage == SKTRIG_SELECT || stage == SKTRIG_GAIN || stage == SKTRIG_USEQUICK || stage == SKTRIG_WAIT || stage == SKTRIG_TARGETCANCEL) ) m_Act_SkillCurrent = skill; - pArgs->m_iN1 = skill; + pScriptArgs->m_iN1 = skill; if ( g_Cfg.IsSkillFlag(skill, SKF_MAGIC) ) - pArgs->m_VarsLocal.SetNum("spell", m_atMagery.m_iSpell, true, false); + pScriptArgs->m_VarsLocal.SetNum("spell", m_atMagery.m_iSpell, true, false); TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; @@ -3869,26 +3872,29 @@ TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScri // RES_SKILL CResourceLock s; if ( pSkillDef->ResourceLock(s) ) - iRet = CScriptObj::OnTriggerScript(s, CSkillDef::sm_szTrigName[stage], pArgs, this); + iRet = CScriptObj::OnTriggerScript(s, CSkillDef::sm_szTrigName[stage], pScriptArgs, this); } return iRet; } -TRIGRET_TYPE CChar::Skill_OnCharTrigger(SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr pArgs ) +TRIGRET_TYPE CChar::Skill_OnCharTrigger(SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr pScriptArgs ) { ADDTOCALLSTACK("CChar::Skill_OnCharTrigger"); if ( !IsSkillBase(skill) ) return TRIGRET_RET_DEFAULT; + if (!pScriptArgs) + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + if ( !(ctrig == CTRIG_SkillSelect || ctrig == CTRIG_SkillGain || ctrig == CTRIG_SkillUseQuick || ctrig == CTRIG_SkillWait || ctrig == CTRIG_SkillTargetCancel) ) m_Act_SkillCurrent = skill; - pArgs->m_iN1 = skill; + pScriptArgs->m_iN1 = skill; if ( g_Cfg.IsSkillFlag(skill, SKF_MAGIC) ) - pArgs->m_VarsLocal.SetNum("spell", m_atMagery.m_iSpell, true, false); + pScriptArgs->m_VarsLocal.SetNum("spell", m_atMagery.m_iSpell, true, false); - return OnTrigger(ctrig, pArgs, this); + return OnTrigger(ctrig, pScriptArgs, this); } diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index 2839c5786..0277ab0e6 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -963,74 +963,25 @@ lpctstr CChar::GetTradeTitle() const // Paperdoll title for character p (2) { if ( !IsIndividualName() ) return ""; // same as type anyhow. - lpctstr ptcArticle = pCharDef->IsFemale() ? g_Cfg.GetDefaultMsg(DEFMSG_TRADETITLE_ARTICLE_FEMALE) : g_Cfg.GetDefaultMsg(DEFMSG_TRADETITLE_ARTICLE_MALE); - snprintf(pTemp, Str_TempLength(), "%s %s", ptcArticle, pCharDef->GetTradeName()); - return pTemp; - } + + //auto gReader = g_ExprGlobals.mtEngineLockedReader(); + lpctstr ptcArticle = g_Cfg.GetDefaultMsg( + pCharDef->IsFemale() + ? DEFMSG_TRADETITLE_ARTICLE_FEMALE + : DEFMSG_TRADETITLE_ARTICLE_MALE); + snprintf(pTemp, Str_TempLength(), "%s %s", ptcArticle, pCharDef->GetTradeName()); + return pTemp; + } // Only players can have skill titles if ( !m_pPlayer ) return pTemp; int len; - SKILL_TYPE skill = Skill_GetBest(); - if ( skill == SKILL_NINJITSU ) - { - static const CValStr sm_SkillTitles[] = - { - { "", INT32_MIN }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_NEOPHYTE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_NEOPHYTE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_NOVICE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_NOVICE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_APPRENTICE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_APPRENTICE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_JOURNEYMAN), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_JOURNEYMAN")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_EXPERT), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_EXPERT")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_ADEPT), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_ADEPT")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_MASTER), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_MASTER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_GRANDMASTER), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_GRANDMASTER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_ELDER_NINJITSU), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_ELDER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_LEGENDARY_NINJITSU), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_LEGENDARY")) }, - { nullptr, INT32_MAX } - }; - len = snprintf(pTemp, Str_TempLength(), "%s ", sm_SkillTitles->FindName(Skill_GetBase(skill))); - } - else if ( skill == SKILL_BUSHIDO ) - { - static const CValStr sm_SkillTitles[] = - { - { "", INT32_MIN }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_NEOPHYTE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_NEOPHYTE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_NOVICE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_NOVICE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_APPRENTICE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_APPRENTICE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_JOURNEYMAN), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_JOURNEYMAN")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_EXPERT), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_EXPERT")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_ADEPT), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_ADEPT")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_MASTER), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_MASTER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_GRANDMASTER), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_GRANDMASTER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_ELDER_BUSHIDO), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_ELDER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_LEGENDARY_BUSHIDO), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_LEGENDARY")) }, - { nullptr, INT32_MAX } - }; - len = snprintf(pTemp, Str_TempLength(), "%s ", sm_SkillTitles->FindName(Skill_GetBase(skill))); - } - else - { - static const CValStr sm_SkillTitles[] = - { - { "", INT32_MIN }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_NEOPHYTE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_NEOPHYTE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_NOVICE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_NOVICE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_APPRENTICE), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_APPRENTICE")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_JOURNEYMAN), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_JOURNEYMAN")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_EXPERT), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_EXPERT")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_ADEPT), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_ADEPT")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_MASTER), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_MASTER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_GRANDMASTER),(int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_GRANDMASTER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_ELDER), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_ELDER")) }, - { g_Cfg.GetDefaultMsg(DEFMSG_SKILLTITLE_LEGENDARY), (int)(g_Exp.m_VarDefs.GetKeyNum("SKILLTITLE_LEGENDARY")) }, - { nullptr, INT32_MAX } - }; - len = snprintf(pTemp, Str_TempLength(), "%s ", sm_SkillTitles->FindName(Skill_GetBase(skill))); - } + const SKILL_TYPE skill = Skill_GetBest(); + const uint uiSkVal = Skill_GetBase(skill); + len = snprintf(pTemp, Str_TempLength(), + "%s ", g_ExprGlobals.mtEngineLockedReader()->SkillTitle(skill, uiSkVal)); snprintf(pTemp + len, Str_TempLength() - len, "%s", g_Cfg.GetSkillDef(skill)->m_sTitle.GetBuffer()); return pTemp; diff --git a/src/game/chars/CStoneMember.cpp b/src/game/chars/CStoneMember.cpp index 0407dd224..14be7e31e 100644 --- a/src/game/chars/CStoneMember.cpp +++ b/src/game/chars/CStoneMember.cpp @@ -441,12 +441,12 @@ lpctstr CStoneMember::GetPrivName() const TemporaryString tsDefname; snprintf(tsDefname.buffer(), tsDefname.capacity(), "STONECONFIG_PRIVNAME_PRIVID-%d", (int)iPriv); - CVarDefCont * pResult = g_Exp.m_VarDefs.GetKey(tsDefname); + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + CVarDefCont * pResult = gReader->m_VarDefs.GetKey(tsDefname); if (pResult) return pResult->GetValStr(); - else - pResult = g_Exp.m_VarDefs.GetKey("STONECONFIG_PRIVNAME_PRIVUNK"); + pResult = gReader->m_VarDefs.GetKey("STONECONFIG_PRIVNAME_PRIVUNK"); return ( pResult == nullptr ) ? "" : pResult->GetValStr(); } diff --git a/src/game/clients/CClientDialog.cpp b/src/game/clients/CClientDialog.cpp index fae8d9424..839ab8240 100644 --- a/src/game/clients/CClientDialog.cpp +++ b/src/game/clients/CClientDialog.cpp @@ -321,10 +321,11 @@ bool CMenuItem::ParseLine( tchar * pszArgs, CScriptObj * pObjBase, CTextConsole m_id = 0; } - if ( pObjBase != nullptr ) - pObjBase->ParseScriptText( pszArgs, CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, pSrc ); - else - g_Serv.ParseScriptText( pszArgs, CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, pSrc ); + + CScriptExprContext scpContext{ + ._pScriptObjI = pObjBase ? pObjBase : &g_Serv + }; + CExpression::GetExprParser().ParseScriptText( pszArgs, scpContext, CScriptTriggerArgsPtr{}, pSrc ); // Parsing @color if ( *pszArgs == '@' ) @@ -379,7 +380,8 @@ void CClient::Menu_Setup( CResourceID rid, CObjBase * pObj ) DEBUG_ERR(("Error getting the menu title.\n")); return; } - pObj->ParseScriptText( s.GetKeyBuffer(), CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, m_pChar ); + CScriptExprContext scpContext{._pScriptObjI = pObj}; + CExpression::GetExprParser().ParseScriptText( s.GetKeyBuffer(), scpContext, CScriptTriggerArgsPtr{}, m_pChar ); CMenuItem item[MAX_MENU_ITEMS]; item[0].m_sText = s.GetKey(); diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index 32d9938fb..1fe13af97 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -1863,8 +1863,13 @@ void CClient::Event_Talk_Common(lpctstr pszText) // PC speech return; // Guards are special - lpctstr pszMsgGuards = g_Exp.m_VarDefs.GetKeyStr("guardcall"); - if ( !strnicmp(pszMsgGuards, "", 0) ) + lpctstr pszMsgGuards; + { + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + pszMsgGuards = Str_mtEngineGetSafeTemp(gReader->m_VarDefs.GetKeyStr("guardcall")); + } + + if ( !strnicmp(pszMsgGuards, "", 0) ) pszMsgGuards = "GUARD,GUARDS"; if ( FindStrWord(pszText, pszMsgGuards) > 0 ) m_pChar->CallGuards(); diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index 2d172f94d..e014be931 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -711,7 +711,7 @@ void CClient::addBarkLocalizedEx( int iClilocId, const CObjBaseTemplate * pSrc, new PacketMessageLocalisedEx(this, iClilocId, pSrc, wHue, mode, font, affix, pAffix, pArgs); } -void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_TYPE wHue, TALKMODE_TYPE mode, FONT_TYPE font, bool fUnicode, lpctstr name) const +void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_TYPE wHue, TALKMODE_TYPE mode, FONT_TYPE font, bool fUnicode, lpctstr ptcName) const { ADDTOCALLSTACK("CClient::addBarkParse"); if ( !pszText ) @@ -720,62 +720,76 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ HUE_TYPE defaultHue = HUE_TEXT_DEF; FONT_TYPE defaultFont = FONT_NORMAL; bool defaultUnicode = false; + bool fUseSpeechHueOverride = false; bool fUseEmoteHueOverride = false; - const CChar * pSrcChar = nullptr; + + const CChar * pSrcChar = nullptr; if (pSrc && pSrc->IsChar()) pSrcChar = static_cast(pSrc); - switch ( mode ) + static constexpr lpctstr talkmode_defs_color[] = + { + "SMSG_DEF_COLOR", + "EMOTE_DEF_COLOR", + "SAY_DEF_COLOR", + "CMSG_DEF_COLOR", + "IMSG_DEF_COLOR", + }; + static constexpr lpctstr talkmode_defs_font[] = + { + "SMSG_DEF_FONT", + "EMOTE_DEF_FONT", + "SAY_DEF_FONT", + "CMSG_DEF_FONT", + "IMSG_DEF_FONT", + }; + static constexpr lpctstr talkmode_defs_unicode[] = + { + "SMSG_DEF_UNICODE", + "EMOTE_DEF_UNICODE", + "SAY_DEF_UNICODE", + "CMSG_DEF_UNICODE", + "IMSG_DEF_UNICODE", + }; + + std::optional iTalkmodeHue, iTalkmodeFont, iTalkmodeUnicode; + switch ( mode ) { case TALKMODE_SYSTEM: - { talkmode_system: - defaultHue = (HUE_TYPE)(g_Exp.m_VarDefs.GetKeyNum("SMSG_DEF_COLOR")); - defaultFont = (FONT_TYPE)(g_Exp.m_VarDefs.GetKeyNum("SMSG_DEF_FONT")); - defaultUnicode = g_Exp.m_VarDefs.GetKeyNum("SMSG_DEF_UNICODE") > 0 ? true : false; + iTalkmodeHue = iTalkmodeFont = iTalkmodeUnicode = 0; break; - } case TALKMODE_EMOTE: - { fUseEmoteHueOverride = true; - defaultHue = (HUE_TYPE)(g_Exp.m_VarDefs.GetKeyNum("EMOTE_DEF_COLOR")); - defaultFont = (FONT_TYPE)(g_Exp.m_VarDefs.GetKeyNum("EMOTE_DEF_FONT")); - defaultUnicode = g_Exp.m_VarDefs.GetKeyNum("EMOTE_DEF_UNICODE") > 0 ? true : false; + iTalkmodeHue = iTalkmodeFont = iTalkmodeUnicode = 1; break; - } case TALKMODE_SAY: - { if (pSrc == nullptr) - { - // It's a SYSMESSAGE - goto talkmode_system; - } + goto talkmode_system; // It's a SYSMESSAGE fUseSpeechHueOverride = true; - defaultHue = (HUE_TYPE)(g_Exp.m_VarDefs.GetKeyNum("SAY_DEF_COLOR")); - defaultFont = (FONT_TYPE)(g_Exp.m_VarDefs.GetKeyNum("SAY_DEF_FONT")); - defaultUnicode = g_Exp.m_VarDefs.GetKeyNum("SAY_DEF_UNICODE") > 0 ? true : false; + iTalkmodeHue = iTalkmodeFont = iTalkmodeUnicode = 2; break; - } case TALKMODE_ITEM: - { if ( pSrc && pSrc->IsChar() ) - { - defaultFont = (FONT_TYPE)(g_Exp.m_VarDefs.GetKeyNum("CMSG_DEF_FONT")); - defaultUnicode = g_Exp.m_VarDefs.GetKeyNum("CMSG_DEF_UNICODE") > 0 ? true : false; - } + iTalkmodeHue = iTalkmodeUnicode = 3; else - { - defaultHue = (HUE_TYPE)(g_Exp.m_VarDefs.GetKeyNum("IMSG_DEF_COLOR")); - defaultFont = (FONT_TYPE)(g_Exp.m_VarDefs.GetKeyNum("IMSG_DEF_FONT")); - defaultUnicode = g_Exp.m_VarDefs.GetKeyNum("IMSG_DEF_UNICODE") > 0 ? true : false; - } - } + iTalkmodeHue = iTalkmodeFont = iTalkmodeUnicode = 4; break; default: break; } + { + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + if (iTalkmodeHue) + defaultHue = (HUE_TYPE)(gReader->m_VarDefs.GetKeyNum(talkmode_defs_color[*iTalkmodeHue])); + if (iTalkmodeFont) + defaultFont = (FONT_TYPE)(gReader->m_VarDefs.GetKeyNum(talkmode_defs_font[*iTalkmodeFont])); + if (iTalkmodeUnicode) + defaultUnicode = gReader->m_VarDefs.GetKeyNum(talkmode_defs_unicode[*iTalkmodeUnicode]) > 0 ? true : false; + } + word Args[] = { (word)wHue, (word)font, (word)fUnicode }; lptstr ptcBarkBuffer = Str_GetTemp(); // Be sure to init this before the goto instruction @@ -845,7 +859,7 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ if ( Args[2] == 0 ) Args[2] = (word)defaultUnicode; - Str_CopyLimitNull( ptcBarkBuffer, name, Str_TempLength()); + Str_CopyLimitNull( ptcBarkBuffer, ptcName, Str_TempLength()); Str_ConcatLimitNull(ptcBarkBuffer, pszText, Str_TempLength()); if (mode == TALKMODE_SPELL) //Set TALKMODE_SPELL to TALKMODE_SAY after every color check completed to block spell flood. @@ -903,7 +917,7 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ bark_default: if (ptcBarkBuffer[0] == '\0') { - Str_CopyLimitNull(ptcBarkBuffer, name, Str_TempLength()); + Str_CopyLimitNull(ptcBarkBuffer, ptcName, Str_TempLength()); Str_ConcatLimitNull(ptcBarkBuffer, pszText, Str_TempLength()); } @@ -1018,8 +1032,13 @@ void CClient::GetAdjustedItemID( const CChar * pChar, const CItem * pItem, ITEMI { tchar * sMountDefname = Str_GetTemp(); sprintf(sMountDefname, "mount_0x%x", idHorse); - ITEMID_TYPE idMountItem = (ITEMID_TYPE)(g_Exp.m_VarDefs.GetKeyNum(sMountDefname)); - if ( idMountItem > ITEMID_NOTHING ) + + ITEMID_TYPE idMountItem; + { + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + idMountItem = (ITEMID_TYPE)(gReader->m_VarDefs.GetKeyNum(sMountDefname)); + } + if ( idMountItem > ITEMID_NOTHING ) { id = idMountItem; pItemDef = CItemBase::FindItemBase(id); diff --git a/src/game/clients/CClientUse.cpp b/src/game/clients/CClientUse.cpp index 90d82006c..8e599a7fe 100644 --- a/src/game/clients/CClientUse.cpp +++ b/src/game/clients/CClientUse.cpp @@ -889,7 +889,8 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte // Check for a skill / non-consumables required. if ( s.IsKey("TEST") ) { - m_pChar->ParseScriptText(s.GetArgRaw(), CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, m_pChar); + CScriptExprContext scpContext{._pScriptObjI = m_pChar}; + CExpression::GetExprParser().ParseScriptText(s.GetArgRaw(), scpContext, CScriptTriggerArgsPtr{}, m_pChar); CResourceQtyArray skills(s.GetArgStr()); if ( !skills.IsResourceMatchAll(m_pChar) ) { @@ -900,7 +901,8 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte if ( s.IsKey("TESTIF") ) { - m_pChar->ParseScriptText(s.GetArgRaw(), CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, m_pChar); + CScriptExprContext scpContext{._pScriptObjI = m_pChar}; + CExpression::GetExprParser().ParseScriptText(s.GetArgRaw(), scpContext, CScriptTriggerArgsPtr{}, m_pChar); if ( !s.GetArgVal() ) { fSkipNeedCleanup = true; diff --git a/src/game/components/CCMultiMovable.cpp b/src/game/components/CCMultiMovable.cpp index 6753308ee..557aa87b8 100644 --- a/src/game/components/CCMultiMovable.cpp +++ b/src/game/components/CCMultiMovable.cpp @@ -1266,7 +1266,10 @@ bool CCMultiMovable::r_Verb(CScript & s, CTextConsole * pSrc) // Execute command tchar szText[MAX_TALK_BUFFER]; Str_CopyLimitNull(szText, pszSpeak, MAX_TALK_BUFFER); - pChar->ParseScriptText(szText, CScriptTriggerArgsPtr{}, CScriptExprContextPtr{}, &g_Serv); + + CScriptExprContext scpContext{._pScriptObjI = pChar}; + CExpression::GetExprParser().ParseScriptText(szText, scpContext, CScriptTriggerArgsPtr{}, &g_Serv); + pTiller->Speak(szText, HUE_TEXT_DEF, TALKMODE_SAY, FONT_NORMAL); } return true; diff --git a/src/game/components/CCSpawn.cpp b/src/game/components/CCSpawn.cpp index 9ee8d2661..b87ab61df 100644 --- a/src/game/components/CCSpawn.cpp +++ b/src/game/components/CCSpawn.cpp @@ -26,7 +26,7 @@ void CCSpawn::AddBadSpawn() { return; } - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); if (std::find(_vBadSpawns.cbegin(), _vBadSpawns.cend(), this) == _vBadSpawns.cend()) { _vBadSpawns.emplace_back(this); //only if it's not inserted already. @@ -37,7 +37,7 @@ void CCSpawn::AddBadSpawn() void CCSpawn::DelBadSpawn() { ADDTOCALLSTACK("CCSpawn::DelBadSpawn"); - MT_ENGINE_UNIQUE_LOCK_SET; + MT_ENGINE_UNIQUE_LOCK_SET(this); if (!_vBadSpawns.empty()) { _vBadSpawns.erase(std::find(_vBadSpawns.begin(), _vBadSpawns.end(), this)); diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 52eb6b799..50186c57a 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -2054,14 +2054,16 @@ height_t CItem::GetHeight() const return tmpHeight; } - char heightDef[24]{"itemheight_"}; + auto reader = g_ExprGlobals.mtEngineLockedReader(); + + char heightDef[24]{"itemheight_"}; Str_FromUI(uint(uiDispID), heightDef + 11, sizeof(heightDef) - 11, 16); - tmpHeight = static_cast(g_Exp.m_VarDefs.GetKeyNum(heightDef)); + tmpHeight = static_cast(reader->m_VarDefs.GetKeyNum(heightDef)); if ( tmpHeight ) //set by a defname ([DEFNAME charheight] height_0a) return tmpHeight; Str_FromUI(uint(uiDispID), heightDef + 11, sizeof(heightDef) - 11, 10); - tmpHeight = static_cast(g_Exp.m_VarDefs.GetKeyNum(heightDef)); + tmpHeight = static_cast(reader->m_VarDefs.GetKeyNum(heightDef)); if ( tmpHeight ) //set by a defname ([DEFNAME charheight] height_10) return tmpHeight; diff --git a/src/game/items/CItemStone.cpp b/src/game/items/CItemStone.cpp index 5f1bad3e4..b7cabbf5e 100644 --- a/src/game/items/CItemStone.cpp +++ b/src/game/items/CItemStone.cpp @@ -100,20 +100,23 @@ lpctstr CItemStone::GetTypeName() const { ADDTOCALLSTACK("CItemStone::GetTypeName"); CVarDefCont * pResult = nullptr; - switch ( GetType() ) - { - case IT_STONE_GUILD: - pResult = g_Exp.m_VarDefs.GetKey("STONECONFIG_TYPENAME_GUILD"); - break; - case IT_STONE_TOWN: - pResult = g_Exp.m_VarDefs.GetKey("STONECONFIG_TYPENAME_TOWN"); - break; - default: - break; - } + { + auto gReader = g_ExprGlobals.mtEngineLockedReader(); + switch ( GetType() ) + { + case IT_STONE_GUILD: + pResult = gReader->m_VarDefs.GetKey("STONECONFIG_TYPENAME_GUILD"); + break; + case IT_STONE_TOWN: + pResult = gReader->m_VarDefs.GetKey("STONECONFIG_TYPENAME_TOWN"); + break; + default: + break; + } - if ( pResult == nullptr ) - pResult = g_Exp.m_VarDefs.GetKey("STONECONFIG_TYPENAME_UNK"); + if ( pResult == nullptr ) + pResult = gReader->m_VarDefs.GetKey("STONECONFIG_TYPENAME_UNK"); + } return ( pResult == nullptr ) ? "" : pResult->GetValStr(); } @@ -172,7 +175,7 @@ lpctstr CItemStone::GetAlignName() const else return ""; - lpctstr sRes = g_Exp.m_VarDefs.GetKeyStr(tsDefname); + lpctstr sRes = g_ExprGlobals.mtEngineLockedReader()->m_VarDefs.GetKeyStr(tsDefname); return ( sRes == nullptr ) ? "" : sRes; } @@ -672,14 +675,16 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr CStoneMember * pMember = GetMember(pCharSrc); CVarDefCont * pResult = nullptr; + auto gReader = g_ExprGlobals.mtEngineLockedReader(); if ( pMember == nullptr ) { - pResult = g_Exp.m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_NONMEMBER"); + pResult = gReader->m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_NONMEMBER"); } else { - pResult = pMember->IsAbbrevOn() ? g_Exp.m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_ABBREVON") : - g_Exp.m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_ABBREVOFF"); + pResult = pMember->IsAbbrevOn() + ? gReader->m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_ABBREVON") + : gReader->m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_ABBREVOFF"); } sVal = pResult ? pResult->GetValStr() : ""; @@ -696,14 +701,14 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr if ( pMember == nullptr ) { - pResult = g_Exp.m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_NONMEMBER"); + pResult = g_ExprGlobals.mtEngineLockedReader()->m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_NONMEMBER"); } else { CChar * pLOYALTO = pMember->GetLoyalToUID().CharFind(); if ((pLOYALTO == nullptr) || (pLOYALTO == pCharSrc )) { - pResult = g_Exp.m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_YOURSELF"); + pResult = g_ExprGlobals.mtEngineLockedReader()->m_VarDefs.GetKey("STONECONFIG_VARIOUSNAME_YOURSELF"); } else { @@ -719,7 +724,9 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr case STC_MASTER: { CChar * pMaster = GetMaster(); - sVal = (pMaster) ? pMaster->GetName() : g_Exp.m_VarDefs.GetKeyStr("STONECONFIG_VARIOUSNAME_PENDVOTE"); + sVal = (pMaster) + ? pMaster->GetName() + : g_ExprGlobals.mtEngineLockedReader()->m_VarDefs.GetKeyStr("STONECONFIG_VARIOUSNAME_PENDVOTE"); } return true; @@ -728,11 +735,12 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr CChar * pMaster = GetMaster(); if ( pMaster == nullptr ) sVal.Clear(); // If no master (vote pending) - else if ( pMaster->Char_GetDef()->IsFemale()) - sVal = g_Exp.m_VarDefs.GetKeyStr("STONECONFIG_VARIOUSNAME_MASTERGENDERFEMALE"); - else - sVal = g_Exp.m_VarDefs.GetKeyStr("STONECONFIG_VARIOUSNAME_MASTERGENDERMALE"); - } + else + sVal = g_ExprGlobals.mtEngineLockedReader()->m_VarDefs.GetKeyStr( + pMaster->Char_GetDef()->IsFemale() + ? "STONECONFIG_VARIOUSNAME_MASTERGENDERFEMALE" + : "STONECONFIG_VARIOUSNAME_MASTERGENDERMALE"); + } return true; case STC_MASTERTITLE: diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 24cba818a..7e5e6e6e1 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -155,7 +155,8 @@ CWorld g_World; // the world. (we save this stuff) CUOInstall g_Install; CVerDataMul g_VerData; CSRand g_Rand; // TODO: remove this, since now all the members are static. -CExpression g_Exp; // Global script variables. +sl::GuardedAccess // TODO: put inside GuardedAccess also g_Cfg, and slowly also the other stuff? + g_ExprGlobals; // Global script variables. CSStringList g_AutoComplete; // auto-complete list CScriptProfiler g_profiler; // script profiler CUOMapList g_MapList; // global maps information diff --git a/src/game/uo_files/uofiles_enums.h b/src/game/uo_files/uofiles_enums.h index 6889838d9..31738052f 100644 --- a/src/game/uo_files/uofiles_enums.h +++ b/src/game/uo_files/uofiles_enums.h @@ -456,7 +456,7 @@ enum STAT_TYPE // MaxFood (7) }; -enum SKILL_TYPE // List of skill numbers (things that can be done at a given time) +enum SKILL_TYPE : int // List of skill numbers (things that can be done at a given time) { SKILL_NONE = -1, diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index 8724fbd55..a2dfc52cb 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -7,6 +7,7 @@ #include "../common/sphere_library/sstringobjs.h" #include "../common/basic_threading.h" #include "../common/CException.h" +#include "../common/CExpression.h" #include "../common/CLog.h" #include "../game/CServer.h" #include "ProfileTask.h" @@ -793,9 +794,10 @@ AbstractSphereThread::AbstractSphereThread(const char *name, ThreadPriority prio : AbstractThread(name, priority) #ifdef THREAD_TRACK_CALLSTACK , m_stackInfo{}, m_stackInfoCopy{}, m_iStackPos(-1), + m_iStackUnwindingStackPos(-1), m_iCaughtExceptionStackPos(-1), m_fFreezeCallStack(false), - m_iStackUnwindingStackPos(-1), m_iCaughtExceptionStackPos(-1) #endif + m_pExpr{std::make_unique()} { // profiles that apply to every thread m_profile.EnableProfile(PROFILE_IDLE); @@ -1058,10 +1060,9 @@ StackDebugInformation::StackDebugInformation(const char *name) noexcept } m_context = static_cast(icontext); - if (m_context != nullptr && !m_context->closing()) + if (m_context != nullptr && !m_context->closing()) [[likely]] { - [[unlikely]] - m_context->pushStackCall(name); + m_context->pushStackCall(name); } } diff --git a/src/sphere/threads.h b/src/sphere/threads.h index 4f5701229..8ebe1c045 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -15,6 +15,7 @@ #include #endif +class CExpression; class TemporaryString; /** @@ -265,11 +266,14 @@ class AbstractSphereThread : public AbstractThread STACK_INFO_REC m_stackInfo[0x500]; STACK_INFO_REC m_stackInfoCopy[0x500]; ssize_t m_iStackPos; - bool m_fFreezeCallStack; ssize_t m_iStackUnwindingStackPos; ssize_t m_iCaughtExceptionStackPos; + bool m_fFreezeCallStack; #endif +public: + std::unique_ptr m_pExpr; + public: AbstractSphereThread(const char *name, ThreadPriority priority = ThreadPriority::Normal); virtual ~AbstractSphereThread(); diff --git a/src/tables/classnames.tbl b/src/tables/classnames.tbl index c499f313e..3ac820be1 100644 --- a/src/tables/classnames.tbl +++ b/src/tables/classnames.tbl @@ -63,6 +63,7 @@ ADD(CClient, "CClient"); ADD(CDialogDef, "CDialogDef"); ADD(CDialogResponseArgs, "CDialogResponseArgs"); ADD(CEntity, "CEntity"); +ADD(CExpression, "CExpression"); ADD(CSFileObj, "CSFileObj"); ADD(CSFileObjContainer, "CSFileObjContainer"); ADD(CScriptObj, "CScriptObj"); From 904d14fef9a83da53736efd31c24a40f9788571d Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 19 May 2025 17:11:07 +0200 Subject: [PATCH 015/112] Fixed MSVC compilation warning. --- src/game/clients/CClientMsg.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index e014be931..cc5464322 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -728,7 +728,8 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ if (pSrc && pSrc->IsChar()) pSrcChar = static_cast(pSrc); - static constexpr lpctstr talkmode_defs_color[] = + static constexpr uint kiManagedTalkmodes = 5; + static constexpr lpctstr s_ptcTalkmodesDefsColor[kiManagedTalkmodes] = { "SMSG_DEF_COLOR", "EMOTE_DEF_COLOR", @@ -736,7 +737,7 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ "CMSG_DEF_COLOR", "IMSG_DEF_COLOR", }; - static constexpr lpctstr talkmode_defs_font[] = + static constexpr lpctstr s_ptcTalkmodesDefsFont[kiManagedTalkmodes] = { "SMSG_DEF_FONT", "EMOTE_DEF_FONT", @@ -744,7 +745,7 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ "CMSG_DEF_FONT", "IMSG_DEF_FONT", }; - static constexpr lpctstr talkmode_defs_unicode[] = + static constexpr lpctstr s_ptcTalkmodesDefsUnicode[kiManagedTalkmodes] = { "SMSG_DEF_UNICODE", "EMOTE_DEF_UNICODE", @@ -753,7 +754,8 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ "IMSG_DEF_UNICODE", }; - std::optional iTalkmodeHue, iTalkmodeFont, iTalkmodeUnicode; + ushort iTalkmodeHue, iTalkmodeFont, iTalkmodeUnicode; + iTalkmodeHue = iTalkmodeFont = iTalkmodeUnicode = UINT16_MAX; switch ( mode ) { case TALKMODE_SYSTEM: @@ -782,12 +784,12 @@ void CClient::addBarkParse( lpctstr pszText, const CObjBaseTemplate * pSrc, HUE_ } { auto gReader = g_ExprGlobals.mtEngineLockedReader(); - if (iTalkmodeHue) - defaultHue = (HUE_TYPE)(gReader->m_VarDefs.GetKeyNum(talkmode_defs_color[*iTalkmodeHue])); - if (iTalkmodeFont) - defaultFont = (FONT_TYPE)(gReader->m_VarDefs.GetKeyNum(talkmode_defs_font[*iTalkmodeFont])); - if (iTalkmodeUnicode) - defaultUnicode = gReader->m_VarDefs.GetKeyNum(talkmode_defs_unicode[*iTalkmodeUnicode]) > 0 ? true : false; + if (iTalkmodeHue != UINT16_MAX) + defaultHue = (HUE_TYPE)(gReader->m_VarDefs.GetKeyNum(s_ptcTalkmodesDefsColor[iTalkmodeHue])); + if (iTalkmodeFont != UINT16_MAX) + defaultFont = (FONT_TYPE)(gReader->m_VarDefs.GetKeyNum(s_ptcTalkmodesDefsFont[iTalkmodeFont])); + if (iTalkmodeUnicode != UINT16_MAX) + defaultUnicode = gReader->m_VarDefs.GetKeyNum(s_ptcTalkmodesDefsUnicode[iTalkmodeUnicode]) > 0 ? true : false; } From c4a7febd24ce5aecf47a5858a3055a9193be2866 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 22 May 2025 11:56:43 +0200 Subject: [PATCH 016/112] Fix state buffer usage for nested conditional expressions. --- cmake/CompilerFlagsBase.cmake | 1 - cmake/CompilerFlagsChecker.cmake | 1 + src/common/CDataBase.cpp | 4 +- src/common/CException.cpp | 2 +- src/common/CExpression.cpp | 121 ++++++++++-------- src/common/CExpression.h | 44 +++++-- src/common/CFloatMath.cpp | 2 +- src/common/CLocalVarsExtra.cpp | 2 +- src/common/CLog.cpp | 2 +- src/common/CPointBase.cpp | 2 +- src/common/CSFileObj.cpp | 4 +- src/common/CSFileObjContainer.cpp | 4 +- src/common/CSVFile.cpp | 2 +- src/common/CScript.cpp | 4 +- src/common/CScriptObj.cpp | 10 +- src/common/CScriptObj.h | 2 - src/common/CScriptParserBufs.cpp | 5 - src/common/CScriptParserBufs.h | 1 - src/common/CScriptTriggerArgs.cpp | 4 +- src/common/CServerMap.cpp | 2 +- src/common/CUOInstall.cpp | 2 +- src/common/CVarDefMap.cpp | 2 +- src/common/ListDefContMap.cpp | 2 +- src/common/crypto/CCrypto.cpp | 2 +- src/common/resource/CResourceDef.cpp | 2 +- src/common/resource/CResourceHolder.cpp | 4 +- src/common/resource/CResourceQty.cpp | 2 +- src/common/resource/CResourceRef.cpp | 2 +- src/common/resource/CValueDefs.cpp | 2 +- src/common/resource/sections/CDialogDef.cpp | 8 +- src/common/resource/sections/CItemTypeDef.cpp | 4 +- .../resource/sections/CRandGroupDef.cpp | 4 +- .../resource/sections/CRegionResourceDef.cpp | 2 +- .../resource/sections/CSkillClassDef.cpp | 2 +- src/common/resource/sections/CSkillDef.cpp | 2 +- src/common/resource/sections/CSpellDef.cpp | 4 +- src/common/resource/sections/CWebPageDef.cpp | 6 +- src/common/sphere_library/CSAssoc.cpp | 2 +- src/common/sphere_library/CSObjCont.cpp | 2 +- src/common/sphere_library/CSObjList.cpp | 2 +- src/common/sphere_library/smap.h | 2 +- src/common/sphere_library/sobjpool.h | 5 +- src/common/sphere_library/squeues.h | 2 +- src/common/sphere_library/sstacks.h | 2 +- src/common/sphere_library/sstring.cpp | 2 +- src/common/sphere_library/sstringobjs.cpp | 2 +- src/common/sqlite/SQLite.cpp | 4 +- src/game/CBase.cpp | 4 +- src/game/CContainer.cpp | 4 +- src/game/CEntity.cpp | 2 +- src/game/CObjBase.cpp | 6 +- src/game/CPathFinder.cpp | 2 +- src/game/CRegion.cpp | 4 +- src/game/CResourceCalc.cpp | 2 +- src/game/CSector.cpp | 4 +- src/game/CSectorTemplate.cpp | 2 +- src/game/CServer.cpp | 6 +- src/game/CServerConfig.cpp | 4 +- src/game/CServerDef.cpp | 6 +- src/game/CServerTime.cpp | 2 +- src/game/CTimedObject.cpp | 2 +- src/game/CWorld.cpp | 8 +- src/game/CWorldComm.cpp | 2 +- src/game/CWorldImport.cpp | 2 +- src/game/CWorldMap.cpp | 4 +- src/game/CWorldSearch.cpp | 2 +- src/game/CWorldTicker.cpp | 2 +- src/game/chars/CChar.cpp | 6 +- src/game/chars/CCharAct.cpp | 6 +- src/game/chars/CCharAttacker.cpp | 5 +- src/game/chars/CCharBase.cpp | 2 +- src/game/chars/CCharFight.cpp | 4 +- src/game/chars/CCharMemory.cpp | 6 +- src/game/chars/CCharNPC.cpp | 3 +- src/game/chars/CCharNPCAct.cpp | 4 +- src/game/chars/CCharNPCAct_Fight.cpp | 3 +- src/game/chars/CCharNPCAct_Magic.cpp | 2 +- src/game/chars/CCharNPCAct_Vendor.cpp | 2 +- src/game/chars/CCharNPCPet.cpp | 3 +- src/game/chars/CCharNPCStatus.cpp | 2 +- src/game/chars/CCharNotoriety.cpp | 6 +- src/game/chars/CCharPlayer.cpp | 4 +- src/game/chars/CCharSkill.cpp | 4 +- src/game/chars/CCharSpell.cpp | 4 +- src/game/chars/CCharStat.cpp | 6 +- src/game/chars/CCharStatus.cpp | 4 +- src/game/chars/CCharUse.cpp | 4 +- src/game/chars/CStoneMember.cpp | 4 +- src/game/clients/CAccount.cpp | 6 +- src/game/clients/CClient.cpp | 6 +- src/game/clients/CClientDialog.cpp | 4 +- src/game/clients/CClientEvent.cpp | 6 +- src/game/clients/CClientLog.cpp | 6 +- src/game/clients/CClientMsg.cpp | 6 +- src/game/clients/CClientMsg_AOSTooltip.cpp | 4 +- src/game/clients/CClientTarg.cpp | 4 +- src/game/clients/CClientUse.cpp | 4 +- src/game/clients/CGMPage.cpp | 2 +- src/game/clients/CParty.cpp | 6 +- src/game/components/CCChampion.cpp | 4 +- src/game/components/CCMultiMovable.cpp | 4 +- src/game/components/CCSpawn.cpp | 6 +- src/game/items/CItem.cpp | 6 +- src/game/items/CItemBase.cpp | 4 +- src/game/items/CItemCommCrystal.cpp | 2 +- src/game/items/CItemContainer.cpp | 6 +- src/game/items/CItemCorpse.cpp | 4 +- src/game/items/CItemMap.cpp | 4 +- src/game/items/CItemMessage.cpp | 4 +- src/game/items/CItemMulti.cpp | 6 +- src/game/items/CItemMultiCustom.cpp | 6 +- src/game/items/CItemPlant.cpp | 4 +- src/game/items/CItemShip.cpp | 4 +- src/game/items/CItemStone.cpp | 6 +- src/game/items/CItemVendable.cpp | 2 +- src/game/spheresvr.cpp | 4 +- src/game/uo_files/CUOMobtypes.cpp | 2 +- src/game/uo_files/CUOTiledata.cpp | 2 +- src/network/CIPHistoryManager.cpp | 2 +- src/network/CNetworkManager.cpp | 2 +- src/network/receive.cpp | 4 +- src/network/send.cpp | 2 +- src/sphere/ProfileTask.cpp | 2 +- src/sphere/UnixTerminal.cpp | 2 +- src/sphere/containers.h | 2 +- src/sphere/ntservice.cpp | 4 +- src/sphere/ntwindow.cpp | 4 +- src/sphere/threads.cpp | 4 +- 128 files changed, 323 insertions(+), 289 deletions(-) diff --git a/cmake/CompilerFlagsBase.cmake b/cmake/CompilerFlagsBase.cmake index 45d46ffdb..65f345ac9 100644 --- a/cmake/CompilerFlagsBase.cmake +++ b/cmake/CompilerFlagsBase.cmake @@ -123,7 +123,6 @@ if(NOT MSVC) set(custom_compile_options_release ${local_compile_options_nondebug} -flto=full - -fvirtual-function-elimination -ffunction-sections -fdata-sections #-fno-unique-section-names diff --git a/cmake/CompilerFlagsChecker.cmake b/cmake/CompilerFlagsChecker.cmake index 83350c590..7271a5f8a 100644 --- a/cmake/CompilerFlagsChecker.cmake +++ b/cmake/CompilerFlagsChecker.cmake @@ -24,6 +24,7 @@ if(NOT MSVC) check_cxx_compiler_flag("-fno-expensive-optimizations" COMP_HAS_FNO_EXPENSIVE_OPTIMIZATIONS) # Compiler option flags. Expected to work on Clang but not GCC, at the moment. + # check_cxx_compiler_flag(CXX "-fvirtual-function-elimination" COMP_HAS_VFUNC_ELIMINATION) # only for release build? is it stable? # check_cxx_compiler_flag("-fforce-emit-vtables" COMP_HAS_F_FORCE_EMIT_VTABLES) # -fwhole-program-vtables # -fstrict-vtable-pointers diff --git a/src/common/CDataBase.cpp b/src/common/CDataBase.cpp index f20db0ec5..27eab2e56 100644 --- a/src/common/CDataBase.cpp +++ b/src/common/CDataBase.cpp @@ -4,8 +4,8 @@ #include "../sphere/asyncdb.h" #include "resource/sections/CResourceNamedDef.h" // Needed because it was only forward declared. #include "CLog.h" -#include "CException.h" -#include "CExpression.h" +//#include "CException.h" // included in the precompiled header +//#include "CExpression.h" // included in the precompiled header #include "CScriptTriggerArgs.h" #include "CDataBase.h" diff --git a/src/common/CException.cpp b/src/common/CException.cpp index f796eab80..e916e2aab 100644 --- a/src/common/CException.cpp +++ b/src/common/CException.cpp @@ -1,5 +1,5 @@ -#include "CException.h" +//#include "CException.h" // included in the precompiled header #ifdef WINDOWS_SHOULD_EMIT_CRASH_DUMP #include "crashdump/crashdump.h" diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index 13027ef7c..4a697d425 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -1,8 +1,7 @@ -#include "CExpression.h" +//#include "CExpression.h" // included in the precompiled header #include "../game/CServerConfig.h" #include "sphere_library/CSRand.h" -//#include "CException.h" -#include "CScriptParserBufs.h" +///include "CException.h" // included in the precompiled header #include "CLog.h" #include #include @@ -615,7 +614,10 @@ int Calc_GetSCurve( int iValDiff, int iVariance ) return iChance; } +// --- + CExpression::CExpression() noexcept : + _pBufs(std::make_unique()), _iGetVal_Reentrant(0) { } @@ -1454,34 +1456,48 @@ int CExpression::GetRangeVals(lpctstr & refStrExpr, int64 * piVals, int iMaxQty, } -int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) +CExpression::PrvBuffersPool::CSubExprStatesArenaPool_t::UniquePtr_t +CExpression::GetConditionalSubexpressions( + lptstr& refStrExpr, + CExpression::PrvBuffersPool::CSubExprStatesArenaPool_t& bufs_arena) { ADDTOCALLSTACK("CExpression::GetConditionalSubexpressions"); // Get the start and end pointers for each logical subexpression (delimited by brackets or by logical operators || and &&) inside a conditional statement (IF/ELIF/ELSEIF and QVAL). // Parse from left to start (like it was always done in Sphere). // Start and end pointers are inclusive (pointed values are valid chars, the end pointer doesn't necessarily point to '\0'). + // Take a struct which holds a "block" (array) of 'sm_kuiMaxConditionalSubexprsPerExpr' amount of prebuilt CExpression::CScriptSubExprState. + auto pSubexprsArena = bufs_arena.acquireUnique(); + memset(pSubexprsArena.get(), 0, sizeof(CSubExprStatesArena)); + if (refStrExpr == nullptr) - return 0; + { + DEBUG_ASSERT(false); + return pSubexprsArena; + } - using SType = CScriptSubExprState::Type; - memset(m_parsingSubexprsStates, 0, sizeof(m_parsingSubexprsStates)); - uint uiSubexprQty = 0; // number of subexpressions + static constexpr auto s_kuiMaxSubexpressionsPerExpr = CSubExprStatesArena::sm_kuiMaxConditionalSubexprsPerExpr; + using SubexprState_t = CExpression::CScriptSubExprState; + using SubexprType_t = SubexprState_t::Type; + + SubexprState_t* parsingSubexprsStates = pSubexprsArena.get()->m_subexprs; + uint& uiSubexprQty = pSubexprsArena.get()->m_uiQty; // number of subexpressions + DEBUG_ASSERT(uiSubexprQty == 0); while (refStrExpr[0] != '\0') { - if (++uiSubexprQty >= sm_uiMaxConditionalSubexprs) + if (++uiSubexprQty >= s_kuiMaxSubexpressionsPerExpr) { - g_Log.EventWarn("Exceeded maximum allowed number of subexpressions (%u). Parsing halted.\n", sm_uiMaxConditionalSubexprs); - return uiSubexprQty; + g_Log.EventWarn("Exceeded maximum allowed number of subexpressions (%u). Parsing halted.\n", s_kuiMaxSubexpressionsPerExpr); + return pSubexprsArena; } GETNONWHITESPACE(refStrExpr); - CScriptSubExprState& sCurSubexpr = m_parsingSubexprsStates[uiSubexprQty - 1]; + SubexprState_t& sCurSubexpr = parsingSubexprsStates[uiSubexprQty - 1]; tchar ch = refStrExpr[0]; // Init the data for the current subexpression and set the position of the first character of the subexpression. - sCurSubexpr = {refStrExpr, nullptr, SType::None, 0}; + sCurSubexpr = {refStrExpr, nullptr, SubexprType_t::None, 0}; // -- What's an expression and what's a subexpression. // An expression can contain a single statement, a single operation (enclosed, or not, by curly brackets), like: IF or IF == 1. @@ -1604,7 +1620,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) // I'm here because the whole expression is enclosed by parentheses // + 1 because i want to point to the character after the ')', even if it's the string terminator. sCurSubexpr.ptcEnd = ptcLastClosingBracket + 1; - sCurSubexpr.uiType |= CScriptSubExprState::TopParenthesizedExpr; + sCurSubexpr.uiType |= SubexprState_t::TopParenthesizedExpr; } else { @@ -1654,7 +1670,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) { g_Log.EventError("Expression started with '(' but isn't closed by a ')' character.\n"); sCurSubexpr.ptcEnd = refStrExpr - 1; // Position of the char just before the last ')' of the bracketed subexpression -> this eats away the last closing bracket - return uiSubexprQty; + return pSubexprsArena; } // Okay, i've eaten the expression in brackets, now fall through the rest of the loop and continue. @@ -1665,7 +1681,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) // Logical two-way OR operator: || if (sCurSubexpr.ptcEnd == nullptr) sCurSubexpr.ptcEnd = refStrExpr; - sCurSubexpr.uiType = SType::Or | (sCurSubexpr.uiType & ~SType::None); + sCurSubexpr.uiType = SubexprType_t::Or | (sCurSubexpr.uiType & ~SubexprType_t::None); refStrExpr += 2u; // Skip the second char of the operator break; // End of subexpr... } @@ -1675,7 +1691,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) // Logical two-way AND operator: && if (sCurSubexpr.ptcEnd == nullptr) sCurSubexpr.ptcEnd = refStrExpr; - sCurSubexpr.uiType = SType::And | (sCurSubexpr.uiType & ~SType::None); + sCurSubexpr.uiType = SubexprType_t::And | (sCurSubexpr.uiType & ~SubexprType_t::None); refStrExpr += 2u; // Skip the second char of the operator break; // End of subexpr... } @@ -1689,18 +1705,18 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) // This can be: <, <= or the start of a bracketed expression < > if (refStrExpr[1] == '=') { - sCurSubexpr.uiType = SType::BinaryNonLogical | (sCurSubexpr.uiType & ~SType::None); + sCurSubexpr.uiType = SubexprType_t::BinaryNonLogical | (sCurSubexpr.uiType & ~SubexprType_t::None); refStrExpr += 1u; } else { const ushort prevSubexprType = ((uiSubexprQty == 1) - ? (ushort)SType::None - : m_parsingSubexprsStates[uiSubexprQty - 2].uiType); - if ((prevSubexprType & SType::None)) + ? (ushort)SubexprType_t::None + : parsingSubexprsStates[uiSubexprQty - 2].uiType); + if ((prevSubexprType & SubexprType_t::None)) { // This subexpr is not preceded by a two-way operator, so probably i'm an operator: skip me. - sCurSubexpr.uiType = SType::BinaryNonLogical | (sCurSubexpr.uiType & ~SType::None); + sCurSubexpr.uiType = SubexprType_t::BinaryNonLogical | (sCurSubexpr.uiType & ~SubexprType_t::None); // This is not a whole logical subexpression but a single operand, or piece/fragment of the current arithmetic subexpr. } @@ -1725,12 +1741,12 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) { if (refStrExpr[1] == '=') { - sCurSubexpr.uiType = SType::BinaryNonLogical | (sCurSubexpr.uiType & ~SType::None); + sCurSubexpr.uiType = SubexprType_t::BinaryNonLogical | (sCurSubexpr.uiType & ~SubexprType_t::None); refStrExpr += 1u; } else { - sCurSubexpr.uiType = SType::BinaryNonLogical | (sCurSubexpr.uiType & ~SType::None); + sCurSubexpr.uiType = SubexprType_t::BinaryNonLogical | (sCurSubexpr.uiType & ~SubexprType_t::None); } } // End of arithmetic subexpression parsing. @@ -1744,7 +1760,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) lptstr ptcStart, ptcEnd; for (uint i = 0; i < uiSubexprQty; ++i) { - CScriptSubExprState& sCurSubexpr = m_parsingSubexprsStates[i]; + SubexprState_t& sCurSubexpr = parsingSubexprsStates[i]; ptcStart = sCurSubexpr.ptcStart; ptcEnd = sCurSubexpr.ptcEnd; @@ -1758,7 +1774,7 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) ) { // We have logical operators inside, so it's a nested subexpression. - sCurSubexpr.uiType |= SType::MaybeNestedSubexpr; + sCurSubexpr.uiType |= SubexprType_t::MaybeNestedSubexpr; break; } } @@ -1778,10 +1794,9 @@ int CExpression::GetConditionalSubexpressions(lptstr& refStrExpr) sCurSubexpr.ptcEnd = ptcEnd; } - return uiSubexprQty; + return pSubexprsArena; } - static constexpr int kiRangeMaxArgs = 96; static int GetRangeArgsPos(lpctstr & pExpr, lpctstr (&pArgPos)[kiRangeMaxArgs][2], bool fIgnoreMissingEndBracket) { @@ -2057,11 +2072,11 @@ CSString CExpression::GetRangeString(lpctstr & refStrExpr) return CSString(pElementsStart[i][0], iToParseLen); } -bool CExpression::_Evaluate_Conditional_EvalSingle( +bool CExpression::EvaluateConditionalSingle( CScriptSubExprState& refSubExprState, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) { - ADDTOCALLSTACK("CExpression::_Evaluate_Conditional_EvalSingle"); + ADDTOCALLSTACK("CExpression::EvaluateConditionalSingle"); ASSERT(refSubExprState.ptcStart); ASSERT(refSubExprState.ptcEnd); @@ -2114,7 +2129,7 @@ bool CExpression::_Evaluate_Conditional_EvalSingle( if (fNested) { // Probably this subexpression has other conditional subexpressions inside. - fVal = Evaluate_Conditional(ptcSubexpr, refExprContext, pScriptArgs, pSrc); + fVal = EvaluateConditionalWhole(ptcSubexpr, refExprContext, pScriptArgs, pSrc); } else { @@ -2147,53 +2162,55 @@ bool CExpression::_Evaluate_Conditional_EvalSingle( return fVal; } -bool CExpression::Evaluate_Conditional(lptstr ptcExpr, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) +bool CExpression::EvaluateConditionalWhole(lptstr ptcExpr, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) { - ADDTOCALLSTACK("CExpression::Evaluate_Conditional"); + ADDTOCALLSTACK("CExpression::EvaluateConditionalWhole"); ASSERT(refExprContext._pScriptObjI != nullptr); if (!pScriptArgs) pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); lptstr ptcExprDbg = ptcExpr; - const int iQty = GetConditionalSubexpressions(ptcExprDbg); // number of arguments + const auto pSubexprArena = GetConditionalSubexpressions(ptcExprDbg, _pBufs.get()->m_poolCScriptExprSubStates); // number of arguments + const uint uiQty = pSubexprArena->m_uiQty; + CScriptSubExprState* parsingSubexprsStates = pSubexprArena->m_subexprs; - if (iQty == 0) + if (uiQty == 0) return 0; using SType = CScriptSubExprState::Type; - if (iQty == 1) + if (uiQty == 1) { // We don't have subexpressions, but only a simple expression. - CScriptSubExprState& sCur = m_parsingSubexprsStates[0]; + CScriptSubExprState& sCur = parsingSubexprsStates[0]; ASSERT((sCur.uiType & SType::None) || (sCur.uiType & SType::BinaryNonLogical)); - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, refExprContext, pScriptArgs, pSrc); + const bool fVal = EvaluateConditionalSingle(sCur, refExprContext, pScriptArgs, pSrc); return fVal; } // We have some subexpressions, connected between them by logical operators. bool fWholeExprVal = false; - for (int i = 0; i < iQty; ++i) + for (uint i = 0; i < uiQty; ++i) { - CScriptSubExprState& sCur = m_parsingSubexprsStates[i]; + CScriptSubExprState& sCur = parsingSubexprsStates[i]; ASSERT(sCur.uiType != SType::Unknown); if (i == 0) { - fWholeExprVal = _Evaluate_Conditional_EvalSingle(sCur, refExprContext, pScriptArgs, pSrc); + fWholeExprVal = EvaluateConditionalSingle(sCur, refExprContext, pScriptArgs, pSrc); continue; } - CScriptSubExprState& sPrev = m_parsingSubexprsStates[i - 1]; + CScriptSubExprState& sPrev = parsingSubexprsStates[i - 1]; if (sPrev.uiType & SType::Or) { if (fWholeExprVal) return true; - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, refExprContext, pScriptArgs, pSrc); + const bool fVal = EvaluateConditionalSingle(sCur, refExprContext, pScriptArgs, pSrc); fWholeExprVal = fWholeExprVal || fVal; } else if (sPrev.uiType & SType::And) @@ -2201,23 +2218,21 @@ bool CExpression::Evaluate_Conditional(lptstr ptcExpr, CScriptExprContext& refEx if (!fWholeExprVal) return false; - const bool fVal = _Evaluate_Conditional_EvalSingle(sCur, refExprContext, pScriptArgs, pSrc); + const bool fVal = EvaluateConditionalSingle(sCur, refExprContext, pScriptArgs, pSrc); fWholeExprVal = (i == 1) ? fVal : (fWholeExprVal && fVal); } - if (sCur.uiType & SType::None) { - ASSERT(i == iQty - 1); // It should be the last subexpression + ASSERT(i == uiQty - 1); // It should be the last subexpression ASSERT((sPrev.uiType & SType::Or) || (sPrev.uiType & SType::And)); } - } return fWholeExprVal; } -static void Evaluate_QvalConditional_ParseArg(tchar* ptcSrc, tchar** ptcDest, lpctstr ptcSep) +static void EvaluateConditionalQval_ParseArg(tchar* ptcSrc, tchar** ptcDest, lpctstr ptcSep) { ASSERT(ptcSep && *ptcSep); @@ -2274,13 +2289,13 @@ static void Evaluate_QvalConditional_ParseArg(tchar* ptcSrc, tchar** ptcDest, lp Str_Parse(ptcSrc, ptcDest, ptcSep); } -bool CExpression::Evaluate_QvalConditional( +bool CExpression::EvaluateConditionalQval( lpctstr ptcKey, CSString& refStrVal, CScriptExprContext& pContext, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) { // Do a switch ? type statement - ADDTOCALLSTACK("CExpression::Evaluate_QvalConditional"); + ADDTOCALLSTACK("CExpression::EvaluateConditionalQval"); ASSERT(pContext._pScriptObjI != nullptr); // Do NOT work on the original arguments, it WILL fuck up the original string! @@ -2292,10 +2307,10 @@ bool CExpression::Evaluate_QvalConditional( ppCmds[0] = ptcArgs; // Get the condition - Evaluate_QvalConditional_ParseArg(ppCmds[0], &(ppCmds[1]), "?"); + EvaluateConditionalQval_ParseArg(ppCmds[0], &(ppCmds[1]), "?"); // Get the first and second retvals - Evaluate_QvalConditional_ParseArg(ppCmds[1], &(ppCmds[2]), ":"); + EvaluateConditionalQval_ParseArg(ppCmds[1], &(ppCmds[2]), ":"); // Complete evaluation of the condition // (do that in another string, since it may overwrite the arguments, which are written later in the same string). @@ -2576,7 +2591,7 @@ int CExpression::ParseScriptText( // Separate evaluation for QVAL. I may need additional script context for it (pScriptArgs isn't available in r_WriteVal). EXC_SET_BLOCK("writeval qval"); ptcKey += 4; // Skip the letters QVAL and pass only the arguments - fRes = Evaluate_QvalConditional(ptcKey, sVal, pContext, pScriptArgs, pSrc); + fRes = EvaluateConditionalQval(ptcKey, sVal, pContext, pScriptArgs, pSrc); eQval = QvalStatus::None; } else diff --git a/src/common/CExpression.h b/src/common/CExpression.h index 64cd492eb..84708cc4d 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -11,6 +11,7 @@ #define _INC_CEXPRSSION_H #include "sphere_library/CSAssoc.h" +#include "sphere_library/sobjpool.h" #include "CScriptParserBufs.h" #include "CVarDefMap.h" #include "ListDefContMap.h" @@ -195,9 +196,24 @@ class CExpression ushort uiNonAssociativeOffset; // How much bytes/characters before the start is (if any) the first non-associative operator preceding the subexpression. }; - static constexpr size_t sm_uiMaxConditionalSubexprs = 32; - CScriptSubExprState m_parsingSubexprsStates[sm_uiMaxConditionalSubexprs]; + struct CSubExprStatesArena + { + static constexpr uint sm_kuiMaxConditionalSubexprsPerExpr = 32; + + CScriptSubExprState m_subexprs[sm_kuiMaxConditionalSubexprsPerExpr]; + uint m_uiQty; + }; + + struct PrvBuffersPool + { + // A pool of arenas. + static constexpr uint sm_subexpr_pool_size = 1'000; + static constexpr bool sm_allow_fallback_objects = false; + using CSubExprStatesArenaPool_t = sl::ObjectPool; + CSubExprStatesArenaPool_t m_poolCScriptExprSubStates; + }; + std::unique_ptr _pBufs; short _iGetVal_Reentrant; public: @@ -213,8 +229,6 @@ class CExpression int64 GetRangeNumber(lpctstr& refStrExpr); // Evaluate a { } range CSString GetRangeString(lpctstr& refStrExpr); // STRRANDRANGE - int GetConditionalSubexpressions(lptstr& refStrExpr); - // Strict G++ Prototyping produces an error when not casting char*& to const char*& // So this is a rather lazy and const-UNsafe workaround inline llong GetSingle(lptstr &refArgs) { @@ -230,17 +244,27 @@ class CExpression return GetRangeNumber(const_cast(refStrArgs)); } - // Arguments inside conditional statements: IF, ELIF, ELSEIF - bool _Evaluate_Conditional_EvalSingle(CScriptSubExprState& refSubExprState, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); - bool Evaluate_Conditional(lptstr ptcExpression, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); - - bool Evaluate_QvalConditional(lpctstr ptcKey, CSString& refStrVal, CScriptExprContext& refContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); - /* * @brief Do the first-level parsing of a script line and eventually replace requested values got by r_WriteVal. */ int ParseScriptText( tchar * pszResponse, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, int iFlags = 0 ); + [[nodiscard]] + bool EvaluateConditionalWhole(lptstr ptcExpression, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + +private: + // Arguments inside conditional statements: IF, ELIF, ELSEIF + [[nodiscard]] + bool EvaluateConditionalSingle(CScriptSubExprState& refSubExprState, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + + [[nodiscard]] + bool EvaluateConditionalQval(lpctstr ptcKey, CSString& refStrVal, CScriptExprContext& refContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + + [[nodiscard]] + PrvBuffersPool::CSubExprStatesArenaPool_t::UniquePtr_t + GetConditionalSubexpressions(lptstr& refStrExpr, PrvBuffersPool::CSubExprStatesArenaPool_t& bufs_arena); + + public: CExpression() noexcept; ~CExpression() noexcept = default; diff --git a/src/common/CFloatMath.cpp b/src/common/CFloatMath.cpp index e6fd9198e..6d2c9de06 100644 --- a/src/common/CFloatMath.cpp +++ b/src/common/CFloatMath.cpp @@ -5,7 +5,7 @@ #include "../game/CServerConfig.h" #include "../sphere/threads.h" #include "sphere_library/CSRand.h" -#include "CExpression.h" +//#include "CExpression.h" // included in the precompiled header #include "CLog.h" #include "CFloatMath.h" diff --git a/src/common/CLocalVarsExtra.cpp b/src/common/CLocalVarsExtra.cpp index 54ee9c263..38f9e5d7b 100644 --- a/src/common/CLocalVarsExtra.cpp +++ b/src/common/CLocalVarsExtra.cpp @@ -1,5 +1,5 @@ #include "../sphere/threads.h" -#include "CExpression.h" +//#include "CExpression.h" // included in the precompiled header #include "CLog.h" #include "CLocalVarsExtra.h" diff --git a/src/common/CLog.cpp b/src/common/CLog.cpp index b4b024963..3571ca8d3 100644 --- a/src/common/CLog.cpp +++ b/src/common/CLog.cpp @@ -3,7 +3,7 @@ #include "../sphere/threads.h" #include "../game/CServer.h" #include "sphere_library/sstringobjs.h" -#include "CException.h" +//#include "CException.h" // included in the precompiled header #include "CScript.h" #include "CLog.h" diff --git a/src/common/CPointBase.cpp b/src/common/CPointBase.cpp index 7c20098d1..c782e8705 100644 --- a/src/common/CPointBase.cpp +++ b/src/common/CPointBase.cpp @@ -1,5 +1,5 @@ #include "../common/sphere_library/scontainer_ops.h" -#include "../common/CExpression.h" +//#include "../common/CExpression.h" // included in the precompiled header #include "../game/items/CItem.h" #include "../game/uo_files/CUOMultiItemRec.h" #include "../game/uo_files/CUOStaticItemRec.h" diff --git a/src/common/CSFileObj.cpp b/src/common/CSFileObj.cpp index 42ad26989..4e9245a6c 100644 --- a/src/common/CSFileObj.cpp +++ b/src/common/CSFileObj.cpp @@ -1,8 +1,8 @@ #include "../sphere/threads.h" #include "CScript.h" #include "CLog.h" -#include "CException.h" -#include "CExpression.h" +//#include "CException.h" // included in the precompiled header +//#include "CExpression.h" // included in the precompiled header #include "CSFileObj.h" diff --git a/src/common/CSFileObjContainer.cpp b/src/common/CSFileObjContainer.cpp index b9a1146d4..788c89216 100644 --- a/src/common/CSFileObjContainer.cpp +++ b/src/common/CSFileObjContainer.cpp @@ -1,7 +1,7 @@ #include "../game/CServerTime.h" #include "../sphere/threads.h" -#include "CException.h" -#include "CExpression.h" +//#include "CException.h" // included in the precompiled header +//#include "CExpression.h" // included in the precompiled header #include "CScript.h" #include "CSFileObj.h" #include "CSFileObjContainer.h" diff --git a/src/common/CSVFile.cpp b/src/common/CSVFile.cpp index 4e03adeca..4f6ef1d55 100644 --- a/src/common/CSVFile.cpp +++ b/src/common/CSVFile.cpp @@ -1,6 +1,6 @@ #include "../sphere/threads.h" -#include "CExpression.h" +//#include "CExpression.h" // included in the precompiled header #include "CSVFile.h" #include "common.h" diff --git a/src/common/CScript.cpp b/src/common/CScript.cpp index 67d6d26ca..a8249516f 100644 --- a/src/common/CScript.cpp +++ b/src/common/CScript.cpp @@ -3,8 +3,8 @@ #include "../common/CLog.h" #include "../sphere/threads.h" -#include "CException.h" -#include "CExpression.h" +//#include "CException.h" // included in the precompiled header +//#include "CExpression.h" // included in the precompiled header #include "CScript.h" #include "common.h" diff --git a/src/common/CScriptObj.cpp b/src/common/CScriptObj.cpp index de81b835b..80ae041fe 100644 --- a/src/common/CScriptObj.cpp +++ b/src/common/CScriptObj.cpp @@ -17,10 +17,10 @@ #include "sphere_library/CSRand.h" #include "resource/sections/CResourceNamedDef.h" #include "resource/CResourceLock.h" -#include "CFloatMath.h" -#include "CExpression.h" +//#include "CExpression.h" // included in the precompiled header +//#include "CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "CSFileObjContainer.h" -#include "CScriptParserBufs.h" +#include "CFloatMath.h" #ifdef _WIN32 # include @@ -2584,7 +2584,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScript CScriptExprContext context = {._pScriptObjI = this}; const lptstr ptcArg = s.GetArgStr(); - bool fTrigger = expr_parser.Evaluate_Conditional(ptcArg, context, pScriptArgs, pSrc); + bool fTrigger = expr_parser.EvaluateConditionalWhole(ptcArg, context, pScriptArgs, pSrc); bool fBeenTrue = false; for (;;) { @@ -2602,7 +2602,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScript else if ( iRet == TRIGRET_ELSEIF ) { context = {._pScriptObjI = this}; - fTrigger = expr_parser.Evaluate_Conditional(s.GetArgStr(), context, pScriptArgs, pSrc); + fTrigger = expr_parser.EvaluateConditionalWhole(s.GetArgStr(), context, pScriptArgs, pSrc); } } } diff --git a/src/common/CScriptObj.h b/src/common/CScriptObj.h index f9192ed9b..1384bf85e 100644 --- a/src/common/CScriptObj.h +++ b/src/common/CScriptObj.h @@ -15,8 +15,6 @@ class CSString; class CUID; class CChar; -struct CScriptSubExprState; - class CScriptTriggerArgs; using CScriptTriggerArgsPtr = std::shared_ptr; diff --git a/src/common/CScriptParserBufs.cpp b/src/common/CScriptParserBufs.cpp index 523f620dc..d113785f7 100644 --- a/src/common/CScriptParserBufs.cpp +++ b/src/common/CScriptParserBufs.cpp @@ -2,7 +2,6 @@ #include "CScriptTriggerArgs.h" #include "CLog.h" #include "sphere_library/sobjpool.h" -//#include //for memset struct CScriptParserBufsImpl @@ -15,10 +14,6 @@ struct CScriptParserBufsImpl static constexpr bool sm_allow_fallback_objects = true; - // We stack allocate this POD struct, so we don't need to keep them in a pool. - //sl::ObjectPool - //m_poolCScriptSubExprState; - // This is even more expensive to construct, so will definitely benefit a lot from having allocated, cached instances. sl::ObjectPool m_poolScriptTriggerArgs; diff --git a/src/common/CScriptParserBufs.h b/src/common/CScriptParserBufs.h index 76d8d1ee3..b119073f6 100644 --- a/src/common/CScriptParserBufs.h +++ b/src/common/CScriptParserBufs.h @@ -19,7 +19,6 @@ class CScriptParserBufs ~CScriptParserBufs() noexcept = default; public: - //static CScriptSubExprStatePtr GetCScriptSubExprStatePtr(); static CScriptTriggerArgsPtr GetCScriptTriggerArgsPtr(); }; diff --git a/src/common/CScriptTriggerArgs.cpp b/src/common/CScriptTriggerArgs.cpp index 196aa9099..4fc6dff01 100644 --- a/src/common/CScriptTriggerArgs.cpp +++ b/src/common/CScriptTriggerArgs.cpp @@ -1,7 +1,7 @@ #include "../game/CObjBase.h" #include "CLog.h" -#include "CException.h" -#include "CExpression.h" +//#include "CException.h" // included in the precompiled header +//#include "CExpression.h" // included in the precompiled header #include "CScriptTriggerArgs.h" diff --git a/src/common/CServerMap.cpp b/src/common/CServerMap.cpp index 72a684785..ef51f140c 100644 --- a/src/common/CServerMap.cpp +++ b/src/common/CServerMap.cpp @@ -2,7 +2,7 @@ // CServerMap.cpp // -#include "CException.h" +//#include "CException.h" // included in the precompiled header #include "CUOInstall.h" #include "CServerMap.h" #include "../game/uo_files/CUOItemInfo.h" diff --git a/src/common/CUOInstall.cpp b/src/common/CUOInstall.cpp index f09727661..816c53d00 100644 --- a/src/common/CUOInstall.cpp +++ b/src/common/CUOInstall.cpp @@ -1,7 +1,7 @@ #include "../game/uo_files/CUOMultiItemRec.h" #include "../game/CServerConfig.h" #include "../sphere/threads.h" -#include "CException.h" +//#include "CException.h" // included in the precompiled header #include "CLog.h" #include "CUOInstall.h" #include diff --git a/src/common/CVarDefMap.cpp b/src/common/CVarDefMap.cpp index 09d2b452b..47b6751e8 100644 --- a/src/common/CVarDefMap.cpp +++ b/src/common/CVarDefMap.cpp @@ -2,7 +2,7 @@ #include "../common/CLog.h" #include "../game/CServer.h" #include "../game/CServerConfig.h" -#include "CExpression.h" +//#include "CExpression.h" // included in the precompiled header #include "CScript.h" #include "CTextConsole.h" #include "CVarDefMap.h" diff --git a/src/common/ListDefContMap.cpp b/src/common/ListDefContMap.cpp index 8944b134f..fafb64911 100644 --- a/src/common/ListDefContMap.cpp +++ b/src/common/ListDefContMap.cpp @@ -2,7 +2,7 @@ #include "../common/sphere_library/sstringobjs.h" #include "../common/CLog.h" #include "../sphere/threads.h" -#include "CExpression.h" +//#include "CExpression.h" // included in the precompiled header #include "CScript.h" #include "CTextConsole.h" #include "ListDefContMap.h" diff --git a/src/common/crypto/CCrypto.cpp b/src/common/crypto/CCrypto.cpp index b6846f16b..f51009e8a 100644 --- a/src/common/crypto/CCrypto.cpp +++ b/src/common/crypto/CCrypto.cpp @@ -4,7 +4,7 @@ #include "../../sphere/threads.h" #include "../sphereproto.h" -#include "../CExpression.h" +//#include "../CExpression.h" // included in the precompiled header #include "../CScript.h" #include "../CLog.h" #include "../CUOClientVersion.h" diff --git a/src/common/resource/CResourceDef.cpp b/src/common/resource/CResourceDef.cpp index 26320cbaf..9c4d4ab23 100644 --- a/src/common/resource/CResourceDef.cpp +++ b/src/common/resource/CResourceDef.cpp @@ -4,7 +4,7 @@ */ #include "../../sphere/threads.h" -#include "../CExpression.h" +#include "../CExpression.h" // included in the precompiled header #include "../CLog.h" #include "CResourceDef.h" diff --git a/src/common/resource/CResourceHolder.cpp b/src/common/resource/CResourceHolder.cpp index f1630b6f7..2777cddb1 100644 --- a/src/common/resource/CResourceHolder.cpp +++ b/src/common/resource/CResourceHolder.cpp @@ -1,7 +1,7 @@ #include "../sphere_library/CSFileList.h" -#include "../CException.h" -#include "../CExpression.h" +//#include "../CException.h" // included in the precompiled header +#include "../CExpression.h" // included in the precompiled header #include "../CLog.h" #include "CResourceHolder.h" #include "CResourceHash.h" diff --git a/src/common/resource/CResourceQty.cpp b/src/common/resource/CResourceQty.cpp index 5d03f5458..9e3e2cf92 100644 --- a/src/common/resource/CResourceQty.cpp +++ b/src/common/resource/CResourceQty.cpp @@ -8,7 +8,7 @@ #include "../../game/chars/CChar.h" #include "../../game/CObjBase.h" #include "../../sphere/threads.h" -#include "../CExpression.h" +#include "../CExpression.h" // included in the precompiled header #include "../CLog.h" #include "CResourceQty.h" diff --git a/src/common/resource/CResourceRef.cpp b/src/common/resource/CResourceRef.cpp index d1a20cacb..cd0d1b3b5 100644 --- a/src/common/resource/CResourceRef.cpp +++ b/src/common/resource/CResourceRef.cpp @@ -4,7 +4,7 @@ */ #include "../../game/CServerConfig.h" -#include "../CException.h" +//#include "../CException.h" // included in the precompiled header #include "../CScript.h" #include "CResourceRef.h" diff --git a/src/common/resource/CValueDefs.cpp b/src/common/resource/CValueDefs.cpp index e24ec51c6..65cb07c87 100644 --- a/src/common/resource/CValueDefs.cpp +++ b/src/common/resource/CValueDefs.cpp @@ -1,6 +1,6 @@ #include "../../sphere/threads.h" #include "../sphere_library/CSRand.h" -#include "../CExpression.h" +#include "../CExpression.h" // included in the precompiled header #include "CValueDefs.h" diff --git a/src/common/resource/sections/CDialogDef.cpp b/src/common/resource/sections/CDialogDef.cpp index 5003b896b..f978663a4 100644 --- a/src/common/resource/sections/CDialogDef.cpp +++ b/src/common/resource/sections/CDialogDef.cpp @@ -1,10 +1,10 @@ -#include "../../../game/chars/CChar.h" +#include "../../../game/chars/CChar.h" // needed, even if clangd says the opposite #include "../../../game/clients/CClient.h" #include "../../../game/CObjBase.h" #include "../../../sphere/threads.h" -#include "../../CException.h" -#include "../../CExpression.h" -#include "../../CScriptParserBufs.h" +//#include "../../CException.h" // included in the precompiled header +//#include "../../CExpression.h" // included in the precompiled header +//#include "../../CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../CResourceLock.h" #include "CDialogDef.h" diff --git a/src/common/resource/sections/CItemTypeDef.cpp b/src/common/resource/sections/CItemTypeDef.cpp index ccd232c0c..5b123ae54 100644 --- a/src/common/resource/sections/CItemTypeDef.cpp +++ b/src/common/resource/sections/CItemTypeDef.cpp @@ -1,8 +1,8 @@ #include "../../../game/CServerConfig.h" #include "../../../game/CWorld.h" #include "../../../sphere/threads.h" -#include "../../CException.h" -#include "../../CExpression.h" +//#include "../../CException.h" // included in the precompiled header +//#include "../../CExpression.h" // included in the precompiled header #include "CItemTypeDef.h" int CItemTypeDef::GetItemType() const diff --git a/src/common/resource/sections/CRandGroupDef.cpp b/src/common/resource/sections/CRandGroupDef.cpp index b9d5d5c74..7a1bae0ec 100644 --- a/src/common/resource/sections/CRandGroupDef.cpp +++ b/src/common/resource/sections/CRandGroupDef.cpp @@ -3,8 +3,8 @@ #include "../../../game/CServerConfig.h" #include "../../../game/triggers.h" #include "../../sphere_library/CSRand.h" -#include "../../CException.h" -#include "../../CExpression.h" +//#include "../../CException.h" // included in the precompiled header +//#include "../../CExpression.h" // included in the precompiled header #include "CRegionResourceDef.h" #include "CRandGroupDef.h" diff --git a/src/common/resource/sections/CRegionResourceDef.cpp b/src/common/resource/sections/CRegionResourceDef.cpp index 8f431c40b..a20626a2c 100644 --- a/src/common/resource/sections/CRegionResourceDef.cpp +++ b/src/common/resource/sections/CRegionResourceDef.cpp @@ -1,7 +1,7 @@ #include "../../../sphere/threads.h" #include "../../../game/items/CItemBase.h" #include "../../../game/CServerConfig.h" -#include "../../CException.h" +//#include "../../CException.h" // included in the precompiled header #include "../CResourceLock.h" #include "CRegionResourceDef.h" diff --git a/src/common/resource/sections/CSkillClassDef.cpp b/src/common/resource/sections/CSkillClassDef.cpp index 251021e57..5019b9db2 100644 --- a/src/common/resource/sections/CSkillClassDef.cpp +++ b/src/common/resource/sections/CSkillClassDef.cpp @@ -1,6 +1,6 @@ #include "../../../sphere/threads.h" #include "../../../game/CServerConfig.h" -#include "../../CException.h" +//#include "../../CException.h" // included in the precompiled header #include "CSkillClassDef.h" enum SCC_TYPE diff --git a/src/common/resource/sections/CSkillDef.cpp b/src/common/resource/sections/CSkillDef.cpp index 621a75f53..72c0259bc 100644 --- a/src/common/resource/sections/CSkillDef.cpp +++ b/src/common/resource/sections/CSkillDef.cpp @@ -1,5 +1,5 @@ #include "../../../sphere/threads.h" -#include "../../CException.h" +//#include "../../CException.h" // included in the precompiled header #include "../../CScript.h" #include "CSkillDef.h" diff --git a/src/common/resource/sections/CSpellDef.cpp b/src/common/resource/sections/CSpellDef.cpp index 9bc0d5ed8..9d777c516 100644 --- a/src/common/resource/sections/CSpellDef.cpp +++ b/src/common/resource/sections/CSpellDef.cpp @@ -1,8 +1,8 @@ #include "../../../game/CServerConfig.h" #include "../../../game/game_macros.h" #include "../../../game/uo_files/uofiles_enums.h" -#include "../../CException.h" -#include "../../CExpression.h" +//#include "../../CException.h" // included in the precompiled header +//#include "../../CExpression.h" // included in the precompiled header #include "CSpellDef.h" diff --git a/src/common/resource/sections/CWebPageDef.cpp b/src/common/resource/sections/CWebPageDef.cpp index b31a90784..b4cfc19a8 100644 --- a/src/common/resource/sections/CWebPageDef.cpp +++ b/src/common/resource/sections/CWebPageDef.cpp @@ -6,9 +6,9 @@ #include "../../../game/CWorldGameTime.h" #include "../../../network/CClientIterator.h" #include "../../sphere_library/CSFileList.h" -#include "../../CException.h" -#include "../../CExpression.h" -#include "../../CScriptParserBufs.h" +//#include "../../CException.h" // included in the precompiled header +//#include "../../CExpression.h" // included in the precompiled header +//#include "../../CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../sphereversion.h" #include "../CResourceLock.h" #include "CWebPageDef.h" diff --git a/src/common/sphere_library/CSAssoc.cpp b/src/common/sphere_library/CSAssoc.cpp index 3831d757f..d2e0722a4 100644 --- a/src/common/sphere_library/CSAssoc.cpp +++ b/src/common/sphere_library/CSAssoc.cpp @@ -1,6 +1,6 @@ #include "CSAssoc.h" -#include "../CExpression.h" +#include "../CExpression.h" // included in the precompiled header #include "../common.h" diff --git a/src/common/sphere_library/CSObjCont.cpp b/src/common/sphere_library/CSObjCont.cpp index 75ae6370d..4985480cb 100644 --- a/src/common/sphere_library/CSObjCont.cpp +++ b/src/common/sphere_library/CSObjCont.cpp @@ -1,5 +1,5 @@ #include "../assertion.h" -#include "../CException.h" +//#include "../CException.h" // included in the precompiled header #include "CSObjCont.h" #include diff --git a/src/common/sphere_library/CSObjList.cpp b/src/common/sphere_library/CSObjList.cpp index a66cad392..b31336c87 100644 --- a/src/common/sphere_library/CSObjList.cpp +++ b/src/common/sphere_library/CSObjList.cpp @@ -1,6 +1,6 @@ #include "CSObjList.h" #include "../assertion.h" -#include "../CException.h" +//#include "../CException.h" // included in the precompiled header // CSObjListRec:: Constructors, Destructor, Assign operator. diff --git a/src/common/sphere_library/smap.h b/src/common/sphere_library/smap.h index 9ad7ceb51..ef87eddaf 100644 --- a/src/common/sphere_library/smap.h +++ b/src/common/sphere_library/smap.h @@ -8,7 +8,7 @@ #include #include -#include "../CException.h" +//#include "../CException.h" // included in the precompiled header #define _SPHERE_MAP_DEFAULT_SIZE 10 diff --git a/src/common/sphere_library/sobjpool.h b/src/common/sphere_library/sobjpool.h index 0ce864bb2..734f88f2d 100644 --- a/src/common/sphere_library/sobjpool.h +++ b/src/common/sphere_library/sobjpool.h @@ -26,6 +26,8 @@ class ObjectPool { public: using index_t = uint32_t; // do we really need size_t elements? + static_assert(tp_pool_size <= std::numeric_limits::max()); + static constexpr size_t sm_pool_size = static_cast(tp_pool_size); static constexpr bool sm_allow_fallback = tp_allow_fallback; @@ -99,7 +101,7 @@ class ObjectPool { // Return a fallback (dynamically allocated) object. T* ptr = new T(); - m_fallbackObjsCount += 1;; + m_fallbackObjsCount += 1; return UniquePtr_t(ptr, PoolDeleter {this, std::nullopt}); } else @@ -154,6 +156,7 @@ class ObjectPool [[nodiscard]] static bool isFromPool(const std::shared_ptr &ptr) { + // Does std::get_deleter work only with RTTI enabled? if (auto deleterPtr = std::get_deleter(ptr)) return deleterPtr->m_index.has_value(); return false; diff --git a/src/common/sphere_library/squeues.h b/src/common/sphere_library/squeues.h index e85ea2f01..43d212991 100644 --- a/src/common/sphere_library/squeues.h +++ b/src/common/sphere_library/squeues.h @@ -7,7 +7,7 @@ #define _INC_SQUEUES_H #include -#include "../CException.h" +//#include "../CException.h" // included in the precompiled header #define _SPHERE_QUEUE_DEFAULT_SIZE 10 diff --git a/src/common/sphere_library/sstacks.h b/src/common/sphere_library/sstacks.h index 7f8eed1f8..587f5157b 100644 --- a/src/common/sphere_library/sstacks.h +++ b/src/common/sphere_library/sstacks.h @@ -7,7 +7,7 @@ #define _INC_CSTACK_H #include -#include "../CException.h" +//#include "../CException.h" // included in the precompiled header namespace sl { diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index ba36c499c..da7b23e4b 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -1,7 +1,7 @@ #include "sstring.h" //#include "../../common/CLog.h" #include "../../sphere/ProfileTask.h" -#include "../CExpression.h" +#include "../CExpression.h" // included in the precompiled header #ifdef MSVC_COMPILER diff --git a/src/common/sphere_library/sstringobjs.cpp b/src/common/sphere_library/sstringobjs.cpp index d7714bbe0..c7d9e2dfc 100644 --- a/src/common/sphere_library/sstringobjs.cpp +++ b/src/common/sphere_library/sstringobjs.cpp @@ -1,5 +1,5 @@ #include "../../sphere/threads.h" -#include "../CException.h" +//#include "../CException.h" // included in the precompiled header #include "sstringobjs.h" /* diff --git a/src/common/sqlite/SQLite.cpp b/src/common/sqlite/SQLite.cpp index 7e2595107..63fc1fe07 100644 --- a/src/common/sqlite/SQLite.cpp +++ b/src/common/sqlite/SQLite.cpp @@ -1,7 +1,7 @@ #include "../../common/CLog.h" #include "../../sphere/threads.h" -#include "../CExpression.h" -#include "../CException.h" +#include "../CExpression.h" // included in the precompiled header +//#include "../CException.h" // included in the precompiled header #include "../CScript.h" #include "SQLite.h" #include diff --git a/src/game/CBase.cpp b/src/game/CBase.cpp index 15ce568bf..a078541a8 100644 --- a/src/game/CBase.cpp +++ b/src/game/CBase.cpp @@ -1,6 +1,6 @@ -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/common.h" #include "CServerConfig.h" #include "CBase.h" diff --git a/src/game/CContainer.cpp b/src/game/CContainer.cpp index 52d86bdb0..f5f61e8f0 100644 --- a/src/game/CContainer.cpp +++ b/src/game/CContainer.cpp @@ -1,7 +1,7 @@ #include "../common/sphere_library/CSRand.h" -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/CUID.h" #include "../network/send.h" #include "chars/CChar.h" diff --git a/src/game/CEntity.cpp b/src/game/CEntity.cpp index 923bbf137..6db19a075 100644 --- a/src/game/CEntity.cpp +++ b/src/game/CEntity.cpp @@ -1,5 +1,5 @@ #include "../sphere/threads.h" -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header #include "../common/CLog.h" #include "../common/CScript.h" #include "CEntity.h" diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index 1e6fbb4b3..6633bf7cd 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -1,8 +1,8 @@ #include "../common/sphere_library/CSRand.h" #include "../common/resource/CResourceLock.h" -#include "../common/CException.h" -#include "../common/CExpression.h" -#include "../common/CScriptParserBufs.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../common/CLog.h" #include "../common/sphereversion.h" #include "../network/CClientIterator.h" diff --git a/src/game/CPathFinder.cpp b/src/game/CPathFinder.cpp index 012992e9a..5ef3fe530 100644 --- a/src/game/CPathFinder.cpp +++ b/src/game/CPathFinder.cpp @@ -1,5 +1,5 @@ -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header #include "../sphere/threads.h" #include "chars/CChar.h" #include "CPathFinder.h" diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 8e2923dd9..9b0d4670a 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -1,8 +1,8 @@ // Common for client and server. #include "../common/resource/sections/CRandGroupDef.h" #include "../common/resource/CResourceLock.h" -#include "../common/CExpression.h" -#include "../common/CScriptParserBufs.h" +//#include "../common/CExpression.h" // included in the precompiled header +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../network/CClientIterator.h" #include "chars/CChar.h" #include "clients/CClient.h" diff --git a/src/game/CResourceCalc.cpp b/src/game/CResourceCalc.cpp index ad4a78e0c..f9987ea3f 100644 --- a/src/game/CResourceCalc.cpp +++ b/src/game/CResourceCalc.cpp @@ -3,7 +3,7 @@ #include "../common/sphere_library/CSRand.h" #include "chars/CChar.h" #include "chars/CCharNPC.h" -#include "../common/CExpression.h" +//#include "../common/CExpression.h" // included in the precompiled header #include "components/CCPropsChar.h" #include "items/CItem.h" #include "CServerConfig.h" diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index 0e170d1fe..f9b556da4 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -1,6 +1,6 @@ #include "../common/sphere_library/CSRand.h" -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/CLog.h" #include "../sphere/ProfileTask.h" #include "../sphere/ProfileData.h" diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index 79acf97df..f5591a2ef 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -1,4 +1,4 @@ -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header #include "../common/CLog.h" #include "../common/CRect.h" #include "../game/CWorld.h" diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index ed00675f7..0d12fa698 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -13,10 +13,10 @@ #include "../common/sphere_library/sstring.h" #include "../common/crypto/CCryptoKeyCalc.h" -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../common/CLog.h" -#include "../common/CScriptParserBufs.h" #include "../common/CUOClientVersion.h" #include "../common/sphereversion.h" #include "../network/CClientIterator.h" diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 9acd51753..0842e31ed 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -5,8 +5,8 @@ #include "../common/resource/sections/CRegionResourceDef.h" #include "../common/resource/sections/CResourceNamedDef.h" #include "../common/sphere_library/CSRand.h" -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/CUOInstall.h" #include "../common/sphereversion.h" #include "../network/CClientIterator.h" diff --git a/src/game/CServerDef.cpp b/src/game/CServerDef.cpp index ad30d5b73..c14027a80 100644 --- a/src/game/CServerDef.cpp +++ b/src/game/CServerDef.cpp @@ -1,10 +1,10 @@ -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../common/sphereproto.h" #include "../common/sphereversion.h" #include "../common/CLog.h" -#include "../common/CScriptParserBufs.h" #include "../sphere/threads.h" #include "CServer.h" #include "CServerConfig.h" diff --git a/src/game/CServerTime.cpp b/src/game/CServerTime.cpp index 4e7ef22ce..05f04c3f2 100644 --- a/src/game/CServerTime.cpp +++ b/src/game/CServerTime.cpp @@ -1,4 +1,4 @@ -#include "../common/CExpression.h" +//#include "../common/CExpression.h" // included in the precompiled header #include "CServerConfig.h" #include "CServerTime.h" diff --git a/src/game/CTimedObject.cpp b/src/game/CTimedObject.cpp index 14c830925..245e9a446 100644 --- a/src/game/CTimedObject.cpp +++ b/src/game/CTimedObject.cpp @@ -1,4 +1,4 @@ -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header #include "../sphere/ProfileTask.h" #include "../sphere/threads.h" #include "CWorldGameTime.h" diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index ae08f2862..4642c673c 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -1,6 +1,6 @@ -#include "../common/CException.h" -#include "../common/CExpression.h" -#include "../common/CScriptParserBufs.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../common/CLog.h" #include "../common/sphereversion.h" #include "../network/CClientIterator.h" @@ -903,7 +903,7 @@ bool CWorld::SaveStage() // Save world state in stages. g_Log.Event(LOGM_SAVE, "World save completed, took %s seconds.\n", ptcTime); - CScriptTriggerArgsPtr pScriptArgs; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); pScriptArgs->Init(ptcTime); g_Serv.r_Call("f_onserver_save_finished", pScriptArgs, &g_Serv); diff --git a/src/game/CWorldComm.cpp b/src/game/CWorldComm.cpp index d7ccb051c..db9f003d6 100644 --- a/src/game/CWorldComm.cpp +++ b/src/game/CWorldComm.cpp @@ -1,5 +1,5 @@ #include "../common/sphere_library/CSRand.h" -#include "../common/CScriptParserBufs.h" +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../network/CClientIterator.h" #include "chars/CChar.h" #include "clients/CClient.h" diff --git a/src/game/CWorldImport.cpp b/src/game/CWorldImport.cpp index 98ee09c38..c8e74c450 100644 --- a/src/game/CWorldImport.cpp +++ b/src/game/CWorldImport.cpp @@ -1,4 +1,4 @@ -#include "../common/CExpression.h" +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/CLog.h" #include "chars/CChar.h" #include "items/CItem.h" diff --git a/src/game/CWorldMap.cpp b/src/game/CWorldMap.cpp index 5c45eed36..99d11bae5 100644 --- a/src/game/CWorldMap.cpp +++ b/src/game/CWorldMap.cpp @@ -1,8 +1,8 @@ #include "../common/resource/sections/CItemTypeDef.h" #include "../common/resource/sections/CRandGroupDef.h" #include "../common/resource/sections/CRegionResourceDef.h" -#include "../common/CException.h" -#include "../common/CScriptParserBufs.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../common/CRect.h" #include "../common/CLog.h" #include "../sphere/threads.h" diff --git a/src/game/CWorldSearch.cpp b/src/game/CWorldSearch.cpp index 91fdecb74..1665f9c08 100644 --- a/src/game/CWorldSearch.cpp +++ b/src/game/CWorldSearch.cpp @@ -1,4 +1,4 @@ -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header #include "../common/CLog.h" #include "../game/chars/CChar.h" #include "../game/items/CItem.h" diff --git a/src/game/CWorldTicker.cpp b/src/game/CWorldTicker.cpp index 3b55fcbe2..31049f182 100644 --- a/src/game/CWorldTicker.cpp +++ b/src/game/CWorldTicker.cpp @@ -1,5 +1,5 @@ #include "../common/sphere_library/scontainer_ops.h" -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header #include "../sphere/threads.h" #include "../sphere/ProfileTask.h" #include "chars/CChar.h" diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index ca485d6a8..16e9e35fc 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -2,9 +2,9 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/resource/CResourceLock.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CUID.h" #include "../../common/CRect.h" #include "../../common/CLog.h" diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 7ed21e89c..a7a835d00 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -1,8 +1,8 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CUOInstall.h" #include "../../network/CClientIterator.h" #include "../../network/send.h" diff --git a/src/game/chars/CCharAttacker.cpp b/src/game/chars/CCharAttacker.cpp index ca1d48ec5..0989818b8 100644 --- a/src/game/chars/CCharAttacker.cpp +++ b/src/game/chars/CCharAttacker.cpp @@ -1,6 +1,7 @@ // Actions specific to an NPC. -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" + +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../clients/CClient.h" #include "../triggers.h" #include "CChar.h" diff --git a/src/game/chars/CCharBase.cpp b/src/game/chars/CCharBase.cpp index 30ecdb6e2..3a61720f7 100644 --- a/src/game/chars/CCharBase.cpp +++ b/src/game/chars/CCharBase.cpp @@ -1,6 +1,6 @@ #include "../../common/resource/CResourceLock.h" -#include "../../common/CException.h" +//#include "../../common/CException.h" // included in the precompiled header #include "../../common/CLog.h" #include "../components/CCPropsChar.h" #include "../components/CCPropsItemChar.h" diff --git a/src/game/chars/CCharFight.cpp b/src/game/chars/CCharFight.cpp index b76aa4381..be2435fdd 100644 --- a/src/game/chars/CCharFight.cpp +++ b/src/game/chars/CCharFight.cpp @@ -2,8 +2,8 @@ // Fight/Criminal actions/Noto. #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/send.h" #include "../clients/CClient.h" #include "../components/CCPropsChar.h" diff --git a/src/game/chars/CCharMemory.cpp b/src/game/chars/CCharMemory.cpp index 5cf0c867c..e823b16ee 100644 --- a/src/game/chars/CCharMemory.cpp +++ b/src/game/chars/CCharMemory.cpp @@ -1,7 +1,7 @@ - // Actions specific to an NPC. -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" + +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/send.h" #include "../items/CItemMemory.h" #include "../items/CItemStone.h" diff --git a/src/game/chars/CCharNPC.cpp b/src/game/chars/CCharNPC.cpp index 26d5c47ca..3d844ab8b 100644 --- a/src/game/chars/CCharNPC.cpp +++ b/src/game/chars/CCharNPC.cpp @@ -1,8 +1,7 @@ // Actions specific to an NPC. #include "../../common/resource/CResourceLock.h" -#include "../../common/CException.h" -//#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header #include "../clients/CClient.h" #include "../items/CItemContainer.h" #include "../CServer.h" diff --git a/src/game/chars/CCharNPCAct.cpp b/src/game/chars/CCharNPCAct.cpp index 5d7f1d1d6..04df2bf60 100644 --- a/src/game/chars/CCharNPCAct.cpp +++ b/src/game/chars/CCharNPCAct.cpp @@ -3,8 +3,8 @@ #include "../../common/sphere_library/CSRand.h" #include "../../common/resource/CResourceLock.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header #include "../../common/CScriptParserBufs.h" #include "../../network/receive.h" #include "../clients/CClient.h" diff --git a/src/game/chars/CCharNPCAct_Fight.cpp b/src/game/chars/CCharNPCAct_Fight.cpp index b606cce01..dbf211d3c 100644 --- a/src/game/chars/CCharNPCAct_Fight.cpp +++ b/src/game/chars/CCharNPCAct_Fight.cpp @@ -1,7 +1,8 @@ // Actions specific to an NPC. + #include "../../common/sphere_library/CSRand.h" #include "../../common/CLog.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../components/CCPropsItemWeapon.h" #include "../items/item_types.h" #include "../uo_files/uofiles_enums_creid.h" diff --git a/src/game/chars/CCharNPCAct_Magic.cpp b/src/game/chars/CCharNPCAct_Magic.cpp index be86aeefd..79e809b52 100644 --- a/src/game/chars/CCharNPCAct_Magic.cpp +++ b/src/game/chars/CCharNPCAct_Magic.cpp @@ -1,7 +1,7 @@ // Actions specific to an NPC. #include "../../common/sphere_library/CSRand.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../items/CItemContainer.h" #include "../items/CItemMemory.h" #include "../triggers.h" diff --git a/src/game/chars/CCharNPCAct_Vendor.cpp b/src/game/chars/CCharNPCAct_Vendor.cpp index f9406109f..215dc4328 100644 --- a/src/game/chars/CCharNPCAct_Vendor.cpp +++ b/src/game/chars/CCharNPCAct_Vendor.cpp @@ -1,6 +1,6 @@ // Actions specific to an NPC. -#include "../../common/CExpression.h" +//#include "../../common/CExpression.h" // included in the precompiled header #include "../clients/CClient.h" #include "../items/CItemContainer.h" #include "../items/CItemMemory.h" diff --git a/src/game/chars/CCharNPCPet.cpp b/src/game/chars/CCharNPCPet.cpp index 466be7d9d..8a870208a 100644 --- a/src/game/chars/CCharNPCPet.cpp +++ b/src/game/chars/CCharNPCPet.cpp @@ -1,8 +1,7 @@ // Actions specific to an NPC. #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -//#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header #include "../clients/CClient.h" #include "../components/CCSpawn.h" #include "../items/CItemContainer.h" diff --git a/src/game/chars/CCharNPCStatus.cpp b/src/game/chars/CCharNPCStatus.cpp index 6f3a9d46f..4d3724838 100644 --- a/src/game/chars/CCharNPCStatus.cpp +++ b/src/game/chars/CCharNPCStatus.cpp @@ -1,7 +1,7 @@ // Test things to judge what an NPC might be thinking. (want to do) // But take no actions here. -#include "../../common/CExpression.h" +//#include "../../common/CExpression.h" // included in the precompiled header #include "../../common/CLog.h" #include "../items/CItemContainer.h" #include "../items/CItemMemory.h" diff --git a/src/game/chars/CCharNotoriety.cpp b/src/game/chars/CCharNotoriety.cpp index 26047afc1..5b844011e 100644 --- a/src/game/chars/CCharNotoriety.cpp +++ b/src/game/chars/CCharNotoriety.cpp @@ -1,7 +1,7 @@ // Actions specific to an NPC. -#include "../../common/CExpression.h" -#include "../../common/CException.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../items/CItemMemory.h" #include "../items/CItemStone.h" #include "../triggers.h" diff --git a/src/game/chars/CCharPlayer.cpp b/src/game/chars/CCharPlayer.cpp index 5fa0e5ba6..2c3f98fda 100644 --- a/src/game/chars/CCharPlayer.cpp +++ b/src/game/chars/CCharPlayer.cpp @@ -3,8 +3,8 @@ #include "../../common/resource/sections/CSkillClassDef.h" #include "../../common/CLog.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header #include "../clients/CClient.h" #include "../items/CItemMulti.h" #include "../items/CItemStone.h" diff --git a/src/game/chars/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index 2bda0ce4e..c1142ba98 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -4,8 +4,8 @@ #include "../../common/resource/sections/CRegionResourceDef.h" #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" #include "../clients/CClient.h" #include "../items/CItemCorpse.h" diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index 6a5dbc413..42efc6da3 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -1,6 +1,6 @@ #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/CClientIterator.h" #include "../../network/send.h" #include "../components/CCPropsChar.h" diff --git a/src/game/chars/CCharStat.cpp b/src/game/chars/CCharStat.cpp index 13c6cc0ba..5307601bc 100644 --- a/src/game/chars/CCharStat.cpp +++ b/src/game/chars/CCharStat.cpp @@ -1,9 +1,9 @@ // CChar is either an NPC or a Player. #include "../../common/sphere_library/CSRand.h" #include "../../common/resource/sections/CSkillClassDef.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../items/CItem.h" #include "../triggers.h" #include "../CServer.h" diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index 0277ab0e6..b54da41f0 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -1,7 +1,7 @@ // CChar is either an NPC or a Player. -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" #include "../components/CCPropsItemEquippable.h" #include "../components/CCPropsItemWeapon.h" diff --git a/src/game/chars/CCharUse.cpp b/src/game/chars/CCharUse.cpp index c60270147..7645b2dc2 100644 --- a/src/game/chars/CCharUse.cpp +++ b/src/game/chars/CCharUse.cpp @@ -1,7 +1,7 @@ // CChar is either an NPC or a Player. #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../clients/CClient.h" #include "../items/CItem.h" #include "../items/CItemCorpse.h" diff --git a/src/game/chars/CStoneMember.cpp b/src/game/chars/CStoneMember.cpp index 14be7e31e..d7401a285 100644 --- a/src/game/chars/CStoneMember.cpp +++ b/src/game/chars/CStoneMember.cpp @@ -1,5 +1,5 @@ -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header #include "../../common/CLog.h" #include "../chars/CChar.h" #include "../items/CItemStone.h" diff --git a/src/game/clients/CAccount.cpp b/src/game/clients/CAccount.cpp index f89be63b5..786202a45 100644 --- a/src/game/clients/CAccount.cpp +++ b/src/game/clients/CAccount.cpp @@ -1,9 +1,9 @@ #include "../../common/crypto/CMD5.h" #include "../../common/sphere_library/CSRand.h" #include "../../common/CLog.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/CClientIterator.h" #include "../chars/CChar.h" #include "../CServer.h" diff --git a/src/game/clients/CClient.cpp b/src/game/clients/CClient.cpp index 3aa0ef42d..7eadd2431 100644 --- a/src/game/clients/CClient.cpp +++ b/src/game/clients/CClient.cpp @@ -1,9 +1,9 @@ #include "../../common/resource/sections/CResourceNamedDef.h" #include "../../common/sphere_library/CSRand.h" #include "../../common/CLog.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CUOClientVersion.h" #include "../../network/CClientIterator.h" #include "../../network/CNetworkManager.h" diff --git a/src/game/clients/CClientDialog.cpp b/src/game/clients/CClientDialog.cpp index 839ab8240..613ef9260 100644 --- a/src/game/clients/CClientDialog.cpp +++ b/src/game/clients/CClientDialog.cpp @@ -1,8 +1,8 @@ #include "../../common/resource/sections/CDialogDef.h" #include "../../common/resource/CResourceLock.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" #include "../../network/receive.h" #include "../../network/send.h" diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index 1fe13af97..e16ff2ee5 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -1,7 +1,7 @@ #include "../../common/resource/CResourceLock.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/CClientIterator.h" #include "../../network/receive.h" #include "../../network/send.h" diff --git a/src/game/clients/CClientLog.cpp b/src/game/clients/CClientLog.cpp index c698d7bec..aeb38df4d 100644 --- a/src/game/clients/CClientLog.cpp +++ b/src/game/clients/CClientLog.cpp @@ -4,9 +4,9 @@ #include "../../common/crypto/CHuffman.h" #include "../../common/sphere_library/CSFileList.h" #include "../../common/CLog.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/CIPHistoryManager.h" #include "../../network/CNetworkManager.h" #include "../../network/send.h" diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index cc5464322..76df2c6e3 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -3,9 +3,9 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/send.h" #include "../chars/CChar.h" #include "../chars/CCharNPC.h" diff --git a/src/game/clients/CClientMsg_AOSTooltip.cpp b/src/game/clients/CClientMsg_AOSTooltip.cpp index ae41d9701..522ceed5f 100644 --- a/src/game/clients/CClientMsg_AOSTooltip.cpp +++ b/src/game/clients/CClientMsg_AOSTooltip.cpp @@ -1,5 +1,5 @@ -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../chars/CChar.h" #include "../chars/CCharNPC.h" #include "../clients/CClientTooltip.h" diff --git a/src/game/clients/CClientTarg.cpp b/src/game/clients/CClientTarg.cpp index e7956fd36..a4e79563d 100644 --- a/src/game/clients/CClientTarg.cpp +++ b/src/game/clients/CClientTarg.cpp @@ -3,8 +3,8 @@ #include "../../network/send.h" #include "../../common/resource/sections/CItemTypeDef.h" #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" #include "../chars/CChar.h" #include "../items/CItemCorpse.h" diff --git a/src/game/clients/CClientUse.cpp b/src/game/clients/CClientUse.cpp index 8e599a7fe..8ea3d58c8 100644 --- a/src/game/clients/CClientUse.cpp +++ b/src/game/clients/CClientUse.cpp @@ -1,8 +1,8 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" #include "../../network/send.h" #include "../chars/CChar.h" diff --git a/src/game/clients/CGMPage.cpp b/src/game/clients/CGMPage.cpp index 984ce8518..46412e580 100644 --- a/src/game/clients/CGMPage.cpp +++ b/src/game/clients/CGMPage.cpp @@ -1,5 +1,5 @@ -#include "../../common/CException.h" +//#include "../../common/CException.h" // included in the precompiled header #include "../chars/CChar.h" #include "../CWorld.h" #include "../CWorldGameTime.h" diff --git a/src/game/clients/CParty.cpp b/src/game/clients/CParty.cpp index 689b6198e..6f2b7fbac 100644 --- a/src/game/clients/CParty.cpp +++ b/src/game/clients/CParty.cpp @@ -1,7 +1,7 @@ -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" #include "../../common/CScriptObj.h" #include "../../network/send.h" diff --git a/src/game/components/CCChampion.cpp b/src/game/components/CCChampion.cpp index d87539769..469dae834 100644 --- a/src/game/components/CCChampion.cpp +++ b/src/game/components/CCChampion.cpp @@ -13,8 +13,8 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" #include "../../common/CScript.h" #include "../chars/CChar.h" diff --git a/src/game/components/CCMultiMovable.cpp b/src/game/components/CCMultiMovable.cpp index 557aa87b8..4e51d8e39 100644 --- a/src/game/components/CCMultiMovable.cpp +++ b/src/game/components/CCMultiMovable.cpp @@ -1,6 +1,6 @@ #include "../../common/sphere_library/CSRand.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/CClientIterator.h" #include "../../network/send.h" #include "../clients/CClient.h" diff --git a/src/game/components/CCSpawn.cpp b/src/game/components/CCSpawn.cpp index b87ab61df..fdcf72df2 100644 --- a/src/game/components/CCSpawn.cpp +++ b/src/game/components/CCSpawn.cpp @@ -1,9 +1,9 @@ #include "../../common/resource/sections/CRandGroupDef.h" #include "../../common/sphere_library/CSRand.h" #include "../../common/CLog.h" -#include "../../common/CScriptParserBufs.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../chars/CChar.h" #include "../chars/CCharNPC.h" #include "../CObjBase.h" diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 50186c57a..d4fb5d22e 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -1,8 +1,8 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../network/CClientIterator.h" #include "../../network/send.h" #include "../components/CCChampion.h" diff --git a/src/game/items/CItemBase.cpp b/src/game/items/CItemBase.cpp index 1d888e94a..9b1f150f0 100644 --- a/src/game/items/CItemBase.cpp +++ b/src/game/items/CItemBase.cpp @@ -4,8 +4,8 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/CUOInstall.h" #include "../../common/CLog.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header #include "../../sphere/ProfileTask.h" #include "../../sphere/threads.h" #include "../components/CCPropsItem.h" diff --git a/src/game/items/CItemCommCrystal.cpp b/src/game/items/CItemCommCrystal.cpp index ce01f2d59..c9e1ad389 100644 --- a/src/game/items/CItemCommCrystal.cpp +++ b/src/game/items/CItemCommCrystal.cpp @@ -1,5 +1,5 @@ #include "../../common/resource/CResourceLock.h" -//#include "../../common/CException.h" +//#include "../../common/CException.h" // included in the precompiled header #include "../CSector.h" #include "CItemVendable.h" #include "CItemCommCrystal.h" diff --git a/src/game/items/CItemContainer.cpp b/src/game/items/CItemContainer.cpp index 6b9f27101..cb3c380bb 100644 --- a/src/game/items/CItemContainer.cpp +++ b/src/game/items/CItemContainer.cpp @@ -1,7 +1,7 @@ #include "../../common/sphere_library/CSRand.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" #include "../../network/send.h" #include "../chars/CChar.h" diff --git a/src/game/items/CItemCorpse.cpp b/src/game/items/CItemCorpse.cpp index d74f94c20..a5fa00a7f 100644 --- a/src/game/items/CItemCorpse.cpp +++ b/src/game/items/CItemCorpse.cpp @@ -1,5 +1,5 @@ -//#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header #include "../../common/CLog.h" #include "../../common/sphereproto.h" #include "../chars/CChar.h" diff --git a/src/game/items/CItemMap.cpp b/src/game/items/CItemMap.cpp index 07d7a6735..c4b2601e9 100644 --- a/src/game/items/CItemMap.cpp +++ b/src/game/items/CItemMap.cpp @@ -1,6 +1,6 @@ -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header #include "CItemMap.h" ///////////////////////////////////////////////////////////////////////////// diff --git a/src/game/items/CItemMessage.cpp b/src/game/items/CItemMessage.cpp index d93e66b09..09788eeb0 100644 --- a/src/game/items/CItemMessage.cpp +++ b/src/game/items/CItemMessage.cpp @@ -1,6 +1,6 @@ -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header #include "CItemMessage.h" #include "CItemVendable.h" diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index 37bd14ff2..d110173a2 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -1,8 +1,8 @@ #include "../../common/resource/CResourceLock.h" #include "../../common/sphere_library/CSRand.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/sphereproto.h" #include "../chars/CChar.h" #include "../clients/CClient.h" diff --git a/src/game/items/CItemMultiCustom.cpp b/src/game/items/CItemMultiCustom.cpp index a983b3759..f257e584f 100644 --- a/src/game/items/CItemMultiCustom.cpp +++ b/src/game/items/CItemMultiCustom.cpp @@ -4,9 +4,9 @@ #include "../../common/resource/sections/CDialogDef.h" #include "../../common/CLog.h" -#include "../../common/CException.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CUOInstall.h" #include "../../network/send.h" #include "../chars/CChar.h" diff --git a/src/game/items/CItemPlant.cpp b/src/game/items/CItemPlant.cpp index f7e252bb7..ebe997ebe 100644 --- a/src/game/items/CItemPlant.cpp +++ b/src/game/items/CItemPlant.cpp @@ -1,6 +1,6 @@ #include "../../common/resource/CResourceID.h" -#include "../../common/CExpression.h" -#include "../../common/CScriptParserBufs.h" +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../chars/CChar.h" #include "../triggers.h" #include "../CServer.h" diff --git a/src/game/items/CItemShip.cpp b/src/game/items/CItemShip.cpp index 549c78307..a997e97d0 100644 --- a/src/game/items/CItemShip.cpp +++ b/src/game/items/CItemShip.cpp @@ -2,8 +2,8 @@ // CItemShip.cpp // -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header #include "../chars/CChar.h" #include "../CServer.h" #include "../CWorldSearch.h" diff --git a/src/game/items/CItemStone.cpp b/src/game/items/CItemStone.cpp index b7cabbf5e..e50802ead 100644 --- a/src/game/items/CItemStone.cpp +++ b/src/game/items/CItemStone.cpp @@ -1,8 +1,8 @@ -#include "../../common/CException.h" -#include "../../common/CExpression.h" +//#include "../../common/CException.h" // included in the precompiled header +//#include "../../common/CExpression.h" // included in the precompiled header +//#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../../common/CLog.h" -#include "../../common/CScriptParserBufs.h" #include "../chars/CChar.h" #include "../CServer.h" #include "../CWorld.h" diff --git a/src/game/items/CItemVendable.cpp b/src/game/items/CItemVendable.cpp index 4d6a06ad6..05ea51752 100644 --- a/src/game/items/CItemVendable.cpp +++ b/src/game/items/CItemVendable.cpp @@ -1,5 +1,5 @@ -#include "../../common/CException.h" +//#include "../../common/CException.h" // included in the precompiled header #include "CItemVendable.h" CItemVendable::CItemVendable( ITEMID_TYPE id, CItemBase * pDef ) : diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 7e5e6e6e1..4e2bf92b8 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -17,8 +17,8 @@ #include "../common/sphere_library/CSRand.h" #include "../common/CLog.h" -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/CUOInstall.h" #include "../common/sphereversion.h" #include "../network/CNetworkManager.h" diff --git a/src/game/uo_files/CUOMobtypes.cpp b/src/game/uo_files/CUOMobtypes.cpp index 131f4d720..1054dafe8 100644 --- a/src/game/uo_files/CUOMobtypes.cpp +++ b/src/game/uo_files/CUOMobtypes.cpp @@ -4,7 +4,7 @@ */ #include "../../sphere/threads.h" -#include "../../common/CExpression.h" +//#include "../../common/CExpression.h" // included in the precompiled header #include "../../common/CLog.h" #include "../../common/CUOInstall.h" #include "../../game/uo_files/uofiles_enums_creid.h" diff --git a/src/game/uo_files/CUOTiledata.cpp b/src/game/uo_files/CUOTiledata.cpp index d3af4a5ee..bc8de9aeb 100644 --- a/src/game/uo_files/CUOTiledata.cpp +++ b/src/game/uo_files/CUOTiledata.cpp @@ -4,7 +4,7 @@ */ #include "../../sphere/threads.h" -#include "../../common/CException.h" +//#include "../../common/CException.h" // included in the precompiled header #include "../../common/CLog.h" #include "../../common/CUOInstall.h" #include "CUOTerrainTypeRec.h" diff --git a/src/network/CIPHistoryManager.cpp b/src/network/CIPHistoryManager.cpp index bf0f48bcb..c6328b4ed 100644 --- a/src/network/CIPHistoryManager.cpp +++ b/src/network/CIPHistoryManager.cpp @@ -1,4 +1,4 @@ -#include "../common/CScriptParserBufs.h" +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../game/CServer.h" #include "../game/CServerConfig.h" #include "../game/CWorldGameTime.h" diff --git a/src/network/CNetworkManager.cpp b/src/network/CNetworkManager.cpp index b6d539492..150d89f78 100644 --- a/src/network/CNetworkManager.cpp +++ b/src/network/CNetworkManager.cpp @@ -1,4 +1,4 @@ -#include "../common/CScriptParserBufs.h" +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../game/clients/CClient.h" #include "../game/CServer.h" #include "../game/CServerConfig.h" diff --git a/src/network/receive.cpp b/src/network/receive.cpp index 216f91382..254b4720e 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -1,7 +1,7 @@ #include "../common/resource/sections/CDialogDef.h" -#include "../common/CExpression.h" -#include "../common/CScriptParserBufs.h" +//#include "../common/CExpression.h" // included in the precompiled header +//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../common/CLog.h" #include "../common/CUOClientVersion.h" #include "../game/uo_files/uofiles_enums_creid.h" diff --git a/src/network/send.cpp b/src/network/send.cpp index 50856ed42..9bb91d1b9 100644 --- a/src/network/send.cpp +++ b/src/network/send.cpp @@ -1,6 +1,6 @@ #include "../common/resource/CResourceLock.h" -#include "../common/CExpression.h" +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/CLog.h" #include "../common/CUOInstall.h" #include "../game/chars/CChar.h" diff --git a/src/sphere/ProfileTask.cpp b/src/sphere/ProfileTask.cpp index 9ff76d6b0..2aff886b9 100644 --- a/src/sphere/ProfileTask.cpp +++ b/src/sphere/ProfileTask.cpp @@ -1,5 +1,5 @@ #include "ProfileTask.h" -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header #include "../game/CServerConfig.h" #include "threads.h" diff --git a/src/sphere/UnixTerminal.cpp b/src/sphere/UnixTerminal.cpp index 92c58d827..30edb15b0 100644 --- a/src/sphere/UnixTerminal.cpp +++ b/src/sphere/UnixTerminal.cpp @@ -1,7 +1,7 @@ #ifndef _WIN32 -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header #include "UnixTerminal.h" #ifndef _USECURSES diff --git a/src/sphere/containers.h b/src/sphere/containers.h index 5029a5e22..3bc32ff90 100644 --- a/src/sphere/containers.h +++ b/src/sphere/containers.h @@ -7,7 +7,7 @@ #define _INC_CONTAINERS_H #include -#include "../common/CException.h" +//#include "../common/CException.h" // included in the precompiled header // a thread-safe implementation of a queue container that doesn't use any locks // this only works as long as there is only a single reader thread and writer thread (can be different) diff --git a/src/sphere/ntservice.cpp b/src/sphere/ntservice.cpp index 32722c198..3e5dd2840 100644 --- a/src/sphere/ntservice.cpp +++ b/src/sphere/ntservice.cpp @@ -3,8 +3,8 @@ #ifdef _WIN32 #include -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/sphereversion.h" #include "../common/CLog.h" #include "../game/CObjBase.h" diff --git a/src/sphere/ntwindow.cpp b/src/sphere/ntwindow.cpp index d5804aa4a..88ee0cff5 100644 --- a/src/sphere/ntwindow.cpp +++ b/src/sphere/ntwindow.cpp @@ -3,8 +3,8 @@ #include "../common/sphere_library/CSWindow.h" #include "../common/sphere_library/CSString.h" -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/CTextConsole.h" #include "../common/CLog.h" #include "../common/sphereversion.h" // sphere version diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index a2dfc52cb..d9f0d8617 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -6,8 +6,8 @@ #include "../common/sphere_library/sresetevents.h" #include "../common/sphere_library/sstringobjs.h" #include "../common/basic_threading.h" -#include "../common/CException.h" -#include "../common/CExpression.h" +//#include "../common/CException.h" // included in the precompiled header +//#include "../common/CExpression.h" // included in the precompiled header #include "../common/CLog.h" #include "../game/CServer.h" #include "ProfileTask.h" From 04f469e3bf7995160ba7236c5c2fe82cb0eaa30e Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 22 May 2025 14:23:21 +0200 Subject: [PATCH 017/112] Changed some included files. --- src/CMakeSources.cmake | 3 +++ src/common/resource/sections/CWebPageDef.cpp | 1 + src/game/clients/CClient.cpp | 7 ++++--- src/game/clients/CClient.h | 9 +++++++-- src/game/clients/CClientMsg.cpp | 2 +- src/game/clients/CClientMsg_AOSTooltip.cpp | 1 + src/network/send.cpp | 2 +- src/network/send.h | 18 +++++++++--------- 8 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index 8897cba8d..dd4284936 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -17,6 +17,9 @@ set(pch_options #PRIVATE src/network/send.h #PRIVATE src/network/CSocket.h #PRIVATE src/network/receive.h + PRIVATE src/game/CEntity.h + PRIVATE src/game/CEntityProps.h + PRIVATE src/game/CServerConfig.h #PRIVATE src/game/clients/CClient.h #PRIVATE src/game/chars/CChar.h #PRIVATE src/game/items/CItem.h diff --git a/src/common/resource/sections/CWebPageDef.cpp b/src/common/resource/sections/CWebPageDef.cpp index b4cfc19a8..1e6e78c95 100644 --- a/src/common/resource/sections/CWebPageDef.cpp +++ b/src/common/resource/sections/CWebPageDef.cpp @@ -5,6 +5,7 @@ #include "../../../game/CWorld.h" #include "../../../game/CWorldGameTime.h" #include "../../../network/CClientIterator.h" +#include "../../../network/send.h" #include "../../sphere_library/CSFileList.h" //#include "../../CException.h" // included in the precompiled header //#include "../../CExpression.h" // included in the precompiled header diff --git a/src/game/clients/CClient.cpp b/src/game/clients/CClient.cpp index 7eadd2431..5e0a72999 100644 --- a/src/game/clients/CClient.cpp +++ b/src/game/clients/CClient.cpp @@ -8,6 +8,7 @@ #include "../../network/CClientIterator.h" #include "../../network/CNetworkManager.h" #include "../../network/CIPHistoryManager.h" +#include "../../network/send.h" #include "../chars/CChar.h" #include "../components/CCSpawn.h" #include "../items/CItemMultiCustom.h" @@ -1263,7 +1264,7 @@ bool CClient::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from pChar = uid.CharFind(); } if ( pChar ) - closeUIWindow(pChar, PacketCloseUIWindow::Paperdoll); + closeUIWindow(pChar, PacketCloseUIWindowType::Paperdoll); } break; @@ -1276,7 +1277,7 @@ bool CClient::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from pChar = uid.CharFind(); } if ( pChar ) - closeUIWindow(pChar, PacketCloseUIWindow::Profile); + closeUIWindow(pChar, PacketCloseUIWindowType::Profile); } break; @@ -1289,7 +1290,7 @@ bool CClient::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from pChar = uid.CharFind(); } if ( pChar ) - closeUIWindow(pChar, PacketCloseUIWindow::Status); + closeUIWindow(pChar, PacketCloseUIWindowType::Status); } break; diff --git a/src/game/clients/CClient.h b/src/game/clients/CClient.h index b58944460..8a201b231 100644 --- a/src/game/clients/CClient.h +++ b/src/game/clients/CClient.h @@ -9,7 +9,7 @@ #include "../../common/crypto/CCrypto.h" #include "../../common/CScriptTriggerArgs.h" #include "../../common/CTextConsole.h" -#include "../../network/send.h" +#include "../../network/CNetState.h" #include "../CSectorEnviron.h" #include "../game_enums.h" #include "CAccount.h" @@ -17,12 +17,17 @@ #include "CGlobalChatChanMember.h" #include "CGMPage.h" +class Packet; +class PacketCloseUIWindow; +enum PacketCloseUIWindowType : uint; +class PacketDisplayPopup; class CItemBase; class CItemContainer; class CItemMap; class CItemMultiCustom; class CSObjCont; + struct VendorItem; enum CREID_TYPE : uint32; enum ITEMID_TYPE : uint32; @@ -439,7 +444,7 @@ class CClient : public CSObjListRec, public CScriptObj, public CChatChanMember, void addTime( bool fCurrent = false ) const; void addObjectRemoveCantSee( const CUID& uid, lpctstr pszName = nullptr ) const; void closeContainer( const CObjBase * pObj ) const; - void closeUIWindow( const CObjBase* pObj, PacketCloseUIWindow::UIWindow windowType ) const; + void closeUIWindow( const CObjBase* pObj, PacketCloseUIWindowType windowType ) const; void addObjectRemove( const CUID& uid ) const; void addObjectRemove( const CObjBase * pObj ) const; void addRemoveAll( bool fItems, bool fChars ); diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index 76df2c6e3..a633c6557 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -289,7 +289,7 @@ void CClient::closeContainer( const CObjBase * pObj ) const new PacketCloseContainer(this, pObj); } -void CClient::closeUIWindow( const CObjBase* pObj, PacketCloseUIWindow::UIWindow windowType ) const +void CClient::closeUIWindow(const CObjBase* pObj, PacketCloseUIWindowType windowType ) const { ADDTOCALLSTACK("CClient::closeUIWindow"); new PacketCloseUIWindow(this, pObj, windowType); diff --git a/src/game/clients/CClientMsg_AOSTooltip.cpp b/src/game/clients/CClientMsg_AOSTooltip.cpp index 522ceed5f..66cd80508 100644 --- a/src/game/clients/CClientMsg_AOSTooltip.cpp +++ b/src/game/clients/CClientMsg_AOSTooltip.cpp @@ -1,5 +1,6 @@ //#include "../../common/CExpression.h" // included in the precompiled header //#include "../../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h +#include "../../network/send.h" #include "../chars/CChar.h" #include "../chars/CCharNPC.h" #include "../clients/CClientTooltip.h" diff --git a/src/network/send.cpp b/src/network/send.cpp index 9bb91d1b9..832a05d5f 100644 --- a/src/network/send.cpp +++ b/src/network/send.cpp @@ -4161,7 +4161,7 @@ void PacketDisplayPopup::finalise(void) * * ***************************************************************************/ -PacketCloseUIWindow::PacketCloseUIWindow(const CClient* target, const CObjBase* obj, UIWindow command) : PacketExtended(EXTDATA_CloseUI_Window, 13, PRI_NORMAL) +PacketCloseUIWindow::PacketCloseUIWindow(const CClient* target, const CObjBase* obj, PacketCloseUIWindowType command) : PacketExtended(EXTDATA_CloseUI_Window, 13, PRI_NORMAL) { ADDTOCALLSTACK("PacketCloseUIWindow::PacketCloseUIWindow"); diff --git a/src/network/send.h b/src/network/send.h index 52f5d41a2..3223d007e 100644 --- a/src/network/send.h +++ b/src/network/send.h @@ -1491,18 +1491,18 @@ class PacketDisplayPopup : public PacketExtended * * ***************************************************************************/ +enum PacketCloseUIWindowType : uint +{ + Paperdoll = 1, + Status = 2, + Profile = 8, + Container = 0xC +}; + class PacketCloseUIWindow : public PacketExtended { public: - enum UIWindow - { - Paperdoll = 1, - Status = 2, - Profile = 8, - Container = 0xC - }; - - PacketCloseUIWindow(const CClient* target, const CObjBase* obj, UIWindow command); + PacketCloseUIWindow(const CClient* target, const CObjBase* obj, PacketCloseUIWindowType command); }; /*************************************************************************** From 23b97b02a1b759329dc7a0ce2ad2f6d39fa2c5ae Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 22 May 2025 15:12:01 +0200 Subject: [PATCH 018/112] Fixed regression: damage not forwarded to @GetHit trigger --- src/game/chars/CCharFight.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/chars/CCharFight.cpp b/src/game/chars/CCharFight.cpp index be2435fdd..88548d6fd 100644 --- a/src/game/chars/CCharFight.cpp +++ b/src/game/chars/CCharFight.cpp @@ -749,10 +749,10 @@ int CChar::OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uiType, int iDmgPhy { CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pScriptArgs->m_VarsLocal.SetNum("ItemDamageLayer", sm_ArmorDamageLayers[(size_t)g_Rand.Get16ValFast(ARRAY_COUNT(sm_ArmorDamageLayers))]); + pScriptArgs->Init(iDmg, uiType, 0, nullptr); + pScriptArgs->m_VarsLocal.SetNum("ItemDamageLayer", sm_ArmorDamageLayers[(uint)g_Rand.Get16ValFast(ARRAY_COUNT(sm_ArmorDamageLayers))]); pScriptArgs->m_VarsLocal.SetNum("ItemDamageChance", 25); pScriptArgs->m_VarsLocal.SetNum("Spell", (int)spell); - if ( fElemental ) { pScriptArgs->m_VarsLocal.SetNum("DamagePercentPhysical", iDmgPhysical); From 9f81388f39aecf880d7f0cebef5936967691daaf Mon Sep 17 00:00:00 2001 From: Jhobean <51728381+Jhobean@users.noreply.github.com> Date: Thu, 22 May 2025 09:17:24 -0400 Subject: [PATCH 019/112] Warning of Replacing existing VarStr/VarNum now appear on nightly build (#1427) * Replacing existing VarStr/VarNum on Boot now give warning in nightly build * Update sphereCrypt.ini --- src/common/CVarDefMap.cpp | 8 ++++---- src/sphereCrypt.ini | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/common/CVarDefMap.cpp b/src/common/CVarDefMap.cpp index 47b6751e8..ce930ea0b 100644 --- a/src/common/CVarDefMap.cpp +++ b/src/common/CVarDefMap.cpp @@ -404,13 +404,13 @@ CVarDefContNum* CVarDefMap::SetNum( lpctstr pszName, int64 iVal, bool fDeleteZer if ( pVarNum ) { if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoading() ) - DEBUG_WARN(( "Replacing existing VarNum '%s' with number: 0x%" PRIx64" \n", pVarBase->GetKey(), iVal )); + g_Log.EventWarn( "Replacing existing VarNum '%s' with number: 0x%" PRIx64" \n", pVarBase->GetKey(), iVal ); pVarNum->SetValNum( iVal ); } else { if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoading() ) - DEBUG_WARN(( "Replacing existing VarStr '%s' with number: 0x%" PRIx64" \n", pVarBase->GetKey(), iVal )); + g_Log.EventWarn( "Replacing existing VarStr '%s' with number: 0x%" PRIx64" \n", pVarBase->GetKey(), iVal ); return SetNumOverride( pszName, iVal ); } @@ -485,13 +485,13 @@ CVarDefCont* CVarDefMap::SetStr( lpctstr pszName, bool fQuoted, lpctstr pszVal, if ( pVarStr ) { if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoading() ) - DEBUG_WARN(( "Replacing existing VarStr '%s' with string: '%s'\n", pVarBase->GetKey(), pszVal )); + g_Log.EventWarn( "Replacing existing VarStr '%s' with string: '%s'\n", pVarBase->GetKey(), pszVal ); pVarStr->SetValStr( pszVal ); } else { if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoading() ) - DEBUG_WARN(( "Replacing existing VarNum '%s' with string: '%s'\n", pVarBase->GetKey(), pszVal )); + g_Log.EventWarn( "Replacing existing VarNum '%s' with string: '%s'\n", pVarBase->GetKey(), pszVal ); return SetStrOverride( pszName, pszVal ); } return pVarStr; diff --git a/src/sphereCrypt.ini b/src/sphereCrypt.ini index 73ff647cd..5e4c1d4b5 100644 --- a/src/sphereCrypt.ini +++ b/src/sphereCrypt.ini @@ -231,10 +231,12 @@ ENC_LOGIN 4 // Rotation cipher used for the Login Server crypt by every client, 1232700 0D47BD45C 034081CD6 ENC_LOGIN // 1.23.27 //Enhanced Clients -6790010600 01AF46B5D 0BA46A27F ENC_TFISH // 4.0.106.00 -6790010500 01B3D816D 0BAA8BE7F ENC_TFISH // 4.0.105.00 -6790010400 01B07237D 0B94B527F ENC_TFISH // 4.0.104 -6790010300 01B49418D 0B964FE7F ENC_TFISH // 4.0.103 +679001080 01A68833D 0BA22127F ENC_TFISH // 4.0.108.0 +679001070 01AA2D14D 0BA7C9E7F ENC_TFISH // 4.0.107.0 +679001060 01AF46B5D 0BA46A27F ENC_TFISH // 4.0.106.0 +679001050 01B3D816D 0BAA8BE7F ENC_TFISH // 4.0.105.0 +679001040 01B07237D 0B94B527F ENC_TFISH // 4.0.104 +679001030 01B49418D 0B964FE7F ENC_TFISH // 4.0.103 670010200 01B92EB9D 0B91E227F ENC_TFISH // 4.0.102 670010100 01BC411AD 0B903DE7F ENC_TFISH // 4.0.101 670010000 0183D83BD 0B8F5127F ENC_TFISH // 4.0.100 From 37cb1d2712c3470b2cb856f7d9ee64e105182053 Mon Sep 17 00:00:00 2001 From: mtwango Date: Fri, 23 May 2025 14:49:15 +0200 Subject: [PATCH 020/112] Clamp item onto container edge, when placed out of bounds (#1426) --- src/game/items/CItemContainer.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/game/items/CItemContainer.cpp b/src/game/items/CItemContainer.cpp index cb3c380bb..60c13e8bc 100644 --- a/src/game/items/CItemContainer.cpp +++ b/src/game/items/CItemContainer.cpp @@ -615,8 +615,8 @@ void CItemContainer::ContentAdd( CItem *pItem, CPointMap pt, bool bForceNoStack, bool fStackInsert = false; - // We are dropping item out of container bounds. - if (pt.m_x <= minValX || pt.m_y <= minValY || pt.m_x > maxValX || pt.m_y > maxValY) + // We are dropping item onto container. + if (pt.m_x < 0 && pt.m_y < 0) { // Try to stack it. if ( !g_Serv.IsLoading() && pItem->Item_GetDef()->IsStackableType() && !bForceNoStack ) @@ -636,6 +636,12 @@ void CItemContainer::ContentAdd( CItem *pItem, CPointMap pt, bool bForceNoStack, if (!fStackInsert) pt = GetRandContainerLoc(); } + else + { + // We might be placing item out of container bounds, clamp it to the side if so. + pt.m_x = std::clamp(pt.m_x, minValX, maxValX); + pt.m_y = std::clamp(pt.m_y, minValY, maxValY); + } // Try drop it on given container grid index (if not available, drop it on next free index) { From f7bca29326dc0f0349e7505e9900f6a9885bca73 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 22 May 2025 20:01:41 +0200 Subject: [PATCH 021/112] CPointBase.cpp: optimized some dist or point-validity calculations --- src/common/CPointBase.cpp | 115 ++++++++++++++++++++++++------- src/common/CPointBase.h | 14 ++-- src/game/CTeleport.cpp | 2 +- src/game/chars/CCharSpell.cpp | 2 +- src/game/items/CItem.cpp | 4 +- src/game/uo_files/CUOMapList.cpp | 10 +-- src/game/uo_files/CUOMapList.h | 26 +++---- src/network/receive.cpp | 2 +- 8 files changed, 117 insertions(+), 58 deletions(-) diff --git a/src/common/CPointBase.cpp b/src/common/CPointBase.cpp index c782e8705..52c9821ed 100644 --- a/src/common/CPointBase.cpp +++ b/src/common/CPointBase.cpp @@ -214,9 +214,7 @@ int CPointBase::GetDist( const CPointBase & pt ) const noexcept // Distance betw //ADDTOCALLSTACK_DEBUG("CPointBase::GetDist"); // Get the basic 2d distance. - if ( !pt.IsValidPoint() || (pt.m_map != m_map)) - return INT16_MAX; - return GetDistBase(pt); + return (!pt.IsValidPoint() || (pt.m_map != m_map)) ? INT16_MAX : GetDistBase(pt); } int CPointBase::GetDistSightBase( const CPointBase & pt ) const noexcept // Distance between points based on UO sight @@ -233,9 +231,7 @@ int CPointBase::GetDistSightBase( const CPointBase & pt ) const noexcept // Dist int CPointBase::GetDistSight( const CPointBase & pt ) const noexcept // Distance between points based on UO sight { - if ( !pt.IsValidPoint() ) - return INT16_MAX; - if ( pt.m_map != m_map ) + if (!pt.IsValidPoint() || (pt.m_map != m_map)) return INT16_MAX; //const int dx = abs(m_x - pt.m_x); @@ -283,15 +279,27 @@ int CPointBase::GetDist3D( const CPointBase & pt ) const noexcept // Distance be bool CPointBase::IsValidXY() const noexcept { + /* if ( (m_x < 0) || (m_y < 0) ) return false; if ( (m_x >= g_MapList.GetMapSizeX(m_map)) || (m_y >= g_MapList.GetMapSizeY(m_map)) ) return false; return true; + */ + + const ushort sx = g_MapList.GetMapSizeX(m_map); + const ushort sy = g_MapList.GetMapSizeY(m_map); + + const ushort ux = uint16(m_x); + const ushort uy = uint16(m_y); + + // Two unsigned compares fold both < 0 and >= size checks + return ux < sx && uy < sy; } -bool CPointBase::IsCharValid() const noexcept +bool CPointBase::IsValidPoint() const noexcept { +/* if ( (m_z <= -UO_SIZE_Z) || (m_z >= UO_SIZE_Z) ) return false; if ((m_x <= 0) || (m_y <= 0)) @@ -299,19 +307,33 @@ bool CPointBase::IsCharValid() const noexcept if ((m_x >= g_MapList.GetMapSizeX(m_map)) || (m_y >= g_MapList.GetMapSizeY(m_map))) return false; return true; +*/ + const ushort sx = g_MapList.GetMapSizeX(m_map); + const ushort sy = g_MapList.GetMapSizeY(m_map); + + const ushort ux = uint16(m_x); + const ushort uy = uint16(m_y); + const ushort uz = uint16(int16(m_z) + UO_SIZE_Z); + + // ux > 0 && ux < sizex folds both x<=0 and x>=sizex + // uz > 0 excludes m_z == –UO_SIZE_Z; uz < 2*UO_SIZE_Z excludes m_z >= UO_SIZE_Z + return uz < (2U * UO_SIZE_Z) + && ux > 0U && ux < sx + && uy > 0U && uy < sy; } void CPointBase::ValidatePoint() noexcept { + const short iMaxX = (short)g_MapList.GetMapSizeX(m_map); + const short iMaxY = (short)g_MapList.GetMapSizeY(m_map); + if ( m_x < 0 ) m_x = 0; - const short iMaxX = (short)g_MapList.GetMapSizeX(m_map); if (m_x >= iMaxX) m_x = iMaxX - 1; if ( m_y < 0 ) m_y = 0; - const short iMaxY = (short)g_MapList.GetMapSizeY(m_map); if (m_y >= iMaxY) m_y = iMaxY - 1; } @@ -615,7 +637,12 @@ bool CPointBase::r_WriteVal( lpctstr ptcKey, CSString & sVal ) const break; if (pMultiItem->m_visible == 0) continue; - CPointMap ptTest((word)(ptMulti.m_x + pMultiItem->m_dx), (word)(ptMulti.m_y + pMultiItem->m_dy), (char)(ptMulti.m_z + pMultiItem->m_dz), this->m_map); + + const CPointMap ptTest( + (word)(ptMulti.m_x + pMultiItem->m_dx), + (word)(ptMulti.m_y + pMultiItem->m_dy), + (char)(ptMulti.m_z + pMultiItem->m_dz), + this->m_map); if (GetDist(ptTest) > 0) continue; @@ -827,30 +854,26 @@ bool CPointBase::r_LoadVal( lpctstr ptcKey, lpctstr pszArgs ) return true; } - DIR_TYPE CPointBase::GetDir( const CPointBase & pt, DIR_TYPE DirDefault ) const // Direction to point pt { ADDTOCALLSTACK_DEBUG("CPointBase::GetDir"); // Get the 2D direction between points. - const int dx = (m_x-pt.m_x); - const int dy = (m_y-pt.m_y); + /* + const int dx = (m_x-pt.m_x); + const int dy = (m_y-pt.m_y); const int ax = abs(dx); const int ay = abs(dy); - if ( ay > ax ) { if ( ! ax ) - { return(( dy > 0 ) ? DIR_N : DIR_S ); - } - int slope = ay / ax; + + const int slope = ay / ax; if ( slope > 2 ) return(( dy > 0 ) ? DIR_N : DIR_S ); if ( dx > 0 ) // westish - { return(( dy > 0 ) ? DIR_NW : DIR_SW ); - } return(( dy > 0 ) ? DIR_NE : DIR_SE ); } else @@ -861,24 +884,66 @@ DIR_TYPE CPointBase::GetDir( const CPointBase & pt, DIR_TYPE DirDefault ) const return( DirDefault ); // here ? return(( dx > 0 ) ? DIR_W : DIR_E ); } - int slope = ax / ay; + const int slope = ax / ay; if ( slope > 2 ) return(( dx > 0 ) ? DIR_W : DIR_E ); if ( dy > 0 ) - { return(( dx > 0 ) ? DIR_NW : DIR_NE ); - } return(( dx > 0 ) ? DIR_SW : DIR_SE ); } + */ + + const int dx = m_x - pt.m_x; + const int dy = m_y - pt.m_y; + + // Early exit if identical point + if ((dx | dy) == 0) //(dx == 0 && dy == 0) + return DirDefault; + + // Absolute values + const int ax = dx < 0 ? -dx : dx; + const int ay = dy < 0 ? -dy : dy; + + // Axis‐dominance and “steepness” test + const bool steep = ay > ax; + const bool extreme = steep + ? (ay > ax * 2) + : (ax > ay * 2); + + // Cardinal direction if “extreme”, else diagonal + // Use boolean-to-int multipliers (true→1, false→0) + const int north = (dy > 0); + const int south = (dy < 0); + const int east = (dx > 0); + const int west = (dx < 0); + + const DIR_TYPE dir_card = enum_alias_cast( + steep + ? ( north * DIR_N + south * DIR_S ) + : ( east * DIR_E + west * DIR_W )); + //const DIR_TYPE card = steep + // ? (north ? DIR_N : DIR_S) + // : (east ? DIR_W : DIR_E); + + const DIR_TYPE dir_diag = enum_alias_cast( + north * east * DIR_NE + + north * west * DIR_NW + + south * east * DIR_SE + + south * west * DIR_SW); + //const DIR_TYPE diag = north + // ? (east ? DIR_NE : DIR_NW) + // : (east ? DIR_SE : DIR_SW); + + return extreme ? dir_card : dir_diag; } int CPointBase::StepLinePath( const CPointBase & ptSrc, int iSteps ) { ADDTOCALLSTACK("CPointBase::StepLinePath"); // Take x steps toward this point. - int dx = m_x - ptSrc.m_x; - int dy = m_y - ptSrc.m_y; - int iDist2D = GetDist( ptSrc ); + const int dx = m_x - ptSrc.m_x; + const int dy = m_y - ptSrc.m_y; + const int iDist2D = GetDist( ptSrc ); if ( ! iDist2D ) return 0; diff --git a/src/common/CPointBase.h b/src/common/CPointBase.h index 4e0670468..8f1121cd0 100644 --- a/src/common/CPointBase.h +++ b/src/common/CPointBase.h @@ -67,17 +67,11 @@ struct CPointBase // Non initialized 3d point. [[nodiscard]] int GetDistSight( const CPointBase & pt ) const noexcept; // Distance between points based on UO sight [[nodiscard]] int GetDist3D( const CPointBase & pt ) const noexcept; // 3D Distance between points - [[nodiscard]] inline bool IsValidZ() const noexcept - { - return ((m_z > -127 /* -UO_SIZE_Z */) && (m_z < 127 /* UO_SIZE_Z */)); - } + //[[nodiscard]] inline bool IsValidZ() const noexcept { + // return ((m_z > -127 /* -UO_SIZE_Z */) && (m_z < 127 /* UO_SIZE_Z */)); + //} [[nodiscard]] bool IsValidXY() const noexcept; - [[nodiscard]] bool IsCharValid() const noexcept; - [[nodiscard]] inline bool IsValidPoint() const noexcept - { - // Called a LOT of times, it's worth inlining it. - return (IsValidXY() && IsValidZ()); - } + [[nodiscard]] bool IsValidPoint() const noexcept; void ValidatePoint() noexcept; diff --git a/src/game/CTeleport.cpp b/src/game/CTeleport.cpp index 871ce7019..1988f1baf 100644 --- a/src/game/CTeleport.cpp +++ b/src/game/CTeleport.cpp @@ -31,7 +31,7 @@ CTeleport::CTeleport(tchar* pszArgs) bool CTeleport::RealizeTeleport() { ADDTOCALLSTACK("CTeleport::RealizeTeleport"); - if (!IsCharValid() || !_ptDst.IsCharValid()) + if (!IsValidPoint() || !_ptDst.IsValidPoint()) { DEBUG_ERR(("CTeleport bad coords %s\n", WriteUsed())); return false; diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index 42efc6da3..e2579b89b 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -111,7 +111,7 @@ bool CChar::Spell_Teleport( CPointMap ptNew, bool fTakePets, bool fCheckAntiMagi // ex. ships plank. // RETURN: true = it worked. - if ( !ptNew.IsCharValid() ) + if ( !ptNew.IsValidPoint() ) return false; ptNew.m_z = GetFixZ(ptNew); diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index d4fb5d22e..f4f74fadb 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -1867,7 +1867,7 @@ lpctstr CItem::GetNameFull( bool fIdentified ) const len += Str_CopyLimitNull( pTemp+len, g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_BLANK ), Str_TempLength() - len); break; case IT_RUNE: - if ( ! m_itRune.m_ptMark.IsCharValid()) + if ( ! m_itRune.m_ptMark.IsValidPoint()) len += Str_CopyLimitNull( pTemp+len, g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_BLANK ), Str_TempLength() - len); else if ( ! m_itRune.m_Strength ) len += Str_CopyLimitNull( pTemp+len, g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_FADED ), Str_TempLength() - len); @@ -3525,7 +3525,7 @@ bool CItem::r_Load( CScript & s ) // Load an item from script if ( GetContainer() == nullptr ) { // Actually place the item into the world. - if ( GetTopPoint().IsCharValid()) + if ( GetTopPoint().IsValidPoint()) MoveToUpdate( GetTopPoint()); } diff --git a/src/game/uo_files/CUOMapList.cpp b/src/game/uo_files/CUOMapList.cpp index fd0f08c01..91952da65 100644 --- a/src/game/uo_files/CUOMapList.cpp +++ b/src/game/uo_files/CUOMapList.cpp @@ -61,7 +61,7 @@ void CUOMapList::Init() if ( map_data.enabled ) // map marked as available. check whatever it's possible { // check coordinates first - if ( map_data.num == -1 ) + if ( map_data.num == (ushort)-1 ) map_data.enabled = false; else if ( map_data.sizex <= 0 || map_data.sizey <= 0 || map_data.sectorsize <= 0 ) map_data.enabled = DetectMapSize(i); @@ -278,27 +278,27 @@ int CUOMapList::CalcSectorRows(int map) const int CUOMapList::GetMapCenterX(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].sizex != -1); + ASSERT(m_mapGeoData.maps[map].sizex != (ushort)-1); return (m_mapGeoData.maps[map].sizex / 2); } int CUOMapList::GetMapCenterY(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].sizey != -1); + ASSERT(m_mapGeoData.maps[map].sizey != (ushort)-1); return (m_mapGeoData.maps[map].sizey / 2); } int CUOMapList::GetMapFileNum(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].num != -1); + ASSERT(m_mapGeoData.maps[map].num != (ushort)-1); return m_mapGeoData.maps[map].num; } int CUOMapList::GetMapID(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].id != -1); + ASSERT(m_mapGeoData.maps[map].id != (ushort)-1); return m_mapGeoData.maps[map].id; } diff --git a/src/game/uo_files/CUOMapList.h b/src/game/uo_files/CUOMapList.h index d4e7c5623..91ebb955e 100644 --- a/src/game/uo_files/CUOMapList.h +++ b/src/game/uo_files/CUOMapList.h @@ -22,24 +22,24 @@ extern class CUOMapList protected: struct MapGeoData { + uint16 num; // real map number (0 for 0 and 1, 2 for 2, and so on) - file name + uint16 id; // map id used by the client + uint16 sizex; + uint16 sizey; + uint16 sectorsize; bool enabled; // supported map? bool initialized; - int num; // real map number (0 for 0 and 1, 2 for 2, and so on) - file name - int id; // map id used by the client - int sizex; - int sizey; - int sectorsize; static constexpr MapGeoData invalid() noexcept { return { + .num = (uint16)-1, + .id = (uint16)-1, + .sizex = (uint16)-1, + .sizey = (uint16)-1, + .sectorsize = (uint16)-1, .enabled = true, - .initialized = false, - .num = -1, - .id = -1, - .sizex = -1, - .sizey = -1, - .sectorsize = -1 + .initialized = false }; } }; @@ -103,7 +103,7 @@ extern class CUOMapList inline int CUOMapList::GetMapSizeX(int map) const noexcept { - // Used by CPointBase::IsValidXY(), which is called a LOT + // Used by CPointBase::IsValidXY() and IsValidPoint(), which is called a LOT //ASSERT(IsMapSupported(map)); //ASSERT(m_sizex[map] != -1); return m_mapGeoData.maps[map].sizex; @@ -111,7 +111,7 @@ inline int CUOMapList::GetMapSizeX(int map) const noexcept inline int CUOMapList::GetMapSizeY(int map) const noexcept { - // Used by CPointBase::IsValidXY(), which is called a LOT + // Used by CPointBase::IsValidXY() and IsValidPoint(), which is called a LOT //ASSERT(IsMapSupported(map)); //ASSERT(m_sizey[map] != -1); return m_mapGeoData.maps[map].sizey; diff --git a/src/network/receive.cpp b/src/network/receive.cpp index 254b4720e..990984165 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -447,7 +447,7 @@ bool PacketItemDropReq::onReceive(CNetState* net) } CUID container(readInt32()); - CPointMap pt(x, y, z, character->GetTopMap()); + CPointMap pt((int16_t)x, (int16_t)y, (int8_t)z, character->GetTopMap()); client->Event_Item_Drop(serial, pt, container, grid); return true; From af895d5fb663345b492b10f2ea5f92b9e3b2c029 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 24 May 2025 14:07:58 +0200 Subject: [PATCH 022/112] Added support for CppCheck static analysis and utility script to run clang-tidy. --- .clang-tidy | 21 +- CMakeLists.txt | 47 +- cmake/toolchains/Linux-Clang-x86_64.cmake | 14 +- src/CMakeSources.cmake | 568 ++++++++++++--------- src/common/sphere_library/ssorted_vector.h | 5 + src/common/sphere_library/stypecast.h | 20 +- src/game/CServer.cpp | 1 - static_analysis/coverity_model.cpp | 5 + static_analysis/cppcheck-suppressions.txt | 12 + static_analysis/run_clang-tidy.sh | 2 + 10 files changed, 432 insertions(+), 263 deletions(-) create mode 100644 static_analysis/coverity_model.cpp create mode 100644 static_analysis/cppcheck-suppressions.txt create mode 100644 static_analysis/run_clang-tidy.sh diff --git a/.clang-tidy b/.clang-tidy index 88cf52568..220aac065 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,4 @@ -ExcludeHeaderFilterRegex: ^.*(lib\/).* +#ExcludeHeaderFilterRegex: ^.*(lib\/).* Checks: > #-*, @@ -16,26 +16,36 @@ Checks: > -clang-analyzer-security.insecureAPI.strcpy, -clang-diagnostic-format-security, -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-avoid-do-while, -cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-no-malloc, -cppcoreguidelines-init-variables, # false positives -cppcoreguidelines-owning-memory, -cppcoreguidelines-prefer-member-initializer, - -cppcoreguidelines-pro-bounds-array-pointer-decay, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + #-cppcoreguidelines-pro-bounds-array-pointer-decay, -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-type-const-cast, # we have some instances, and we should remove them... - -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-special-member-functions, # explicit move, copy contructors... ##-hicpp-use-auto, #duplicate ##-hicpp-braces-around-statements, ##-hicpp-vararg, #duplicate + -misc-const-correctness, # temporary -misc-include-cleaner, + -misc-non-private-member-variables-in-classes, -modernize-avoid-c-arrays, -modernize-use-auto, -modernize-use-trailing-return-type, -modernize-loop-convert, + -modernize-macro-to-enum, + -modernize-use-designated-initializers, + -modernize-use-nodiscard, + -modernize-use-using, -readability-braces-around-statements, -readability-delete-null-pointer, -readability-else-after-return, @@ -43,6 +53,7 @@ Checks: > -readability-identifier-length, -readability-identifier-naming, -readability-implicit-bool-conversion, + -readability-inconsistent-declaration-parameter-name, -readability-magic-numbers, -readability-misleading-indentation, # to be enabled after the source is properly formatted -readability-simplify-boolean-expr, @@ -58,8 +69,8 @@ Checks: > #WarningsAsErrors: "*" CheckOptions: - - key: 'clang-analyzer-core.NonNullParamChecker:assert_like_macro' - value: 'DEBUG_ASSERT,ASSERT,ASSERT_ALWAYS' +# - key: 'clang-analyzer-core.NonNullParamChecker:assert_like_macro' +# value: 'DEBUG_ASSERT,ASSERT,ASSERT_ALWAYS' - key: 'clang-analyzer-config:noreturn_function' value: 'RaiseImmediateAbort,RaiseRecoverableAbort' - { key: readability-identifier-naming.NamespaceCase, value: lower_case } diff --git a/CMakeLists.txt b/CMakeLists.txt index 85b5eb7d9..1a74e8543 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,8 @@ endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_SCAN_FOR_MODULES OFF) # disable C++ module scanning +set(CMAKE_CXX_VISIBILITY_PRESET "hidden") +set(CMAKE_VISIBILITY_INLINES_HIDDEN "YES") # Need to clear shared library flags. If not, cmake sets -rdynamic and this # add to the executable the full symbol table (included unused symbols). @@ -129,11 +131,36 @@ option(USE_UBSAN "Enable Undefined Behavior Sanitizer." FALSE) option(USE_LSAN "Enable LeakSanitizer." FALSE) option(USE_MSAN "Enable MemorySanitizer." FALSE) +option(USE_CPPCHECK "Perform static analysis with CppCheck." FALSE) + + set(ENABLED_SANITIZER FALSE CACHE INTERNAL BOOL "Am i using a sanitizer?") if(USE_ASAN OR USE_MSAN OR USE_LSAN OR USE_MSAN) set(ENABLED_SANITIZER TRUE) endif() +if(USE_CPPCHECK) + find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck) + if(CMAKE_CXX_CPPCHECK) + include(CheckTypeSize) + check_type_size("void*" SIZEOF_VOID_P) + #message(STATUS "Pointer size = ${SIZEOF_VOID_P}") + + list( + APPEND CMAKE_CXX_CPPCHECK + "-v" + "--enable=all" + "--inconclusive" + "--force" + "--inline-suppr" + "--suppress=missingIncludeSystem" + "--template='{file}({line}): {severity} ({id}): {message}'" + "--checkers-report=cppcheck_out.txt" + #"--suppressions-list=${CMAKE_SOURCE_DIR}/CppCheckSuppressions.txt" + "-D__SIZEOF_POINTER__=${SIZEOF_VOID_P}" + ) + endif() +endif() # -------------------- Stuff to set right away, after having parsed the checkboxes/CLI arguments. @@ -210,13 +237,13 @@ if(SINGLE_TARGET) add_executable( spheresvr_release ${is_win32_app_linker} - ${SPHERE_SOURCES} + ${SPHERE_SOURCES} ${SPHERE_HEADERS} #${docs_TEXT} ) set_target_properties(spheresvr_release PROPERTIES OUTPUT_NAME SphereSvrX${ARCH_BITS}_release) #set_target_properties(spheresvr_release PROPERTIES CXX_EXTENSIONS OFF) #target_compile_features(spheresvr_release PUBLIC cxx_std_20) - target_precompile_headers(spheresvr_release ${pch_options}) + target_precompile_headers(spheresvr_release ${pch_list}) if(NOT ${FORCE_DEBUG_INFO}) add_custom_command(TARGET spheresvr_release POST_BUILD @@ -230,13 +257,13 @@ if(SINGLE_TARGET) add_executable( spheresvr_nightly ${is_win32_app_linker} - ${SPHERE_SOURCES} + ${SPHERE_SOURCES} ${SPHERE_HEADERS} #${docs_TEXT} ) set_target_properties(spheresvr_nightly PROPERTIES OUTPUT_NAME SphereSvrX${ARCH_BITS}_nightly) #set_target_properties(spheresvr_nightly PROPERTIES CXX_EXTENSIONS OFF) #target_compile_features(spheresvr_nightly PUBLIC cxx_std_20) - target_precompile_headers(spheresvr_nightly ${pch_options}) + target_precompile_headers(spheresvr_nightly ${pch_list}) if(NOT ${FORCE_DEBUG_INFO}) add_custom_command(TARGET spheresvr_nightly POST_BUILD @@ -250,23 +277,27 @@ if(SINGLE_TARGET) add_executable( spheresvr_debug ${is_win32_app_linker} - ${SPHERE_SOURCES} + ${SPHERE_SOURCES} ${SPHERE_HEADERS} #${docs_TEXT} ) set_target_properties(spheresvr_debug PROPERTIES OUTPUT_NAME SphereSvrX${ARCH_BITS}_debug) #set_target_properties(spheresvr_debug PROPERTIES CXX_EXTENSIONS OFF) #target_compile_features(spheresvr_debug PUBLIC cxx_std_20) - target_precompile_headers(spheresvr_debug ${pch_options}) + target_precompile_headers(spheresvr_debug ${pch_list}) endif() else(SINGLE_TARGET) set(TARGETS ${TARGETS} spheresvr) - add_executable(spheresvr ${is_win32_app_linker} ${SPHERE_SOURCES} ${docs_TEXT}) + add_executable(spheresvr + ${is_win32_app_linker} + ${SPHERE_SOURCES} ${SPHERE_HEADERS} + ${docs_TEXT} + ) set_target_properties(spheresvr PROPERTIES OUTPUT_NAME_RELEASE SphereSvrX${ARCH_BITS}_release) set_target_properties(spheresvr PROPERTIES OUTPUT_NAME_NIGHTLY SphereSvrX${ARCH_BITS}_nightly) set_target_properties(spheresvr PROPERTIES OUTPUT_NAME_DEBUG SphereSvrX${ARCH_BITS}_debug) #set_target_properties(spheresvr PROPERTIES CXX_EXTENSIONS OFF) #target_compile_features(spheresvr PUBLIC cxx_std_20) - target_precompile_headers(spheresvr ${pch_options}) + target_precompile_headers(spheresvr ${pch_list}) # To be tested #add_custom_command(TARGET spheresvr POST_BUILD diff --git a/cmake/toolchains/Linux-Clang-x86_64.cmake b/cmake/toolchains/Linux-Clang-x86_64.cmake index 1cb11614e..2baa30e91 100644 --- a/cmake/toolchains/Linux-Clang-x86_64.cmake +++ b/cmake/toolchains/Linux-Clang-x86_64.cmake @@ -1,8 +1,18 @@ include("${CMAKE_CURRENT_LIST_DIR}/include/Linux-Clang_common.inc.cmake") function(toolchain_force_compiler) - set(CMAKE_C_COMPILER "clang" CACHE STRING "C compiler" FORCE) - set(CMAKE_CXX_COMPILER "clang++" CACHE STRING "C++ compiler" FORCE) + #if(DEFINED ENV{CC}) + # set(CMAKE_C_COMPILER $ENV{CC}) + #else() + # set(CMAKE_C_COMPILER "clang" CACHE STRING "C compiler") + set(CMAKE_C_COMPILER "clang" CACHE STRING "C compiler" FORCE) + #endif() + #if(DEFINED ENV{CXX}) + # set(CMAKE_CXX_COMPILER $ENV{CXX}) + #elseif("${CMAKE_CXX_COMPILER}" STREQUAL "") + #set(CMAKE_CXX_COMPILER "clang++" CACHE STRING "C++ compiler") + set(CMAKE_CXX_COMPILER "clang++" CACHE STRING "C++ compiler" FORCE) + #endif() if(CROSSCOMPILING_ARCH) message(FATAL_ERROR "Incomplete/to be tested.") # are the names/paths correct? diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index dd4284936..03752453d 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -1,6 +1,11 @@ +############################## +# Precompiled Header Files +############################## + # Precompiled header: it's parsed only once and stored in memory as an intermediate form. This speeds up compilation drastically. # Be wise in chosing those, since every time we change one of them, we'll have to recompile every one on the list. -set(pch_options + +set(pch_list PRIVATE src/common/common.h #PRIVATE src/common/sphere_library/CSRand.h PRIVATE src/common/sphere_library/CSString.h @@ -23,159 +28,172 @@ set(pch_options #PRIVATE src/game/clients/CClient.h #PRIVATE src/game/chars/CChar.h #PRIVATE src/game/items/CItem.h - ) -# Main program files: threads, console... -set(sphere_SRCS +############################## +# Sphere Main +############################## +set(sphere_S src/sphere/asyncdb.cpp + src/sphere/ConsoleInterface.cpp + src/sphere/ProfileData.cpp + src/sphere/ProfileTask.cpp + src/sphere/threads.cpp + src/sphere/ntservice.cpp + src/sphere/ntwindow.cpp + src/sphere/UnixTerminal.cpp +) +set(sphere_H src/sphere/asyncdb.h src/sphere/containers.h - src/sphere/ConsoleInterface.cpp src/sphere/ConsoleInterface.h - src/sphere/ProfileData.cpp src/sphere/ProfileData.h - src/sphere/ProfileTask.cpp src/sphere/ProfileTask.h - src/sphere/threads.cpp src/sphere/threads.h - src/sphere/ntservice.cpp src/sphere/ntservice.h - src/sphere/ntwindow.cpp src/sphere/ntwindow.h - src/sphere/UnixTerminal.cpp src/sphere/UnixTerminal.h ) -source_group(sphere FILES ${sphere_SRCS}) +source_group(sphere FILES ${sphere_H} ${sphere_S}) -# Network management files -set(network_SRCS +############################## +# Network Management +############################## +set(network_S src/network/CClientIterator.cpp - src/network/CClientIterator.h src/network/CIPHistoryManager.cpp - src/network/CIPHistoryManager.h src/network/CNetState.cpp - src/network/CNetState.h src/network/CNetworkInput.cpp - src/network/CNetworkInput.h src/network/CNetworkManager.cpp - src/network/CNetworkManager.h src/network/CNetworkOutput.cpp - src/network/CNetworkOutput.h src/network/CNetworkThread.cpp - src/network/CNetworkThread.h src/network/CPacketManager.cpp - src/network/CPacketManager.h src/network/CSocket.cpp - src/network/CSocket.h src/network/linuxev.cpp - src/network/linuxev.h - src/network/net_datatypes.h src/network/net_datatypes.cpp src/network/packet.cpp - src/network/packet.h src/network/receive.cpp - src/network/receive.h src/network/send.cpp - src/network/send.h src/network/PingServer.cpp +) +set(network_H + src/network/CClientIterator.h + src/network/CIPHistoryManager.h + src/network/CNetState.h + src/network/CNetworkInput.h + src/network/CNetworkManager.h + src/network/CNetworkOutput.h + src/network/CNetworkThread.h + src/network/CPacketManager.h + src/network/CSocket.h + src/network/linuxev.h + src/network/net_datatypes.h + src/network/packet.h + src/network/receive.h + src/network/send.h src/network/PingServer.h ) -source_group(network FILES ${network_SRCS}) +source_group(network FILES ${network_H} ${network_S}) -# Handle UO Client files -set(uofiles_SRCS - src/game/uo_files/CUOHuesRec.h +############################## +# UO Files +############################## +set(uofiles_S src/game/uo_files/CUOHuesRec.cpp - src/game/uo_files/CUOIndexRec.h src/game/uo_files/CUOIndexRec.cpp - src/game/uo_files/CUOItemInfo.h src/game/uo_files/CUOItemInfo.cpp + src/game/uo_files/CUOMapList.cpp + src/game/uo_files/CUOMapMeter.cpp + src/game/uo_files/CUOMobtypes.cpp + src/game/uo_files/CUOMultiItemRec.cpp + src/game/uo_files/CUOStaticItemRec.cpp + src/game/uo_files/CUOTerrainInfo.cpp + src/game/uo_files/CUOTiledata.cpp + src/game/uo_files/CUOVersionBlock.cpp +) +set(uofiles_H + src/game/uo_files/CUOHuesRec.h + src/game/uo_files/CUOIndexRec.h + src/game/uo_files/CUOItemInfo.h src/game/uo_files/CUOItemTypeRec.h src/game/uo_files/CUOMapBlock.h src/game/uo_files/CUOMapList.h - src/game/uo_files/CUOMapList.cpp src/game/uo_files/CUOMapMeter.h - src/game/uo_files/CUOMapMeter.cpp src/game/uo_files/CUOMobtypes.h - src/game/uo_files/CUOMobtypes.cpp src/game/uo_files/CUOMultiItemRec.h - src/game/uo_files/CUOMultiItemRec.cpp src/game/uo_files/CUOStaticItemRec.h - src/game/uo_files/CUOStaticItemRec.cpp src/game/uo_files/CUOTerrainInfo.h - src/game/uo_files/CUOTerrainInfo.cpp src/game/uo_files/CUOTerrainTypeRec.h src/game/uo_files/CUOTiledata.h - src/game/uo_files/CUOTiledata.cpp src/game/uo_files/CUOVersionBlock.h - src/game/uo_files/CUOVersionBlock.cpp src/game/uo_files/uofiles_enums.h src/game/uo_files/uofiles_enums_itemid.h src/game/uo_files/uofiles_enums_creid.h src/game/uo_files/uofiles_macros.h src/game/uo_files/uofiles_types.h ) -source_group(game\\uo_files FILES ${uofiles_SRCS}) +source_group(game\\uo_files FILES ${uofiles_H} ${uofiles_S}) -# Files containing 'background work' -set(common_SRCS +############################## +# Common +############################## +set(common_S src/common/sqlite/SQLite.cpp + src/common/CCacheableScriptFile.cpp + src/common/CDataBase.cpp + src/common/CException.cpp + src/common/CExpression.cpp + src/common/CFloatMath.cpp + src/common/CLanguageID.cpp + src/common/CLocalVarsExtra.cpp + src/common/CLog.cpp + src/common/CServerMap.cpp + src/common/CUID.cpp + src/common/CPointBase.cpp + src/common/CRect.cpp + src/common/CScript.cpp + src/common/CScriptContexts.cpp + src/common/CScriptObj.cpp + src/common/CScriptParserBufs.cpp + src/common/CScriptTriggerArgs.cpp + src/common/CSFileObj.cpp + src/common/CSFileObjContainer.cpp + src/common/CSVFile.cpp + src/common/CTextConsole.cpp + src/common/CUOClientVersion.cpp + src/common/CUOInstall.cpp + src/common/CVarDefMap.cpp + src/common/ListDefContMap.cpp +) +set(common_H src/common/sqlite/SQLite.h src/common/assertion.h src/common/basic_threading.h - src/common/common.cpp - src/common/common.h - src/common/CCacheableScriptFile.cpp src/common/CCacheableScriptFile.h - src/common/CDataBase.cpp src/common/CDataBase.h - src/common/CException.cpp src/common/CException.h - src/common/CExpression.cpp src/common/CExpression.h - src/common/CFloatMath.cpp src/common/CFloatMath.h - src/common/CLanguageID.cpp src/common/CLanguageID.h - src/common/CLocalVarsExtra.cpp src/common/CLocalVarsExtra.h - src/common/CLog.cpp src/common/CLog.h - src/common/CServerMap.cpp src/common/CServerMap.h - src/common/CUID.cpp src/common/CUID.h - src/common/CPointBase.cpp src/common/CPointBase.h - src/common/CRect.cpp src/common/CRect.h - src/common/CScript.cpp src/common/CScript.h - src/common/CScriptContexts.cpp src/common/CScriptContexts.h - src/common/CScriptObj.cpp src/common/CScriptObj.h - src/common/CScriptParserBufs.cpp src/common/CScriptParserBufs.h - src/common/CScriptTriggerArgs.cpp src/common/CScriptTriggerArgs.h - src/common/CSFileObj.cpp src/common/CSFileObj.h - src/common/CSFileObjContainer.cpp src/common/CSFileObjContainer.h - src/common/CSVFile.cpp src/common/CSVFile.h - src/common/CTextConsole.cpp src/common/CTextConsole.h - src/common/CUOClientVersion.cpp src/common/CUOClientVersion.h - src/common/CUOInstall.cpp src/common/CUOInstall.h - src/common/CVarDefMap.cpp src/common/CVarDefMap.h src/common/datatypes.h - src/common/ListDefContMap.cpp src/common/ListDefContMap.h src/common/os_unix.h src/common/os_windows.h @@ -183,262 +201,300 @@ set(common_SRCS src/common/sphereversion.h src/common/target_info.h ) -source_group(common FILES ${common_SRCS}) +source_group(common FILES ${common_H} ${common_S}) + +############################## +# Windows CrashDump +############################## +set(crashdump_S + src/common/crashdump/crashdump.cpp +) +set(crashdump_H + src/common/crashdump/crashdump.h + src/common/crashdump/mingwdbghelp.h +) +source_group(common\\crashdump FILES ${crashdump_H} ${crashdump_S}) -# Login encryption handling -set(crypto_SRCS +############################## +# Login / Cryptography +############################## +set(crypto_S src/common/crypto/CBCrypt.cpp - src/common/crypto/CBCrypt.h src/common/crypto/CCrypto.cpp - src/common/crypto/CCrypto.h src/common/crypto/CCryptoBlowFish.cpp src/common/crypto/CCryptoLogin.cpp src/common/crypto/CCryptoKeyCalc.cpp - src/common/crypto/CCryptoKeyCalc.h src/common/crypto/CCryptoMD5Interface.cpp src/common/crypto/CCryptoTwoFishInterface.cpp src/common/crypto/CHuffman.cpp - src/common/crypto/CHuffman.h src/common/crypto/CMD5.cpp +) +set(crypto_H + src/common/crypto/CBCrypt.h + src/common/crypto/CCrypto.h + src/common/crypto/CCryptoKeyCalc.h + src/common/crypto/CHuffman.h src/common/crypto/CMD5.h src/common/crypto/crypto_common.h ) -source_group(common\\crypto FILES ${crypto_SRCS}) - +source_group(common\\crypto FILES ${crypto_H} ${crypto_S}) -set(resource_SRCS +############################## +# Sphere Resources +############################## +set(resource_S src/common/resource/CResourceDef.cpp - src/common/resource/CResourceDef.h src/common/resource/CResourceHolder.cpp - src/common/resource/CResourceHolder.h src/common/resource/CResourceID.cpp - src/common/resource/CResourceID.h src/common/resource/CResourceHash.cpp - src/common/resource/CResourceHash.h src/common/resource/CResourceLink.cpp - src/common/resource/CResourceLink.h src/common/resource/CResourceLock.cpp - src/common/resource/CResourceLock.h src/common/resource/CResourceRef.cpp - src/common/resource/CResourceRef.h src/common/resource/CResourceScript.cpp - src/common/resource/CResourceScript.h src/common/resource/CResourceSortedArrays.cpp - src/common/resource/CResourceSortedArrays.h src/common/resource/CResourceQty.cpp - src/common/resource/CResourceQty.h src/common/resource/CValueDefs.cpp +) +set(resource_H + src/common/resource/CResourceDef.h + src/common/resource/CResourceHolder.h + src/common/resource/CResourceID.h + src/common/resource/CResourceHash.h + src/common/resource/CResourceLink.h + src/common/resource/CResourceLock.h + src/common/resource/CResourceRef.h + src/common/resource/CResourceScript.h + src/common/resource/CResourceSortedArrays.h + src/common/resource/CResourceQty.h src/common/resource/CValueDefs.h ) -source_group(common\\resource FILES ${resource_SRCS}) +source_group(common\\resource FILES ${resource_H} ${resource_S}) -set(resourcesections_SRCS +############################## +# Scripted resource sections +############################## +set(resourcesections_S src/common/resource/sections/CDialogDef.cpp - src/common/resource/sections/CDialogDef.h src/common/resource/sections/CItemTypeDef.cpp - src/common/resource/sections/CItemTypeDef.h src/common/resource/sections/CRandGroupDef.cpp - src/common/resource/sections/CRandGroupDef.h src/common/resource/sections/CRegionResourceDef.cpp - src/common/resource/sections/CRegionResourceDef.h src/common/resource/sections/CResourceNamedDef.cpp - src/common/resource/sections/CResourceNamedDef.h src/common/resource/sections/CSkillClassDef.cpp - src/common/resource/sections/CSkillClassDef.h src/common/resource/sections/CSkillDef.cpp - src/common/resource/sections/CSkillDef.h src/common/resource/sections/CSpellDef.cpp - src/common/resource/sections/CSpellDef.h src/common/resource/sections/CWebPageDef.cpp +) +set(resourcesections_H + src/common/resource/sections/CDialogDef.h + src/common/resource/sections/CItemTypeDef.h + src/common/resource/sections/CRandGroupDef.h + src/common/resource/sections/CRegionResourceDef.h + src/common/resource/sections/CResourceNamedDef.h + src/common/resource/sections/CSkillClassDef.h + src/common/resource/sections/CSkillDef.h + src/common/resource/sections/CSpellDef.h src/common/resource/sections/CWebPageDef.h ) -source_group(common\\resource\\sections FILES ${resourcesections_SRCS}) +source_group(common\\resource\\sections FILES ${resourcesections_H} ${resourcesections_S}) -# Sphere library files -set(spherelibrary_SRCS +############################## +# Sphere Library files +############################## +set(spherelibrary_S src/common/sphere_library/CSAssoc.cpp - src/common/sphere_library/CSAssoc.h src/common/sphere_library/CSFile.cpp - src/common/sphere_library/CSFile.h src/common/sphere_library/CSFileList.cpp - src/common/sphere_library/CSFileList.h src/common/sphere_library/CSFileText.cpp - src/common/sphere_library/CSFileText.h src/common/sphere_library/CSMemBlock.cpp + src/common/sphere_library/CSObjCont.cpp + src/common/sphere_library/CSObjList.cpp + src/common/sphere_library/CSQueue.cpp + src/common/sphere_library/CSRand.cpp + src/common/sphere_library/CSString.cpp + src/common/sphere_library/CSTime.cpp + src/common/sphere_library/CSWindow.cpp + src/common/sphere_library/smutex.cpp + src/common/sphere_library/sresetevents.cpp + src/common/sphere_library/sstring.cpp + src/common/sphere_library/sstringobjs.cpp + src/common/sphere_library/stypecast.cpp +) +set(spherelibrary_H + src/common/sphere_library/CSAssoc.h + src/common/sphere_library/CSFile.h + src/common/sphere_library/CSFileList.h + src/common/sphere_library/CSFileText.h src/common/sphere_library/CSMemBlock.h src/common/sphere_library/CSObjArray.h - src/common/sphere_library/CSObjCont.cpp src/common/sphere_library/CSObjCont.h src/common/sphere_library/CSObjContRec.h - src/common/sphere_library/CSObjList.cpp src/common/sphere_library/CSObjList.h src/common/sphere_library/CSObjListRec.h src/common/sphere_library/CSObjSortArray.h src/common/sphere_library/CSPtrTypeArray.h src/common/sphere_library/CSReferenceCount.h src/common/sphere_library/CSTypedArray.h - src/common/sphere_library/CSQueue.cpp src/common/sphere_library/CSQueue.h - src/common/sphere_library/CSRand.cpp src/common/sphere_library/CSRand.h - src/common/sphere_library/CSString.cpp src/common/sphere_library/CSString.h - src/common/sphere_library/CSTime.cpp src/common/sphere_library/CSTime.h - src/common/sphere_library/CSWindow.cpp src/common/sphere_library/CSWindow.h src/common/sphere_library/scontainer_ops.h src/common/sphere_library/sfastmath.h src/common/sphere_library/smap.h src/common/sphere_library/smutex.h - src/common/sphere_library/smutex.cpp src/common/sphere_library/sobjpool.h src/common/sphere_library/squeues.h - src/common/sphere_library/sresetevents.cpp src/common/sphere_library/sresetevents.h src/common/sphere_library/ssorted_vector.h src/common/sphere_library/sptr.h src/common/sphere_library/sptr_containers.h src/common/sphere_library/sstacks.h - src/common/sphere_library/sstring.cpp src/common/sphere_library/sstring.h - src/common/sphere_library/sstringobjs.cpp src/common/sphere_library/sstringobjs.h - src/common/sphere_library/stypecast.cpp src/common/sphere_library/stypecast.h ) -source_group(common\\sphere_library FILES ${spherelibrary_SRCS}) +source_group(common\\sphere_library FILES ${spherelibrary_H} ${spherelibrary_S}) -# Main game files. -set(game_SRCS +############################## +# Main game-related classes +############################## +set(game_S src/game/CBase.cpp - src/game/CBase.h src/game/CContainer.cpp - src/game/CContainer.h src/game/CComponent.cpp - src/game/CComponent.h src/game/CComponentProps.cpp - src/game/CComponentProps.h src/game/CEntity.cpp - src/game/CEntity.h src/game/CEntityProps.cpp - src/game/CEntityProps.h src/game/CObjBase.cpp - src/game/CObjBase.h src/game/CObjBaseTemplate.cpp - src/game/CObjBaseTemplate.h src/game/CPathFinder.cpp - src/game/CPathFinder.h src/game/CRegion.cpp - src/game/CRegion.h src/game/CRegionBase.cpp - src/game/CRegionBase.h src/game/CResourceCalc.cpp - src/game/CScriptProfiler.h src/game/CSector.cpp - src/game/CSector.h - src/game/CSectorEnviron.h src/game/CSectorEnviron.cpp src/game/CSectorTemplate.cpp - src/game/CSectorTemplate.h src/game/CSectorList.cpp - src/game/CSectorList.h src/game/CServer.cpp - src/game/CServer.h src/game/CServerConfig.cpp - src/game/CServerConfig.h src/game/CServerDef.cpp - src/game/CServerDef.h src/game/CServerTime.cpp - src/game/CServerTime.h - src/game/CStartLoc.h src/game/CTeleport.cpp - src/game/CTeleport.h src/game/CTimedFunction.cpp - src/game/CTimedFunction.h src/game/CTimedFunctionHandler.cpp - src/game/CTimedFunctionHandler.h src/game/CTimedObject.cpp - src/game/CTimedObject.h src/game/CWorld.cpp - src/game/CWorld.h src/game/CWorldCache.cpp - src/game/CWorldCache.h src/game/CWorldClock.cpp - src/game/CWorldClock.h src/game/CWorldComm.cpp - src/game/CWorldComm.h src/game/CWorldGameTime.cpp - src/game/CWorldGameTime.h src/game/CWorldImport.cpp src/game/CWorldMap.cpp - src/game/CWorldMap.h src/game/CWorldSearch.cpp - src/game/CWorldSearch.h src/game/CWorldTicker.cpp - src/game/CWorldTicker.h src/game/CWorldTickingList.cpp - src/game/CWorldTickingList.h src/game/CWorldTimedFunctions.cpp + src/game/spheresvr.cpp + src/game/triggers.cpp + src/game/CSectorEnviron.cpp +) +set(game_H + src/game/CBase.h + src/game/CContainer.h + src/game/CComponent.h + src/game/CComponentProps.h + src/game/CEntity.h + src/game/CEntityProps.h + src/game/CObjBase.h + src/game/CObjBaseTemplate.h + src/game/CPathFinder.h + src/game/CRegion.h + src/game/CRegionBase.h + src/game/CScriptProfiler.h + src/game/CSector.h + src/game/CSectorEnviron.h + src/game/CSectorTemplate.h + src/game/CSectorList.h + src/game/CServer.h + src/game/CServerConfig.h + src/game/CServerDef.h + src/game/CServerTime.h + src/game/CStartLoc.h + src/game/CTeleport.h + src/game/CTimedFunction.h + src/game/CTimedFunctionHandler.h + src/game/CTimedObject.h + src/game/CWorld.h + src/game/CWorldCache.h + src/game/CWorldClock.h + src/game/CWorldComm.h + src/game/CWorldGameTime.h + src/game/CWorldMap.h + src/game/CWorldSearch.h + src/game/CWorldTicker.h + src/game/CWorldTickingList.h src/game/CWorldTimedFunctions.h src/game/game_enums.h src/game/game_macros.h - src/game/spheresvr.cpp src/game/spheresvr.h src/game/triggers.h - src/game/triggers.cpp ) -source_group(game FILES ${game_SRCS}) +source_group(game FILES ${game_H} ${game_S}) -set(items_SRCS +############################## +# Item entities +############################## +set(items_S src/game/items/CItem.cpp - src/game/items/CItem.h src/game/items/CItemBase.cpp - src/game/items/CItemBase.h src/game/items/CItemCommCrystal.cpp - src/game/items/CItemCommCrystal.h src/game/items/CItemContainer.cpp - src/game/items/CItemContainer.h src/game/items/CItemCorpse.cpp - src/game/items/CItemCorpse.h src/game/items/CItemMap.cpp - src/game/items/CItemMap.h src/game/items/CItemMemory.cpp - src/game/items/CItemMemory.h src/game/items/CItemMessage.cpp - src/game/items/CItemMessage.h src/game/items/CItemMulti.cpp - src/game/items/CItemMulti.h src/game/items/CItemMultiCustom.cpp - src/game/items/CItemMultiCustom.h src/game/items/CItemPlant.cpp src/game/items/CItemScript.cpp - src/game/items/CItemScript.h src/game/items/CItemShip.cpp - src/game/items/CItemShip.h src/game/items/CItemScript.cpp - src/game/items/CItemScript.h src/game/items/CItemStone.cpp - src/game/items/CItemStone.h src/game/items/CItemVendable.cpp +) +set(items_H + src/game/items/CItem.h + src/game/items/CItemBase.h + src/game/items/CItemCommCrystal.h + src/game/items/CItemContainer.h + src/game/items/CItemCorpse.h + src/game/items/CItemMap.h + src/game/items/CItemMemory.h + src/game/items/CItemMessage.h + src/game/items/CItemMulti.h + src/game/items/CItemMultiCustom.h + src/game/items/CItemScript.h + src/game/items/CItemShip.h + src/game/items/CItemStone.h src/game/items/CItemVendable.h src/game/items/item_types.h ) -source_group(game\\items FILES ${items_SRCS}) +source_group(game\\items FILES ${items_H} ${items_S}) -set(chars_SRCS +############################## +# Chars entities +############################## +set(chars_S src/game/chars/CCharAct.cpp src/game/chars/CCharBase.cpp src/game/chars/CChar.cpp - src/game/chars/CChar.h src/game/chars/CCharAttacker.cpp - src/game/chars/CCharBase.h src/game/chars/CCharFight.cpp src/game/chars/CCharLOS.cpp src/game/chars/CCharMemory.cpp src/game/chars/CCharNotoriety.cpp src/game/chars/CCharNPC.cpp - src/game/chars/CCharNPC.h src/game/chars/CCharNPCAct.cpp src/game/chars/CCharNPCAct_Fight.cpp src/game/chars/CCharNPCAct_Magic.cpp @@ -446,84 +502,93 @@ set(chars_SRCS src/game/chars/CCharNPCPet.cpp src/game/chars/CCharNPCStatus.cpp src/game/chars/CCharPlayer.cpp - src/game/chars/CCharPlayer.h - src/game/chars/CCharRefArray.h src/game/chars/CCharRefArray.cpp src/game/chars/CCharSkill.cpp src/game/chars/CCharSpell.cpp src/game/chars/CCharStat.cpp src/game/chars/CCharStatus.cpp src/game/chars/CCharUse.cpp - src/game/chars/CStoneMember.h src/game/chars/CStoneMember.cpp ) -source_group(game\\chars FILES ${chars_SRCS}) +set(chars_H + src/game/chars/CChar.h + src/game/chars/CCharBase.h + src/game/chars/CCharNPC.h + src/game/chars/CCharPlayer.h + src/game/chars/CCharRefArray.h + src/game/chars/CStoneMember.h +) +source_group(game\\chars FILES ${chars_H} ${items_S}) -set(clients_SRCS +############################## +# Clients +############################## +set(clients_S src/game/clients/CAccount.cpp - src/game/clients/CAccount.h src/game/clients/CChat.cpp - src/game/clients/CChat.h src/game/clients/CChatChannel.cpp - src/game/clients/CChatChannel.h src/game/clients/CChatChanMember.cpp - src/game/clients/CChatChanMember.h src/game/clients/CClient.cpp src/game/clients/CClientDialog.cpp src/game/clients/CClientEvent.cpp - src/game/clients/CClient.h src/game/clients/CClientLog.cpp src/game/clients/CClientMsg.cpp src/game/clients/CClientMsg_AOSTooltip.cpp src/game/clients/CClientTarg.cpp - src/game/clients/CClientTooltip.h src/game/clients/CClientTooltip.cpp src/game/clients/CClientUse.cpp src/game/clients/CGlobalChatChanMember.cpp - src/game/clients/CGlobalChatChanMember.h src/game/clients/CGMPage.cpp - src/game/clients/CGMPage.h src/game/clients/CParty.cpp +) +set(clients_H + src/game/clients/CAccount.h + src/game/clients/CChat.h + src/game/clients/CChatChannel.h + src/game/clients/CChatChanMember.h + src/game/clients/CClient.h + src/game/clients/CClientTooltip.h + src/game/clients/CGlobalChatChanMember.h + src/game/clients/CGMPage.h src/game/clients/CParty.h ) -source_group(game\\clients FILES ${clients_SRCS}) +source_group(game\\clients FILES ${clients_H} ${clients_S}) -set(components_SRCS +############################## +# ECS: components +############################## +set(components_S src/game/components/subcomponents/CFactionDef.cpp - src/game/components/subcomponents/CFactionDef.h src/game/components/CCChampion.cpp - src/game/components/CCChampion.h src/game/components/CCItemDamageable.cpp - src/game/components/CCItemDamageable.h src/game/components/CCMultiMovable.cpp - src/game/components/CCMultiMovable.h src/game/components/CCPropsChar.cpp - src/game/components/CCPropsChar.h src/game/components/CCPropsItem.cpp - src/game/components/CCPropsItem.h src/game/components/CCPropsItemChar.cpp - src/game/components/CCPropsItemChar.h src/game/components/CCPropsItemEquippable.cpp - src/game/components/CCPropsItemEquippable.h src/game/components/CCPropsItemWeapon.cpp - src/game/components/CCPropsItemWeapon.h src/game/components/CCPropsItemWeaponRanged.cpp - src/game/components/CCPropsItemWeaponRanged.h src/game/components/CCSpawn.cpp - src/game/components/CCSpawn.h ) -source_group(game\\components FILES ${components_SRCS}) - -# CrashDump files -set(crashdump_SRCS - src/common/crashdump/crashdump.cpp - src/common/crashdump/crashdump.h - src/common/crashdump/mingwdbghelp.h +set(components_H + src/game/components/subcomponents/CFactionDef.h + src/game/components/CCChampion.h + src/game/components/CCItemDamageable.h + src/game/components/CCMultiMovable.h + src/game/components/CCPropsChar.h + src/game/components/CCPropsItem.h + src/game/components/CCPropsItemChar.h + src/game/components/CCPropsItemEquippable.h + src/game/components/CCPropsItemWeapon.h + src/game/components/CCPropsItemWeaponRanged.h + src/game/components/CCSpawn.h ) -source_group(common\\crashdump FILES ${crashdump_SRCS}) +source_group(game\\components FILES ${components_H} ${components_S}) -# Table definitions -set(tables_SRCS +############################## +# Table Definitions +############################## +set(tables_S src/tables/CBaseBaseDef_props.tbl src/tables/CChar_functions.tbl src/tables/CChar_props.tbl @@ -560,28 +625,57 @@ set(tables_SRCS src/tables/defmessages.tbl src/tables/triggers.tbl ) -source_group(tables FILES ${tables_SRCS}) +source_group(tables FILES ${tables_S}) -set(app_resources_SRCS src/resources/SphereSvr.rc) +############################## +# App Resources (Windows .rc) +############################## +set(app_resources_S + src/resources/SphereSvr.rc +) + +############################## +# Docs and Configs +############################## +set(docs_TEXT + Changelog.txt + src/sphere.ini + src/sphereCrypt.ini +) -# Misc doc and *.ini files -set(docs_TEXT Changelog.txt src/sphere.ini src/sphereCrypt.ini) + +set(SPHERE_HEADERS + ${game_H} + ${items_H} + ${chars_H} + ${clients_H} + ${components_H} + ${uofiles_H} + ${common_H} + ${resource_H} + ${resourcesections_H} + ${network_H} + ${crypto_H} + ${sphere_H} + ${crashdump_H} + ${spherelibrary_H} +) set(SPHERE_SOURCES - ${game_SRCS} - ${items_SRCS} - ${chars_SRCS} - ${clients_SRCS} - ${components_SRCS} - ${uofiles_SRCS} - ${common_SRCS} - ${resource_SRCS} - ${resourcesections_SRCS} - ${network_SRCS} - ${crypto_SRCS} - ${sphere_SRCS} - ${crashdump_SRCS} - ${spherelibrary_SRCS} - ${tables_SRCS} - ${app_resources_SRCS} + ${game_S} + ${items_S} + ${chars_S} + ${clients_S} + ${components_S} + ${uofiles_S} + ${common_S} + ${resource_S} + ${resourcesections_S} + ${network_S} + ${crypto_S} + ${sphere_S} + ${crashdump_S} + ${spherelibrary_S} + ${tables_S} + ${app_resources_S} ) diff --git a/src/common/sphere_library/ssorted_vector.h b/src/common/sphere_library/ssorted_vector.h index ebc54d672..7ef54dfa6 100644 --- a/src/common/sphere_library/ssorted_vector.h +++ b/src/common/sphere_library/ssorted_vector.h @@ -98,6 +98,9 @@ namespace sl return 0; */ + if (!dataptr) + return sl::scont_bad_index(); + size_t _lo = 0; while (_lo < _size) { @@ -169,6 +172,8 @@ namespace sl return sl::scont_bad_index(); } const _Type* const _dataptr = base_type::data(); + if (!_dataptr) + return sl::scont_bad_index(); size_t _idx = this->lower_element(_mySize, _dataptr, value); if (_idx == _mySize) return sl::scont_bad_index(); diff --git a/src/common/sphere_library/stypecast.h b/src/common/sphere_library/stypecast.h index d4143ec2a..2a2d28360 100644 --- a/src/common/sphere_library/stypecast.h +++ b/src/common/sphere_library/stypecast.h @@ -496,9 +496,9 @@ constexpr uint32 usize_narrow_u32(const size_t source_val) noexcept else return a; */ -#if SIZE_MAX == UINT64_MAX +#if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return n64_narrow_n32(source_val); -#elif SIZE_MAX == UINT32_MAX +#elif (__SIZEOF_POINTER__ == 4) //SIZE_MAX == UINT32_MAX return source_val; #else # error "size_t is neither 8 nor 4 bytes?" @@ -696,9 +696,9 @@ template int64 i64_from_u64_checked(T, bool) = delete; // disable i [[nodiscard]] inline int8 i8_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping { -#if SIZE_MAX == UINT64_MAX +#if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return n64_narrow_n8_checked(source_val, should_assert); -#elif SIZE_MAX == UINT32_MAX +#elif (__SIZEOF_POINTER__ == 4) // SIZE_MAX == UINT32_MAX return n32_narrow_n8_checked(source_val, should_assert); #else # error "size_t is neither 8 nor 4 bytes?" @@ -710,9 +710,9 @@ template int8 i8_from_usize_checked(T, bool) = delete; // disable i [[nodiscard]] inline int16 i16_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping { -#if SIZE_MAX == UINT64_MAX +#if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return n64_narrow_n16_checked(source_val, should_assert); -#elif SIZE_MAX == UINT32_MAX +#elif (__SIZEOF_POINTER__ == 4) // SIZE_MAX == UINT32_MAX return n32_narrow_n16_checked(source_val, should_assert); #else # error "size_t is neither 8 nor 4 bytes?" @@ -723,9 +723,9 @@ template int16 i16_from_usize_checked(T, bool) = delete; // disable [[nodiscard]] inline int32 i32_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping { -#if SIZE_MAX == UINT64_MAX +#if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return i32_from_u32_clamping(n64_narrow_n32_checked(source_val, should_assert)); -#elif SIZE_MAX == UINT32_MAX +#elif (__SIZEOF_POINTER__ == 4) // SIZE_MAX == UINT32_MAX return i32_from_u32_checked(n_alias_cast(source_val), should_assert); #else # error "size_t is neither 8 nor 4 bytes?" @@ -736,9 +736,9 @@ template int32 i32_from_usize_checked(T, bool) = delete; // disable [[nodiscard]] inline int64 i64_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping { -#if SIZE_MAX == UINT64_MAX +#if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return i64_from_u64_checked(n_alias_cast(source_val), should_assert); -#elif SIZE_MAX == UINT32_MAX +#elif (__SIZEOF_POINTER__ == 4) // SIZE_MAX == UINT32_MAX (void)should_assert; return static_cast(source_val); // For sure it will fit #else diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index 0d12fa698..3d0a8763b 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -1988,7 +1988,6 @@ bool CServer::r_Verb( CScript &s, CTextConsole * pSrc ) { g_Log.Event(LOGL_EVENT, "Not allowed during world save and/or resync pause"); } - break; } else { diff --git a/static_analysis/coverity_model.cpp b/static_analysis/coverity_model.cpp new file mode 100644 index 000000000..c6d94240c --- /dev/null +++ b/static_analysis/coverity_model.cpp @@ -0,0 +1,5 @@ +// Avoid false-positive issues by giving an alternative definition of Assert_Fail, used by ASSERT and PERSISTANT_ASSERT. +void Assert_Fail(const char *, const char *, long long) +{ + __coverity_panic__(); +} \ No newline at end of file diff --git a/static_analysis/cppcheck-suppressions.txt b/static_analysis/cppcheck-suppressions.txt new file mode 100644 index 000000000..55b010e41 --- /dev/null +++ b/static_analysis/cppcheck-suppressions.txt @@ -0,0 +1,12 @@ +*:/lib/* +unreachableCode +unusedFunction +duplInheritedMember +missingOverride // suppress temporarily +virtualCallInConstructor // suppress temporarily +constParameterPointer // suppress temporarily +passedByValue // suppress temporarily +funcArgNamesDifferent // suppress temporarily +noExplicitConstructor // suppress temporarily +returnByReference // suppress temporarily +functionConst diff --git a/static_analysis/run_clang-tidy.sh b/static_analysis/run_clang-tidy.sh new file mode 100644 index 000000000..7efc982df --- /dev/null +++ b/static_analysis/run_clang-tidy.sh @@ -0,0 +1,2 @@ +#!/use/bin/sh +clang-tidy -p ../build/compile_commands.json -checks='-*,clang-analyzer*' ../src/**/*.cpp ../src/**/*.h From 6c492d2be9b0aecfe416995c5d0940c5237d8e4a Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 24 May 2025 14:08:19 +0200 Subject: [PATCH 023/112] Updated CodeQL actions. --- .github/workflows/codeql.yml | 6 +++--- src/common/common.h | 14 ++++++++++++-- src/game/uo_files/CUOMapList.h | 8 ++++---- ...ang-tidy.sh => run-clang-tidy-analyzer-only.sh} | 0 static_analysis/run-clang-tidy-config.sh | 2 ++ tests/coverity_model.cpp | 5 ----- .../{configure_asan.bat => configure-asan.bat} | 1 + utilities/{configure_asan.sh => configure-asan.sh} | 0 .../{run_clang-format.sh => run-clang-format.sh} | 0 .../{run_gersemi_cmake.sh => run-gersemi-cmake.sh} | 0 10 files changed, 22 insertions(+), 14 deletions(-) rename static_analysis/{run_clang-tidy.sh => run-clang-tidy-analyzer-only.sh} (100%) create mode 100644 static_analysis/run-clang-tidy-config.sh delete mode 100644 tests/coverity_model.cpp rename utilities/{configure_asan.bat => configure-asan.bat} (91%) rename utilities/{configure_asan.sh => configure-asan.sh} (100%) rename utilities/{run_clang-format.sh => run-clang-format.sh} (100%) rename utilities/{run_gersemi_cmake.sh => run-gersemi-cmake.sh} (100%) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6ba6ae5be..6090c293b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -88,7 +88,7 @@ jobs: # https://josh-ops.com/posts/github-codeql-ignore-files/ # https://github.com/advanced-security/filter-sarif - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" upload: false # disable the upload here - we will upload in a different action @@ -103,6 +103,6 @@ jobs: output: sarif-results/${{ matrix.language }}.sarif - name: Upload SARIF - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: sarif-results/${{ matrix.language }}.sarif diff --git a/src/common/common.h b/src/common/common.h index 098e25ae1..8fecc2308 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -66,9 +66,19 @@ /* Coding helpers */ // Target arch. -#if defined(_WIN64) || (__SIZEOF_POINTER__ == 8) +#ifndef __SIZEOF_POINTER__ +# if defined(_WIN64) +# define __SIZEOF_POINTER__ 8 +# elif defined(_WIN32) +# define __SIZEOF_POINTER__ 4 +# else +# error "Can't detect the arch?" +# endif +#endif + +#if (__SIZEOF_POINTER__ == 8) # define ARCH_64 -#elif defined(_WIN32) || (__SIZEOF_POINTER__ == 4) +#elif (__SIZEOF_POINTER__ == 4) # define ARCH_32 #else # error "Can't detect the arch?" diff --git a/src/game/uo_files/CUOMapList.h b/src/game/uo_files/CUOMapList.h index 91ebb955e..3823be4ac 100644 --- a/src/game/uo_files/CUOMapList.h +++ b/src/game/uo_files/CUOMapList.h @@ -87,8 +87,8 @@ extern class CUOMapList int CalcSectorQty(int map) const; // Use it only when initializing the map sectors! (because it's slower than the Get* method) int CalcSectorCols(int map) const; // Use it only when initializing the map sectors! (because it's slower than the Get* method) int CalcSectorRows(int map) const; // Use it only when initializing the map sectors! (because it's slower than the Get* method) - inline int GetMapSizeX(int map) const noexcept; - inline int GetMapSizeY(int map) const noexcept; + inline uint16 GetMapSizeX(int map) const noexcept; + inline uint16 GetMapSizeY(int map) const noexcept; int GetMapCenterX(int map) const; int GetMapCenterY(int map) const; @@ -101,7 +101,7 @@ extern class CUOMapList // Inline methods definition -inline int CUOMapList::GetMapSizeX(int map) const noexcept +inline uint16 CUOMapList::GetMapSizeX(int map) const noexcept { // Used by CPointBase::IsValidXY() and IsValidPoint(), which is called a LOT //ASSERT(IsMapSupported(map)); @@ -109,7 +109,7 @@ inline int CUOMapList::GetMapSizeX(int map) const noexcept return m_mapGeoData.maps[map].sizex; } -inline int CUOMapList::GetMapSizeY(int map) const noexcept +inline uint16 CUOMapList::GetMapSizeY(int map) const noexcept { // Used by CPointBase::IsValidXY() and IsValidPoint(), which is called a LOT //ASSERT(IsMapSupported(map)); diff --git a/static_analysis/run_clang-tidy.sh b/static_analysis/run-clang-tidy-analyzer-only.sh similarity index 100% rename from static_analysis/run_clang-tidy.sh rename to static_analysis/run-clang-tidy-analyzer-only.sh diff --git a/static_analysis/run-clang-tidy-config.sh b/static_analysis/run-clang-tidy-config.sh new file mode 100644 index 000000000..a819504d3 --- /dev/null +++ b/static_analysis/run-clang-tidy-config.sh @@ -0,0 +1,2 @@ +#!/use/bin/sh +clang-tidy -p ../build/compile_commands.json -config-file='../.clang-tidy' ../src/**/*.cpp ../src/**/*.h diff --git a/tests/coverity_model.cpp b/tests/coverity_model.cpp deleted file mode 100644 index c6d94240c..000000000 --- a/tests/coverity_model.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Avoid false-positive issues by giving an alternative definition of Assert_Fail, used by ASSERT and PERSISTANT_ASSERT. -void Assert_Fail(const char *, const char *, long long) -{ - __coverity_panic__(); -} \ No newline at end of file diff --git a/utilities/configure_asan.bat b/utilities/configure-asan.bat similarity index 91% rename from utilities/configure_asan.bat rename to utilities/configure-asan.bat index 2f91dc276..6a7f47e63 100644 --- a/utilities/configure_asan.bat +++ b/utilities/configure-asan.bat @@ -3,6 +3,7 @@ REM SET ASAN_SYMBOLIZER_PATH="C:\Program Files\LLVM\bin\llvm-symbolizer.exe" REM ASAN_MORE=check_initialization_order=1: @echo on +REM use SETX to add asan options as environmental variables SET _SAN_COMMON_FLAGS=symbolize=1:handle_abort=true:abort_on_error=false SET ASAN_OPTIONS=%_SAN_COMMON_FLAGS%:strict_init_order=true:detect_stack_use_after_return=true SET ASAN_OPTIONS=%ASAN_OPTIONS%:sleep_before_dying=3:print_stats=true:stack_trace_format="[frame=%%n, function=%%f, location=%%S]" diff --git a/utilities/configure_asan.sh b/utilities/configure-asan.sh similarity index 100% rename from utilities/configure_asan.sh rename to utilities/configure-asan.sh diff --git a/utilities/run_clang-format.sh b/utilities/run-clang-format.sh similarity index 100% rename from utilities/run_clang-format.sh rename to utilities/run-clang-format.sh diff --git a/utilities/run_gersemi_cmake.sh b/utilities/run-gersemi-cmake.sh similarity index 100% rename from utilities/run_gersemi_cmake.sh rename to utilities/run-gersemi-cmake.sh From 36c0b71c635a9c57721f4e8dc3d339b3b366f9ce Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 24 May 2025 23:04:44 +0200 Subject: [PATCH 024/112] Added option to run static analysis via CppCheck --- CMakeLists.txt | 23 +++++--- src/common/CPointBase.cpp | 3 +- src/common/sphere_library/ssorted_vector.h | 10 +--- src/game/CServer.cpp | 57 ++++++++++--------- static_analysis/cppcheck-suppressions.txt | 41 +++++++++---- .../run-clang-tidy-analyzer-only.sh | 2 +- static_analysis/run-clang-tidy-config.sh | 2 +- 7 files changed, 81 insertions(+), 57 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a74e8543..43574f935 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,8 +131,9 @@ option(USE_UBSAN "Enable Undefined Behavior Sanitizer." FALSE) option(USE_LSAN "Enable LeakSanitizer." FALSE) option(USE_MSAN "Enable MemorySanitizer." FALSE) -option(USE_CPPCHECK "Perform static analysis with CppCheck." FALSE) +option(UNIT_TESTS "Create an executable for unit tests." FALSE) +option(USE_CPPCHECK "Perform static analysis with CppCheck." FALSE) set(ENABLED_SANITIZER FALSE CACHE INTERNAL BOOL "Am i using a sanitizer?") if(USE_ASAN OR USE_MSAN OR USE_LSAN OR USE_MSAN) @@ -148,16 +149,24 @@ if(USE_CPPCHECK) list( APPEND CMAKE_CXX_CPPCHECK - "-v" + "--verbose" + #"--debug" + "--report-progress" "--enable=all" + "--safety" "--inconclusive" - "--force" - "--inline-suppr" - "--suppress=missingIncludeSystem" + #"--force" + #"--inline-suppr" + "--check-level=exhaustive" "--template='{file}({line}): {severity} ({id}): {message}'" - "--checkers-report=cppcheck_out.txt" - #"--suppressions-list=${CMAKE_SOURCE_DIR}/CppCheckSuppressions.txt" + #"--checkers-report=${CMAKE_SOURCE_DIR}/cppcheck_out.txt" + "--output-file=${CMAKE_BINARY_DIR}/cppcheck.log" + "--suppressions-list=${CMAKE_SOURCE_DIR}/static_analysis/cppcheck-suppressions.txt" + "--config-exclude=lib/" + "-i lib/" "-D__SIZEOF_POINTER__=${SIZEOF_VOID_P}" + "-U_TESTEXCEPTION" + "-U_EXCEPTIONS_DEBUG" ) endif() endif() diff --git a/src/common/CPointBase.cpp b/src/common/CPointBase.cpp index 52c9821ed..ba450b05a 100644 --- a/src/common/CPointBase.cpp +++ b/src/common/CPointBase.cpp @@ -3,6 +3,7 @@ #include "../game/items/CItem.h" #include "../game/uo_files/CUOMultiItemRec.h" #include "../game/uo_files/CUOStaticItemRec.h" +#include "../game/uo_files/CUOMapList.h" #include "../game/CSector.h" #include "../game/CServer.h" #include "../game/CWorldMap.h" @@ -12,8 +13,6 @@ #include "CPointBase.h" #include -#include "../game/uo_files/CUOMapList.h" - static_assert(sizeof(CPointBase) == sizeof(CPointMap), "CPointBase and CPointMap have to have the same size. Was a virtual method added?"); diff --git a/src/common/sphere_library/ssorted_vector.h b/src/common/sphere_library/ssorted_vector.h index 7ef54dfa6..b3d9f261e 100644 --- a/src/common/sphere_library/ssorted_vector.h +++ b/src/common/sphere_library/ssorted_vector.h @@ -168,13 +168,10 @@ namespace sl size_t sorted_vector<_Type, _Comp>::find(_Type const& value) const noexcept { const size_t _mySize = base_type::size(); - if (_mySize == 0) { - return sl::scont_bad_index(); - } const _Type* const _dataptr = base_type::data(); - if (!_dataptr) + if ((_mySize == 0) || !_dataptr) return sl::scont_bad_index(); - size_t _idx = this->lower_element(_mySize, _dataptr, value); + const size_t _idx = this->lower_element(_mySize, _dataptr, value); if (_idx == _mySize) return sl::scont_bad_index(); return (!this->_comparatorObj(value, _dataptr[_idx])) ? _idx : sl::scont_bad_index(); @@ -185,9 +182,8 @@ namespace sl size_t sorted_vector<_Type, _Comp>::find_predicate(_ValType const& value, _Pred && predicate) const noexcept { const size_t _mySize = base_type::size(); - if (_mySize == 0) { + if ((_mySize == 0) || !base_type::data()) return sl::scont_bad_index(); - } return this->binary_search_predicate(_mySize, value, predicate); } diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index 3d0a8763b..38acbde62 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -1030,42 +1030,44 @@ bool CServer::OnConsoleCmd( CSString & sText, CTextConsole * pSrc ) SetExitFlag( 1 ); } } break; + +// Something here makes CppCheck stop for 'syntax error'... #ifdef _TESTEXCEPTION - case '$': // call stack integrity + case '$': // call stack integrity { #ifdef _EXCEPTIONS_DEBUG - { // test without freezing the call stack + { + / test without freezing the call stack ADDTOCALLSTACK("CServer::TestException1"); EXC_TRY("Test1"); throw CSError( LOGM_DEBUG, 0, "Test Exception #1"); - } - catch (const CSError& e) - { - // the following call will destroy the stack trace on linux due to - // a call to CSFile::Close fromn CLog::EventStr. - g_Log.Event( LOGM_DEBUG, "Caught exception\n" ); - EXC_CATCH_EXCEPTION_SPHERE(&e); - } - } + } + catch (const CSError& e) + { + // the following call will destroy the stack trace on linux due to + // a call to CSFile::Close fromn CLog::EventStr. + g_Log.Event( LOGM_DEBUG, "Caught exception\n" ); + _EXC_CATCH_EXCEPTION_GENERIC(&e, "CSError"); + } - { // test pausing the call stack + { + // test pausing the call stack ADDTOCALLSTACK("CServer::TestException2"); EXC_TRY("Test2"); throw CSError( LOGM_DEBUG, 0, "Test Exception #2"); - } - catch (const CSError& e) - { - StackDebugInformation::freezeCallStack(true); - // with freezeCallStack, the following call won't be recorded - g_Log.Event( LOGM_DEBUG, "Caught exception\n" ); - StackDebugInformation::freezeCallStack(false); - EXC_CATCH_EXCEPTION_SPHERE(&e); - } - } + } + catch (const CSError& e) + { + StackDebugInformation::freezeCallStack(true); + // with freezeCallStack, the following call won't be recorded + g_Log.Event( LOGM_DEBUG, "Caught exception\n" ); + StackDebugInformation::freezeCallStack(false); + _EXC_CATCH_EXCEPTION_GENERIC(&e, "CSError"); + } #else - throw CSError(LOGL_CRIT, E_FAIL, "This test requires exception debugging enabled"); + throw CSError(LOGL_CRIT, E_FAIL, "This test requires exception debugging enabled"); #endif - } break; + } break; case '%': // throw simple exception { throw 0; @@ -1086,8 +1088,9 @@ bool CServer::OnConsoleCmd( CSString & sText, CTextConsole * pSrc ) throw CSError(LOGL_CRIT, (dword)E_FAIL, "Test Exception"); } #endif - default: - goto longcommand; + + default: + goto longcommand; } goto endconsole; @@ -1108,7 +1111,7 @@ bool CServer::OnConsoleCmd( CSString & sText, CTextConsole * pSrc ) if ( g_Cfg.m_sStripPath.IsEmpty() ) { - if (pSrc != this) + if (pSrc && pSrc != this) { pSrc->SysMessage("StripPath not defined, function aborted.\n"); } diff --git a/static_analysis/cppcheck-suppressions.txt b/static_analysis/cppcheck-suppressions.txt index 55b010e41..79cbf544f 100644 --- a/static_analysis/cppcheck-suppressions.txt +++ b/static_analysis/cppcheck-suppressions.txt @@ -1,12 +1,29 @@ -*:/lib/* -unreachableCode -unusedFunction -duplInheritedMember -missingOverride // suppress temporarily -virtualCallInConstructor // suppress temporarily -constParameterPointer // suppress temporarily -passedByValue // suppress temporarily -funcArgNamesDifferent // suppress temporarily -noExplicitConstructor // suppress temporarily -returnByReference // suppress temporarily -functionConst +# 1) All warnings in any subdir “lib” +*:*/lib/* + +# 2) Specific warnings everywhere +unmatchedSuppression:* +missingIncludeSystem:* +unreachableCode:* +unusedFunction:* +duplInheritedMember:* +missingOverride:* +virtualCallInConstructor:* +passedByValue:* +funcArgNamesDifferent:* +noExplicitConstructor:* +useStlAlgorithm:* +# Performance: Not a priority right now: +returnByReference:* +useInitializationList:* +# Style: Not a priority right now: +functionConst:* +constParameterPointer:* +constVariablePointer:* +functionStatic:* +unreadVariable:* +variableScope:* + +#3) False positives in some files +nullPointer:*/src/common/sphere_library/ssorted_vector.h +knownConditionTrueFalse:*/src/common/sphere_library/ssorted_vector.h diff --git a/static_analysis/run-clang-tidy-analyzer-only.sh b/static_analysis/run-clang-tidy-analyzer-only.sh index 7efc982df..c9c4f858b 100644 --- a/static_analysis/run-clang-tidy-analyzer-only.sh +++ b/static_analysis/run-clang-tidy-analyzer-only.sh @@ -1,2 +1,2 @@ -#!/use/bin/sh +#!/usr/bin/sh clang-tidy -p ../build/compile_commands.json -checks='-*,clang-analyzer*' ../src/**/*.cpp ../src/**/*.h diff --git a/static_analysis/run-clang-tidy-config.sh b/static_analysis/run-clang-tidy-config.sh index a819504d3..6c310e897 100644 --- a/static_analysis/run-clang-tidy-config.sh +++ b/static_analysis/run-clang-tidy-config.sh @@ -1,2 +1,2 @@ -#!/use/bin/sh +#!/usr/bin/sh clang-tidy -p ../build/compile_commands.json -config-file='../.clang-tidy' ../src/**/*.cpp ../src/**/*.h From 86373c92335c9184e070876f4ac9be0d564c9d7a Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 25 May 2025 17:08:56 +0200 Subject: [PATCH 025/112] Add unit testing via DocTest Moved CppCheck cmake code in a separate file. --- .clangd | 3 +- CMakeLists.txt | 60 +++------ cmake/CppCheck.cmake | 28 ++++ lib/CMakeLists.txt | 13 +- lib/mariadb_connector_c/CMakeLists.txt | 6 +- src/game/spheresvr.cpp | 22 ++++ src/sphere/UnixTerminal.cpp | 4 +- src/sphere/threads.cpp | 4 +- src/sphere/threads.h | 2 + tests/CMakeLists.txt | 56 ++++++++ tests/CMakeSources.cmake | 7 + tests/src/t_CPointBase.cpp | 176 +++++++++++++++++++++++++ 12 files changed, 326 insertions(+), 55 deletions(-) create mode 100644 cmake/CppCheck.cmake create mode 100644 tests/CMakeLists.txt create mode 100644 tests/CMakeSources.cmake create mode 100644 tests/src/t_CPointBase.cpp diff --git a/.clangd b/.clangd index 243a3c7f2..bdc6dfd1c 100644 --- a/.clangd +++ b/.clangd @@ -17,4 +17,5 @@ CompileFlags: # Diagnostics: # Suppress: ['-Wunused-include-directive'] -# TODO: disable clazy-non-pod-global-static +Diagnostics: + Suppress: non-pod-global-static diff --git a/CMakeLists.txt b/CMakeLists.txt index 43574f935..435b7411d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,7 +131,7 @@ option(USE_UBSAN "Enable Undefined Behavior Sanitizer." FALSE) option(USE_LSAN "Enable LeakSanitizer." FALSE) option(USE_MSAN "Enable MemorySanitizer." FALSE) -option(UNIT_TESTS "Create an executable for unit tests." FALSE) +option(UNIT_TESTING "Create an executable for unit tests." FALSE) option(USE_CPPCHECK "Perform static analysis with CppCheck." FALSE) @@ -141,34 +141,7 @@ if(USE_ASAN OR USE_MSAN OR USE_LSAN OR USE_MSAN) endif() if(USE_CPPCHECK) - find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck) - if(CMAKE_CXX_CPPCHECK) - include(CheckTypeSize) - check_type_size("void*" SIZEOF_VOID_P) - #message(STATUS "Pointer size = ${SIZEOF_VOID_P}") - - list( - APPEND CMAKE_CXX_CPPCHECK - "--verbose" - #"--debug" - "--report-progress" - "--enable=all" - "--safety" - "--inconclusive" - #"--force" - #"--inline-suppr" - "--check-level=exhaustive" - "--template='{file}({line}): {severity} ({id}): {message}'" - #"--checkers-report=${CMAKE_SOURCE_DIR}/cppcheck_out.txt" - "--output-file=${CMAKE_BINARY_DIR}/cppcheck.log" - "--suppressions-list=${CMAKE_SOURCE_DIR}/static_analysis/cppcheck-suppressions.txt" - "--config-exclude=lib/" - "-i lib/" - "-D__SIZEOF_POINTER__=${SIZEOF_VOID_P}" - "-U_TESTEXCEPTION" - "-U_EXCEPTIONS_DEBUG" - ) - endif() + include("cmake/CppCheck.cmake") endif() # -------------------- Stuff to set right away, after having parsed the checkboxes/CLI arguments. @@ -196,7 +169,6 @@ endif() # TODO: check if Ninja can handle different targets. if(SINGLE_TARGET) # If you want to manually specify the build type, call cmake with parameter: -DCMAKE_BUILD_TYPE=something - # TODO: add NightlyWithDebInfo ? message(STATUS "Single-target build system (${CMAKE_GENERATOR}) detected: generating multiple projects!") set (CMAKE_BUILD_TYPE "Nightly" CACHE STRING "Set the build type. Expected values: Debug, Nightly or Release.") # Set only if unset. set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Nightly" "Release") @@ -237,6 +209,10 @@ endif(SINGLE_TARGET) # Include the list of all Sphere source files include("src/CMakeSources.cmake") +if(UNIT_TESTING) + # Add the unit tests source files, to compile them and add to the IDe project files list + include("tests/CMakeSources.cmake") +endif() # Configure output binaries set(TARGETS "" CACHE INTERNAL "List of Sphere targets to build.") @@ -250,9 +226,6 @@ if(SINGLE_TARGET) #${docs_TEXT} ) set_target_properties(spheresvr_release PROPERTIES OUTPUT_NAME SphereSvrX${ARCH_BITS}_release) - #set_target_properties(spheresvr_release PROPERTIES CXX_EXTENSIONS OFF) - #target_compile_features(spheresvr_release PUBLIC cxx_std_20) - target_precompile_headers(spheresvr_release ${pch_list}) if(NOT ${FORCE_DEBUG_INFO}) add_custom_command(TARGET spheresvr_release POST_BUILD @@ -270,9 +243,6 @@ if(SINGLE_TARGET) #${docs_TEXT} ) set_target_properties(spheresvr_nightly PROPERTIES OUTPUT_NAME SphereSvrX${ARCH_BITS}_nightly) - #set_target_properties(spheresvr_nightly PROPERTIES CXX_EXTENSIONS OFF) - #target_compile_features(spheresvr_nightly PUBLIC cxx_std_20) - target_precompile_headers(spheresvr_nightly ${pch_list}) if(NOT ${FORCE_DEBUG_INFO}) add_custom_command(TARGET spheresvr_nightly POST_BUILD @@ -290,9 +260,6 @@ if(SINGLE_TARGET) #${docs_TEXT} ) set_target_properties(spheresvr_debug PROPERTIES OUTPUT_NAME SphereSvrX${ARCH_BITS}_debug) - #set_target_properties(spheresvr_debug PROPERTIES CXX_EXTENSIONS OFF) - #target_compile_features(spheresvr_debug PUBLIC cxx_std_20) - target_precompile_headers(spheresvr_debug ${pch_list}) endif() else(SINGLE_TARGET) set(TARGETS ${TARGETS} spheresvr) @@ -304,9 +271,6 @@ else(SINGLE_TARGET) set_target_properties(spheresvr PROPERTIES OUTPUT_NAME_RELEASE SphereSvrX${ARCH_BITS}_release) set_target_properties(spheresvr PROPERTIES OUTPUT_NAME_NIGHTLY SphereSvrX${ARCH_BITS}_nightly) set_target_properties(spheresvr PROPERTIES OUTPUT_NAME_DEBUG SphereSvrX${ARCH_BITS}_debug) - #set_target_properties(spheresvr PROPERTIES CXX_EXTENSIONS OFF) - #target_compile_features(spheresvr PUBLIC cxx_std_20) - target_precompile_headers(spheresvr ${pch_list}) # To be tested #add_custom_command(TARGET spheresvr POST_BUILD @@ -316,6 +280,17 @@ else(SINGLE_TARGET) #) endif(SINGLE_TARGET) +foreach(tgt ${TARGETS}) + #set_target_properties(${tgt} PROPERTIES CXX_EXTENSIONS OFF) + #target_compile_features(${tgt} PUBLIC cxx_std_20) + target_precompile_headers(${tgt} ${pch_list}) +endforeach() + +if(UNIT_TESTING) + include("tests/CMakeLists.txt") # quick and dirty + setup_tests() +endif() + # -------------------- message(STATUS) @@ -336,3 +311,4 @@ message(STATUS) message(STATUS "Retrieving git data...") include("cmake/GitStatus.cmake") message(STATUS) + diff --git a/cmake/CppCheck.cmake b/cmake/CppCheck.cmake new file mode 100644 index 000000000..31caa9bf1 --- /dev/null +++ b/cmake/CppCheck.cmake @@ -0,0 +1,28 @@ +find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck) +if(CMAKE_CXX_CPPCHECK) + include(CheckTypeSize) + check_type_size("void*" SIZEOF_VOID_P) + #message(STATUS "Pointer size = ${SIZEOF_VOID_P}") + + list( + APPEND CMAKE_CXX_CPPCHECK + "--verbose" + #"--debug" + "--report-progress" + "--enable=all" + "--safety" + "--inconclusive" + #"--force" + #"--inline-suppr" + "--check-level=exhaustive" + "--template='{file}({line}): {severity} ({id}): {message}'" + #"--checkers-report=${CMAKE_SOURCE_DIR}/cppcheck_out.txt" + "--output-file=${CMAKE_BINARY_DIR}/cppcheck.log" + "--suppressions-list=${CMAKE_SOURCE_DIR}/static_analysis/cppcheck-suppressions.txt" + "--config-exclude=lib/" + "-i lib/" + "-D__SIZEOF_POINTER__=${SIZEOF_VOID_P}" + "-U_TESTEXCEPTION" + "-U_EXCEPTIONS_DEBUG" + ) +endif() diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index f153491b3..fdf20b991 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -44,7 +44,7 @@ foreach(tgt ${TARGETS}) target_include_directories( ${tgt} PRIVATE $ - PRIVATE $ + PRIVATE $ PRIVATE $ PRIVATE $ PRIVATE $ @@ -58,11 +58,14 @@ foreach(tgt ${TARGETS}) target_link_libraries(${tgt} PRIVATE bcrypt twofish) # cdrc (not needed for now) if(${LIB_LIBEV_BUILD}) target_link_libraries(${tgt} PRIVATE libev) + add_dependencies(${tgt} libev) endif() - if(${LIB_SQLITE_BUILD}) + #if(${LIB_SQLITE_BUILD}) # search it in the system, if not compiling from sources? target_link_libraries(${tgt} PRIVATE sqlite) - endif() - if(${LIB_ZLIB_BUILD}) + #endif() + #if(${LIB_ZLIB_BUILD}) # search it in the system, if not compiling from sources? target_link_libraries(${tgt} PRIVATE zlib) - endif() + #endif() + add_dependencies(${tgt} bcrypt twofish sqlite zlib) + endforeach() diff --git a/lib/mariadb_connector_c/CMakeLists.txt b/lib/mariadb_connector_c/CMakeLists.txt index 460c5090b..af685ff94 100644 --- a/lib/mariadb_connector_c/CMakeLists.txt +++ b/lib/mariadb_connector_c/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.19) -project(mariadb_connector C) +project(mariadb_connector_c C) # we do not build the library here, only provide the headers for a precompiled one. #include("lib_compiler_flags_common_c.cmake") @@ -9,9 +9,9 @@ include("mysql/configure.cmake") set(lib_SOURCES mysql/config.h mysql/mysql.h) # create the target. equivalent to add_executable, but for libraries -add_library(mariadb_connector INTERFACE ${lib_SOURCES}) +add_library(mariadb_connector_c INTERFACE ${lib_SOURCES}) # make the headers available in the include path (accessible with < >). -target_include_directories(mariadb_connector INTERFACE .) +target_include_directories(mariadb_connector_c INTERFACE .) # add to the proper directories in the solution explorer (eg. Visual Studio) source_group(lib\\mariadb_connector_c FILES ${lib_SOURCES}) diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 4e2bf92b8..27110fdb0 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -33,6 +33,11 @@ #include #include +#ifdef UNIT_TESTING +# define DOCTEST_CONFIG_IMPLEMENT +# include +#endif + // Dynamic allocation of some global stuff std::string g_sServerDescription; @@ -473,6 +478,19 @@ void atexit_handler() ThreadHolder::get().markThreadsClosing(); } +#ifdef UNIT_TESTING +static int DocTestMain() +{ + doctest::Context context; + + int res = context.run(); // run + + if(context.shouldExit()) // important - query flags (and --exit) rely on the user doing this + return res; // propagate the result of the tests + + return res; +} +#endif #ifdef _WIN32 int Sphere_MainEntryPoint( int argc, char *argv[] ) @@ -488,6 +506,10 @@ int main( int argc, char * argv[] ) UnreferencedParameter(curthread); } +#ifdef UNIT_TESTING + return DocTestMain(); +#endif + static constexpr lpctstr m_sClassName = "main"; EXC_TRY("MAIN"); diff --git a/src/sphere/UnixTerminal.cpp b/src/sphere/UnixTerminal.cpp index 30edb15b0..1a650fd5a 100644 --- a/src/sphere/UnixTerminal.cpp +++ b/src/sphere/UnixTerminal.cpp @@ -264,8 +264,8 @@ void UnixTerminal::prepareColor() void UnixTerminal::restore() { - ADDTOCALLSTACK("UnixTerminal::restore"); - ASSERT(m_prepared); + if (!m_prepared) + return; #ifdef _USECURSES endwin(); // clean up diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index d9f0d8617..701415d00 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -795,9 +795,9 @@ AbstractSphereThread::AbstractSphereThread(const char *name, ThreadPriority prio #ifdef THREAD_TRACK_CALLSTACK , m_stackInfo{}, m_stackInfoCopy{}, m_iStackPos(-1), m_iStackUnwindingStackPos(-1), m_iCaughtExceptionStackPos(-1), - m_fFreezeCallStack(false), + m_fFreezeCallStack(false) #endif - m_pExpr{std::make_unique()} + , m_pExpr{std::make_unique()} { // profiles that apply to every thread m_profile.EnableProfile(PROFILE_IDLE); diff --git a/src/sphere/threads.h b/src/sphere/threads.h index 8ebe1c045..e736dd4f9 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -314,6 +314,7 @@ class AbstractSphereThread : public AbstractThread virtual bool shouldExit() noexcept; }; +#ifdef THREAD_TRACK_CALLSTACK bool AbstractSphereThread::isExceptionCaught() const noexcept { return (m_iCaughtExceptionStackPos >= 0); @@ -328,6 +329,7 @@ void AbstractSphereThread::freezeCallStack(bool freeze) noexcept { m_fFreezeCallStack = freeze; } +#endif // Dummy thread for context when no thread really exists. To be called only once, at startup. class DummySphereThread : public AbstractSphereThread diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..8ab5f1d29 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,56 @@ +#set(FETCHCONTENT_BASE_DIR +# "${CMAKE_SOURCE_DIR}/third_party" +#) +include(FetchContent) + +message(STATUS "") +message(STATUS "Unit Tests requested") +message(STATUS "Fetching DocTest") + +# Download the JSON for the latest release +set(DOCTEST_RELEASE_API + "https://api.github.com/repos/doctest/doctest/releases/latest" +) + +file(DOWNLOAD + ${DOCTEST_RELEASE_API} + ${CMAKE_BINARY_DIR}/doctest_latest.json + SHOW_PROGRESS +) + +# Extract "tag_name" via regex +file(READ "${CMAKE_BINARY_DIR}/doctest_latest.json" _json) +string(REGEX MATCH "\"tag_name\"[ \t]*:[ \t]*\"([^\"]+)\"" _ + "${_json}") +set(DOCTEST_TAG "${CMAKE_MATCH_1}") + +message(STATUS "Using doctest tag: ${DOCTEST_TAG}") + +FetchContent_Declare( + doctest + GIT_REPOSITORY https://github.com/doctest/doctest.git + GIT_TAG ${DOCTEST_TAG} +) +FetchContent_MakeAvailable(doctest) + +# Enable testing +enable_testing() + +function(setup_tests) + foreach(tgt ${TARGETS}) + target_link_libraries(${tgt} PRIVATE doctest::doctest) + target_compile_definitions(${tgt} PRIVATE UNIT_TESTING) + + #[[ + add_custom_target(run_tests + COMMAND spheresvr --reporters=console + DEPENDS spheresvr + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + ]] + + # Register with CTest + add_test(NAME all_tests_${tgt} + COMMAND ${tgt}) + endforeach() +endfunction() diff --git a/tests/CMakeSources.cmake b/tests/CMakeSources.cmake new file mode 100644 index 000000000..0181ef5b6 --- /dev/null +++ b/tests/CMakeSources.cmake @@ -0,0 +1,7 @@ +set(SPHERE_TESTS_SOURCES + tests/src/t_CPointBase.cpp +) +source_group(tests FILES ${SPHERE_TESTS_SOURCES}) + +#set(SPHERE_HEADERS ${SPHERE_HEADERS} ${SPHERE_TESTS_HEADERS}) +set(SPHERE_SOURCES ${SPHERE_SOURCES} ${SPHERE_TESTS_SOURCES}) diff --git a/tests/src/t_CPointBase.cpp b/tests/src/t_CPointBase.cpp new file mode 100644 index 000000000..b42fe00b2 --- /dev/null +++ b/tests/src/t_CPointBase.cpp @@ -0,0 +1,176 @@ +#include +#include "../../src/common/CPointBase.h" +#include "../../src/game/uo_files/uofiles_macros.h" + +// Reference implementations, inside anonymous namespace to make clear that the functions are local to this translation unit. +namespace { namespace ref_impl +{ + DIR_TYPE GetDir(const CPointBase &ptMe, const CPointBase &ptOther, DIR_TYPE DirDefault = DIR_QTY ) + { + // Direction to point pt + const int dx = (ptMe.m_x-ptOther.m_x); + const int dy = (ptMe.m_y-ptOther.m_y); + const int ax = abs(dx); + const int ay = abs(dy); + if ( ay > ax ) + { + if ( ! ax ) + return(( dy > 0 ) ? DIR_N : DIR_S ); + + const int slope = ay / ax; + if ( slope > 2 ) + return(( dy > 0 ) ? DIR_N : DIR_S ); + if ( dx > 0 ) // westish + return(( dy > 0 ) ? DIR_NW : DIR_SW ); + return(( dy > 0 ) ? DIR_NE : DIR_SE ); + } + else + { + if ( ! ay ) + { + if ( ! dx ) + return( DirDefault ); // here ? + return(( dx > 0 ) ? DIR_W : DIR_E ); + } + const int slope = ax / ay; + if ( slope > 2 ) + return(( dx > 0 ) ? DIR_W : DIR_E ); + if ( dy > 0 ) + return(( dx > 0 ) ? DIR_NW : DIR_NE ); + return(( dx > 0 ) ? DIR_SW : DIR_SE ); + } + } + + bool IsValidPoint(CPointBase const& pt) noexcept + { + if (!((pt.m_z > -127 /* -UO_SIZE_Z */) && (pt.m_z < 127 /* UO_SIZE_Z */))) + return false; + if ( (pt.m_x < 0) || (pt.m_y < 0) ) + return false; + if ( (pt.m_x >= 7168 /*g_MapList.GetMapSizeX(pt.m_map)*/) + || (pt.m_y >= 4096 /*g_MapList.GetMapSizeY(pt.m_map)*/)) + return false; + return true; + } + + bool IsCharValid(CPointBase const& pt) noexcept + { + if ( (pt.m_z <= -UO_SIZE_Z) || (pt.m_z >= UO_SIZE_Z) ) + return false; + if ((pt.m_x <= 0) || (pt.m_y <= 0)) + return false; + if ( (pt.m_x >= 7168 /*g_MapList.GetMapSizeX(pt.m_map)*/) + || (pt.m_y >= 4096 /*g_MapList.GetMapSizeY(pt.m_map)*/)) + return false; + return true; + } +}} + +TEST_CASE("CPointBase::GetDir")\ +// clazy:exclude=non-pod-global-static +{ + const CPointBase pt(100, 100, 0, 0); + CPointBase ptOther; + //DIR_TYPE def = DIR_N; + + for (short x : {-20, -5, 0, 5, 20}) + { + for (short y : {-20, -5, 0, 5, 20}) + { + ptOther = pt; + ptOther.m_x += x; + ptOther.m_y += y; + + CHECK(pt.GetDir(ptOther) == ref_impl::GetDir(pt, ptOther)); + } + } +} + + +TEST_CASE("CPointBase::IsValidPoint")\ +// clazy:exclude=non-pod-global-static +{ + CPointBase pt; + + SUBCASE("Valid") + { + pt.Set(0, 0, 0, 0); + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + pt.Set(1, 2, 3, 4); + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + pt.Set(0, 0, -126, 0); + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + pt.Set(0, 0, 126, 0); + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + } + + SUBCASE("Invalid") + { + // For now avoid negative values, the unsigned conversion trick will fail because sphere hasn't + // yet loaded map sizes and the default is -1. + //pt.Set(-11, 2, 3, 4); + //CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + pt.Set(0, 0, -128, 0); // uint8_t goes from [-128; 127] + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + pt.Set(0, 0, -127, 0); + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + pt.Set(0, 0, 127, 0); + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + pt.Set(7168, 4096, 127, 0); + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + } +} + + +TEST_CASE("CPointBase::IsCharValid")\ +// clazy:exclude=non-pod-global-static +{ + CPointBase pt; + + SUBCASE("Valid") + { + pt.Set(1, 2, 3, 4); + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + + pt.Set(1, 1, -126, 0); + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + + pt.Set(1, 1, 126, 0); + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + } + + SUBCASE("Invalid") + { + pt.Set(0, 0, 0, 0); + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + + //pt.Set(-11, 2, 3, 4); + //CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + + pt.Set(1, 1, -127, 0); + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + + pt.Set(1, 1, 127, 0); + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + + pt.Set(1, 1, -128, 0); // uint8_t goes from [-128; 127] + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + + pt.Set(7168, 4096, 127, 0); + CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + + pt.Set(0, 1, 1, 0); + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + + pt.Set(1, 0, 1, 0); + CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + } +} From 0b99cd00d3599be5d3a91493b6d927c6fce5789a Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 25 May 2025 17:10:26 +0200 Subject: [PATCH 026/112] Reviewed edge cases for the optimized CPointBase checks. Changed fc::vector_set usage to std::vector. --- src/common/CPointBase.cpp | 74 +++++++++++----------- src/common/CPointBase.h | 1 + src/common/sphere_library/scontainer_ops.h | 7 ++ src/game/CTeleport.cpp | 2 +- src/game/chars/CCharAct.cpp | 24 ++++--- src/game/chars/CCharNPC.cpp | 15 +++-- src/game/chars/CCharSpell.cpp | 2 +- src/game/items/CItem.cpp | 6 +- 8 files changed, 71 insertions(+), 60 deletions(-) diff --git a/src/common/CPointBase.cpp b/src/common/CPointBase.cpp index ba450b05a..cb8bccbad 100644 --- a/src/common/CPointBase.cpp +++ b/src/common/CPointBase.cpp @@ -276,49 +276,47 @@ int CPointBase::GetDist3D( const CPointBase & pt ) const noexcept // Distance be } } -bool CPointBase::IsValidXY() const noexcept +// Difference between IsCharValid and IsValidPoint: the first returns false if x or y == 0. +bool CPointBase::IsCharValid() const noexcept { - /* - if ( (m_x < 0) || (m_y < 0) ) - return false; - if ( (m_x >= g_MapList.GetMapSizeX(m_map)) || (m_y >= g_MapList.GetMapSizeY(m_map)) ) - return false; - return true; - */ + // 0 for X or Y is invalid. Z is valid if (-127, 127), excluding the extremes. + const uint32_t sx = g_MapList.GetMapSizeX(m_map) - 1; // -1 because 0 is not valid. + const uint32_t sy = g_MapList.GetMapSizeY(m_map) - 1; + constexpr uint32_t sz = 2 * UO_SIZE_Z; // exclude -128, -127, 127 - const ushort sx = g_MapList.GetMapSizeX(m_map); - const ushort sy = g_MapList.GetMapSizeY(m_map); + const uint32_t ux = static_cast(static_cast(m_x) - 1); // -1 because 0 is not valid + const uint32_t uy = static_cast(static_cast(m_y) - 1); + const uint32_t uz = static_cast(static_cast(m_z) + UO_SIZE_Z); - const ushort ux = uint16(m_x); - const ushort uy = uint16(m_y); - - // Two unsigned compares fold both < 0 and >= size checks - return ux < sx && uy < sy; + return (ux < sx) && (uy < sy) && (uz > 0) && (uz < sz); } bool CPointBase::IsValidPoint() const noexcept { -/* - if ( (m_z <= -UO_SIZE_Z) || (m_z >= UO_SIZE_Z) ) - return false; - if ((m_x <= 0) || (m_y <= 0)) - return false; - if ((m_x >= g_MapList.GetMapSizeX(m_map)) || (m_y >= g_MapList.GetMapSizeY(m_map))) - return false; - return true; -*/ - const ushort sx = g_MapList.GetMapSizeX(m_map); - const ushort sy = g_MapList.GetMapSizeY(m_map); - - const ushort ux = uint16(m_x); - const ushort uy = uint16(m_y); - const ushort uz = uint16(int16(m_z) + UO_SIZE_Z); - - // ux > 0 && ux < sizex folds both x<=0 and x>=sizex - // uz > 0 excludes m_z == –UO_SIZE_Z; uz < 2*UO_SIZE_Z excludes m_z >= UO_SIZE_Z - return uz < (2U * UO_SIZE_Z) - && ux > 0U && ux < sx - && uy > 0U && uy < sy; + // 0 for X or Y is valid. Z is valid if (-127, 127), excluding the extremes. + const uint32_t sx = g_MapList.GetMapSizeX(m_map); + const uint32_t sy = g_MapList.GetMapSizeY(m_map); + constexpr uint32_t sz = 2 * UO_SIZE_Z; // exclude -128, -127, 127 + + // Cast to unsigned - negative values wrap to large numbers + const uint32_t ux = static_cast(static_cast(m_x)); + const uint32_t uy = static_cast(static_cast(m_y)); + const uint32_t uz = static_cast(static_cast(m_z) + UO_SIZE_Z); + + return (ux < sx) && (uy < sy) && (uz > 0) && (uz < sz); +} + +bool CPointBase::IsValidXY() const noexcept +{ + // 0 for X or Y is valid. + const uint32 sx = g_MapList.GetMapSizeX(m_map); + const uint32 sy = g_MapList.GetMapSizeY(m_map); + + const uint32 ux = uint32(uint16(m_x)); + const uint32 uy = uint32(uint16(m_y)); + + // Two unsigned compares fold both < 0 and >= size checks. 0 is valid x or y, + return ux < sx && uy < sy; } void CPointBase::ValidatePoint() noexcept @@ -913,8 +911,8 @@ DIR_TYPE CPointBase::GetDir( const CPointBase & pt, DIR_TYPE DirDefault ) const // Use boolean-to-int multipliers (true→1, false→0) const int north = (dy > 0); const int south = (dy < 0); - const int east = (dx > 0); - const int west = (dx < 0); + const int east = (dx < 0); + const int west = (dx > 0); const DIR_TYPE dir_card = enum_alias_cast( steep diff --git a/src/common/CPointBase.h b/src/common/CPointBase.h index 8f1121cd0..4a9f5bca9 100644 --- a/src/common/CPointBase.h +++ b/src/common/CPointBase.h @@ -71,6 +71,7 @@ struct CPointBase // Non initialized 3d point. // return ((m_z > -127 /* -UO_SIZE_Z */) && (m_z < 127 /* UO_SIZE_Z */)); //} [[nodiscard]] bool IsValidXY() const noexcept; + [[nodiscard]] bool IsCharValid() const noexcept; [[nodiscard]] bool IsValidPoint() const noexcept; void ValidatePoint() noexcept; diff --git a/src/common/sphere_library/scontainer_ops.h b/src/common/sphere_library/scontainer_ops.h index d3851bee2..a43b07fc8 100644 --- a/src/common/sphere_library/scontainer_ops.h +++ b/src/common/sphere_library/scontainer_ops.h @@ -38,6 +38,13 @@ void AssignInitlistToCSizedArray(outT* dest, const size_t dest_size, std::initia } } +template +[[nodiscard]] constexpr + bool ContainerHas(Tcont const& cont, Telem const& elem) +{ + return cont.end() != std::find(cont.begin(), cont.end(), elem); +} + template [[nodiscard]] constexpr bool ContainerIsSorted(T const& cont) diff --git a/src/game/CTeleport.cpp b/src/game/CTeleport.cpp index 1988f1baf..8f6cde8f1 100644 --- a/src/game/CTeleport.cpp +++ b/src/game/CTeleport.cpp @@ -31,7 +31,7 @@ CTeleport::CTeleport(tchar* pszArgs) bool CTeleport::RealizeTeleport() { ADDTOCALLSTACK("CTeleport::RealizeTeleport"); - if (!IsValidPoint() || !_ptDst.IsValidPoint()) + if (!IsCharValid() || !_ptDst.IsCharValid()) { DEBUG_ERR(("CTeleport bad coords %s\n", WriteUsed())); return false; diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index a7a835d00..2498847a3 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -25,7 +25,6 @@ #include "../triggers.h" #include "CChar.h" #include "CCharNPC.h" -#include // "GONAME", "GOTYPE", "GOCHAR" @@ -5575,7 +5574,12 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip // if ( IsTrigUsed(pszTrigName) ) { - fc::vector_set executedEvents; + std::vector executedEvents; + auto fnShouldSkipLink = [&executedEvents, &iAction](const CResourceLink *pLink) -> bool + { + return (!pLink || !pLink->HasTrigger(iAction) + || (executedEvents.end() != std::find(executedEvents.begin(), executedEvents.end(), pLink))); + }; { EXC_SET_BLOCK("events"); @@ -5584,14 +5588,14 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip for (size_t i = 0; i < curEvents; ++i) // EVENTS (could be modifyed ingame!) { CResourceLink* pLink = m_OEvents[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + if (fnShouldSkipLink(pLink)) continue; CResourceLock s; if (!pLink->ResourceLock(s)) continue; - executedEvents.emplace(pLink); + executedEvents.emplace_back(pLink); iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) goto stopandret; @@ -5612,14 +5616,14 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip for ( size_t i = 0; i < pCharDef->m_TEvents.size(); ++i ) { CResourceLink * pLink = pCharDef->m_TEvents[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + if (fnShouldSkipLink(pLink)) continue; CResourceLock s; if (!pLink->ResourceLock(s)) continue; - executedEvents.emplace(pLink); + executedEvents.emplace_back(pLink); iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) goto stopandret; @@ -5649,14 +5653,14 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip for (size_t i = 0; i < g_Cfg.m_pEventsPetLink.size(); ++i) { CResourceLink * pLink = g_Cfg.m_pEventsPetLink[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + if (fnShouldSkipLink(pLink)) continue; CResourceLock s; if (!pLink->ResourceLock(s)) continue; - executedEvents.emplace(pLink); + executedEvents.emplace_back(pLink); iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) goto stopandret; @@ -5671,14 +5675,14 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip for ( size_t i = 0; i < g_Cfg.m_pEventsPlayerLink.size(); ++i ) { CResourceLink *pLink = g_Cfg.m_pEventsPlayerLink[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + if (fnShouldSkipLink(pLink)) continue; CResourceLock s; if (!pLink->ResourceLock(s)) continue; - executedEvents.emplace(pLink); + executedEvents.emplace_back(pLink); iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) goto stopandret; diff --git a/src/game/chars/CCharNPC.cpp b/src/game/chars/CCharNPC.cpp index 3d844ab8b..f3c075ee2 100644 --- a/src/game/chars/CCharNPC.cpp +++ b/src/game/chars/CCharNPC.cpp @@ -7,7 +7,6 @@ #include "../CServer.h" #include "../triggers.h" #include "CCharNPC.h" -#include lpctstr const CCharNPC::sm_szLoadKeys[CNC_QTY+1] = @@ -299,7 +298,7 @@ void CChar::NPC_CreateTrigger() ADDTOCALLSTACK("CChar::NPC_CreateTrigger"); ASSERT(m_pNPC); - fc::vector_set executedEvents; + std::vector executedEvents; CCharBase *pCharDef = Char_GetDef(); TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; @@ -310,14 +309,15 @@ void CChar::NPC_CreateTrigger() for (size_t i = 0; i < pCharDef->m_TEvents.size(); ++i) { CResourceLink * pLink = pCharDef->m_TEvents[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + if (!pLink || !pLink->HasTrigger(iAction) + || (executedEvents.end() != std::find(executedEvents.begin(), executedEvents.end(), pLink))) continue; CResourceLock s; if (!pLink->ResourceLock(s)) continue; - executedEvents.emplace(pLink); + executedEvents.emplace_back(pLink); iRet = CScriptObj::OnTriggerScript(s, pszTrigName, CScriptTriggerArgsPtr{}, this); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) return; @@ -327,14 +327,15 @@ void CChar::NPC_CreateTrigger() for (size_t i = 0; i < g_Cfg.m_pEventsPetLink.size(); ++i) { CResourceLink * pLink = g_Cfg.m_pEventsPetLink[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) - continue; + if (!pLink || !pLink->HasTrigger(iAction) + || (executedEvents.end() != std::find(executedEvents.begin(), executedEvents.end(), pLink))) + continue; CResourceLock s; if (!pLink->ResourceLock(s)) continue; - executedEvents.emplace(pLink); + executedEvents.emplace_back(pLink); iRet = CScriptObj::OnTriggerScript(s, pszTrigName, CScriptTriggerArgsPtr{}, this); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) return; diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index e2579b89b..d80cd1ce8 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -111,7 +111,7 @@ bool CChar::Spell_Teleport( CPointMap ptNew, bool fTakePets, bool fCheckAntiMagi // ex. ships plank. // RETURN: true = it worked. - if ( !ptNew.IsValidPoint() ) + if ( !ptNew.IsCharValid() ) return false; ptNew.m_z = GetFixZ(ptNew); diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index f4f74fadb..570b02771 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -1867,13 +1867,13 @@ lpctstr CItem::GetNameFull( bool fIdentified ) const len += Str_CopyLimitNull( pTemp+len, g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_BLANK ), Str_TempLength() - len); break; case IT_RUNE: - if ( ! m_itRune.m_ptMark.IsValidPoint()) + if ( ! m_itRune.m_ptMark.IsCharValid()) len += Str_CopyLimitNull( pTemp+len, g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_BLANK ), Str_TempLength() - len); else if ( ! m_itRune.m_Strength ) len += Str_CopyLimitNull( pTemp+len, g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_FADED ), Str_TempLength() - len); break; case IT_TELEPAD: - if ( ! m_itTelepad.m_ptMark.IsValidPoint()) + if ( ! m_itTelepad.m_ptMark.IsCharValid()) len += Str_CopyLimitNull( pTemp+len, g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_BLANK ), Str_TempLength() - len); break; default: @@ -3525,7 +3525,7 @@ bool CItem::r_Load( CScript & s ) // Load an item from script if ( GetContainer() == nullptr ) { // Actually place the item into the world. - if ( GetTopPoint().IsValidPoint()) + if ( GetTopPoint().IsCharValid()) MoveToUpdate( GetTopPoint()); } From 0f73e785d5e85c24421661c936706825591c38a8 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 25 May 2025 17:25:39 +0200 Subject: [PATCH 027/112] Fix Windows compilation error. --- src/game/uo_files/CUOMapList.cpp | 5 ++++- src/game/uo_files/CUOMapList.h | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/game/uo_files/CUOMapList.cpp b/src/game/uo_files/CUOMapList.cpp index 91952da65..ed39bdbc2 100644 --- a/src/game/uo_files/CUOMapList.cpp +++ b/src/game/uo_files/CUOMapList.cpp @@ -37,7 +37,9 @@ void CUOMapList::Clear() noexcept //ResetMap(i, -1, -1, -1, i, i); } -void CUOMapList::ResetMap(int map, int maxx, int maxy, int sectorsize, int realmapnum, int mapid) +// Unused +/* +void CUOMapList::ResetMap(uint map, ushort maxx, ushort maxy, ushort sectorsize, ushort realmapnum, ushort mapid) { MapGeoData& map_data = m_mapGeoData.maps[map]; map_data.sizex = maxx; @@ -46,6 +48,7 @@ void CUOMapList::ResetMap(int map, int maxx, int maxy, int sectorsize, int realm map_data.num = realmapnum; map_data.id = mapid; } +*/ void CUOMapList::Init() { diff --git a/src/game/uo_files/CUOMapList.h b/src/game/uo_files/CUOMapList.h index 3823be4ac..334c42968 100644 --- a/src/game/uo_files/CUOMapList.h +++ b/src/game/uo_files/CUOMapList.h @@ -6,6 +6,7 @@ #ifndef _INC_CUOMAPLIST_H #define _INC_CUOMAPLIST_H +#include "../../common/common.h" class CServerMapDiffCollection; @@ -69,7 +70,7 @@ extern class CUOMapList */ ///@{ void Clear() noexcept; - void ResetMap(int map, int maxx, int maxy, int sectorsize, int realmapnum, int mapid); + //void ResetMap(uint map, ushort maxx, ushort maxy, ushort sectorsize, ushort realmapnum, ushort mapid); void Init(); bool Load(int map, char *args); ///@} From b439f7160a646199679fa95f533fb6e65431b489 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 25 May 2025 17:25:56 +0200 Subject: [PATCH 028/112] Github workflows: update jobs permissions. --- .github/workflows/build_aux_files.yml | 4 ++++ .github/workflows/build_linux_x86.yml | 9 +++++++++ .github/workflows/build_linux_x86_64.yml | 9 +++++++++ .github/workflows/build_osx_arm.yml | 13 +++++++++++-- .github/workflows/build_osx_x86_64.yml | 8 ++++++++ .github/workflows/build_win_x86.yml | 10 ++++++++++ .github/workflows/build_win_x86_64.yml | 9 +++++++++ .github/workflows/coverity-scan.yml | 6 +++++- 8 files changed, 65 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_aux_files.yml b/.github/workflows/build_aux_files.yml index 8fc747e9d..cf33df985 100644 --- a/.github/workflows/build_aux_files.yml +++ b/.github/workflows/build_aux_files.yml @@ -15,6 +15,10 @@ jobs: # Upload artifact linked to GitHub RELEASE we are creating - only if the run is for a pull request, or for selected branches #if: contains(fromJson('["master", "main"]'), github.ref_name) + permissions: + contents: write + actions: write + runs-on: ubuntu-latest steps: - name: Checkout repository diff --git a/.github/workflows/build_linux_x86.yml b/.github/workflows/build_linux_x86.yml index 4ea4c16f6..45486b7bf 100644 --- a/.github/workflows/build_linux_x86.yml +++ b/.github/workflows/build_linux_x86.yml @@ -24,6 +24,9 @@ on: jobs: linux-x86: runs-on: ubuntu-22.04 + permissions: + contents: read + actions: read steps: - name: Checkout repository @@ -130,6 +133,9 @@ jobs: needs: linux-x86 # Upload artifact linked to GitHub RELEASE we are creating - only if the run is for a pull request, or for selected branches if: contains(fromJson('["master", "main"]'), github.ref_name) + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: @@ -153,6 +159,9 @@ jobs: upload_selfhost_release: needs: linux-x86 if: contains(fromJson('["master", "main"]'), github.ref_name) && (github.repository == 'SphereServer/Source-X') + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: diff --git a/.github/workflows/build_linux_x86_64.yml b/.github/workflows/build_linux_x86_64.yml index 006cdfca1..86b95a075 100644 --- a/.github/workflows/build_linux_x86_64.yml +++ b/.github/workflows/build_linux_x86_64.yml @@ -21,6 +21,9 @@ on: jobs: linux-x86_64: runs-on: ubuntu-22.04 + permissions: + contents: read + actions: read steps: - name: Checkout repository @@ -85,6 +88,9 @@ jobs: needs: linux-x86_64 # Upload artifact linked to GitHub RELEASE we are creating - only if the run is for a pull request, or for selected branches if: contains(fromJson('["master", "main"]'), github.ref_name) + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: @@ -107,6 +113,9 @@ jobs: upload_selfhost_release: needs: linux-x86_64 if: contains(fromJson('["master", "main"]'), github.ref_name) && (github.repository == 'SphereServer/Source-X') + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: diff --git a/.github/workflows/build_osx_arm.yml b/.github/workflows/build_osx_arm.yml index 1ae8559f4..0ddcf1983 100644 --- a/.github/workflows/build_osx_arm.yml +++ b/.github/workflows/build_osx_arm.yml @@ -21,6 +21,10 @@ on: jobs: macos-arm64: runs-on: macos-14 # apple silicon + permissions: + contents: read + actions: read + env: CMAKE_GEN: Ninja #CMAKE_TCH: cmake/toolchains/OSX-AppleClang-native.cmake @@ -72,9 +76,11 @@ jobs: upload_github_release: - needs: macos-arm64 - # Upload artifact linked to GitHub RELEASE we are creating - only if the run is for a pull request, or for selected branches + needs: macos-arm64 # Upload artifact linked to GitHub RELEASE we are creating - only if the run is for a pull request, or for selected branches if: contains(fromJson('["master", "main"]'), github.ref_name) + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: @@ -98,6 +104,9 @@ jobs: upload_selfhost_release: needs: macos-arm64 if: contains(fromJson('["master", "main"]'), github.ref_name) && (github.repository == 'SphereServer/Source-X') + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: diff --git a/.github/workflows/build_osx_x86_64.yml b/.github/workflows/build_osx_x86_64.yml index f2e65ec58..f3eb58f7d 100644 --- a/.github/workflows/build_osx_x86_64.yml +++ b/.github/workflows/build_osx_x86_64.yml @@ -21,6 +21,10 @@ on: jobs: macos-x86_64: runs-on: macos-13 # intel + permissions: + contents: read + actions: read + env: CMAKE_GEN: Ninja CMAKE_TCH: cmake/toolchains/OSX-AppleClang-x86_64.cmake @@ -70,6 +74,10 @@ jobs: upload_github_release: needs: macos-x86_64 + permissions: + contents: read + actions: write + # Upload artifact linked to GitHub RELEASE we are creating - only if the run is for a pull request, or for selected branches if: contains(fromJson('["master", "main"]'), github.ref_name) diff --git a/.github/workflows/build_win_x86.yml b/.github/workflows/build_win_x86.yml index 6664b77ed..165ef56bc 100644 --- a/.github/workflows/build_win_x86.yml +++ b/.github/workflows/build_win_x86.yml @@ -22,6 +22,10 @@ jobs: windows-x86: runs-on: windows-latest + permissions: + contents: read + actions: read + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -54,6 +58,9 @@ jobs: needs: windows-x86 # Upload artifact linked to GitHub RELEASE we are creating - only if the run is for a pull request, or for selected branches if: contains(fromJson('["master", "main"]'), github.ref_name) + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: @@ -77,6 +84,9 @@ jobs: upload_selfhost_release: needs: windows-x86 if: contains(fromJson('["master", "main"]'), github.ref_name) && (github.repository == 'SphereServer/Source-X') + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: diff --git a/.github/workflows/build_win_x86_64.yml b/.github/workflows/build_win_x86_64.yml index ee74649de..fd817079b 100644 --- a/.github/workflows/build_win_x86_64.yml +++ b/.github/workflows/build_win_x86_64.yml @@ -21,6 +21,9 @@ on: jobs: windows-x86_64: runs-on: windows-latest + permissions: + contents: read + actions: read steps: - name: Checkout repository @@ -55,6 +58,9 @@ jobs: needs: windows-x86_64 # Upload artifact linked to GitHub RELEASE we are creating - only if the run is for a pull request, or for selected branches if: contains(fromJson('["master", "main"]'), github.ref_name) + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: @@ -77,6 +83,9 @@ jobs: upload_selfhost_release: needs: windows-x86_64 if: contains(fromJson('["master", "main"]'), github.ref_name) && (github.repository == 'SphereServer/Source-X') + permissions: + contents: read + actions: write runs-on: ubuntu-latest steps: diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index df0f9c86c..9ac29c536 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -14,6 +14,10 @@ on: jobs: latest: runs-on: ubuntu-22.04 + permissions: + contents: read + actions: read + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -43,7 +47,7 @@ jobs: - name: Download Coverity Build Tool env: TOKEN: ${{ secrets.COVERITY_TOKEN }} -#echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- + #echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- run: | wget -nv https://scan.coverity.com/download/cxx/linux64 --post-data "token=${TOKEN}&project=Sphereserver/Source-X" -O cov-analysis-linux64.tar.gz mkdir cov-analysis-linux64 From 22bf1f37a0bb28a3009f36564460f5e8bb8ab46a Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 25 May 2025 17:39:47 +0200 Subject: [PATCH 029/112] Fixed implicit sign conversion --- src/CMakeSources.cmake | 1 + src/game/uo_files/CUOMapList.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index 03752453d..8289b4eeb 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -139,6 +139,7 @@ source_group(game\\uo_files FILES ${uofiles_H} ${uofiles_S}) ############################## set(common_S src/common/sqlite/SQLite.cpp + src/common/common.cpp src/common/CCacheableScriptFile.cpp src/common/CDataBase.cpp src/common/CException.cpp diff --git a/src/game/uo_files/CUOMapList.cpp b/src/game/uo_files/CUOMapList.cpp index ed39bdbc2..4da2329d4 100644 --- a/src/game/uo_files/CUOMapList.cpp +++ b/src/game/uo_files/CUOMapList.cpp @@ -1,3 +1,5 @@ +//#include "../../common/sphere_library/sstring.h" +//#include "../../common/CExpression.h" #include "../../common/CLog.h" #include "../../common/CServerMap.h" #include "../../common/CUOInstall.h" @@ -108,7 +110,7 @@ bool CUOMapList::Load(int map, char *args) map, maxx, map_data.sizex); } else - map_data.sizex = maxx; + map_data.sizex = (ushort)std::clamp(maxx, 0, (int)UINT16_MAX); } if ( maxy ) { @@ -118,16 +120,16 @@ bool CUOMapList::Load(int map, char *args) map, maxy, map_data.sizey); } else - map_data.sizey = maxy; + map_data.sizey = (ushort)std::clamp(maxy, 0, (int)UINT16_MAX); } if ( sectorsize > 0 ) - map_data.sectorsize = sectorsize; + map_data.sectorsize = (ushort)sectorsize; if ( realmapnum >= 0 ) - map_data.num = realmapnum; + map_data.num = (ushort)realmapnum; if ( mapid >= 0 ) - map_data.id = mapid; + map_data.id = (ushort)mapid; else - map_data.id = map; + map_data.id = (ushort)std::clamp(map, 0, (int)UINT16_MAX); } map_data.initialized = true; From 19576735e93d1e3094a8173ebb071dfb0e6dbb27 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 25 May 2025 23:38:11 +0200 Subject: [PATCH 030/112] Workflows: update coverity-scan.yml. --- .github/workflows/coverity-scan.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index 9ac29c536..215b8e687 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -79,13 +79,12 @@ jobs: cd build tar czvf build-data.tgz cov-int curl \ - --form project=SphereServer/Source-X \ --form token=$TOKEN \ --form email=$EMAIL \ --form file=@build-data.tgz \ - --form version=trunk \ + --form version="trunk" \ --form description="${{github.sha}}" \ - https://scan.coverity.com/builds?project=SphereServer-X + https://scan.coverity.com/builds?project=Sphereserver%2FSource-X env: TOKEN: ${{ secrets.COVERITY_TOKEN }} EMAIL: ${{ secrets.COVERITY_EMAIL }} From 5ffd80b1645230ff09843bb9892b33c545f4b9bb Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 26 May 2025 10:52:06 +0200 Subject: [PATCH 031/112] Fixed some Coverity-reported issues. --- src/common/CException.cpp | 1 + src/common/CScriptParserBufs.cpp | 2 +- src/common/CUOClientVersion.cpp | 57 +++++++++++++------ src/common/crypto/CCrypto.cpp | 6 +- .../resource/sections/CRegionResourceDef.cpp | 2 +- src/common/sphere_library/sobjpool.h | 8 +-- src/common/sphere_library/ssorted_vector.h | 3 - src/game/CObjBase.cpp | 11 ++-- src/game/CObjBaseTemplate.h | 44 +++++++++++--- src/game/CWorldTimedFunctions.cpp | 2 +- src/game/chars/CChar.cpp | 2 +- src/game/chars/CChar.h | 4 +- src/game/chars/CCharAct.cpp | 2 +- src/game/chars/CCharUse.cpp | 8 ++- src/game/components/CCChampion.cpp | 4 +- src/game/items/CItem.cpp | 2 +- src/game/items/CItem.h | 4 +- src/sphere/threads.cpp | 13 ++++- 18 files changed, 121 insertions(+), 54 deletions(-) diff --git a/src/common/CException.cpp b/src/common/CException.cpp index e916e2aab..35bf12db2 100644 --- a/src/common/CException.cpp +++ b/src/common/CException.cpp @@ -25,6 +25,7 @@ int IsDebuggerPresent(void) return 0; ssize_t num_read = read(status_fd, buf, sizeof(buf)-1); + close(status_fd); if (num_read > 0) { diff --git a/src/common/CScriptParserBufs.cpp b/src/common/CScriptParserBufs.cpp index d113785f7..11b5dfec8 100644 --- a/src/common/CScriptParserBufs.cpp +++ b/src/common/CScriptParserBufs.cpp @@ -27,7 +27,7 @@ auto CScriptParserBufs::GetCScriptTriggerArgsPtr() -> CScriptTriggerArgsPtr { static_assert(CScriptParserBufsImpl::sm_allow_fallback_objects); g_Log.EventDebug( - "Requesting CScriptTriggerArgs from an exhausted pool (max size: %" PRIuSIZE_T "). Alive new heap-allocated fallback objects: %" PRIuSIZE_T ".\n", + "Requesting CScriptTriggerArgs from an exhausted pool (max size: %" PRIu32 "). Alive new heap-allocated fallback objects: %" PRIu32 ".\n", pool.sm_pool_size, pool.getFallbackCount()); } diff --git a/src/common/CUOClientVersion.cpp b/src/common/CUOClientVersion.cpp index e2c38bbb0..ca2256160 100644 --- a/src/common/CUOClientVersion.cpp +++ b/src/common/CUOClientVersion.cpp @@ -169,7 +169,6 @@ bool CUOClientVersion::operator <=(CUOClientVersion const& other) const noexcept return other > *this; } - void CUOClientVersion::ApplyVersionFromStringOldFormat(lptstr ptcVersion) noexcept { // Get version of old clients, which report the client version as ASCII string (eg: '5.0.2b') @@ -186,12 +185,16 @@ void CUOClientVersion::ApplyVersionFromStringOldFormat(lptstr ptcVersion) noexce } tchar *piVer[3]; - Str_ParseCmds(ptcVersion, piVer, ARRAY_COUNT(piVer), "."); + lptstr ptcVersionParsed = ptcVersion; + Str_ParseCmds(ptcVersionParsed, piVer, ARRAY_COUNT(piVer), "."); // Don't rely on all values reported by client, because it can be easily faked. Injection users can report any // client version they want, and some custom clients may also report client version as "Custom" instead X.X.Xy if (!piVer[0] || !piVer[1] || !piVer[2] || (uiLetter > 26)) + { + g_Log.EventDebug("Invalid version string '%s' passed to CUOClientVersion::ApplyVersionFromStringOldFormat.\n", ptcVersion); return; + } m_major = uint(atoi(piVer[0])); m_minor = uint(atoi(piVer[1])); @@ -203,30 +206,50 @@ void CUOClientVersion::ApplyVersionFromStringNewFormat(lptstr ptcVersion) noexce { // Get version of newer clients, which use only 4 numbers separated by dots (example: 6.0.1.1) + constexpr auto np = std::string_view::npos; const std::string_view sv(ptcVersion); + const size_t dot1 = sv.find_first_of('.', 0); + if (dot1 == np) + { + ret_err: + g_Log.EventDebug("Invalid version string '%s' passed to CUOClientVersion::ApplyVersionFromStringNewFormat.\n", ptcVersion); + return; + } + const size_t dot2 = sv.find_first_of('.', dot1 + 1); + if (dot2 == np) + goto ret_err; + const size_t dot3 = sv.find_first_of('.', dot2 + 1); - constexpr auto np = std::string_view::npos; - if (np == dot1 || np == dot2 || np == dot3) - return; + if (dot3 == np) + goto ret_err; const std::string_view sv1(sv.data(), dot1 - 1); const std::string_view sv2(sv1.data() + dot1, dot2 - 1); const std::string_view sv3(sv2.data() + dot2, dot3 - 1); const std::string_view sv4(sv3.data() + dot3); - std::optional val1, val2, val3, val4; bool ok = true; - ok = ok && (val1 = Str_ToU(sv1.data(), 10, true)).has_value(); - ok = ok && (val2 = Str_ToU(sv1.data(), 10, true)).has_value(); - ok = ok && (val3 = Str_ToU(sv1.data(), 10, true)).has_value(); - ok = ok && (val4 = Str_ToU(sv1.data(), 10, true)).has_value(); - if (!ok) - return; - - m_major = val1.value(); - m_minor = val2.value(); - m_revision = val3.value(); - m_build = val4.value(); + try + { + std::optional val1, val2, val3, val4; + ok = ok && (val1 = Str_ToU(sv1.data(), 10, true)).has_value(); + ok = ok && (val2 = Str_ToU(sv1.data(), 10, true)).has_value(); + ok = ok && (val3 = Str_ToU(sv1.data(), 10, true)).has_value(); + ok = ok && (val4 = Str_ToU(sv1.data(), 10, true)).has_value(); + if (!ok) + return; + + m_major = val1.value(); + m_minor = val2.value(); + m_revision = val3.value(); + m_build = val4.value(); + } + catch (std::bad_optional_access const&) + { + // Shouldn't really happen... + m_major = m_minor = m_revision = m_build = 0; + DEBUG_MSG(("std::bad_optional_access at CUOClientVersion::ApplyVersionFromStringNewFormat.\n")); + } } diff --git a/src/common/crypto/CCrypto.cpp b/src/common/crypto/CCrypto.cpp index f51009e8a..19e9b6a62 100644 --- a/src/common/crypto/CCrypto.cpp +++ b/src/common/crypto/CCrypto.cpp @@ -221,10 +221,11 @@ CCrypto::CCrypto() //SetClientVerNumber(client_keys[0][2]); SetClientVerNumber(0u); - tf_cipher = new cipherInstance; - tf_key = new keyInstance; + tf_cipher = new cipherInstance; + tf_key = new keyInstance; m_md5_engine = new CMD5(); + m_MasterHi = m_MasterLo = 0; m_CryptMaskHi = m_CryptMaskLo = 0; m_seed = 0; m_ConnectType = CONNECT_NONE; @@ -233,6 +234,7 @@ CCrypto::CCrypto() m_gameBlockPos = 0; m_gameStreamPos = 0; m_md5_position = 0; + m_GameEnc = ENC_QTY; } CCrypto::~CCrypto() diff --git a/src/common/resource/sections/CRegionResourceDef.cpp b/src/common/resource/sections/CRegionResourceDef.cpp index a20626a2c..7b592a50d 100644 --- a/src/common/resource/sections/CRegionResourceDef.cpp +++ b/src/common/resource/sections/CRegionResourceDef.cpp @@ -46,7 +46,7 @@ TRIGRET_TYPE CRegionResourceDef::OnTrigger(lpctstr pszTrigName, CScriptTriggerAr CResourceLock s; if ( ResourceLock( s )) { - TRIGRET_TYPE iRet = CScriptObj::OnTriggerScript( s, pszTrigName, pArgs, pSrc); + TRIGRET_TYPE iRet = CScriptObj::OnTriggerScript( s, pszTrigName, std::move(pArgs), pSrc); return iRet; } return TRIGRET_RET_DEFAULT; diff --git a/src/common/sphere_library/sobjpool.h b/src/common/sphere_library/sobjpool.h index 734f88f2d..886352905 100644 --- a/src/common/sphere_library/sobjpool.h +++ b/src/common/sphere_library/sobjpool.h @@ -28,8 +28,8 @@ class ObjectPool using index_t = uint32_t; // do we really need size_t elements? static_assert(tp_pool_size <= std::numeric_limits::max()); - static constexpr size_t sm_pool_size = static_cast(tp_pool_size); - static constexpr bool sm_allow_fallback = tp_allow_fallback; + static constexpr index_t sm_pool_size = static_cast(tp_pool_size); + static constexpr bool sm_allow_fallback = tp_allow_fallback; // Ensure that T is default constructible and destructible. static_assert(std::is_default_constructible_v, @@ -193,8 +193,8 @@ class TSObjectPool { public: using index_t = uint32_t; // do we really need size_t elements? - static constexpr size_t sm_pool_size = static_cast(tp_pool_size); - static constexpr bool sm_allow_fallback = tp_allow_fallback; + static constexpr index_t sm_pool_size = static_cast(tp_pool_size); + static constexpr bool sm_allow_fallback = tp_allow_fallback; // Ensure that T is default constructible and destructible. static_assert(std::is_default_constructible_v, diff --git a/src/common/sphere_library/ssorted_vector.h b/src/common/sphere_library/ssorted_vector.h index b3d9f261e..443040ab3 100644 --- a/src/common/sphere_library/ssorted_vector.h +++ b/src/common/sphere_library/ssorted_vector.h @@ -98,9 +98,6 @@ namespace sl return 0; */ - if (!dataptr) - return sl::scont_bad_index(); - size_t _lo = 0; while (_lo < _size) { diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index 6633bf7cd..7a48b42c4 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -2964,6 +2964,7 @@ bool CObjBase::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command fro EXC_SET_BLOCK("DCLICK"); if (!pCharSrc) return false; + if (s.HasArgs()) { if (!IsChar()) @@ -2988,12 +2989,11 @@ bool CObjBase::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command fro if (!IsChar()) return false; - CObjBase* pObj = CUID::ObjFindFromUID(s.GetArgDWVal()); - if (!pObj) + CChar* pChar = CUID::CharFindFromUID(s.GetArgDWVal()); + if (!pChar) return false; - CChar *pChar = static_cast (this); - return pChar->Use_Obj( pObj, false, true ); + return pChar->Use_Obj( pChar, false, true ); } else return pCharSrc->Use_Obj( this, false, true ); @@ -3481,6 +3481,7 @@ void CObjBase::ModPropNum( COMPPROPS_TYPE iCompPropsType, CComponentProps::Prope { const CBaseBaseDef* pBase = Base_GetDef(); const CComponentProps* pBaseCompProps = pBase->GetComponentProps(iCompPropsType); + ASSERT(pBaseCompProps); pBaseCompProps->GetPropertyNumPtr(iPropIndex, &iVal); } if (!iVal && !iMod) @@ -3640,7 +3641,7 @@ TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CSc CResourceLock s; if ( pSpellDef->ResourceLock( s )) { - return CScriptObj::OnTriggerScript( s, CSpellDef::sm_szTrigName[stage], pArgs, pSrc ); + return CScriptObj::OnTriggerScript( s, CSpellDef::sm_szTrigName[stage], std::move(pArgs), pSrc ); } } return TRIGRET_RET_DEFAULT; diff --git a/src/game/CObjBaseTemplate.h b/src/game/CObjBaseTemplate.h index 65c3e0bd3..62f8d904a 100644 --- a/src/game/CObjBaseTemplate.h +++ b/src/game/CObjBaseTemplate.h @@ -35,28 +35,36 @@ class CObjBaseTemplate : public CSObjContRec CObjBaseTemplate& operator=(const CObjBaseTemplate& other) = delete; public: + [[nodiscard]] const CUID& GetUID() const noexcept { return m_UID; } - bool IsItem() const noexcept { + [[nodiscard]] + bool IsItem() const noexcept { return m_UID.IsItem(); } + [[nodiscard]] bool IsChar() const noexcept { return m_UID.IsChar(); } - bool IsItemInContainer() const noexcept { + [[nodiscard]] + bool IsItemInContainer() const noexcept { return m_UID.IsItemInContainer(); } - bool IsItemEquipped() const noexcept { + [[nodiscard]] + bool IsItemEquipped() const noexcept { return m_UID.IsItemEquipped(); } - bool IsDisconnected() const noexcept { + [[nodiscard]] + bool IsDisconnected() const noexcept { return m_UID.IsObjDisconnected(); } - bool IsTopLevel() const noexcept { + [[nodiscard]] + bool IsTopLevel() const noexcept { return m_UID.IsObjTopLevel(); } - bool IsValidUID() const noexcept { + [[nodiscard]] + bool IsValidUID() const noexcept { return m_UID.IsValidUID(); } @@ -83,16 +91,19 @@ class CObjBaseTemplate : public CSObjContRec // Location + [[nodiscard]] LAYER_TYPE GetEquipLayer() const noexcept { return (LAYER_TYPE)(m_pt.m_z); } void SetEquipLayer( LAYER_TYPE layer ); + [[nodiscard]] inline byte GetContainedLayer() const noexcept { // used for corpse or Restock count as well in Vendor container. return m_pt.m_z; } void SetContainedLayer( byte layer ) noexcept; + [[nodiscard]] inline const CPointMap & GetContainedPoint() const noexcept { return m_pt; } @@ -101,45 +112,60 @@ class CObjBaseTemplate : public CSObjContRec // - *Top* methods: are virtual and may do additional checks. void SetTopPoint(const CPointMap& pt); virtual void SetTopZ(char z); + [[nodiscard]] inline const CPointMap & GetTopPoint() const noexcept { return m_pt; } - char GetTopZ() const noexcept; - uchar GetTopMap() const noexcept; + [[nodiscard]] + char GetTopZ() const noexcept; + [[nodiscard]] + uchar GetTopMap() const noexcept; + [[nodiscard]] CSector* GetTopSector() const noexcept; // can return a nullptr! // - *Unk* methods: are not virtual and get/set raw values, without any check. void SetUnkPoint(const CPointMap& pt) noexcept { m_pt = pt; } + [[nodiscard]] inline const CPointMap & GetUnkPoint() const noexcept { // don't care where this return m_pt; } - inline char GetUnkZ() const noexcept { + [[nodiscard]] + inline char GetUnkZ() const noexcept { return m_pt.m_z; } // Distance and direction + [[nodiscard]] int GetTopDist( const CPointMap & pt ) const; + [[nodiscard]] int GetTopDist( const CObjBaseTemplate * pObj ) const; + [[nodiscard]] int GetTopDistSight( const CPointMap & pt ) const; + [[nodiscard]] int GetTopDistSight( const CObjBaseTemplate * pObj ) const; + [[nodiscard]] int GetDist( const CObjBaseTemplate * pObj ) const; + [[nodiscard]] int GetTopDist3D( const CObjBaseTemplate * pObj ) const; + [[nodiscard]] DIR_TYPE GetTopDir( const CObjBaseTemplate * pObj, DIR_TYPE DirDefault = DIR_QTY ) const; // Can return DIR_QTY if are on the same tile! + [[nodiscard]] DIR_TYPE GetDir( const CObjBaseTemplate * pObj, DIR_TYPE DirDefault = DIR_QTY ) const; // Can return DIR_QTY if are on the same tile! + [[nodiscard]] virtual int GetVisualRange() const; // Names diff --git a/src/game/CWorldTimedFunctions.cpp b/src/game/CWorldTimedFunctions.cpp index 2d83091bd..6c923fbd0 100644 --- a/src/game/CWorldTimedFunctions.cpp +++ b/src/game/CWorldTimedFunctions.cpp @@ -26,7 +26,7 @@ void CWorldTimedFunctions::Clear() // static TRIGRET_TYPE CWorldTimedFunctions::Loop(lpctstr ptcCommand, int LoopsMade, CScriptLineContext StartContext, CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc, CSString* pResult) // static { - return g_World._Ticker._TimedFunctions.Loop(ptcCommand, LoopsMade, StartContext, s, pArgs, pSrc, pResult); + return g_World._Ticker._TimedFunctions.Loop(ptcCommand, LoopsMade, StartContext, s, std::move(pArgs), pSrc, pResult); } void CWorldTimedFunctions::Add(const CUID& uid, int64 iTimeout, lpctstr ptcCommand) // static diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index 16e9e35fc..fa121591f 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -579,7 +579,7 @@ void CChar::SetDisconnected(CSector* pNewSector) else SetUIDContainerFlags(UID_O_DISCONNECT); - IsDisconnected(); + ASSERT(IsDisconnected()); } } diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index 8bec31f76..d59ee64f1 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -381,7 +381,9 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; CCharBase * Char_GetDef() const; CRegionWorld * GetRegion() const; CRegion * GetRoom() const; - virtual int GetVisualRange() const override; + + [[nodiscard]] + virtual int GetVisualRange() const override; void SetVisualRange(byte newSight); virtual bool IsResourceMatch( const CResourceID& rid, dword dwArg ) const override; diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 2498847a3..dc0317036 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -5704,7 +5704,7 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip TRIGRET_TYPE CChar::OnTrigger( CTRIG_TYPE trigger, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) { ASSERT( (trigger > CTRIG_AAAUNUSED) && (trigger < CTRIG_QTY) ); - return OnTrigger( CChar::sm_szTrigName[trigger], pScriptArgs, pSrc); + return OnTrigger( CChar::sm_szTrigName[trigger], std::move(pScriptArgs), pSrc); } // process m_fStatusUpdate flags diff --git a/src/game/chars/CCharUse.cpp b/src/game/chars/CCharUse.cpp index 7645b2dc2..3dbe909ac 100644 --- a/src/game/chars/CCharUse.cpp +++ b/src/game/chars/CCharUse.cpp @@ -1984,8 +1984,12 @@ bool CChar::Use_Obj( CObjBase * pObj, bool fTestTouch, bool fScript ) return false; if ( IsClientActive() ) return GetClientActive()->Event_DoubleClick(pObj->GetUID(), false, fTestTouch, fScript); - else - return Use_Item(dynamic_cast(pObj), fTestTouch); + + CItem *pItem = dynamic_cast(pObj); + if (!pItem) + return false; + + return Use_Item(pItem, fTestTouch); } bool CChar::ItemEquipArmor( bool fForce ) diff --git a/src/game/components/CCChampion.cpp b/src/game/components/CCChampion.cpp index 469dae834..5e94660a3 100644 --- a/src/game/components/CCChampion.cpp +++ b/src/game/components/CCChampion.cpp @@ -77,6 +77,8 @@ CCChampion::CCChampion(CItem* pLink) : CComponent(COMP_CHAMPION) _idChampion = CREID_INVALID; _iLastActivationTime = 0; Init(); + _iSpawnsNextRed = 0; + _iSpawnsNextWhite = 0; } void CCChampion::Copy(const CComponent* target) @@ -1214,7 +1216,7 @@ TRIGRET_TYPE CCChampion::OnTrigger(ITRIG_TYPE trig, CScriptTriggerArgsPtr pArgs, } if (iRet == TRIGRET_RET_DEFAULT) { - iRet = GetLink()->OnTrigger(trig, pArgs, pSrc); + iRet = GetLink()->OnTrigger(trig, std::move(pArgs), pSrc); } return iRet; } diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 570b02771..49ef73008 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -3898,7 +3898,7 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip TRIGRET_TYPE CItem::OnTrigger( ITRIG_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) { ASSERT((trigger >= 0) && (trigger < ITRIG_QTY)); - return OnTrigger( CItem::sm_szTrigName[trigger], pArgs, pSrc ); + return OnTrigger( CItem::sm_szTrigName[trigger], std::move(pArgs), pSrc ); } // Item type specific stuff. diff --git a/src/game/items/CItem.h b/src/game/items/CItem.h index 89a84654c..638ea60be 100644 --- a/src/game/items/CItem.h +++ b/src/game/items/CItem.h @@ -726,7 +726,9 @@ class CItem : public CObjBase bool IsTopLevelMultiLocked() const; bool IsMovableType() const; bool IsMovable() const; - virtual int GetVisualRange() const override; + + [[nodiscard]] + virtual int GetVisualRange() const override; bool IsStackableException() const; bool IsStackable( const CItem * pItem ) const; diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index 701415d00..dd0d6f6f8 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -449,12 +449,19 @@ void AbstractThread::terminate(bool ended) // if the thread is current then terminating here will prevent cleanup from occurring if (wasCurrentThread == false) { + if (!m_handle.has_value()) + { + STDERR_LOG("AbstractThread::terminate: no handle?.\n"); + } + else + { #ifdef _WIN32 - TerminateThread(m_handle.value(), 0); - CloseHandle(m_handle.value()); + TerminateThread(m_handle.value(), 0); + CloseHandle(m_handle.value()); #else - pthread_cancel(m_handle.value()); // IBM say it so + pthread_cancel(m_handle.value()); // IBM say it so #endif + } } } From ca0666938c14b7660d9c97ee5cef8d69126c3ea6 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 26 May 2025 10:52:44 +0200 Subject: [PATCH 032/112] Added automatic CodeQL and Coverity scans also to master branch commit pushes. --- .github/workflows/codeql.yml | 2 +- .github/workflows/coverity-scan.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6090c293b..e65f7fc2a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -3,7 +3,7 @@ name: CodeQL on: push: - branches: ["static-analysis"] + branches: ["static-analysis", "master", "main"] paths-ignore: - '.gitignore' - '.gitattributes' diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index 215b8e687..e1667aa6d 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -2,7 +2,7 @@ name: coverity-scan on: push: - branches: ["static-analysis"] + branches: ["static-analysis", "master", "main"] paths-ignore: - '.gitignore' - '.gitattributes' From 72350cb52174f13fe76a34f64852bed5469b250c Mon Sep 17 00:00:00 2001 From: David Kindl Date: Mon, 26 May 2025 11:38:27 +0200 Subject: [PATCH 033/112] Fix: Unresolved reference in a Doxygen comments --- src/common/crypto/CMD5.h | 6 +- src/common/os_unix.h | 2 +- src/common/resource/sections/CSpellDef.h | 2 +- src/common/resource/sections/CWebPageDef.h | 4 +- src/common/sphere_library/CSFile.h | 12 +-- src/common/sphere_library/CSFileText.h | 4 +- src/common/sphere_library/CSMemBlock.h | 8 +- src/common/sphere_library/CSObjCont.h | 2 +- src/common/sphere_library/CSQueue.cpp | 2 +- src/common/sphere_library/CSString.h | 32 +++---- src/common/sphere_library/CSTime.h | 2 +- src/common/sphere_library/sstring.h | 4 - src/game/CContainer.h | 18 ++-- src/game/CEntity.h | 3 +- src/game/CEntityProps.h | 5 +- src/game/CObjBase.h | 12 +-- src/game/CServerConfig.h | 9 +- src/game/CTimedObject.h | 4 +- src/game/chars/CChar.h | 18 ++-- src/game/clients/CAccount.h | 9 +- src/game/components/CCChampion.h | 6 +- src/game/components/CCSpawn.h | 4 +- src/game/items/CItemMulti.h | 100 ++++++++++----------- src/game/items/CItemMultiCustom.h | 2 +- 24 files changed, 129 insertions(+), 141 deletions(-) diff --git a/src/common/crypto/CMD5.h b/src/common/crypto/CMD5.h index 1cad81592..86d28a1af 100644 --- a/src/common/crypto/CMD5.h +++ b/src/common/crypto/CMD5.h @@ -1,5 +1,5 @@ /** - * @file CUID.h + * @file CMD5.h * @brief MD5 hashing. */ @@ -8,7 +8,7 @@ public domain code. */ -#ifndef _INC_CMD5_H +#ifndef _INC_CMD5_H #define _INC_CMD5_H #include "../datatypes.h" @@ -37,7 +37,7 @@ class CMD5 void digest( char * digest ) noexcept; // Get digest in a "numeric" form to be usable void numericDigest( uchar * digest ) noexcept; - + static void fastDigest(char * digest, const char * message) noexcept; }; diff --git a/src/common/os_unix.h b/src/common/os_unix.h index c69590695..c574e60f9 100644 --- a/src/common/os_unix.h +++ b/src/common/os_unix.h @@ -1,5 +1,5 @@ /** -* @file ox_unix.h +* @file os_unix.h * @brief Unix-specific declarations. */ diff --git a/src/common/resource/sections/CSpellDef.h b/src/common/resource/sections/CSpellDef.h index 24dd98e89..5c406154f 100644 --- a/src/common/resource/sections/CSpellDef.h +++ b/src/common/resource/sections/CSpellDef.h @@ -78,7 +78,7 @@ class CSpellDef : public CResourceLink * * @brief Check if this Spell has the given flags. * - * @param wFlags The flags. + * @param uiFlags The flags. * * @return true if match, false if not. */ diff --git a/src/common/resource/sections/CWebPageDef.h b/src/common/resource/sections/CWebPageDef.h index 358e2d0d4..4bacb28c4 100644 --- a/src/common/resource/sections/CWebPageDef.h +++ b/src/common/resource/sections/CWebPageDef.h @@ -124,7 +124,7 @@ class CWebPageDef : public CResourceLink * @param [in,out] pClient If non-null, the client. * @param pszURLArgs The URL arguments. * @param [in,out] pPostData If non-null, information describing the post. - * @param stContentLength Length of the content. + * @param uiContentLength Length of the content. * * @return true if it succeeds, false if it fails. */ @@ -155,7 +155,7 @@ class CWebPageDef : public CResourceLink * * @param [in,out] pClient If non-null, the client. * @param [in,out] pszPage If non-null, the page. - * @param [in,out] pdateLastMod If non-null, the pdate last modifier. + * @param [in,out] pDateLastMod If non-null, the pdate last modifier. */ static void ServPage(CClient * pClient, tchar * pszPage, CSTime * pDateLastMod); diff --git a/src/common/sphere_library/CSFile.h b/src/common/sphere_library/CSFile.h index 4929cd1e1..db7e5cbb1 100644 --- a/src/common/sphere_library/CSFile.h +++ b/src/common/sphere_library/CSFile.h @@ -79,8 +79,8 @@ protected: virtual void _Close(); public: virtual void Close(); /** * @brief Open a file in a specified mode. - * @param ptcName file to open. - * @param uiMode open mode. + * @param ptcFilename file to open. + * @param uiModeFlags open mode. * @return true if file is open, false otherwise. */ protected: virtual bool _Open( lpctstr ptcFilename = nullptr, uint uiModeFlags = OF_READ|OF_SHARE_DENY_NONE ); @@ -127,14 +127,14 @@ public: virtual int GetPosition() const; /** * @brief Reads data from the file. * @param pData buffer where store the readed data. - * @param stLength count of bytes to read. + * @param iLength count of bytes to read. * @return count of bytes readed. */ virtual int Read( void * pData, int iLength ) const; /** * @brief Set the position indicator. - * @param stOffset position to set. - * @param stOrigin origin (current position or init of the file). + * @param iOffset position to set. + * @param iOrigin origin (current position or init of the file). * @return position where the position indicator is set on success, -1 on error. */ protected: virtual int _Seek( int iOffset = 0, int iOrigin = SEEK_SET ); @@ -154,7 +154,7 @@ public: int SeekToEnd(); /** * @brief writes supplied data into file. * @param pData data to write. - * @param dwLength lenght of the data to write. + * @param iLength lenght of the data to write. * @return true is success, false otherwise. */ protected: virtual bool _Write(const void* pData, int iLength); diff --git a/src/common/sphere_library/CSFileText.h b/src/common/sphere_library/CSFileText.h index 8727491b4..b8ae6f4c3 100644 --- a/src/common/sphere_library/CSFileText.h +++ b/src/common/sphere_library/CSFileText.h @@ -43,8 +43,8 @@ protected: virtual bool _IsFileOpen() const override; public: virtual bool IsFileOpen() const override; /** * @brief Open a file in a specified mode. - * @param ptcName file to open. - * @param uiMode open mode. + * @param ptcFilename file to open. + * @param uiModeFlags open mode. * @return true if file is open, false otherwise. */ protected: virtual bool _Open(lpctstr ptcFilename = nullptr, uint uiModeFlags = OF_READ|OF_SHARE_DENY_NONE) override; diff --git a/src/common/sphere_library/CSMemBlock.h b/src/common/sphere_library/CSMemBlock.h index f38648fa0..17a043983 100644 --- a/src/common/sphere_library/CSMemBlock.h +++ b/src/common/sphere_library/CSMemBlock.h @@ -34,13 +34,13 @@ class CSMemBlock public: /** * @brief Clear internal data pointer and, if size is valid, alloc mem, updating internal data pointer. - * @param dwSize size to alloc. + * @param uiSize size to alloc. */ void Alloc( size_t uiSize ); protected: /** * @brief Alloc mem (new byte[*] wrapper). Fails if can not alloc or if size is invalid. - * @param dwSize size to alloc. + * @param uiSize size to alloc. * @return pointer to the allocated data. */ static byte * AllocBase( size_t uiSize ); @@ -99,7 +99,7 @@ class CSMemLenBlock : public CSMemBlock ///@{ /** * @brief Set the size of the buffer and alloc mem. - + @param dwSize new size of the buffer. + + @param uiSize new size of the buffer. */ void Alloc( size_t uiSize ); /** @@ -108,7 +108,7 @@ class CSMemLenBlock : public CSMemBlock void Free(); /** * @brief Resizes the buffer, maintaining the current data. - + @param dwSizeNew new size of the buffer. + + @param uiSizeNew new size of the buffer. */ void Resize( size_t uiSizeNew ); ///@} diff --git a/src/common/sphere_library/CSObjCont.h b/src/common/sphere_library/CSObjCont.h index e0174b1ab..7b22c42a4 100644 --- a/src/common/sphere_library/CSObjCont.h +++ b/src/common/sphere_library/CSObjCont.h @@ -194,7 +194,7 @@ class CSObjCont * Never called directly. Called CSObjContRec::RemoveSelf() * @see CSObjContRec::RemoveSelf(). * - * @param pObRec removed record. + * @param pObjRec removed record. */ virtual void OnRemoveObj( CSObjContRec* pObjRec ); ///@} diff --git a/src/common/sphere_library/CSQueue.cpp b/src/common/sphere_library/CSQueue.cpp index 22b76e8d1..1901def49 100644 --- a/src/common/sphere_library/CSQueue.cpp +++ b/src/common/sphere_library/CSQueue.cpp @@ -1,5 +1,5 @@ /** -* @file CQueue.cpp +* @file CSQueue.cpp */ #include diff --git a/src/common/sphere_library/CSString.h b/src/common/sphere_library/CSString.h index b1ebf5404..78d9e4392 100644 --- a/src/common/sphere_library/CSString.h +++ b/src/common/sphere_library/CSString.h @@ -1,5 +1,5 @@ /** -* @file CSString. +* @file CSString.h * @brief Custom string implementation. */ @@ -89,7 +89,7 @@ class CSString /** * @brief Move constructor. * - * @param pStr string to move the contents from. + * @param s string to move the contents from. */ inline CSString(CSString&& s) noexcept; @@ -236,14 +236,14 @@ class CSString /** * @brief Concatenate CSString with a string. - * @param pointer to zero-terminated tchar string to concatenate with. + * @param string to zero-terminated tchar string to concatenate with. * @return The result of concatenate the CSString with string. */ const CSString& operator+=(lpctstr string); /** * @brief Concatenate CSString with a string. - * @param pointer to zero-terminated tchar string to concatenate with. + * @param string to zero-terminated tchar string to concatenate with. * @return The result of concatenate the CSString with string. */ CSString operator+(lpctstr string); @@ -352,7 +352,7 @@ class CSString /** * @brief Print a unsigned char value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatUCVal(uchar uiVal); @@ -366,7 +366,7 @@ class CSString /** * @brief Print a unsigned short value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatUSVal(ushort uiVal); @@ -380,7 +380,7 @@ class CSString /** * @brief Print a unsigned int value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatUVal(uint uiVal); @@ -394,35 +394,35 @@ class CSString /** * @brief Print a ullong value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatULLVal(ullong uiVal); /** * @brief Print a size_t (unsigned) value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatSTVal(size_t uiVal); /** * @brief Print a byte value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatBVal(byte uiVal); /** * @brief Print a word value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatWVal(word uiVal); /** * @brief Print a dword value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatDWVal(dword uiVal); @@ -436,7 +436,7 @@ class CSString /** * @brief Print a unsigned char value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatU8Val(uint8 uiVal); @@ -450,7 +450,7 @@ class CSString /** * @brief Print a unsigned short value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatU16Val(uint16 uiVal); @@ -464,7 +464,7 @@ class CSString /** * @brief Print a unsigned int value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatU32Val(uint32 uiVal); @@ -478,7 +478,7 @@ class CSString /** * @brief Print a ullong value into the string. * @see Format() - * @param iVal value to print. + * @param uiVal value to print. */ void FormatU64Val(uint64 uiVal); diff --git a/src/common/sphere_library/CSTime.h b/src/common/sphere_library/CSTime.h index 4d173aacf..ab332b240 100644 --- a/src/common/sphere_library/CSTime.h +++ b/src/common/sphere_library/CSTime.h @@ -1,5 +1,5 @@ /** -* @file CSTime. +* @file CSTime.h * @brief Custom time management. */ diff --git a/src/common/sphere_library/sstring.h b/src/common/sphere_library/sstring.h index 39cb2fa2e..a82122734 100644 --- a/src/common/sphere_library/sstring.h +++ b/src/common/sphere_library/sstring.h @@ -270,7 +270,6 @@ int StrncpyCharBytesWritten(int iBytesToWrite, size_t uiBufSize, bool fPrintErro * @param pFind string we are looking for. * @param ppTable table where we are looking for the string. * @param iCount max iterations. -* @param uiElemSize size of elements of the table. * @return the index of string if success, -1 otherwise. */ int FindTable(const tchar * pFind, const tchar * const * ppTable, int iCount) noexcept; @@ -280,7 +279,6 @@ int FindTable(const tchar * pFind, const tchar * const * ppTable, int iCount) no * @param pFind string we are looking for. * @param ppTable table where we are looking for the string. * @param iCount max iterations. -* @param uiElemSize size of elements of the table. * @return the index of string if success, -1 otherwise. */ int FindTableSorted(const tchar * pFind, const tchar * const * ppTable, int iCount) noexcept; @@ -300,7 +298,6 @@ int FindCAssocRegTableHeadSorted(const tchar * pFind, const tchar * const* ppTab * @param pFind string we are looking for. * @param ppTable table where we are looking for the string. * @param iCount max iterations. -* @param uiElemSize size of elements of the table. * @return the index of string if success, -1 otherwise. */ int FindTableHead(const tchar * pFind, const tchar * const * ppTable, int iCount) noexcept; @@ -310,7 +307,6 @@ int FindTableHead(const tchar * pFind, const tchar * const * ppTable, int iCount * @param pFind string we are looking for. * @param ppTable table where we are looking for the string. * @param iCount max iterations. -* @param uiElemSize size of elements of the table. * @return the index of string if success, -1 otherwise. */ int FindTableHeadSorted(const tchar * pFind, const tchar * const * ppTable, int iCount) noexcept; diff --git a/src/game/CContainer.h b/src/game/CContainer.h index 226d9222d..958ac6bbc 100644 --- a/src/game/CContainer.h +++ b/src/game/CContainer.h @@ -44,9 +44,9 @@ class CContainer : public CSObjCont // This class contains a list of items but m void ContentDelete(bool fForce); /** - * @fn virtual void CContainer::OnRemoveObj( CSObjListRec* pObRec ); + * @fn virtual void CContainer::OnRemoveObj( CSObjListRec* pObjRec ); * @brief Override this = called when removed from list. - * @param [in,out] pObRec If non-null, the ob record. + * @param [in,out] pObjRec If non-null, the ob record. */ virtual void OnRemoveObj(CSObjContRec* pObjRec) override; @@ -94,10 +94,10 @@ class CContainer : public CSObjCont // This class contains a list of items but m CItem * ContentFindRandom() const; /** - * @fn void CContainer::ContentsDump( const CPointMap & pt, uint64 iAttr = 0 ); + * @fn void CContainer::ContentsDump( const CPointMap & pt, uint64 uiAttr = 0 ); * @brief Contents dump. * @param pt The point. - * @param dwAttr The attribute. + * @param uiAttr The attribute. */ void ContentsDump( const CPointMap & pt, uint64 uiAttr = 0 ); @@ -110,9 +110,9 @@ class CContainer : public CSObjCont // This class contains a list of items but m void ContentsTransfer( CItemContainer * pCont, bool fNoNewbie ); /** - * @fn void CContainer::ContentAttrMod( uint64 iAttr, bool fSet ); + * @fn void CContainer::ContentAttrMod( uint64 uiAttr, bool fSet ); * @brief Content attribute modifier. - * @param dwAttr The attribute. + * @param uiAttr The attribute. * @param fSet true to set. */ void ContentAttrMod( uint64 uiAttr, bool fSet ); @@ -195,10 +195,10 @@ class CContainer : public CSObjCont // This class contains a list of items but m int ContentConsume( const CResourceID& rid, int iQty = 1, dword dwArg = 0 ); /** - * @fn int CContainer::ContentConsumeTest( RESOURCE_ID_BASE rid, int iQty = 1, dword dwArg = 0 ); + * @fn int CContainer::ContentConsumeTest( RESOURCE_ID_BASE rid, int iAmount = 1, dword dwArg = 0 ); * @brief Content consume. * @param rid The rid. - * @param iQty Zero-based index of the qty. + * @param iAmount Zero-based index of the qty. * @param dwArg The argument. * * @return 0 = all consumed, # = number left to be consumed. @@ -240,7 +240,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m * @fn virtual void CContainer::ContentAdd( CItem * pItem ) = 0; * @brief Content add. * @param [in,out] pItem If non-null, the item. - * @param [in,out] bForceNoStack Do not stack on other identical items, even if it's a stackable type. + * @param [in,out] fForceNoStack Do not stack on other identical items, even if it's a stackable type. */ virtual void ContentAdd( CItem * pItem, bool fForceNoStack = false ) = 0; }; diff --git a/src/game/CEntity.h b/src/game/CEntity.h index 2e83551c6..5409d9a47 100644 --- a/src/game/CEntity.h +++ b/src/game/CEntity.h @@ -23,7 +23,7 @@ class CEntity /** * @brief Calls Delete on contained CComponents. * - * @param fForce. + * @param fForce */ void Delete(bool fForce = false); @@ -80,7 +80,6 @@ class CEntity * Will save important data on worldsave files. * * @param s the save file. - * @param pRef a pointer to the object found. */ void r_Write(CScript & s); /** diff --git a/src/game/CEntityProps.h b/src/game/CEntityProps.h index 1735aac7b..82d56d721 100644 --- a/src/game/CEntityProps.h +++ b/src/game/CEntityProps.h @@ -69,7 +69,7 @@ class CEntityProps /** * @brief Unsubscribes a CComponentProps. * - * @param pComponent the CComponentProps to unsuscribe. + * @param pCCProp the CComponentProps to unsuscribe. */ void UnsubscribeComponentProps(CComponentProps *pCCProp); @@ -99,7 +99,6 @@ class CEntityProps * Will save important data on worldsave files. * * @param s the save file. - * @param pRef a pointer to the object found. */ void r_Write(CScript & s); @@ -177,7 +176,7 @@ _ComponentType* CEntityProps::TrySubscribeAllowedComponentProps(const _Subscribe { if (!_ComponentType::CanSubscribe(pSubscriber)) return nullptr; - + return TrySubscribeComponentProps<_ComponentType, _ArgsType...>(std::forward<_ArgsType>(args)...); } diff --git a/src/game/CObjBase.h b/src/game/CObjBase.h index a47592438..7b917f397 100644 --- a/src/game/CObjBase.h +++ b/src/game/CObjBase.h @@ -125,7 +125,7 @@ public: virtual bool IsDeleted() const override; /** * @brief Deletes this CObjBase from game (doesn't delete the raw class instance). - * @param bForce Force deletion. + * @param fForce Force deletion. * @return Was deleted. */ virtual bool Delete(bool fForce = false); @@ -195,7 +195,7 @@ public: virtual bool IsDeleted() const override; /** * @brief sets the Spawn item. - * @param The CCSpawn. + * @param spawn The CCSpawn. */ void SetSpawn(CCSpawn *spawn); @@ -584,8 +584,8 @@ public: virtual bool IsDeleted() const override; * @param wHue The hue. * @param fAvoidTrigger true to avoid trigger. * @param [in,out] pSrc (Optional) If non-null, source for the. - * @param [in,out] SourceObj (Optional) If non-null, source object. - * @param sound The sound. + * @param [in,out] pSourceObj (Optional) If non-null, source object. + * @param iSound The sound. */ void SetHue( HUE_TYPE wHue, bool fAvoidTrigger = true, CTextConsole *pSrc = nullptr, CObjBase * pSourceObj = nullptr, llong iSound = 0 ); @@ -695,8 +695,8 @@ public: virtual bool IsDeleted() const override; * @brief Adds an Effect to a map point. * @param motion The motion. * @param id The identifier. - * @param pt The map point. - * @param pSource Source for the. + * @param ptSrc The map point. + * @param ptDest Source for the. * @param bspeedseconds The bspeedseconds. * @param bloop The bloop. * @param fexplode true to fexplode. diff --git a/src/game/CServerConfig.h b/src/game/CServerConfig.h index 6c69d4d46..7f5d3b0e5 100644 --- a/src/game/CServerConfig.h +++ b/src/game/CServerConfig.h @@ -726,7 +726,7 @@ extern class CServerConfig : public CResourceHolder * * @param restype Resource Type. * - * @param ptcName Resource Name. + * @param pszName Resource Name. * * @param wPage Resource Page attribute. * @@ -839,7 +839,7 @@ extern class CServerConfig : public CResourceHolder /** * @brief Get the def for the server in position 'index' in servers list. * - * @param pszText The server index. + * @param index The server index. * * @return CServerRef of the server. */ @@ -999,7 +999,6 @@ extern class CServerConfig : public CResourceHolder * * @param [in,out] pChar If non-null, the character. * @param [in,out] pCharTarg If non-null, the character targ. - * @param skill The skill. * * @return The calculated combat chance to hit. */ @@ -1009,7 +1008,7 @@ extern class CServerConfig : public CResourceHolder * @brief Calculates the combat chance to parry. * * @param [in,out] pChar If non-null, the character attempting to parry. - * @param skill The skill. + * @param pItemParry @todo * * @return The calculated combat chance to parry. */ @@ -1135,7 +1134,7 @@ extern class CServerConfig : public CResourceHolder /** * @brief Gets default message (sphere_msgs.scp). * - * @param ptcKey The key. + * @param lKeyNum The key. * * @return The default message. */ diff --git a/src/game/CTimedObject.h b/src/game/CTimedObject.h index 9f4e78665..0ee67ce91 100644 --- a/src/game/CTimedObject.h +++ b/src/game/CTimedObject.h @@ -101,14 +101,14 @@ public: virtual void SetTimeout(int64 iDelayInMsecs); /** * @brief < Timer. - * @param iDelayInSecs Delay in seconds. + * @param iSeconds Delay in seconds. */ protected: void _SetTimeoutS(int64 iSeconds); public: void SetTimeoutS(int64 iSeconds); /** * @brief < Timer. - * @param iDelayInTenths Delay in tenths of second. + * @param iTenths Delay in tenths of second. */ protected: void _SetTimeoutD(int64 iTenths); public: void SetTimeoutD(int64 iTenths); diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index 8bec31f76..bbabe22b8 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -642,9 +642,9 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * Used to increase/decrease Karma values, checks if you can have the resultant values, * fire @KarmaChange trigger and show a message as result of the change (if procceed). * Can't never be greater than g_Cfg.m_iMaxKarma or lower than g_Cfg.m_iMinKarma or iBottom. - * @param iKarma Amount of karma to change, can be possitive and negative. + * @param iKarmaChange Amount of karma to change, can be possitive and negative. * @param iBottom is the lower value you can have for this execution. - * @param bMessage show message to the char or not. + * @param fMessage show message to the char or not. */ void Noto_Karma( int iKarmaChange, int iBottom = INT32_MIN, bool fMessage = false, CChar* pNPC = nullptr ); @@ -660,7 +660,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; /** * @brief I have a new notoriety Level? check it and show a message if so. * - * @param iPriv The 'new' notoriety level, it will be checked against the current level to see if it changed. + * @param iPrv The 'new' notoriety level, it will be checked against the current level to see if it changed. */ void Noto_ChangeNewMsg( int iPrv ); @@ -769,7 +769,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * @brief I killed someone, should I have credits? and penalties? * * Here fires the @MurderMark trigger, also gives exp if level system is enabled to give exp on killing. - * @param pKill, the chara I killed (or participated to kill). + * @param pKill the chara I killed (or participated to kill). * @param iTotalKillers how many characters participated in this kill. */ void Noto_Kill(CChar * pKill, int iTotalKillers = 0); @@ -831,14 +831,14 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; /** * @brief Deleting myself and sending data again for given char. * - * @param pChar, the CChar* of the char of which we want to resend the noto. + * @param pChar the CChar* of the char of which we want to resend the noto. */ void NotoSave_Resend( CChar *pChar ); /** * @brief Gets the entry list of the given CChar. * - * @param pChar, CChar to retrieve the entry number for. + * @param pChar CChar to retrieve the entry number for. * @return the entry number. */ int NotoSave_GetID( CChar * pChar ) const; @@ -846,7 +846,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; /** * @brief Removing stored data for pChar. * - * @param pChar, the CChar I want to remove from my list. + * @param pChar the CChar I want to remove from my list. * @return true if successfully removed it. */ bool NotoSave_Delete( CChar * pChar ); @@ -871,8 +871,8 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * * Main function for default Level system. * Triggers @ExpChange and @LevelChange if needed - * @param iExpDelta, amount of exp gaining (or losing?) - * @param ppCharDead from who we gained the experience. + * @param iExpDelta amount of exp gaining (or losing?) + * @param pCharDead from who we gained the experience. */ void ChangeExperience(llong iExpDelta = 0, CChar *pCharDead = nullptr); uint GetSkillTotal(int what = 0, bool how = true); diff --git a/src/game/clients/CAccount.h b/src/game/clients/CAccount.h index 85ac42d09..54f7587af 100644 --- a/src/game/clients/CAccount.h +++ b/src/game/clients/CAccount.h @@ -1,5 +1,5 @@ /** -* @file CAcount.h +* @file CAccount.h */ #ifndef _INC_CACCOUNT_H @@ -236,7 +236,6 @@ class CAccount : public CScriptObj static PLEVEL_TYPE GetPrivLevelText( lpctstr pszFlags ); /** * @brief Gets the CAccount PLEVEL. - * @param pszFlags TODOC. * @return TODOC. */ PLEVEL_TYPE GetPrivLevel() const { return( m_PrivLevel ); } @@ -314,7 +313,7 @@ class CAccount : public CScriptObj bool IsMyAccountChar( const CChar * pChar ) const; /** * @brief Unlink the CChar from this CAccount. - * @param CChar to detach. + * @param pChar to detach. * @return TODOC. */ size_t DetachChar( CChar * pChar ); @@ -442,13 +441,13 @@ class CAccounts * If there is not an CAccount with the providded name, AutoAccount is enabled in sphere.ini and the name is a valid account name, a CAcount is created and a CAccount * of the returned. * Otherwise, nullptr is returned. * @param pszName name of the account. - * @param fAutoCreate try to create the account if not exists. + * @param fCreate try to create the account if not exists. * @return CAccount * if account exists or created, nullptr otherwise. */ CAccount * Account_FindCreate( lpctstr pszName, bool fCreate = false ); /** * @brief Check if a chat name is already used. - * @param pszChatName string containing the name. + * @param pszName string containing the name. * @return CAccount * if the name is already used, nullptr otherwise. */ CAccount * Account_FindChat( lpctstr pszName ); diff --git a/src/game/components/CCChampion.h b/src/game/components/CCChampion.h index dd1cf4cb5..661840406 100644 --- a/src/game/components/CCChampion.h +++ b/src/game/components/CCChampion.h @@ -220,13 +220,13 @@ class CCChampion : public CComponent * otherwise it calls AddRedCandle and stop execution. * Create a pCandle pointer and set it's properties, color, and places it or sets the pointer to the item given in the param 'uid'. * Store in m_WhiteCandle[m_WhiteCandles] the CItem stored in pCandle. - * @param uid, if given (mostly during world loading) instead of creating a new candle, only the uid will be set to hold the already existing candle and do not have duplicates. + * @param uid if given (mostly during world loading) instead of creating a new candle, only the uid will be set to hold the already existing candle and do not have duplicates. */ void AddWhiteCandle(const CUID& uid = CUID()); /** * @brief Red candles check, add new one or I am in boss phase? * - * @param uid, if given (mostly during world loading) instead of creating a new candle, only the uid will be set to hold the already existing candle and do not have duplicates. + * @param uid if given (mostly during world loading) instead of creating a new candle, only the uid will be set to hold the already existing candle and do not have duplicates. */ void AddRedCandle(const CUID& uid = CUID()); /** @@ -265,7 +265,6 @@ class CCChampion : public CComponent /** * @brief How many monsters do we need to kill to gain a White Candle. * - * @param iMonsters total amount of monsters of the champion. * @return amount of killed monsters needed to reach the next level. */ uint16 GetMonstersCount(); @@ -273,7 +272,6 @@ class CCChampion : public CComponent /** * @brief Retrieves how much Red Candles are needed to reach next Champion's Level. * - * @param iLevel the level to check for (0 = reach for next Level). * More detailed description ... * Called from SetLevel() to set the amount of candles. * Value stored in morex (m_itNormal.m_morep.m_x). diff --git a/src/game/components/CCSpawn.h b/src/game/components/CCSpawn.h index bea92a9c1..9725064ed 100644 --- a/src/game/components/CCSpawn.h +++ b/src/game/components/CCSpawn.h @@ -154,13 +154,13 @@ class CCSpawn : public CComponent /** * @brief Removing one UID in Spawn's m_obj[]. - * @param UID of the obj to remove. + * @param uid of the obj to remove. */ void DelObj(const CUID& uid); /** * @brief Storing one UID in Spawn's m_obj[]. - * @param UID of the obj to add. + * @param uid of the obj to add. */ void AddObj(const CUID& uid); diff --git a/src/game/items/CItemMulti.h b/src/game/items/CItemMulti.h index e384468e4..8e5cb122e 100644 --- a/src/game/items/CItemMulti.h +++ b/src/game/items/CItemMulti.h @@ -114,7 +114,6 @@ class CItemMulti : public CItem, public CCMultiMovable * @param dy p.y * @param dz p.z * @param dwKeyCode the lock hash to apply on the item (if is lockable). - * @param fIsAddon true when the created component is an addon. * @return true if the component need a key (wether the key is created or not is not checked here). */ bool Multi_CreateComponent(ITEMID_TYPE id, short dx, short dy, char dz, dword dwKeyCode); @@ -157,18 +156,18 @@ class CItemMulti : public CItem, public CCMultiMovable /** * @brief Revokes any privileges from this house. - * @param pSrc the object whom privileges to revoke. + * @param uidSrc the object whom privileges to revoke. */ void RevokePrivs(const CUID& uidSrc); //Owner /** * @brief Set's the owner. - * @param pOwner the new owner. + * @param uidOwner the new owner. */ void SetOwner(const CUID& uidOwner); /** * @brief Checks if the given CChar in the param is the owner. - * @param pTarget the char to check. + * @param uidTarget the char to check. * @return true if true. */ bool IsOwner(const CUID& uidTarget) const; @@ -181,12 +180,12 @@ class CItemMulti : public CItem, public CCMultiMovable // Guilds /** * @brief Links this multi to a guild. - * @param pGuild the guild on which to link. + * @param uidGuild the guild on which to link. */ void SetGuild(const CUID& uidGuild); /** * @brief Checks if the given guild is the one linked to this multi. - * @param pGuild the guild to check on. + * @param uidGuild the guild to check on. * @return true if match. */ bool IsGuild(const CUID& uidGuild) const; @@ -199,12 +198,12 @@ class CItemMulti : public CItem, public CCMultiMovable // Coowner /** * @brief Adds a coowner to the _lCoowners list. - * @param pCoowner the coowner + * @param uidCoowner the coowner */ void AddCoowner(const CUID& uidCoowner); /** * @brief Deletes a coowner to the _lCoowners list. - * @param pCoowner the coowner + * @param uidCoowner the coowner */ void DeleteCoowner(const CUID& uidCoowner, bool fRemoveFromList); /** @@ -214,7 +213,7 @@ class CItemMulti : public CItem, public CCMultiMovable size_t GetCoownerCount() const; /** * @brief Get's the position on the _lCoowners list of the given char. - * @param pTarget the coowner. + * @param uidTarget the coowner. * @return the position. */ int GetCoownerIndex(const CUID& uidTarget) const; @@ -222,12 +221,12 @@ class CItemMulti : public CItem, public CCMultiMovable // Friend /** * @brief Adds a friend to the _lFriends list. - * @param pFriend the friend + * @param uidFriend the friend */ void AddFriend(const CUID& uidFriend); /** * @brief Deletes a friend to the _lFriends list. - * @param pFriend the friend + * @param uidFriend the friend */ void DeleteFriend(const CUID& uidFriend, bool fRemoveFromList); /** @@ -237,7 +236,7 @@ class CItemMulti : public CItem, public CCMultiMovable size_t GetFriendCount() const; /** * @brief Get's the position on the _lFriends list of the given char. - * @param pTarget the friend. + * @param uidTarget the friend. * @return the position. */ int GetFriendIndex(const CUID& uidTarget) const; @@ -245,12 +244,12 @@ class CItemMulti : public CItem, public CCMultiMovable // Ban /** * @brief Adds a char to the _lBans list. - * @param pBan the char to ban. + * @param uidBan the char to ban. */ void AddBan(const CUID& uidBan); /** * @brief Deletes a char from the _lBans list. - * @param pBan the char. + * @param uidBan the char. */ void DeleteBan(const CUID& uidBan, bool fRemoveFromList); /** @@ -260,7 +259,7 @@ class CItemMulti : public CItem, public CCMultiMovable size_t GetBanCount() const; /** * @brief Returns the position on the _lBans list of the given char. - * @param pBan the char. + * @param uidBan the char. * @return the pos. */ int GetBanIndex(const CUID& uidBan) const; @@ -268,14 +267,14 @@ class CItemMulti : public CItem, public CCMultiMovable // Access /** * @brief Adds access to this house to the given char. - * @param pAccess the char. + * @param uidAccess the char. */ void AddAccess(const CUID& uidAccess); /** * @brief Revokes the access to this house to the given char. * * Note: This removes the char from the list, but won't prevent it from enter like a Ban. - * @param pAccess the char. + * @param uidAccess the char. */ void DeleteAccess(const CUID& uidAccess, bool fRemoveFromList); /** @@ -285,18 +284,18 @@ class CItemMulti : public CItem, public CCMultiMovable size_t GetAccessCount() const; /** * @brief Returns the position on the _lAccess list of the given char. - * @param pAccess the char. + * @param uidAccess the char. * @return the position. */ int GetAccessIndex(const CUID& uidAccess) const; /** * @brief Ejects a char from inside the house to the House Sign. - * @param pChar the char. + * @param uidChar the char. */ void Eject(const CUID& uidChar); /** * @brief Ejects all chars from this house - * @param pChar this char will not be ejected (eg: the owner kicking all people) + * @param uidChar this char will not be ejected (eg: the owner kicking all people) */ void EjectAll(CUID uidChar = CUID()); ///@} @@ -309,14 +308,14 @@ class CItemMulti : public CItem, public CCMultiMovable // Keys: /** * @brief Creates a key for the given character. - * @param pTarget the target that receives the key. + * @param uidTarget the target that receives the key. * @param fDupeOnBank creates a copy on the bank if true. * @return the Key. */ CItem *GenerateKey(const CUID& uidTarget, bool fDupeOnBank = false); /** * @brief Removes all the keys of this house from the given char. - * @param pTarget the char. + * @param uidTarget the char. */ void RemoveKeys(const CUID& uidTarget); /** @@ -339,14 +338,14 @@ class CItemMulti : public CItem, public CCMultiMovable * @brief Redeeds this multi * @param fDisplayMsg Display the bounce message on the player. * @param fMoveToBank Places the deed on the bank. - * @param pChar the char doing the redeed (if any). + * @param uidChar the char doing the redeed (if any). */ void Redeed(bool fDisplayMsg = true, bool fMoveToBank = true, CUID uidChar = CUID()); //Moving Crate /** * @brief Sets the Moving Crate to the target. - * @param pCrate the new Moving Crate. + * @param uidCrate the new Moving Crate. */ void SetMovingCrate(const CUID& uidCrate); /** @@ -376,17 +375,17 @@ class CItemMulti : public CItem, public CCMultiMovable // AddOns /** * @brief Adds an Addon to the addons list. - * @param pAddon the Addon + * @param uidAddon the Addon */ void AddAddon(const CUID& uidAddon); /** * @brief Removes an Addon from the addons list. - * @param pAddon the Addon + * @param uidAddon the Addon */ void DeleteAddon(const CUID& uidAddon); /** * @brief Returns the position of a given Addon. - * @param pAddon the Addon + * @param uidAddon the Addon * @return the position */ int GetAddonIndex(const CUID& uidAddon) const; @@ -403,17 +402,17 @@ class CItemMulti : public CItem, public CCMultiMovable // Components /** * @brief Adds a CMultiComponent to the components list. - * @param pComponent the component. + * @param uidComponent the component. */ void AddComponent(const CUID& uidComponent); /** * @brief Removes a CMultiComponent from the components list. - * @param pComponent the component. + * @param uidComponent the component. */ virtual void DeleteComponent(const CUID& uidComponent, bool fRemoveFromList); /** * @brief Returns the position of a given CMultiComponent. - * @param pComponent the component + * @param uidComponent the component * @return the position */ int GetComponentIndex(const CUID& uidComponent) const; @@ -428,7 +427,7 @@ class CItemMulti : public CItem, public CCMultiMovable void RemoveAllComponents(); /** * @brief Generates the base components of the multi. - * @param fNeedKey wether a key is required or not. + * @param pfNeedKey wether a key is required or not. * @param dwKeyCode the code hash for keys. */ void GenerateBaseComponents(bool *pfNeedKey, dword dwKeyCode); @@ -449,7 +448,7 @@ class CItemMulti : public CItem, public CCMultiMovable uint16 GetBaseStorage() const; /** * @brief Sets the modifier of Base Storage. - * @param iIncrease the modifier. + * @param uiIncrease the modifier. */ void SetIncreasedStorage(uint16 uiIncrease); /** @@ -476,7 +475,6 @@ class CItemMulti : public CItem, public CCMultiMovable void SetBaseVendors(uint8 iLimit); /** * @brief Returns the Base Vendors. - * @param the value. */ uint8 GetBaseVendors() const; /** @@ -503,12 +501,12 @@ class CItemMulti : public CItem, public CCMultiMovable // -Lockdowns /** * @brief Locks an item and add it to the Lockdowns list. - * @param pItem the item. + * @param uidItem the item. */ void LockItem(const CUID& uidItem); /** * @brief Unlocks an item and remove it from the Lockdowns list. - * @param pItem the item. + * @param uidItem the item. */ void UnlockItem(const CUID& uidItem, bool fRemoveFromList); void UnlockAllItems(); @@ -525,17 +523,17 @@ class CItemMulti : public CItem, public CCMultiMovable // -Secured storage (Containers + items). /** * @brief Secures a container and adds it to the containers list. - * @param pContainer the container. + * @param uidContainer the container. */ void Secure(const CUID& uidContainer); /** * @brief Releases a container and removes it from the containers list. - * @param pContainer the container. + * @param uidContainer the container. */ void Release(const CUID& uidContainer, bool fRemoveFromList); /** * @brief Returns the position of the given container - * @param pContainer the container + * @param uidContainer the container * @return the pos. */ int GetSecuredContainerIndex(const CUID& uidContainer) const; @@ -552,17 +550,17 @@ class CItemMulti : public CItem, public CCMultiMovable // -Vendors /** * @brief Adds a char to the vendors list. - * @param pVendor the vendor. + * @param uidVendor the vendor. */ void AddVendor(const CUID& uidVendor); /** * @brief Removes a char from the vendors list. - * @param pVendor the vendor + * @param uidVendor the vendor */ void DeleteVendor(const CUID& uidVendor, bool fRemoveFromList); /** * @brief Returns the position of the given char. - * @param pVendor the char + * @param uidVendor the char * @return the pos. */ int GetVendorIndex(const CUID& uidVendor) const; @@ -682,12 +680,12 @@ class CMultiStorage /** * @brief Adds a multi - * @param pMulti the multi + * @param uidMulti the multi */ void AddMulti(const CUID& uidMulti, HOUSE_PRIV ePriv); /** * @brief Removes a multi - * @param pMulti the multi + * @param uidMulti the multi */ void DelMulti(const CUID& uidMulti); /** @@ -698,17 +696,17 @@ class CMultiStorage /** * @brief Adds a house multi. - * @param pHouse the house. + * @param uidHouse the house. */ void AddHouse(const CUID& uidHouse, HOUSE_PRIV ePriv); /** * @brief Removes a house multi. - * @param pHouse the house. + * @param uidHouse the house. */ void DelHouse(const CUID& uidHouse); /** * @brief Checks if the char can place a house. - * @param pChar the char. + * @param uidChar the char. * @param iHouseCount the MultiCount of the house. * @return true if the char has space for the house. */ @@ -745,24 +743,24 @@ class CMultiStorage /** * @brief Adds a ship. - * @param pShip the ship. + * @param uidShip the ship. */ void AddShip(const CUID& uidShip, HOUSE_PRIV ePriv); /** * @brief Removes a ship. - * @param pShip the ship. + * @param uidShip the ship. */ void DelShip(const CUID& uidShip); /** * @brief Checks if the char can place a ship. - * @param pChar the char. - * @param iHouseCount the MultiCount of the ship. + * @param uidChar the char. + * @param iShipCount the MultiCount of the ship. * @return true if the char has space for the ship. */ bool CanAddShip(const CUID& uidChar, int16 iShipCount); /** * @brief Returns the position of the given ship. - * @param pShip the ship. + * @param uidShip the ship. * @return the position. */ int16 GetShipPos(const CUID& uidShip); diff --git a/src/game/items/CItemMultiCustom.h b/src/game/items/CItemMultiCustom.h index 5839f3542..144b5fd25 100644 --- a/src/game/items/CItemMultiCustom.h +++ b/src/game/items/CItemMultiCustom.h @@ -68,7 +68,7 @@ class CItemMultiCustom : public CItemMulti /** * @brief Removes a CMultiComponent from the components list. - * @param pComponent the component. + * @param uidComponent the component. */ virtual void DeleteComponent(const CUID& uidComponent, bool fRemoveFromList) override final; From 887bd78fdc994f264ab509c96e11fc98448417b5 Mon Sep 17 00:00:00 2001 From: David Kindl Date: Mon, 26 May 2025 16:25:06 +0200 Subject: [PATCH 034/112] Grammar check and proofreading comments. --- src/CMakeSources.cmake | 2 +- src/common/sphere_library/CSString.h | 2 +- src/game/CServerDef.cpp | 4 +- src/game/chars/CChar.h | 56 ++++++++--------- src/game/chars/CCharNPC.cpp | 16 ++--- src/game/chars/CCharNPC.h | 6 +- src/game/chars/CCharNPCAct.cpp | 90 ++++++++++++++-------------- src/game/chars/CCharNPCStatus.cpp | 54 ++++++++--------- src/game/chars/CCharSkill.cpp | 68 ++++++++++----------- src/sphere.ini | 11 ++-- 10 files changed, 154 insertions(+), 155 deletions(-) diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index 8289b4eeb..3f517b8de 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -3,7 +3,7 @@ ############################## # Precompiled header: it's parsed only once and stored in memory as an intermediate form. This speeds up compilation drastically. -# Be wise in chosing those, since every time we change one of them, we'll have to recompile every one on the list. +# Be wise in choosing those, since every time we change one of them, we'll have to recompile every one on the list. set(pch_list PRIVATE src/common/common.h diff --git a/src/common/sphere_library/CSString.h b/src/common/sphere_library/CSString.h index 78d9e4392..cc79b186e 100644 --- a/src/common/sphere_library/CSString.h +++ b/src/common/sphere_library/CSString.h @@ -74,7 +74,7 @@ class CSString * * @see CopyLen() * @param pStr string to copy. - * #param iLen max number of chars (single-byte) to copy. + * @param iLen max number of chars (single-byte) to copy. */ CSString(lpctstr pStr, int iLen); diff --git a/src/game/CServerDef.cpp b/src/game/CServerDef.cpp index c14027a80..9c698140e 100644 --- a/src/game/CServerDef.cpp +++ b/src/game/CServerDef.cpp @@ -294,8 +294,8 @@ static lpctstr constexpr sm_AccAppTable[ ACCAPP_QTY ] = "CLOSED", // Closed. Not accepting more. "UNUSED", "FREE", // Anyone can just log in and create a full account. - "GUESTAUTO", // You get to be a guest and are automatically sent email with u're new password. - "GUESTTRIAL", // You get to be a guest til u're accepted for full by an Admin. + "GUESTAUTO", // You get to be a guest and are automatically sent email with your new password. + "GUESTTRIAL", // You get to be a guest until you are accepted for full by an Admin. "UNUSED", "UNSPECIFIED", // Not specified. "UNUSED", diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index bbabe22b8..849bb3b9a 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -42,7 +42,7 @@ enum NPCBRAIN_TYPE // General AI type. NPCBRAIN_STABLE, // 7 = will store your animals for you. NPCBRAIN_MONSTER, // 8 = not tamable. normally evil. NPCBRAIN_BERSERK, // 9 = attack closest (blades, vortex) - NPCBRAIN_DRAGON, // 10 = we can breath fire. may be tamable ? hirable ? + NPCBRAIN_DRAGON, // 10 = we can breathe fire. may be tamable ? hirable ? NPCBRAIN_QTY }; @@ -81,15 +81,15 @@ class CChar : public CObjBase, public CContainer, public CTextConsole #define STATF_INCOGNITO 0x00000800 // Dont show skill titles #define STATF_SPIRITSPEAK 0x00001000 // I can hear ghosts clearly. #define STATF_INSUBSTANTIAL 0x00002000 // Ghost has not manifest. or GM hidden -#define STATF_EMOTEACTION 0x00004000 // The creature will emote its actions to it's owners. -#define STATF_COMM_CRYSTAL 0x00008000 // I have a IT_COMM_CRYSTAL or listening item on me. +#define STATF_EMOTEACTION 0x00004000 // The creature will emote its actions to its owners. +#define STATF_COMM_CRYSTAL 0x00008000 // I have an IT_COMM_CRYSTAL or listening item on me. #define STATF_HASSHIELD 0x00010000 // Using a shield #define STATF_ARCHERCANMOVE 0x00020000 // Can move with archery #define STATF_STONE 0x00040000 // turned to stone. #define STATF_HOVERING 0x00080000 // hovering (flying gargoyle) #define STATF_FLY 0x00100000 // Flying or running ? (anim) // 0x00200000 -#define STATF_HALLUCINATING 0x00400000 // eat 'shrooms or bad food. +#define STATF_HALLUCINATING 0x00400000 // eat mushrooms or bad food. #define STATF_HIDDEN 0x00800000 // Hidden (non-magical) #define STATF_INDOORS 0x01000000 // we are covered from the rain. #define STATF_CRIMINAL 0x02000000 // The guards will attack me. (someone has called guards) @@ -117,7 +117,7 @@ class CChar : public CObjBase, public CContainer, public CTextConsole struct NotoSaves { - int64 time; // Updaete timer + int64 time; // Update timer dword charUID; // Character viewing me NOTO_TYPE color; // Color sent on movement packets NOTO_TYPE value; // Notoriety type @@ -128,7 +128,7 @@ class CChar : public CObjBase, public CContainer, public CTextConsole CCharPlayer * m_pPlayer; // May even be an off-line player ! CCharNPC * m_pNPC; // we can be both a player and an NPC if "controlled" ? - CPartyDef * m_pParty; // What party am i in ? + CPartyDef * m_pParty; // What party am I in ? CRegionWorld * m_pArea; // What region are we in now. (for guarded message) CRegion * m_pRoom; // What room we are in now. @@ -143,7 +143,7 @@ class CChar : public CObjBase, public CContainer, public CTextConsole word m_defense; // calculated armor worn (NOT intrinsic armor) ushort _iRegenTickCount; // ticks until next regen. - CUID m_UIDLastNewItem; // Last item created, used to store on this CChar the UID of the last created item via ITEM or ITEMNEWBIe in @Create and @Restock to prevent COLOR, etc properties to be called with no reference when the item was not really created, ie: ITEM=i_dagger,R5 + CUID m_UIDLastNewItem; // Last item created, used to store on this CChar the UID of the last created item via ITEM or ITEMNEWBIe in @Create and @Restock to prevent COLOR, etc. properties to be called with no reference when the item was not really created, ie: ITEM=i_dagger,R5 uint m_exp; // character experience uint m_level; // character experience level //DIR_TYPE m_dirClimb; // we are standing on a CAN_I_CLIMB or UFLAG2_CLIMBABLE, DIR_QTY = not on climbable @@ -160,9 +160,9 @@ class CChar : public CObjBase, public CContainer, public CTextConsole CSString m_sTitle; // Special title such as "the guard" (replaces the normal skill title) CPointMap m_ptHome; // What is our "home" region. (towns and bounding of NPC's) - int64 _iTimeCreate; // When was i created ? + int64 _iTimeCreate; // When was I created ? int64 _iTimePeriodicTick; - int64 _iTimeNextRegen; // When did i get my last regen tick ? + int64 _iTimeNextRegen; // When did I get my last regen tick ? int64 _iTimeLastHitsUpdate; int64 _iTimeLastCallGuards; @@ -191,7 +191,7 @@ class CChar : public CObjBase, public CContainer, public CTextConsole ushort m_val; // Hits, Mana, Stam ushort m_max; // MaxVal: MaxHits, MaxMana, MaxStam ushort m_regenVal; // Amount of Stat to gain at each regen - int64 m_regenRate; // Regen each this much milliseconds. + int64 m_regenRate; // Regen each this many milliseconds. int64 m_regenLast; // Time of the last regen. } m_Stat[STAT_QTY]; @@ -285,7 +285,7 @@ class CChar : public CObjBase, public CContainer, public CTextConsole int16 m_iRecoilDelay; // ACTARG2 & 0x0000FFFF = Duration (in tenth of secs) of the previous swing recoil time. int16 m_iSwingAnimationDelay; // ACTARG2 & 0xFFFF0000 = Duration (in tenth of secs) of the previous swing animation duration. int16 m_iSwingAnimation; // ACTARG3 & 0x0000FFFF = hit animation id. - int16 m_iSwingIgnoreLastHitTag; // ACTARG3 & 0xFFFF0000. Internally used by PreHit. If == 1 (which happens only for the hit after the first instahit), for this hit TAG.LastHit delay checking will be ignored. + int16 m_iSwingIgnoreLastHitTag; // ACTARG3 & 0xFFFF0000. Internally used by PreHit. If == 1 (which happens only for the hit after the first instant hit), for this hit TAG.LastHit delay checking will be ignored. } m_atFight; // SKILL_ENTICEMENT @@ -321,9 +321,9 @@ class CChar : public CObjBase, public CContainer, public CTextConsole // NPCACT_TALK_FOLLOW struct { - dword m_dwHearUnknown; // ACTARG1 = Speaking NPC has no idea what u're saying. - dword m_dwWaitCount; // ACTARG2 = How long have i been waiting (xN sec) - // m_Act_UID = who am i talking to ? + dword m_dwHearUnknown; // ACTARG1 = Speaking NPC has no idea what you are saying. + dword m_dwWaitCount; // ACTARG2 = How long have I been waiting (xN sec) + // m_Act_UID = who am I talking to ? } m_atTalk; // NPCACT_FLEE @@ -331,7 +331,7 @@ class CChar : public CObjBase, public CContainer, public CTextConsole { dword m_iStepsMax; // ACTARG1 = How long should it take to get there. dword m_iStepsCurrent; // ACTARG2 = How long has it taken ? - // m_Act_UID = who am i fleeing from ? + // m_Act_UID = who am I fleeing from ? } m_atFlee; }; @@ -640,9 +640,9 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * @brief Update Karma with the given values. * * Used to increase/decrease Karma values, checks if you can have the resultant values, - * fire @KarmaChange trigger and show a message as result of the change (if procceed). - * Can't never be greater than g_Cfg.m_iMaxKarma or lower than g_Cfg.m_iMinKarma or iBottom. - * @param iKarmaChange Amount of karma to change, can be possitive and negative. + * fire @KarmaChange trigger and show a message as result of the change (if proceed). + * Can't be greater than g_Cfg.m_iMaxKarma or lower than g_Cfg.m_iMinKarma or iBottom. + * @param iKarmaChange Amount of karma to change, can be positive and negative. * @param iBottom is the lower value you can have for this execution. * @param fMessage show message to the char or not. */ @@ -652,7 +652,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * @brief Update Fame with the given value. * * Used to increase/decrease Fame, it fires @FameChange trigger. - * Can't never exceed g_Cfg.m_iMaxFame and can't never be lower than 0. + * Can't exceed g_Cfg.m_iMaxFame and can't be lower than 0. * @param iFameChange is the amount of fame to change over the current one. */ void Noto_Fame( int iFameChange, CChar* pNPC = nullptr ); @@ -667,7 +667,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; /** * @brief I've become murderer or criminal, let's see a message for it. * - * MSG_NOTO_CHANGE_0-8 contains the strings 'you have gained a bit/a lot/etc of', so the given iDelta is used to check wether of these MSG should be used. + * MSG_NOTO_CHANGE_0-8 contains the strings 'you have gained a bit/a lot/etc of', so the given iDelta is used to check which of these MSG should be used. * @param iDelta Amount of Karma/Fame changed. * @param pszType String containing 'Karma' or 'Fame' to pass as argument to the given text. */ @@ -680,11 +680,11 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * @brief Returns what is this char to the viewer. * * This allows the noto attack check in the client. - * Notoriety handler using std::vector, it's saved and readed here but calcs are being made in Noto_CalcFlag(). + * Notoriety handler using std::vector, it's saved and read here but calcs are being made in Noto_CalcFlag(). * Actually 2 values are stored in this vectored list: Notoriety (the notoriety level) and Color (the color we are showing in the HP bar and in our character for the viewer). * Calls @NotoSend trigger with src = pChar, argn1 = notoriety level, argn2 = color to send. * @param pChar is the CChar that needs to know what I am (good, evil, criminal, neutral...) to him. - * @param fIncog if set to true and he has STATF_INCOGNITO, this character will be gray for the viever (pChar). + * @param fIncog if set to true, and he has STATF_INCOGNITO, this character will be gray for the viewer (pChar). * @param fInvul if set to true invulnerable characters will return NOTO_INVUL (yellow bar, etc). * @param fGetColor if set to true only the color will be returned and not the notoriety (note that they can differ if set to so in the @NotoSend trigger). * @return NOTO_TYPE notoriety level. @@ -695,9 +695,9 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * @brief Notoriety calculations * * TAG.OVERRIDE.NOTO will override everything and use the value in the tag for everyone, regardless of what I really are for them. - * If this char is a pet, check if notoriety must be inherited from it's master or do regular checks for it. + * If this char is a pet, check if notoriety must be inherited from its master or do regular checks for it. * @param pChar is the CChar that needs to know what I am (good, evil, criminal, neutral...) to him. - * @param fIncog if set to true (usually because of Incognito spell), this character will be gray for the viever (pChar). + * @param fIncog if set to true (usually because of Incognito spell), this character will be gray for the viewer (pChar). * @param fInvul if set to true invulnerable characters will return NOTO_INVUL (yellow bar, etc). * @return NOTO_TYPE notoriety level. */ @@ -708,7 +708,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * * Used to retrieve color for character and corpse's names. * @param pChar is the CChar that needs to know what I am (good, evil, criminal, neutral...) to him. - * @param fIncog if set to true (usually because of Incognito spell), this character will be gray for the viever (pChar). + * @param fIncog if set to true (usually because of Incognito spell), this character will be gray for the viewer (pChar). * @return HUE_TYPE my color. */ HUE_TYPE Noto_GetHue( const CChar * pChar, bool fIncog = false ) const; @@ -852,7 +852,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; bool NotoSave_Delete( CChar * pChar ); /** - * @brief Removing expired notorieties. + * @brief Removing expired notoriety. */ void NotoSave_CheckTimeout(); @@ -1378,7 +1378,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; virtual bool _CanTick() const override final; -protected: virtual bool _OnTick() override final; // _OnTick timeout for skills, AI, etc +protected: virtual bool _OnTick() override final; // _OnTick timeout for skills, AI, etc. //public: virtual bool _OnTick() override final; public: @@ -1386,7 +1386,7 @@ protected: virtual bool _OnTick() override final; // _OnTick timeout for skills void OnTickFood( ushort uiVal, int HitsHungerLoss ); virtual void OnTickStatusUpdate() override; - bool OnTickPeriodic(); // Periodic tick calls (update stats, status bar, notoriety & attackers, death check, etc) + bool OnTickPeriodic(); // Periodic tick calls (update stats, status bar, notoriety & attackers, death check, etc.) void OnTickSkill(); // _OnTick timeout specific for the skill behavior diff --git a/src/game/chars/CCharNPC.cpp b/src/game/chars/CCharNPC.cpp index f3c075ee2..da9e8e751 100644 --- a/src/game/chars/CCharNPC.cpp +++ b/src/game/chars/CCharNPC.cpp @@ -42,7 +42,7 @@ CCharNPC::CCharNPC( CChar * pChar, NPCBRAIN_TYPE NPCBrain ) { UnreferencedParameter(pChar); m_Brain = NPCBrain; - m_Home_Dist_Wander = INT16_MAX; // as far as i want. + m_Home_Dist_Wander = INT16_MAX; // as far as I want. m_Act_Motivation = 0; m_bonded = 0; #ifndef _WIN32 @@ -150,7 +150,7 @@ bool CCharNPC::r_WriteVal( CChar * pChar, lpctstr ptcKey, CSString & sVal ) { //return as string or hex number or nullptr if not set - //On these ones, check BaseDef too if not found on dynamic + // On these, check BaseDef too if not found on dynamic. case CNC_THROWDAM: case CNC_THROWDAMTYPE: case CNC_THROWOBJ: @@ -158,7 +158,7 @@ bool CCharNPC::r_WriteVal( CChar * pChar, lpctstr ptcKey, CSString & sVal ) sVal = pChar->GetDefStr(ptcKey, false, true); break; //return as decimal number or 0 if not set - //On these ones, check BaseDef if not found on dynamic + // On these, check BaseDef if not found on dynamic. case CNC_BONDED: sVal.FormatVal( m_bonded ); break; @@ -253,7 +253,7 @@ bool CCharNPC::IsVendor() const return ( (m_Brain == NPCBRAIN_HEALER) || (m_Brain == NPCBRAIN_BANKER) || (m_Brain == NPCBRAIN_VENDOR) || (m_Brain == NPCBRAIN_STABLE) ); } -int CCharNPC::GetNpcAiFlags( const CChar *pChar ) const +int CCharNPC::GetNpcAiFlags( const CChar *pChar ) const { CVarDefCont *pVar = pChar->GetKey("OVERRIDE.NPCAI", true ); if (pVar != nullptr) @@ -269,8 +269,8 @@ void CChar::NPC_LoadScript( bool fRestock ) { // Set a default brain type til we get the real one from scripts. // should have a default brain. watch out for override vendor. - SetNPCBrain(GetNPCBrainAuto()); - } + SetNPCBrain(GetNPCBrainAuto()); + } CCharBase * pCharDef = Char_GetDef(); @@ -289,7 +289,7 @@ void CChar::NPC_LoadScript( bool fRestock ) if ( fRestock && IsTrigUsed(TRIGGER_NPCRESTOCK) ) pChar->ReadScriptReducedTrig(pCharDef, CTRIG_NPCRestock); } - CreateNewCharCheck(); //This one is giving stats, etc to the char, so we can read/set them in the next triggers. + CreateNewCharCheck(); // This one is giving stats, etc. to the char, so we can read/set them in the next triggers. } // @Create trigger, NPC version @@ -303,7 +303,7 @@ void CChar::NPC_CreateTrigger() TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; lpctstr pszTrigName = "@Create"; - CTRIG_TYPE iAction = (CTRIG_TYPE)FindTableSorted(pszTrigName, sm_szTrigName, ARRAY_COUNT(sm_szTrigName) - 1); + CTRIG_TYPE iAction = (CTRIG_TYPE)FindTableSorted(pszTrigName, sm_szTrigName, ARRAY_COUNT(sm_szTrigName) - 1); // 2) TEVENTS for (size_t i = 0; i < pCharDef->m_TEvents.size(); ++i) diff --git a/src/game/chars/CCharNPC.h b/src/game/chars/CCharNPC.h index 94ce26bb2..d3e21bb87 100644 --- a/src/game/chars/CCharNPC.h +++ b/src/game/chars/CCharNPC.h @@ -1,6 +1,6 @@ /** * @file CCharNPC.h -* +* */ #ifndef _INC_CCHARNPC_H @@ -30,13 +30,13 @@ class CCharNPC NPCBRAIN_TYPE m_Brain; // For NPCs: Number of the assigned basic AI block word m_Home_Dist_Wander; // Distance to allow to "wander". - byte m_Act_Motivation; // 0-100 (100=very greatly) how bad do i want to do the current action. + byte m_Act_Motivation; // 0-100 (100=very greatly) how bad do I want to do the current action. bool m_bonded; // Bonded pet int64 m_timeRestock; // when last restock happened in sell/buy container CResourceRefArray m_Speech; // Speech fragment list (other stuff we know): We respond to what we hear with this. - CResourceQty m_Need; // What items might i need/Desire ? (coded as resource scripts) ex "10 gold,20 logs" etc. + CResourceQty m_Need; // What items might I need/Desire ? (coded as resource scripts) ex "10 gold,20 logs" etc. short m_nextX[MAX_NPC_PATH_STORAGE_SIZE]; // array of X coords of the next step short m_nextY[MAX_NPC_PATH_STORAGE_SIZE]; // array of Y coords of the next step diff --git a/src/game/chars/CCharNPCAct.cpp b/src/game/chars/CCharNPCAct.cpp index 04df2bf60..4732de971 100644 --- a/src/game/chars/CCharNPCAct.cpp +++ b/src/game/chars/CCharNPCAct.cpp @@ -68,7 +68,7 @@ void CChar::Action_StartSpecial( CREID_TYPE id ) ASSERT(m_pNPC); // Take the special creature action. // lay egg, breath weapon (fire, lightning, acid, code, paralyze), - // create web, fire patch, fire ball, + // create web, fire patch, fireball, // steal, teleport, level drain, absorb magic, curse items, // rust items, stealing, charge, hiding, grab, regenerate, play dead. // Water = put out fire ! @@ -244,8 +244,8 @@ void CChar::NPC_ActStart_SpeakTo( CChar * pSrc ) { ADDTOCALLSTACK("CChar::NPC_ActStart_SpeakTo"); ASSERT(m_pNPC); - // My new action is that i am speaking to this person. - // Or just update the amount of time i will wait for this person. + // My new action is that I am speaking to this person. + // Or just update the amount of time I will wait for this person. m_Act_UID = pSrc->GetUID(); m_atTalk.m_dwWaitCount = 20; m_atTalk.m_dwHearUnknown = 0; @@ -267,7 +267,7 @@ void CChar::NPC_OnHear( lpctstr pszCmd, CChar * pSrc, bool fAllPets ) if ( NPC_OnHearPetCmd(pszCmd, pSrc, fAllPets) || !NPC_CanSpeak() ) return; - // What where we doing ? + // What were we doing ? // too busy to talk ? switch ( Skill_GetActive()) @@ -519,9 +519,9 @@ int CChar::NPC_WalkToPoint( bool fRun ) if ( ! CanMoveWalkTo(pMe, true, false, Dir )) { bool fClearedWay = false; - // Some object in my way that i could move ? Try to move it. + // Some object in my way that I could move ? Try to move it. if ( !Can(CAN_C_USEHANDS) || Can(CAN_C_STATUE) || IsStatFlag(STATF_DEAD|STATF_SLEEPING|STATF_FREEZE|STATF_STONE) ) - ; // i cannot use hands or i am frozen, so cannot move objects + ; // I cannot use hands, or I am frozen, so cannot move objects. else if ( (NPC_GetAiFlags() & NPC_AI_MOVEOBSTACLES) && (iInt > iRand) ) { int i; @@ -741,7 +741,7 @@ bool CChar::NPC_LookAtCharGuard( CChar * pChar, bool bFromTrigger ) if ( g_Cfg.m_fGuardsInstantKill || pChar->Skill_GetBase(SKILL_MAGERY) ) Spell_Teleport(pChar->GetTopPoint(), false, false); - // If we got intant kill guards enabled, allow the guards to swing immidiately + // If we got instant kill guards enabled, allow the guards to swing immediately. if ( g_Cfg.m_fGuardsInstantKill ) { pChar->Stat_SetVal(STAT_STR, 1); @@ -765,7 +765,7 @@ bool CChar::NPC_LookAtCharMonster( CChar * pChar ) // false = continue with any previous action. // motivation level = // 0 = not at all. - // 100 = definitly. + // 100 = definitely. // int iFoodLevel = Food_GetLevelPercent(); @@ -774,7 +774,7 @@ bool CChar::NPC_LookAtCharMonster( CChar * pChar ) if ( ! Noto_IsCriminal() && (iFoodLevel > 40) ) // Am I not evil ? return NPC_LookAtCharHuman( pChar ); - // Attack if i am stronger, if it's the same target i was attacking, or i'm just stupid. + // Attack if I am stronger, if it's the same target I was attacking, or I'm just stupid. int iActMotivation = NPC_GetAttackMotivation(pChar); if (iActMotivation <= 0) return false; @@ -783,7 +783,7 @@ bool CChar::NPC_LookAtCharMonster( CChar * pChar ) int iDist = GetTopDist3D( pChar ); if ( IsStatFlag( STATF_HIDDEN ) && ! NPC_FightMayCast() && (iDist > 1) ) - return false; // element of suprise. + return false; // element of surprise. if ( Fight_Attack( pChar ) == false ) return false; @@ -823,7 +823,7 @@ bool CChar::NPC_LookAtCharHuman( CChar * pChar ) { Speak(pChar->IsStatFlag(STATF_CRIMINAL) ? g_Cfg.GetDefaultMsg(DEFMSG_NPC_GENERIC_SEECRIM) : - g_Cfg.GetDefaultMsg(DEFMSG_NPC_GENERIC_SEEMONS)); // only speak if can really call the guards. + g_Cfg.GetDefaultMsg(DEFMSG_NPC_GENERIC_SEEMONS)); // only speak if I can really call the guards. } if (IsStatFlag(STATF_WAR)) return false; @@ -1007,9 +1007,9 @@ bool CChar::NPC_LookAtChar( CChar * pChar, int iDist ) ADDTOCALLSTACK("CChar::NPC_LookAtChar"); ASSERT(m_pNPC); // I see a char. - // Do I want to do something to this char (more that what i'm already doing ?) + // Do I want to do something to this char (more that what I'm already doing ?) // RETURN: - // true = yes i do want to take a new action. + // true = yes I do want to take a new action. // Don't call this function frequently, since CanSeeLOS is an expensive function (lot of calculations but // most importantly it has to read the UO files, and file I/O is slow) @@ -1058,7 +1058,7 @@ bool CChar::NPC_LookAtChar( CChar * pChar, int iDist ) } } } - + if (IsStatFlag(STATF_DEAD)) return false; @@ -1129,19 +1129,19 @@ bool CChar::NPC_LookAround( bool fForceCheckItems ) // Call the rand function once, since repeated calls can be expensive (and this function is called a LOT of times, if there are lots of active NPCs) const int iRand = g_Rand.Get16ValFast(g_Cfg.m_iMapViewRadar); const CPointMap& ptTop(GetTopPoint()); - + int iRange = std::min(GetVisualRange(), int(g_Cfg.m_iMapViewRadar)); int iRangeBlur = UO_MAP_VIEW_SIGHT; // If I can't move don't look too far. if ( Can(CAN_C_NONMOVER) || !Can(CAN_C_MOVEMENTCAPABLEMASK) || IsStatFlag(STATF_FREEZE|STATF_STONE) ) { - if ( !NPC_FightMayCast() ) // And i have no distance attack. + if ( !NPC_FightMayCast() ) // And I have no distance attack. iRange = iRangeBlur = 2; } else { - // I'm mobile. do basic check if i would move here first. + // I'm mobile. do basic check if I would move here first. if ( !NPC_CheckWalkHere(ptTop, m_pArea) ) { // I should move. Someone lit a fire under me. @@ -1232,12 +1232,12 @@ void CChar::NPC_Act_Wander() if ( Can(CAN_C_NONMOVER) ) return; - // Call the rand function once, since repeated calls can be expensive (and this function is called a LOT of times, if there are lots of active NPCs) + // Call the rand function once, since repeated calls can be expensive (and this function is called a LOT of times, if there are lots of active NPCs). const uint uiRand = uint32_t(g_Rand.Get16ValFast(100)); int iStopWandering = 0; if ( !(uiRand % (7u + (Stat_GetVal(STAT_DEX) / 30u))) ) - iStopWandering = 1; // i'm stopping to wander "for the dexterity". + iStopWandering = 1; // I'm stopping to wander "for the dexterity". const CVarDefCont* pTagOverride = GetKey("OVERRIDE.LOOKAROUNDCHANCE", true); uint uiLookAroundChance = pTagOverride @@ -1251,7 +1251,7 @@ void CChar::NPC_Act_Wander() // NPC_LookAround() is very expensive, so since NPC_Act_Wander is called every tick for every char with ACTION == NPCACT_WANDER, // don't look around every time. if ( NPC_LookAround() ) - iStopWandering = 2; // i'm stopping to wander because i have seen something interesting + iStopWandering = 2; // I'm stopping to wander because I have seen something interesting. } // Staggering Walk around. @@ -1314,14 +1314,14 @@ bool CChar::NPC_Act_Follow(bool fFlee, int maxDistance, bool fMoveAway) ADDTOCALLSTACK("CChar::NPC_Act_Follow"); ASSERT(m_pNPC); // Follow our target or owner (m_Act_UID), we may be fighting (m_Fight_Targ_UID). - // false = can't follow any more, give up. + // false = can't follow anymore, give up. if (Can(CAN_C_NONMOVER)) { /* If the NPC has the MT_NONMOVER flag we need to check if it is actually in combat, otherwise it will spam the attack because it constantly "forget" the character is attacking (See NPCAct_Fight method). - */ + */ if (!Fight_IsActive()) return false; else @@ -1334,12 +1334,12 @@ bool CChar::NPC_Act_Follow(bool fFlee, int maxDistance, bool fMoveAway) * Replaced the Fight_IsActive() check with a check on m_fight_targ_UID. * Red npcs usually never interact "in a peaceful way" with the players and thus m_act_UID is usually never set preventing the creature from fleeing and putting it in an immobile "state". * Fight_IsActive returns true if the character is actively fighting (using a combat skill) in this case it's false because of the NPC'sFleeing - * Action and thus it will never pass the Fight_IsActive() check + * Action, and thus it will never pass the Fight_IsActive() check */ //CChar * pChar = Fight_IsActive() ? m_Fight_Targ_UID.CharFind() : m_Act_UID.CharFind(); CChar* pChar = nullptr; - //If the NPC action is following somebody, directly assign the character from the m_Act_UID value. + //If the NPC action is following somebody, directly assign the character from the m_Act_UID value. if (Skill_GetActive() == NPCACT_FOLLOW_TARG) pChar = m_Act_UID.CharFind(); else if (Fight_IsActive() || Skill_GetActive() == NPCACT_FLEE) @@ -1348,7 +1348,7 @@ bool CChar::NPC_Act_Follow(bool fFlee, int maxDistance, bool fMoveAway) pChar = m_Act_UID.CharFind(); if (pChar == nullptr) { - // free to do as i wish ! + // free to do as I wish ! Skill_Start(SKILL_NONE); return false; } @@ -1505,8 +1505,8 @@ void CChar::NPC_Act_GoHome() if ( m_pNPC->m_Brain == NPCBRAIN_GUARD ) { - // Had to change this guards were still roaming the forests - // this goes hand in hand with the change that guards arent + // Had to change this. Guards were still roaming the forests + // this goes hand in hand with the change that guards aren't // called if the criminal makes it outside guarded territory. const CRegion * pAreaHome = m_ptHome.GetRegion( REGION_TYPE_AREA ); @@ -1525,7 +1525,7 @@ void CChar::NPC_Act_GoHome() { g_Log.Event( LOGL_WARN, "Guard 0%x '%s' has no guard post (%s)! Removing it.\n", (dword)GetUID(), GetName(), GetTopPoint().WriteUsed()); - // If we arent conjured and still got no valid home + // If we aren't conjured and still got no valid home // then set our status to conjured and take our life. if ( ! IsStatFlag(STATF_CONJURED)) { @@ -1597,7 +1597,7 @@ void CChar::NPC_Act_Looting() // We killed something, let's take a look on the corpse. // Or we find something interesting on ground // - // m_Act_UID = UID of the item/corpse that we trying to loot + // m_Act_UID = UID of the item/corpse that we're trying to loot if ( m_pArea->IsFlag(REGION_FLAG_SAFE|REGION_FLAG_GUARDED) ) return; @@ -1777,7 +1777,7 @@ bool CChar::NPC_Act_Food() int iSearchDistance = 2; CItem *pClosestFood = nullptr; int iClosestFood = 100; - + bool fSearchGrass = false; CItem *pCropItem = nullptr; @@ -1787,7 +1787,7 @@ bool CChar::NPC_Act_Food() for (CSObjContRec* pObjRec : *pPack) { CItem* pFood = static_cast(pObjRec); - // I have some food personaly, so no need to search for something + // I have some food personally, so no need to search for anything. if ( pFood->IsType(IT_FOOD) ) { if ( (uiEatAmount = Food_CanEat(pFood)) > 0 ) @@ -1889,7 +1889,7 @@ bool CChar::NPC_Act_Food() } else { - // no food around, but maybe i am ok with grass? Or shall I try to pick crops? + // no food around, but maybe I am ok with grass? Or shall I try to pick crops? const NPCBRAIN_TYPE brain = GetNPCBrainGroup(); if ( brain == NPCBRAIN_ANIMAL ) // animals eat grass always @@ -2147,7 +2147,7 @@ bool CChar::NPC_OnItemGive( CChar *pCharSrc, CItem *pItem ) if (Use_Item(pItem)) return true; else - return false; //If can't use item, return it inside player's backpack. + return false; // If I can't use item, return it inside player's backpack. } } @@ -2252,7 +2252,7 @@ void CChar::NPC_OnTickAction() return; } - + bool fSkillFight = false; if ( g_Cfg.IsSkillFlag( iSkillActive, SKF_SCRIPTED ) ) { @@ -2288,7 +2288,7 @@ void CChar::NPC_OnTickAction() // just remain hidden unless we find something new to do. if ( g_Rand.GetVal( Skill_GetBase(SKILL_HIDING))) break; - EXC_SET_BLOCK("idle: Hidding"); + EXC_SET_BLOCK("idle: Hiding"); NPC_Act_Idle(); break; @@ -2308,7 +2308,7 @@ void CChar::NPC_OnTickAction() case SKILL_CHIVALRY: case SKILL_SPELLWEAVING: EXC_SET_BLOCK("magic"); - NPC_Act_Fight(); // May be we can split Fight and Magic from here? + NPC_Act_Fight(); // Maybe we can split Fight and Magic from here? break; case NPCACT_GUARD_TARG: @@ -2383,7 +2383,7 @@ void CChar::NPC_OnTickAction() } EXC_SET_BLOCK("timer expired (NPC)"); - if ( _IsTimerExpired() && !fSkillFight) // If i'm fighting, i don't want to wait to start another swing + if ( _IsTimerExpired() && !fSkillFight) // If I'm fighting, I don't want to wait to start another swing { int32 timeout = (150-Stat_GetAdjusted(STAT_DEX))/2; timeout = maximum(timeout, 0); @@ -2417,16 +2417,16 @@ void CChar::NPC_Pathfinding() const int dist = ptLocal.GetDist(ptTarg); // If NPC_AI_ALWAYSINT is set, just make it as smart as possible. const int iInt = ( NPC_GetAiFlags() & NPC_AI_ALWAYSINT ) ? 300 : Stat_GetAdjusted(STAT_INT); - + // do we really need to find the path? if ( iInt < 30 ) // too dumb - return; + return; if ( m_pNPC->m_nextPt == ptTarg ) // we have path to that position already saved in m_NextX/Y - return; + return; if ( !ptTarg.IsValidPoint() ) // invalid point return; if (( ptTarg.m_x == ptLocal.m_x ) && ( ptTarg.m_y == ptLocal.m_y )) // same spot - return; + return; if ( ptTarg.m_map != ptLocal.m_map ) // cannot just move to another map return; if ( dist >= MAX_NPC_PATH_STORAGE_SIZE/2 ) // skip too far locations which should be too slow @@ -2510,7 +2510,7 @@ void CChar::NPC_Food() for (CSObjContRec* pObjRec : *pPack) { CItem* pFood = static_cast(pObjRec); - // i have some food personaly, so no need to search for something + // I have some food personally, so no need to search for anything. if ( pFood->IsType(IT_FOOD) ) { if ( (uiEatAmount = Food_CanEat(pFood)) > 0 ) @@ -2597,7 +2597,7 @@ void CChar::NPC_Food() } } } - // no food around, but maybe i am ok with grass? + // No food around, but maybe I am ok with grass? else { const NPCBRAIN_TYPE brain = GetNPCBrainGroup(); @@ -2613,7 +2613,7 @@ void CChar::NPC_Food() const CResourceID rid = CResourceID(RES_TYPEDEF, IT_GRASS); EXC_SET_BLOCK("searching grass"); - if ( pCharDef->m_FoodType.ContainsResourceID(rid) ) // do I accept grass as a food? + if ( pCharDef->m_FoodType.ContainsResourceID(rid) ) // do I accept grass as food? { CItem *pResBit = CWorldMap::CheckNaturalResource(ptMe, IT_GRASS, true, this); if ( pResBit && pResBit->GetAmount() && ( pResBit->GetTopPoint().m_z == iMyZ ) ) @@ -2714,7 +2714,7 @@ void CChar::NPC_ExtraAI() return; } - // Equip lightsource at night time + // Equip light source at nighttime. EXC_SET_BLOCK("light source"); const CPointMap& pt = GetTopPoint(); const CSector *pSector = pt.GetSector(); diff --git a/src/game/chars/CCharNPCStatus.cpp b/src/game/chars/CCharNPCStatus.cpp index 4d3724838..d33505b94 100644 --- a/src/game/chars/CCharNPCStatus.cpp +++ b/src/game/chars/CCharNPCStatus.cpp @@ -432,7 +432,7 @@ bool CChar::NPC_FightMayCast(bool fCheckSkill) const // This NPC could cast spells if they wanted to? // Check mana, anti-magic and tag.noCastTill. // Don't check for skill if !fCheckSkill. - + //Don't cast the spell if tag.NPCNoCastTill is > than CurrentTime. if (GetKeyNum("NPCNoCastTill") > (CWorldGameTime::GetCurrentTime().GetTimeRaw() / MSECS_PER_TENTH)) return false; @@ -472,7 +472,7 @@ CChar * CChar::NPC_PetGetOwner() const { ADDTOCALLSTACK("CChar::NPC_PetGetOwner"); ASSERT(m_pNPC); - // Assume i am a pet. Get my owner. not just friends. used for blame. + // Assume I am a pet. Get my owner. not just friends. used for blame. if ( !IsStatFlag(STATF_PET) ) return nullptr; @@ -488,7 +488,7 @@ CChar * CChar::NPC_PetGetOwnerRecursive() const { ADDTOCALLSTACK("CChar::NPC_PetGetOwnerRecursive"); ASSERT(m_pNPC); - // Assume i am a pet. Get the primary owner (the owner of the owner of my owner and so on). + // Assume I am a pet. Get the primary owner (the owner of the owner of my owner and so on). static int iReentrantCheck_PetGetOwnerRecursive = 0; @@ -515,7 +515,7 @@ ushort CChar::NPC_GetTrainMax( const CChar * pStudent, SKILL_TYPE Skill ) const { ADDTOCALLSTACK("CChar::NPC_GetTrainMax"); ASSERT(m_pNPC); - + // What is the max I can train to ? ushort uiMax; ushort uiMaxAllowed; @@ -556,7 +556,7 @@ bool CChar::NPC_CheckWalkHere( const CPointMap & pt, const CRegion * pArea ) con /* if (IsSetOF(OF_GuardOutsideGuardedArea)) { - // I come from a guarded area, so i don't want to leave it unprotected; otherwise, i don't care if my destination region is guarded or not + // I come from a guarded area, so I don't want to leave it unprotected; otherwise, I don't care if my destination region is guarded or not const CRegion * pAreaHome = m_ptHome.GetRegion( REGION_TYPE_AREA ); if (pAreaHome->IsGuarded() && !pArea->IsGuarded()) return false; @@ -633,8 +633,8 @@ int CChar::NPC_WantThisItem( CItem * pItem ) const // May not want to use it but rather just put it in my pack. // // NOTE: - // Don't check if i can see this or i can reach it. - // Don't check if i can carry this ? + // Don't check if I can see this, or I can reach it. + // Don't check if I can carry this ? // // RETURN: // 0-100 percent = how bad do we want it ? @@ -672,7 +672,7 @@ int CChar::NPC_GetWeaponUseScore( CItem * pWeapon ) { ADDTOCALLSTACK("CChar::NPC_GetWeaponUseScore"); ASSERT(m_pNPC); - // How good would i be at this weapon ? + // How good would I be at this weapon ? SKILL_TYPE skill; @@ -688,7 +688,7 @@ int CChar::NPC_GetWeaponUseScore( CItem * pWeapon ) // I can't equip this anyhow. if ( CanEquipLayer( pWeapon, LAYER_QTY, nullptr, true ) == LAYER_NONE ) return 0; - // How much damage could i do with this ? + // How much damage could I do with this ? } int iDmg = Fight_CalcDamage( pWeapon ); @@ -704,15 +704,15 @@ int CChar::NPC_GetHostilityLevelToward( const CChar * pCharTarg ) const // What is my general hostility level toward this type of creature ? // // based on: - // npc vs player, (evil npc's don't like players regurdless of align, except in town) + // npc vs player, (evil npc's don't like players regardless of align, except in town) // karma (we are of different alignments) // creature body type. (allie groups) // hunger, (they could be food) // memories of this creature. // // DO NOT consider: - // strength, he is far stronger or waeker than me. - // health, i may be near death. + // strength, he is far stronger or weaker than me. + // health, I may be near death. // location (guarded area), (xcept in the case that evil people like other evils in town) // loot, etc. // @@ -749,22 +749,22 @@ int CChar::NPC_GetHostilityLevelToward( const CChar * pCharTarg ) const bool fDoMemBase = false; - if ( Noto_IsEvil() && // i am evil. + if ( Noto_IsEvil() && // I am evil. (m_pArea && !m_pArea->IsGuarded()) && // we are not in an evil town. pCharTarg->m_pPlayer ) // my target is a player. { - // If i'm evil i give no benefit to players with bad karma. + // If I'm evil I give no benefit to players with bad karma. // I hate all players. - // Unless i'm in a guarded area. then they are cool. + // Unless I'm in a guarded area. then they are cool. iHostility = 51; } - else if ( m_pNPC->m_Brain == NPCBRAIN_BERSERK ) // i'm beserk. + else if ( m_pNPC->m_Brain == NPCBRAIN_BERSERK ) // I'm berserk. { - // beserks just hate everyone all the time. + // Berserk just hate everyone all the time. iHostility = 100; } else if ( pCharTarg->m_pNPC && // my target is an NPC - pCharTarg->m_pNPC->m_Brain != NPCBRAIN_BERSERK && // ok to hate beserks. + pCharTarg->m_pNPC->m_Brain != NPCBRAIN_BERSERK && // Ok to hate berserk. ! g_Cfg.m_fMonsterFight ) // monsters are not supposed to fight other monsters ! { iHostility = -50; @@ -772,7 +772,7 @@ int CChar::NPC_GetHostilityLevelToward( const CChar * pCharTarg ) const } else { - // base hostillity on karma diff. + // Base hostility on karma diff. int iKarmaTarg = pCharTarg->GetKarma(); if ( Noto_IsEvil()) @@ -798,7 +798,7 @@ int CChar::NPC_GetHostilityLevelToward( const CChar * pCharTarg ) const { if ( pCharTarg->m_pNPC ) { - // Human NPC's will attack humans . + // Human NPC's will attack humans. if ( GetDispID() == pCharTarg->GetDispID()) { @@ -817,7 +817,7 @@ int CChar::NPC_GetHostilityLevelToward( const CChar * pCharTarg ) const } else { - // Not immediately hostile if looks the same as me. + // Not immediately hostile if it looks the same as me. if ( ! IsPlayableCharacter() && NPC_GetAllyGroupType( GetDispID()) == NPC_GetAllyGroupType(pCharTarg->GetDispID())) { iHostility -= 51; @@ -841,14 +841,14 @@ int CChar::NPC_GetAttackContinueMotivation( CChar * pChar, int iMotivation ) con ADDTOCALLSTACK("CChar::NPC_GetAttackContinueMotivation"); ASSERT(m_pNPC); // I have seen fit to attack them. - // How much do i want to continue an existing fight ? cowardice ? + // How much do I want to continue an existing fight ? cowardice ? // ARGS: // iMotivation = My base motivation toward this creature. // // RETURN: // -101 = ? dead meat. (run away) // - // 0 = I'm have no interest. + // 0 = I have no interest. // 50 = even match. // 100 = he's a push over. if ( !pChar ) @@ -892,7 +892,7 @@ int CChar::NPC_GetAttackMotivation( CChar * pChar, int iMotivation ) const // Take into consideration AC, health, skills, etc.. // RETURN: // < 0 = dead meat. (run away) - // 0 = I'm have no interest. + // 0 = I have no interest. // 50 = even match. // 100 = he's a push over. @@ -903,10 +903,10 @@ int CChar::NPC_GetAttackMotivation( CChar * pChar, int iMotivation ) const if ( pChar->m_pArea->IsFlag(REGION_FLAG_SAFE) ) return 0; - if (Fight_IsActive() && (m_Fight_Targ_UID == pChar->GetUID())) // Am i attacking the same target as before? + if (Fight_IsActive() && (m_Fight_Targ_UID == pChar->GetUID())) // Am I attacking the same target as before? { - // Was i told to attack by my master? This is the only hardcoded case in which we have such an high threat value. - // In this case, i want that my pet attacks the target without doing any other consideration. + // Was I told to attack by my master? This is the only hardcoded case in which we have such a high threat value. + // In this case, I want that my pet attacks the target without doing any other consideration. const int iCharID = Attacker_GetID(pChar); ASSERT(iCharID != -1); if (Attacker_GetThreat(iCharID) >= ATTACKER_THREAT_TOLDBYMASTER) diff --git a/src/game/chars/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index c1142ba98..e4d20ea7b 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -25,7 +25,7 @@ SKILL_TYPE CChar::Skill_GetBest( uint iRank ) const { ADDTOCALLSTACK("CChar::Skill_GetBest"); - // Get the top n best skills. + // Get the top and best skills. if ( iRank >= g_Cfg.m_iMaxSkill ) iRank = 0; @@ -523,7 +523,7 @@ bool CChar::Skill_CheckSuccess( SKILL_TYPE skill, int iDifficulty, bool fUseBell // RETURN: // true = success in skill. - if ( IsPriv(PRIV_GM) && skill != SKILL_PARRYING ) // GM's can't always succeed Parrying or they won't receive any damage on combat even without STATF_Invul set + if ( IsPriv(PRIV_GM) && skill != SKILL_PARRYING ) // GM's can't always succeed Parrying, or they won't receive any damage on combat even without STATF_Invul set return true; if ( !IsSkillBase(skill) || (iDifficulty < 0) ) // auto failure. @@ -845,7 +845,7 @@ bool CChar::Skill_MakeItem_Success() SysMessage(pszMsg); } - // Experience gain on craftings + // Experience gains on crafting. if ( g_Cfg.m_fExperienceSystem && (g_Cfg.m_iExperienceMode & EXP_MODE_RAISE_CRAFT) ) { int exp = 0; @@ -886,7 +886,7 @@ bool CChar::Skill_MakeItem( ITEMID_TYPE id, CUID uidTarg, SKTRIG_TYPE stage, boo // Fail = do a partial consume of the resources. // // ARGS: - // uidTarg = item targetted to try to make this . (this item should be used to make somehow) + // uidTarg = item targeted to try to make this (this item should be used to make somehow). // Skill_GetActive() // // RETURN: @@ -957,7 +957,7 @@ bool CChar::Skill_MakeItem( ITEMID_TYPE id, CUID uidTarg, SKTRIG_TYPE stage, boo return false; } - m_Act_UID = uidTarg; // targetted item to start the make process + m_Act_UID = uidTarg; // Targeted item to start the make process. m_atCreate.m_iItemID = id; m_atCreate.m_dwAmount = (word)(iReplicationQty); @@ -967,7 +967,7 @@ bool CChar::Skill_MakeItem( ITEMID_TYPE id, CUID uidTarg, SKTRIG_TYPE stage, boo if ( stage == SKTRIG_SUCCESS ) { - m_atCreate.m_dwAmount = (word)(iReplicationQty); // how much resources we really consumed + m_atCreate.m_dwAmount = (word)(iReplicationQty); // how many resources we really consumed return Skill_MakeItem_Success(); } @@ -1008,7 +1008,7 @@ CItem * CChar::Skill_NaturalResource_Create( CItem * pResBit, SKILL_TYPE skill ) if ( !pOreDef ) return nullptr; - // Skill effects how much of the ore i can get all at once. + // Skill effects how much of the ore I can get all at once. if ( pOreDef->m_ReapItem == ITEMID_NOTHING ) return nullptr; // I intended for there to be nothing here @@ -1043,7 +1043,7 @@ CItem * CChar::Skill_NaturalResource_Create( CItem * pResBit, SKILL_TYPE skill ) //Creating the 'id' variable with the local given through->by the trigger(s) instead on top of method ITEMID_TYPE id = (ITEMID_TYPE)(ResGetIndex((dword)pScriptArgs->m_VarsLocal.GetKeyNum("ResourceID"))); - wAmount = pResBit->ConsumeAmount( (word)(pScriptArgs->m_iN1) ); // amount i used up. + wAmount = pResBit->ConsumeAmount( (word)(pScriptArgs->m_iN1) ); // amount I used up. if ( wAmount <= 0 ) return nullptr; @@ -1147,14 +1147,14 @@ bool CChar::Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ) if ( pOreDef->IsType( IT_ORE )) { ITEMID_TYPE idIngot = (ITEMID_TYPE)(ResGetIndex( pOreDef->m_ttOre.m_idIngot)); - const CItemBase* pBaseDef = CItemBase::FindItemBase(idIngot); //Usually a lingot, but could be a a gem also. + const CItemBase* pBaseDef = CItemBase::FindItemBase(idIngot); //Usually a lingot, but could be a gem also. if (!pBaseDef) { SysMessageDefault(DEFMSG_MINING_NOTHING); return false; } iResourceQty = 1; // ingots per ore. - iResourceTotalQty = 1; //Ores only gives one type of resouce. + iResourceTotalQty = 1; // Ores only gives one type of resource. pScriptArgs->m_iN2 = iResourceTotalQty; pScriptArgs->m_VarsLocal.SetNum("resource.0.ID", pBaseDef->GetID()); pScriptArgs->m_VarsLocal.SetNum("resource.0.amount", iResourceQty); @@ -1235,7 +1235,7 @@ bool CChar::Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ) if (iMiningSkill < pBaseDef->m_ttIngot.m_iSkillMin && !fSkipMiningSmeltReq) { SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_MINING_SKILL), pBaseDef->GetName()); - if (iResourceTotalQty > 1) // This is a niche scenario where an item can provide more than one type ingots, so we continue to loop for because we can successfull get the other type of lingots. + if (iResourceTotalQty > 1) // This is a niche scenario where an item can provide more than one type ingots, so we continue to loop for because we can successfully get the other type of lingots. continue; return false; } @@ -1250,11 +1250,11 @@ bool CChar::Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ) word iAmountLost = (word)(g_Rand.GetVal(pItemOre->GetAmount() / 2) + 1); pItemOre->ConsumeAmount(iAmountLost); // lose up to half the resources. iOreQty -= iAmountLost; - if ( iResourceTotalQty > 1 ) // This is a niche scenario where an item can provide more than one type ingots, so we continue to loop for because we can successfull get the other type of lingots. + if ( iResourceTotalQty > 1 ) // This is a niche scenario where an item can provide more than one type ingots, so we continue to loop for because we can successfully get the other type of lingots. continue; return false; } - // Payoff - Amount of ingots i get. + // Payoff - Amount of ingots I get. ingots.emplace_back(CItem::CreateScript(pBaseDef->GetID(), this)); if (ingots.at(i) == nullptr) { @@ -1349,7 +1349,7 @@ int CChar::Skill_Tracking( SKTRIG_TYPE stage ) { ADDTOCALLSTACK("CChar::Skill_Tracking"); // SKILL_TRACKING - // m_Act_UID = what am i tracking ? + // m_Act_UID = what am I tracking ? // m_atTracking.m_iPrvDir = the previous dir it was in. // m_atTracking.m_dwDistMax = the maximum tracking distance. @@ -1486,7 +1486,7 @@ int CChar::Skill_Fishing( SKTRIG_TYPE stage ) // SKILL_FISHING // m_Act_p = where to fish. // NOTE: don't check LOS else you can't fish off boats. - // Check that we dont stand too far away + // Check that we are not standing too far away // Make sure we aren't in a house // // RETURN: @@ -1772,7 +1772,7 @@ int CChar::Skill_Cartography( SKTRIG_TYPE stage ) int CChar::Skill_Musicianship( SKTRIG_TYPE stage ) { ADDTOCALLSTACK("CChar::Skill_Musicianship"); - // m_Act_UID = the intrument i targetted to play. + // m_Act_UID = the instrument i targeted to play. if ( stage == SKTRIG_ABORT ) return -SKTRIG_ABORT; @@ -1806,7 +1806,7 @@ int CChar::Skill_Peacemaking( SKTRIG_TYPE stage ) { case SKTRIG_START: { - // ACTARG1: UID of the instrument i want to play, instead of picking a random one + // ACTARG1: UID of the instrument I want to play, instead of picking a random one CItem * pInstrument = nullptr; if (m_atBard.m_dwInstrumentUID != 0) { @@ -1919,7 +1919,7 @@ int CChar::Skill_Enticement( SKTRIG_TYPE stage ) { case SKTRIG_START: { - // ACTARG1: UID of the instrument i want to play, instead of picking a random one + // ACTARG1: UID of the instrument I want to play, instead of picking a random one CItem * pInstrument = nullptr; if (m_atBard.m_dwInstrumentUID != 0) { @@ -2035,7 +2035,7 @@ int CChar::Skill_Provocation(SKTRIG_TYPE stage) { case SKTRIG_START: { - // ACTARG1: UID of the instrument i want to play, instead of picking a random one + // ACTARG1: UID of the instrument I want to play, instead of picking a random one CItem * pInstrument = nullptr; if (m_atBard.m_dwInstrumentUID != 0) { @@ -2112,7 +2112,7 @@ int CChar::Skill_Provocation(SKTRIG_TYPE stage) iMaxRange = UO_MAP_VIEW_SIGHT; } - // If out of range we might get attacked ourself. + // If out of range we might get attacked ourselves. if ((pCharProv->GetTopDist3D(pCharTarg) > iMaxRange) || (pCharProv->GetTopDist3D(this) > iMaxRange)) { // Check that only "evil" monsters attack provoker back @@ -2122,7 +2122,7 @@ int CChar::Skill_Provocation(SKTRIG_TYPE stage) return -SKTRIG_ABORT; } - // If the npcs are in the same ally groups, both can attack you + // If the npcs are in the same groups, both can attack you. if ( m_atProvocation.m_dwIsAlly != 0 ) { if ( pCharProv->Noto_IsEvil() ) @@ -2309,7 +2309,7 @@ int CChar::Skill_Taming( SKTRIG_TYPE stage ) ASSERT( pChar->m_pNPC ); int iTameBase = pChar->Skill_GetBase(SKILL_TAMING); - if ( !IsPriv( PRIV_GM )) // if its a gm doing it, just check that its not + if ( !IsPriv( PRIV_GM )) // if it's a gm doing it, just check that it's not { if ( pChar->IsStatFlag( STATF_PET )) // is it tamable ? { @@ -2475,7 +2475,7 @@ int CChar::Skill_Hiding( SKTRIG_TYPE stage ) if ( stage == SKTRIG_ABORT ) return -SKTRIG_ABORT; - if ( stage == SKTRIG_STROKE ) // we shoud just stay in HIDING skill ? + if ( stage == SKTRIG_STROKE ) // We should just stay in HIDING skill? return 0; if ( stage == SKTRIG_FAIL ) @@ -2929,7 +2929,7 @@ int CChar::Skill_RemoveTrap( SKTRIG_TYPE stage ) int CChar::Skill_Begging( SKTRIG_TYPE stage ) { ADDTOCALLSTACK("CChar::Skill_Begging"); - // m_Act_UID = Our begging target.. + // m_Act_UID = Our begging target. CChar * pChar = m_Act_UID.CharFind(); if ( pChar == nullptr || pChar == this ) @@ -3024,7 +3024,7 @@ int CChar::Skill_Fighting( SKTRIG_TYPE stage ) if ( stage == SKTRIG_START ) { /* - The two lines below need to be moved after the @SkillStart/@Start triggers, otherwise if we are using a custom combat system + The two lines below need to be moved after the @SkillStart/@Start triggers, otherwise if we are using a custom combat system, and we are forcing a miss (actdiff < 0) combat will be blocked because it will not pass the @SkillStart/@Start triggers check. */ //m_atFight.m_iWarSwingState = WAR_SWING_EQUIPPING; @@ -3057,7 +3057,7 @@ int CChar::Skill_Fighting( SKTRIG_TYPE stage ) { /*Super cheap fix : When we are casting the SUMMON CREATURE spell while we are in an active combat (we have an active fighting skill going on) - resetting both the RecoilDelay and the SwingAnimationDelay will also cause the ID of the summoned creatured to be resetted. + resetting both the RecoilDelay and the SwingAnimationDelay will also cause the ID of the summoned created to reset. This only happens when the creature to be summoned is chosen on the default "summon menu". */ if ( !m_atMagery.m_uiSummonID ) @@ -3482,7 +3482,7 @@ int CChar::Skill_Act_Training( SKTRIG_TYPE stage ) { ADDTOCALLSTACK("CChar::Skill_Act_Training"); // NPCACT_TRAINING - // finished some traing maneuver. + // Finished some training maneuver. if ( stage == SKTRIG_ABORT ) return -SKTRIG_ABORT; @@ -4327,7 +4327,7 @@ int CChar::Skill_Stealing(SKTRIG_TYPE stage) if (pItem->GetParent() != pPack && pPack) { pItem->RemoveFromView(); - // Put in my invent. + // Put in my invention. pPack->ContentAdd(pItem); } } @@ -4348,7 +4348,7 @@ int CChar::Skill_Stealing(SKTRIG_TYPE stage) } /* -The Focus skill is used passively and it works automatically only if FEATURES_AOS_UPDATE_B is enabled. +The Focus skill is used passively, and it works automatically only if FEATURES_AOS_UPDATE_B is enabled. The skill increase the amount of stamina gained by 1 for each 10% points of Focus and increase the amount of mana by 1 for each 20% points of Focus. The Skill_Focus method is called from Stats_Regen64 method found in CCharStat.cpp @@ -4385,9 +4385,9 @@ int CChar::Skill_Focus(STAT_TYPE stat) bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) { ADDTOCALLSTACK("CChar::Skill_Start"); - // We have all the info we need to do the skill. (targeting etc) + // We have all the info we need to do the skill. (targeting etc.) // Set up how long we have to wait before we get the desired results from this skill. - // Set up any animations/sounds in the mean time. + // Set up any animations/sounds in the meantime. // Calc if we will succeed or fail. // RETURN: // false = failed outright with no wait. "You have no chance of taming this" @@ -4427,7 +4427,7 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) anim = Skill_GetAnim(skill); } - // Some skill can start right away. Need no targetting. + // Some skill can start right away. Need no targeting. // 0-100 scale of Difficulty if ( IsTrigUsed(TRIGGER_SKILLPRESTART) ) { @@ -4529,7 +4529,7 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) if (_IsTimerExpired()) { - _SetTimeoutD(1); // the skill should have set it's own delay!? + _SetTimeoutD(1); // the skill should have set its own delay!? } if ( fCraftSkill ) @@ -4572,7 +4572,7 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) } } - // emote the action i am taking. + // emote the action I am taking. if ( (g_Cfg.m_iDebugFlags & DEBUGF_NPC_EMOTE) || IsStatFlag(STATF_EMOTEACTION) ) Emote(Skill_GetName(true)); diff --git a/src/sphere.ini b/src/sphere.ini index f2cf06b75..eefe7300d 100644 --- a/src/sphere.ini +++ b/src/sphere.ini @@ -87,8 +87,8 @@ Log=logs/ //MapX = Sets X to the given number, so .map X will send work over these settings //MaxX = Sets Maximum X value for this map's size. Same for MaxY. //SectorSize = Sets the size of sectors, in each server's tick all sectors for all maps are read in a loop, this should be taken in consideration for performance along with SectorSleep. Default: 64 (8 x 8 tiles). -//MapReadID = Sets the mapX file to read from muls folder (This means which file will server read for walkchecks). -//MapSendID = Sets the mapX file to read for the client (this can be different than MapReadID but, obviously, not recomended for normal usage). +//MapReadID = Sets the mapX file to read from muls folder (This means which file will server read for walk checks). +//MapSendID = Sets the mapX file to read for the client (this can be different than MapReadID but, obviously, not recommended for normal usage). // Map of Felucca //Map0=6144,4096,-1,0,0 //Old size @@ -155,8 +155,8 @@ SaveStepMaxComplexity=500 //Code for servers account application process // 0=Closed, // Closed. Not accepting more. // 2=Free, // Anyone can just log in and create a full account. -// 3=GuestAuto, // You get to be a guest and are automatically sent email with u're new password. -// 4=GuestTrial, // You get to be a guest til u're accepted for full by an Admin. +// 3=GuestAuto, // You get to be a guest and are automatically sent email with your new password. +// 4=GuestTrial, // You get to be a guest until you are accepted for full by an Admin. // 6=Unspecified, // Not specified. // To enable auto account you must set this to 2 AccApp=0 @@ -380,8 +380,7 @@ PayFromPackOnly=0 // Disable weather effects? NoWeather=1 -// Set to 1 for items to keep their attr_newbie flag -// when item is transfered to an NPC. +// Set to 1 for items to keep their attr_newbie flag when item is transferred to an NPC. //AllowNewbTransfer=0 // Default light level in dungeons From 9171ea392358426ab293ad690c144ea6c72fe510 Mon Sep 17 00:00:00 2001 From: David Kindl Date: Mon, 26 May 2025 16:27:03 +0200 Subject: [PATCH 035/112] Make links secure (https) --- src/game/CServerDef.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/CServerDef.cpp b/src/game/CServerDef.cpp index 9c698140e..911126e73 100644 --- a/src/game/CServerDef.cpp +++ b/src/game/CServerDef.cpp @@ -498,7 +498,7 @@ bool CServerDef::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc sVal = GetName(); break; } - sVal.Format("%s", static_cast(m_sURL), GetName()); + sVal.Format("%s", static_cast(m_sURL), GetName()); break; case SC_VERSION: sVal = SPHERE_BUILD_INFO_STR; From cc5079cc852b54f174d95004f6168dca73a88491 Mon Sep 17 00:00:00 2001 From: David Kindl Date: Mon, 26 May 2025 16:55:47 +0200 Subject: [PATCH 036/112] Doxygen config file update (1.8.6 -> 1.14.0) - moved generated path to docs/doxygen --- src/doxygen.cfg | 1591 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 1108 insertions(+), 483 deletions(-) diff --git a/src/doxygen.cfg b/src/doxygen.cfg index 335b26a59..19edfb0cc 100644 --- a/src/doxygen.cfg +++ b/src/doxygen.cfg @@ -1,7 +1,7 @@ -# Doxyfile 1.8.6 +# Doxyfile 1.14.0 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. +# Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. @@ -12,16 +12,26 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use Doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use Doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -32,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "SphereServer" +PROJECT_NAME = "SphereServer X" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -41,58 +51,84 @@ PROJECT_NAME = "SphereServer" PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a +# for a project that appears at the top of each page and should give viewers a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = "Ultima Online game server, developed in C++." -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. PROJECT_LOGO = +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If +# entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = doc +OUTPUT_DIRECTORY = ../docs/doxygen -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding Doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise cause +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = YES +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this +# documentation generated by Doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -113,13 +149,13 @@ REPEAT_BRIEF = YES ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief +# Doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. @@ -127,7 +163,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -137,11 +173,11 @@ FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to +# If left blank the directory from which Doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. +# will be relative from the directory where Doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = @@ -155,31 +191,42 @@ STRIP_FROM_PATH = STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't +# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but +# less readable) file names. This can be useful if your file system doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the +# first line (until the first dot, question mark or exclamation mark) of a +# Javadoc-style comment as the brief description. If set to NO, the Javadoc- +# style will behave just like regular Qt-style comments (thus requiring an +# explicit @brief command for a brief description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) +# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by Doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first +# line (until the first dot, question mark or exclamation mark) of a Qt-style +# comment as the brief description. If set to NO, the Qt-style will behave just +# like regular Qt-style comments (thus requiring an explicit \brief command for +# a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this @@ -191,15 +238,23 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and Doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# Doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as Doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO @@ -214,20 +269,19 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -256,46 +310,90 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make Doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by Doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by Doxygen, so you can +# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES -# When enabled doxygen tries to link words that correspond to documented +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 6. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 6 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled Doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. Words listed in the +# AUTOLINK_IGNORE_WORDS tag are excluded from automatic linking. # The default value is: YES. AUTOLINK_SUPPORT = YES +# This tag specifies a list of words that, when matching the start of a word in +# the documentation, will suppress auto links generation, if it is enabled via +# AUTOLINK_SUPPORT. This list does not affect links explicitly created using \# +# or the \link or commands. +# This tag requires that the tag AUTOLINK_SUPPORT is set to YES. + +AUTOLINK_IGNORE_WORDS = + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and +# tag to YES in order to let Doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES @@ -307,16 +405,16 @@ BUILTIN_STL_SUPPORT = YES CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. +# Doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. @@ -325,13 +423,20 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then Doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -376,21 +481,42 @@ TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The +# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# Doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest +# symbols. At the end of a run Doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use +# during processing. When set to 0 Doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -400,35 +526,41 @@ LOOKUP_CACHE_SIZE = 0 EXTRACT_ALL = YES -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. @@ -443,7 +575,14 @@ EXTRACT_LOCAL_METHODS = YES EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. @@ -451,23 +590,32 @@ EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. +# If the HIDE_UNDOC_NAMESPACES tag is set to YES, Doxygen will hide all +# undocumented namespaces that are normally visible in the namespace hierarchy. +# If set to NO, these namespaces will be included in the various overviews. This +# option has no effect if EXTRACT_ALL is enabled. +# The default value is: YES. + +HIDE_UNDOC_NAMESPACES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -480,23 +628,44 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and macOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = YES -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -509,7 +678,7 @@ SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. @@ -521,22 +690,22 @@ FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that +# name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. @@ -548,7 +717,7 @@ SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. @@ -565,37 +734,35 @@ SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. @@ -620,8 +787,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES @@ -641,24 +808,25 @@ SHOW_FILES = YES SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from +# Doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file +# by Doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated +# by Doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can +# that represents Doxygen's defaults, run Doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# Note that if you run Doxygen from a directory containing a file called +# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = @@ -666,27 +834,42 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. +# search path. See also \cite for info how to create references. CITE_BIB_FILES = +# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH +# environment variable) so that external tools such as latex and gs can be +# found. +# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the +# path already specified by the PATH variable, and are added in the order +# specified. +# Note: This option is particularly useful for macOS version 14 (Sonoma) and +# higher, when running Doxygen from Doxywizard, because in this case any user- +# defined changes to the PATH are ignored. A typical example on macOS is to set +# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin +# together with the standard path, the full search path used by doxygen when +# launching external tools will then become +# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +EXTERNAL_TOOL_PATH = + #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the +# standard output by Doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -694,42 +877,97 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete +# function parameter documentation. If set to NO, Doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, Doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about +# undocumented enumeration values. If set to NO, Doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If WARN_LAYOUT_FILE option is set to YES, Doxygen will warn about issues found +# while parsing the user defined layout file, such as missing or wrong elements. +# See also LAYOUT_FILE for details. If set to NO, problems with the layout file +# will be suppressed. +# The default value is: YES. + +WARN_LAYOUT_FILE = YES + +# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the Doxygen process Doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of Doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -740,28 +978,48 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses. The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by Doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be +# provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = @@ -775,7 +1033,7 @@ RECURSIVE = YES # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # -# Note that relative paths are relative to the directory from which doxygen is +# Note that relative paths are relative to the directory from which Doxygen is # run. EXCLUDE = @@ -800,10 +1058,7 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = @@ -833,7 +1088,7 @@ EXAMPLE_RECURSIVE = NO IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should +# The INPUT_FILTER tag can be used to specify a program that Doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # @@ -847,6 +1102,15 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that Doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by Doxygen. INPUT_FILTER = @@ -856,11 +1120,15 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by Doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. @@ -877,10 +1145,28 @@ FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. +# and want to reuse the introduction page also for the Doxygen output. USE_MDFILE_AS_MAINPAGE = +# If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- +# directories of the project's root, is used as the documentation for that sub- +# directory, except when the README.md starts with a \dir, \page or \mainpage +# command. If set to NO, the README.md file needs to start with an explicit \dir +# command in order to be used as directory documentation. +# The default value is: YES. + +IMPLICIT_DIR_DOCS = YES + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -895,12 +1181,13 @@ USE_MDFILE_AS_MAINPAGE = SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. @@ -908,7 +1195,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -920,7 +1207,7 @@ REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -938,28 +1225,28 @@ REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# point to the HTML generated by the htags(1) tool instead of Doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # -# The result: instead of the source browser generated by doxygen, the links to +# The result: instead of the source browser generated by Doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. @@ -967,6 +1254,46 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which Doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not Doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then Doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by Doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not Doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -978,17 +1305,11 @@ VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -997,7 +1318,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1018,40 +1339,40 @@ HTML_OUTPUT = html HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a +# each generated HTML page. If the tag is left blank Doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. +# that Doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally +# for information on how to generate the default header that Doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description +# default header when upgrading to a newer version of Doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard +# generated HTML page. If the tag is left blank Doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. +# that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. +# the HTML output. If left blank Doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. +# sheet that Doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. @@ -1059,13 +1380,20 @@ HTML_FOOTER = HTML_STYLESHEET = -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by Doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1080,10 +1408,23 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1092,7 +1433,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1110,13 +1451,16 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_TIMESTAMP = YES +HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the @@ -1126,6 +1470,33 @@ HTML_TIMESTAMP = YES HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1141,13 +1512,14 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, Doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1161,6 +1533,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1183,14 +1562,18 @@ DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline (the HTML help workshop was already many +# years in maintenance mode). You can download the HTML help workshop from the +# web archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for @@ -1209,28 +1592,29 @@ GENERATE_HTMLHELP = NO CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty -# doxygen will try to run the HTML help compiler on the generated index.hhp. +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# Doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1243,6 +1627,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1261,7 +1655,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1269,8 +1664,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1278,30 +1673,30 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1343,19 +1738,40 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by Doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has more details information than the tab index, you +# could consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO +# When GENERATE_TREEVIEW is set to YES, the PAGE_OUTLINE_PANEL option determines +# if an additional navigation panel is shown at the right hand side of the +# screen, displaying an outline of the contents of the main page, similar to +# e.g. https://developer.android.com/reference If GENERATE_TREEVIEW is set to +# NO, this option has no effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +PAGE_OUTLINE_PANEL = YES + +# When GENERATE_TREEVIEW is set to YES, the FULL_SIDEBAR option determines if +# the side bar is limited to only the treeview area (value NO) or if it should +# extend to the full height of the window (value YES). Setting this to YES gives +# a layout similar to e.g. https://docs.readthedocs.io with more room for +# contents, but less room for the project logo, title, and description. If +# GENERATE_TREEVIEW is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = YES + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. +# Doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. @@ -1364,6 +1780,12 @@ GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 +# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified +# enumeration values besides the enumeration mnemonics. +# The default value is: NO. + +SHOW_ENUM_VALUES = NO + # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. @@ -1371,36 +1793,49 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML +# Doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. -FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. @@ -1409,11 +1844,29 @@ FORMULA_TRANSPARENT = YES USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1426,33 +1879,40 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and +# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for +# the HTML output. The underlying search engine uses JavaScript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then +# For large projects the JavaScript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically @@ -1469,26 +1929,27 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There -# are two flavours of web server based searching depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. See -# the section "External Indexing and Searching" for details. +# implemented using a web server instead of a web client using JavaScript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, Doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. SERVER_BASED_SEARCH = NO -# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# When EXTERNAL_SEARCH tag is enabled Doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1499,10 +1960,11 @@ EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1523,7 +1985,7 @@ SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through Doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of @@ -1537,7 +1999,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, Doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -1553,22 +2015,36 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + +# If the COMPACT_LATEX tag is set to YES, Doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1586,39 +2062,57 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank Doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that Doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will -# replace them by respectively the title of the page, the current date and time, -# only the current date, the version number of doxygen, the project name (see -# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of Doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank Doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that Doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by Doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or @@ -1636,53 +2130,59 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, Doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BATCHMODE = NO -# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# If the LATEX_HIDE_INDICES tag is set to YES then Doxygen will not include the # index chapters (such as File Index, Compound Index, etc.) in the output. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. -# The default value is: plain. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plainnat. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, Doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1697,7 +2197,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, Doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1717,28 +2217,36 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to Doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the -# default style sheet that doxygen normally uses. +# default style sheet that Doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to Doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = +# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the RTF_OUTPUT output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTRA_FILES = + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, Doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -1762,7 +2270,14 @@ MAN_OUTPUT = man MAN_EXTENSION = .3 -# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without # them the man command would be unable to find the correct page. @@ -1775,7 +2290,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, Doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -1789,19 +2304,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify a XML DTD, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, Doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -1810,11 +2313,18 @@ XML_DTD = XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, Doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -1832,19 +2342,45 @@ DOCBOOK_OUTPUT = docbook # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, Doxygen will generate an +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + +# If the GENERATE_SQLITE3 tag is set to YES Doxygen will generate a Sqlite3 +# database with symbols found by Doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each Doxygen run. If set to NO, Doxygen +# will warn if a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, Doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -1852,7 +2388,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, Doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -1860,9 +2396,9 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. @@ -1882,14 +2418,14 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, Doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, Doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. @@ -1905,7 +2441,7 @@ MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1914,7 +2450,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -1946,10 +2483,10 @@ PREDEFINED = EXPAND_AS_DEFINED = -# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all refrences to function-like macros that are alone on a line, have an -# all uppercase name, and do not end with a semicolon. Such function macros are -# typically used for boiler-plate code, and will confuse the parser if not +# If the SKIP_FUNCTION_MACROS tag is set to YES then Doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not # removed. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1969,90 +2506,60 @@ SKIP_FUNCTION_MACROS = YES # where loc1 and loc2 can be relative or absolute paths or URLs. See the # section "Linking to external documentation" for more information about the use # of tag files. -# Note: Each tag file must have an unique name (where the name does NOT include -# the path). If a tag file is not located in the directory in which doxygen is +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which Doxygen is # run, you must also specify the path to the tagfile here. TAGFILES = -# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# When a file name is specified after GENERATE_TAGFILE, Doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. HIDE_UNDOC_RELATIONS = YES -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# If you set the HAVE_DOT tag to YES then Doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. HAVE_DOT = YES -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed -# to run in parallel. When set to 0 doxygen will base this on the number of +# The DOT_NUM_THREADS specifies the number of dot invocations Doxygen is allowed +# to run in parallel. When set to 0 Doxygen will base this on the number of # processors available in the system. You can set it explicitly to a value # larger than 0 to get control over the balance between CPU load and processing # speed. @@ -2061,55 +2568,83 @@ HAVE_DOT = YES DOT_NUM_THREADS = 6 -# When you want a differently looking font n the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# Doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES -# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES -# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2126,10 +2661,41 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the UML_LOOK tag is enabled, field labels are shown along the edge between +# two class nodes. If there are many fields and many nodes the graph may become +# too cluttered. The UML_MAX_EDGE_LABELS threshold limits the number of items to +# make the size more manageable. Set this to 0 for no limit. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag UML_LOOK is set to YES. + +UML_MAX_EDGE_LABELS = 10 + +# If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, Doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2139,79 +2705,105 @@ UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to -# YES then doxygen will generate a graph for each documented file showing the +# YES then Doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are -# set to YES then doxygen will generate a graph for each documented file showing +# set to YES then Doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDED_BY_GRAPH = YES -# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# If the CALL_GRAPH tag is set to YES then Doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. CALL_GRAPH = NO -# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. CALLER_GRAPH = NO -# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = YES -# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. -# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order -# to make the SVG files visible in IE 9+ (other browsers do not have this -# requirement). -# Possible values are: png, jpg, gif and svg. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# https://www.graphviz.org/)). +# +# Note the formats svg:cairo and svg:cairo:cairo cannot be used in combination +# with INTERACTIVE_SVG (the INTERACTIVE_SVG will be set to NO). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus, +# png:gdiplus:gdiplus, svg:cairo, svg:cairo:cairo, svg:svg, svg:svg:core, +# gif:cairo, gif:cairo:gd, gif:cairo:gdiplus, gif:gdiplus, gif:gdiplus:gdiplus, +# gif:gd, gif:gd:gd, jpg:cairo, jpg:cairo:gd, jpg:cairo:gdiplus, jpg:gd, +# jpg:gd:gd, jpg:gdiplus and jpg:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. DOT_IMAGE_FORMAT = png -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. +# If DOT_IMAGE_FORMAT is set to svg or svg:svg or svg:svg:core, then this option +# can be set to YES to enable generation of interactive SVG images that allow +# zooming and panning. # # Note that this requires a modern browser other than Internet Explorer. Tested # and working are Firefox, Chrome, Safari, and Opera. -# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make -# the SVG files visible. Older versions of IE do not have SVG support. +# +# Note This option will be automatically disabled when DOT_IMAGE_FORMAT is set +# to svg:cairo or svg:cairo:cairo. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2230,11 +2822,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in Doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2242,10 +2835,34 @@ MSCFILE_DIRS = DIAFILE_DIRS = +# When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for PlantUML. + +PLANTUML_CFG_FILE = + +# When using PlantUML, the specified paths are searched for files specified by +# the !include statement in a PlantUML block. + +PLANTUML_INCLUDE_PATH = + +# The PLANTUMLFILE_DIRS tag can be used to specify one or more directories that +# contain PlantUml files that are included in the documentation (see the +# \plantumlfile command). + +PLANTUMLFILE_DIRS = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes -# larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct +# larger than this value, Doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. @@ -2266,19 +2883,7 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. @@ -2287,17 +2892,37 @@ DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = YES -# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES + +# You can define message sequence charts within Doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, Doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = From d0a51b429126878b939c07ce4e87e2624d3c9ae2 Mon Sep 17 00:00:00 2001 From: David Kindl Date: Wed, 28 May 2025 09:34:58 +0200 Subject: [PATCH 037/112] Cleanup: - remove trailing whitespaces - clang-format: MaxEmptyLinesToKeep: 2 - grammar --- README.md | 36 +++++++++---------- docs/Getting-started.md | 22 ++++++------ docs/Porting from 0.56 to X.txt | 8 ++--- src/common/CCacheableScriptFile.h | 4 +-- src/common/CDataBase.cpp | 20 +++++------ src/common/CExpression.h | 2 +- src/common/CLocalVarsExtra.cpp | 4 +-- src/common/CSFileObjContainer.cpp | 2 +- src/common/CScriptObj.cpp | 3 +- src/common/CServerMap.cpp | 1 - src/common/CUID.h | 2 +- src/common/CUOInstall.h | 4 +-- src/common/CVarDefMap.cpp | 20 +++++------ src/common/CVarDefMap.h | 2 +- src/common/ListDefContMap.cpp | 30 ++++++++-------- src/common/ListDefContMap.h | 2 +- src/common/basic_threading.h | 2 +- src/common/crypto/CCrypto.cpp | 1 - src/common/crypto/CCrypto.h | 8 ++--- src/common/crypto/CCryptoLogin.cpp | 2 +- src/common/resource/CResourceHash.cpp | 2 +- src/common/resource/CResourceLink.h | 2 +- src/common/resource/CResourceSortedArrays.h | 2 +- src/common/sphere_library/CSFileList.cpp | 2 +- src/common/sphere_library/CSObjCont.h | 1 - src/common/sphere_library/CSRand.h | 2 +- src/common/sphere_library/CSTypedArray.h | 1 - src/common/sphere_library/sptr.h | 2 -- src/common/sphere_library/ssorted_vector.h | 3 +- src/common/sphere_library/sstringobjs.h | 1 - src/common/sphere_library/stypecast.h | 2 -- src/common/sqlite/SQLite.cpp | 6 ++-- src/game/CPathFinder.cpp | 6 ++-- src/game/CPathFinder.h | 1 - src/game/CRegion.cpp | 1 - src/game/CResourceCalc.cpp | 26 +++++++------- src/game/CSectorTemplate.cpp | 1 - src/game/CServerConfig.h | 1 - src/game/CServerDef.h | 2 +- src/game/CStartLoc.h | 2 +- src/game/CWorld.cpp | 1 - src/game/CWorldCache.h | 2 +- src/game/CWorldGameTime.cpp | 4 +-- src/game/CWorldGameTime.h | 2 +- src/game/CWorldImport.cpp | 2 -- src/game/CWorldMap.cpp | 1 - src/game/CWorldSearch.cpp | 2 +- src/game/CWorldTickingList.cpp | 1 - src/game/chars/CChar.cpp | 1 - src/game/chars/CCharMemory.cpp | 2 +- src/game/chars/CCharNPCAct_Magic.cpp | 20 +++++------ src/game/chars/CCharNPCPet.cpp | 18 +++++----- src/game/chars/CCharPlayer.h | 2 +- src/game/chars/CCharSpell.cpp | 1 - src/game/chars/CCharStatus.cpp | 16 ++++----- src/game/chars/CStoneMember.cpp | 2 +- src/game/clients/CClientEvent.cpp | 4 --- src/game/clients/CClientMsg.cpp | 8 ++--- src/game/clients/CParty.cpp | 12 +++---- src/game/components/CCChampion.cpp | 2 +- src/game/components/CCChampion.h | 1 - src/game/game_enums.h | 1 - src/game/items/CItemCorpse.cpp | 4 +-- src/game/items/CItemMulti.cpp | 8 ++--- src/game/items/CItemStone.cpp | 38 ++++++++++----------- src/game/items/CItemVendable.cpp | 6 ++-- src/game/items/CItemVendable.h | 2 +- src/game/uo_files/CUOMapList.h | 2 +- src/game/uo_files/CUOMobtypes.h | 3 -- src/network/CIPHistoryManager.h | 1 - src/network/CNetState.cpp | 2 +- src/network/CNetworkOutput.cpp | 1 - src/network/CNetworkOutput.h | 1 - src/network/CSocket.cpp | 4 +-- src/network/CSocket.h | 10 +++--- src/network/linuxev.cpp | 26 +++++++------- src/network/linuxev.h | 18 +++++----- src/network/packet.cpp | 1 - src/network/packet.h | 2 -- src/network/receive.cpp | 1 - src/sphere/ntservice.h | 1 - 81 files changed, 219 insertions(+), 258 deletions(-) diff --git a/README.md b/README.md index 6b1225eae..3f4c2521d 100644 --- a/README.md +++ b/README.md @@ -14,23 +14,23 @@ Ultima Online game server, developed in C++. | [![Discord Shield](https://discordapp.com/api/guilds/354358315373035542/widget.png?style=shield)](https://discord.gg/ZrMTXrs) | ## Getting Started tutorial -If you're new to the Sphere server and want to setup your first shard, [this is your go-to guide](docs/Getting-started.md)! +If you're new to the Sphere server and want to set up your first shard, [this is your go-to guide](docs/Getting-started.md)! ## Releases ### **Core** -| Branch: Master
(most stable pre-releases) | Branch: Dev
(most recent, potentially unstable) | -| :--- | :--- | -| [![GitHub last commit on Master branch](https://img.shields.io/github/last-commit/Sphereserver/Source-X/master.svg)](https://github.com/Sphereserver/Source-X/)   Changelog | [![GitHub last commit on Dev branch](https://img.shields.io/github/last-commit/Sphereserver/Source-X/dev.svg)](https://github.com/Sphereserver/Source-X/tree/dev)   Changelog | -| **Nightly builds:** | **Nightly builds:** | -| [![Build status: Windows x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86_64.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86_64.yml?query=branch%3Amaster) [![Build status: Windows x86](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86.yml?query=branch%3Amaster) | [![Build status: Windows x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86_64.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86_64.yml?query=branch%3Adev) [![Build status: Windows x86](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86.yml?query=branch%3Adev)| -| [![Build status: Linux x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86_64.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86_64.yml) [![Build status: Linux x86](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86.yml?query=branch%3Amaster) | [![Build status: Linux x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86_64.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86_64.yml?query=branch%3Adev) [![Build status: Linux x86](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86.yml?query=branch%3Adev) | -| [![Build status: MacOS x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_x86_64.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_x86_64.yml?query=branch%3Amaster) [![Build status: MacOS ARM](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_arm.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_arm.yml?query=branch%3Amaster) | [![Build status: MacOS x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_x86_64.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_x86_64.yml?query=branch%3Adev) [![Build status: MacOS ARM](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_arm.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_arm.yml?query=branch%3Adev) | +| Branch: Master
(most stable pre-releases) | Branch: Dev
(most recent, potentially unstable) | +|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [![GitHub last commit on Master branch](https://img.shields.io/github/last-commit/Sphereserver/Source-X/master.svg)](https://github.com/Sphereserver/Source-X/)   Changelog | [![GitHub last commit on Dev branch](https://img.shields.io/github/last-commit/Sphereserver/Source-X/dev.svg)](https://github.com/Sphereserver/Source-X/tree/dev)   Changelog | +| **Nightly builds:** | **Nightly builds:** | +| [![Build status: Windows x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86_64.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86_64.yml?query=branch%3Amaster) [![Build status: Windows x86](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86.yml?query=branch%3Amaster) | [![Build status: Windows x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86_64.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86_64.yml?query=branch%3Adev) [![Build status: Windows x86](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_win_x86.yml?query=branch%3Adev) | +| [![Build status: Linux x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86_64.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86_64.yml) [![Build status: Linux x86](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86.yml?query=branch%3Amaster) | [![Build status: Linux x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86_64.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86_64.yml?query=branch%3Adev) [![Build status: Linux x86](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_linux_x86.yml?query=branch%3Adev) | +| [![Build status: macOS x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_x86_64.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_x86_64.yml?query=branch%3Amaster) [![Build status: macOS ARM](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_arm.yml/badge.svg?branch=master)](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_arm.yml?query=branch%3Amaster) | [![Build status: macOS x86_64](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_x86_64.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_x86_64.yml?query=branch%3Adev) [![Build status: macOS ARM](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_arm.yml/badge.svg?branch=dev)](https://github.com/Sphereserver/Source-X/actions/workflows/build_osx_arm.yml?query=branch%3Adev) | **Click the badges or follow the links:** -+ Github Nightly builds ++ GitHub Nightly builds + SphereServer Website ### **ScriptPack** @@ -95,7 +95,7 @@ Most notable changes (right now) are: Only recent compilers are supported, since Sphere uses C++20 features: the newer the compiler, the better. Oldest compiler versions supporting C++20: Visual Studio 2019 version 16.11, GCC 8, MinGW distributions using GCC 8, Clang version 10. No pre-built project files are included. You need to build Visual Studio solution (.sln), Makefiles or Ninja Build files (or project files for other IDE or build systems) with CMake. -Below there's is a small guide. +Below there is a small guide. You can use CMake GUI (graphical interface) or its CLI (command line interface). To pass a flag to using the CLI, just prepend `-D` to the name of the variable, then assign the value with the equal sign, eg: `-DFOO=TRUE`. @@ -117,15 +117,15 @@ Before starting: does CMake give you an error? Ensure that you have Git installe + Via CMake CLI (command line interface): pass the parameter `-DCMAKE_TOOLCHAIN_FILE="..."` + Build **Configuration** (type) - When using Makefiles or Ninja, you can specify a build type by setting (also this via GUI or CLI) `CMAKE_BUILD_TYPE="build"`, where build is **Nightly**, **Debug** or **Release**. If the build type was not set, by default the makefiles for all of the three build types are generated. - **Debug** build is expected to be slow and it's to be used, you guessed it, for debugging purposes (best coupled with a debugger or with sanitizers enabled, more on them right below), so don't use it for a live shard! + When using Makefiles or Ninja, you can specify a build type by setting (also this via GUI or CLI) `CMAKE_BUILD_TYPE="build"`, where build is **Nightly**, **Debug** or **Release**. If the build type was not set, by default the makefiles for all three build types are generated. + **Debug** build is expected to be slow, and it's to be used, you guessed it, for debugging purposes (best coupled with a debugger or with sanitizers enabled, more on them right below), so don't use it for a live shard! Other useful CMake flags: + You can add other compiler flags with the custom variables `C_FLAGS_EXTRA`, `CXX_FLAGS_EXTRA`, `CMAKE_EXE_LINKER_FLAGS_EXTRA`. + Enable Sanitizers: `USE_ASAN[=ON]`, `USE_UBSAN`, `USE_LSAN`, etc. If you are using Address Sanitizer, Undefined Behaviour Sanitizer, Leak Sanitizer on Windows, it might be useful to redirect stderr to stdout to correctly show the output: `SphereSvrX64_nightly > Sphere_ASan_log.txt 2>&1`. - Remember to setup Sanitizers settings by setting the respective shell variables. To ease this, there are some batch/shell scripts doing that for you inside `utilities/`. + Remember to set up Sanitizers settings by setting the respective shell variables. To ease this, there are some batch/shell scripts doing that for you inside `utilities/`. + `CROSSCOMPILING_ARCH`: set this to TRUE to tell the compiler you are building binary files for a different architecture (not from x86_64 to x86, but for example from x86 to ARM). Example to build makefiles on Linux for a 64 bits Nightly version, inside the "build" directory (run the command inside the project's root folder): @@ -145,14 +145,14 @@ Building will require more packages than the ones needed to run Sphere. Install these additional packages: + Build tools (other than the compiler): `sudo apt-get install git cmake`. + MariaDB client: `sudo apt-get install libmariadb-dev` and `libmariadb3` or `mariadb-client` (depends on the OS version) - If you are on a 64 bits architecture but you want to compile (or execute) a 32 bits binary, you will need to + If you are on a 64 bits architecture, but you want to compile (or execute) a 32 bits binary, you will need to install MariaDB packages adding the postfix `:i386` to each package name. + CentOS - Red Hat Enterprise Linux - Fedora Install these additional packages via yum (CentOS or RH) or dnf (Fedora). + Build tools (other than the compiler): `git cmake glibc-devel` + MariaDB client: `mariadb-connector-c mariadb-connector-c-devel` - If you are on a 64 bits architecture but you want to compile (or execute) a 32 bits binary, you will need to install MariaDB packages adding the postfix `.i686` to each package name. + If you are on a 64 bits architecture, but you want to compile (or execute) a 32 bits binary, you will need to install MariaDB packages adding the postfix `.i686` to each package name. #### Compiling on Linux @@ -216,7 +216,7 @@ to sphereserver will be deleted. + Rebasing instead of pulling the project is a better practice to avoid unnecessary "merge branch master" commits. + Removing/Changing/Adding anything that was working in one way for years should be followed by an ini setting if the changes cannot be replicated from script to keep some backwards compatibility. -+ Comment your code, add informations about its logic. It's very important since it helps others to understand your work. ++ Comment your code, add information about its logic. It's very important since it helps others to understand your work. + Be sure to use Sphere's custom datatypes and the string formatting macros described in src/common/datatypes.h. + When casting numeric data types, always prefer C-style casts, like (int), to C++ static_cast<int>(). It's way more concise. + Be wary that in SphereScript unsigned values does not exist, all numbers are considered signed, and that 64 bits integers meant @@ -225,7 +225,7 @@ to sphereserver will be deleted. Use fixed width variables only for values that need to fit a limited range. + For strings, use pointers: to "char" for strings that should always have ASCII encoding; - to "tchar" for strings that may be ASCII or Unicode, depending from compilation settings (more info in "datatypes.h"); + to "tchar" for strings that may be ASCII or Unicode, depending on compilation settings (more info in "datatypes.h"); to "wchar" for string that should always have Unicode encoding. ### Naming Conventions @@ -251,7 +251,7 @@ These are meant to be applied to new code and, if there's some old code not foll + For char, wchar, tchar use respectively the prefixes "c", "wc", "tc". + When handling strings, "lpstr", "lpcstr", "lpwstr", "lpcwstr", "lptstr", "lpctstr" data types are preferred aliases. You'll find a lot of "psz" prefixes for strings: the reason is that in the past Sphere coders wanted to be consistent with Microsoft's Hungarian Notation. - The correct and up to date notation is "pc" for lpstr/lpcstr (which are respectively char*and const char*), "pwc" (wchar*and const wchar*), + The correct and up-to-date notation is "pc" for lpstr/lpcstr (which are respectively char*and const char*), "pwc" (wchar*and const wchar*), "ptc" for lptstr/lpctstr (tchar*and const tchar*). Use the "s" or "ps" (if pointer) when using CString or std::string. Always prefer CString over std::string, unless in your case there are obvious advantages for using the latter. diff --git a/docs/Getting-started.md b/docs/Getting-started.md index 8a63917cf..ee8b6a56e 100644 --- a/docs/Getting-started.md +++ b/docs/Getting-started.md @@ -51,7 +51,7 @@ Once you've extracted your archive, you'll need to move the server's executable into the root directory of the server. The file is contained into the `build-xx` folder. -For instance, for a 64 bits linux installation, your root folder's structure +For instance, for 64 bits linux installation, your root folder's structure should go from this: ``` - accounts/ @@ -113,7 +113,7 @@ shard. Before you can start fiddling with different things, you'll need to agree of using a nightly build. A nightly build gets created automatically every night based on the latest changes made by the developers. This means it might contain -potential bugs. With this settings you agree to take full responsibility of all +potential bugs. With this setting you agree to take full responsibility of all the implications of this choice. So, let's open the `sphere.ini` file and add a new line to the `SPHERE` @@ -167,7 +167,7 @@ contain all the assets of the game. This includes (but not limited to): These files are contained into the Ultima Online client's installation. -**WARNING:** If there a discrepancy between server's muls and client's muls +**WARNING:** If there is a discrepancy between server's muls and client's muls players will experience serious bugs or crashes. For this reason we need to make sure that the server is picking up the mul @@ -175,7 +175,7 @@ files from the Ultima Online client's installation. There are different ways to achieve this. #### A. Do not change the Sphere.ini file -If you're using Windows and you've got just one Ultima Online Client installed +If you're using Windows, and you've got just one Ultima Online Client installed through the official EA installer, you can just skip this step and move on with the next step of this guide. @@ -200,7 +200,7 @@ MulFiles=C:/Program Files/EA Games/Ultima Online/ ``` #### C. Copy client's files into the server's folder -Create a new folder into the server's root folder. For instance you can call it +Create a new folder into the server's root folder. For instance, you can call it `mul`. For simplicity, copy all the contents of your Ultima Online client's @@ -228,7 +228,7 @@ MulFiles=mul/ **NOTE**: You don't need all the files contained into the Ultima Online client's installation. You just need all the `.mul` and `.idx` files. -**NOTE 2**: On linux the name of the files is case sensitive so, make sure all +**NOTE 2**: On linux the name of the files is case-sensitive so, make sure all the file names are lowercase! ### Configure maps @@ -265,14 +265,14 @@ Map1=7168,4096,-1,1,1 //ML size ## Setup Scripts We've got a nice server. But it is completely brainless: it doesn't know what -is a vendor, an npc and what are their behaviours. In order to give it a brain +is a vendor, a npc and what are their behaviours. In order to give it a brain we should add some scripts. If we were to write all of them, it would take ages. For this reason Sphere comes with a pre-made set of scripts to get you started in no time. ### Download scripts Head out to the -[SphereX Github page](https://github.com/Sphereserver/Scripts-X) click on `Code` +[SphereX GitHub page](https://github.com/Sphereserver/Scripts-X) click on `Code` and hit `Download ZIP`. Unzip the content of the package into the server's `scripts` folder. @@ -410,7 +410,7 @@ Use '?' to view available console commands ## Connect to your server Now that you've configured the server, you're finally ready to start playing! -By default the Ultima Online Client will connect to the official game servers. +By default, the Ultima Online Client will connect to the official game servers. However, what we want to do is to connect to our server. There are multiple tools you can use like Razor - just Google a bit ;). @@ -434,7 +434,7 @@ configuration you can now try to login with any username and password: this operation will automatically create a new user for you. So, pick a username and password and try to login. Create a new character et -voila': you're the first player of your own personal Ultima Online Shard! +voilà': you're the first player of your own personal Ultima Online Shard! ## Making yourself an admin Well, since this is your server, I guess it's only fair to set yourself as an @@ -551,4 +551,4 @@ about Sphere server, access the If you need further help, get into the [Sphere discord server](https://discord.gg/ZrMTXrs). -Have fun! \ No newline at end of file +Have fun! diff --git a/docs/Porting from 0.56 to X.txt b/docs/Porting from 0.56 to X.txt index 7749cdcc4..8fd54ffd0 100644 --- a/docs/Porting from 0.56 to X.txt +++ b/docs/Porting from 0.56 to X.txt @@ -4,7 +4,7 @@ Changed starting from 0.56b ------------------------ - [sphere_spells.scp]: mostly new spell flags. - [sphere_skills.scp]: mostly new skill flags. -- [sphere_defs.scp]: lots of new defs, most importantly new mounts defs. +- [sphere_defs.scp]: lots of new defs, most importantly new mounts' defs. (Mounts are now handled there, if they are not specified in the file they cannot be ridden). - Update multis definitions from [itemdef 04xxx] to [multidef 0x]. - Changed MAPPLANE->MAP, LOCALLIGHT->LIGHT. @@ -32,7 +32,7 @@ Compatibility changes from 0.56d to X1 Changes to script parsing: - Changed: Now conditional statements (IF/ELIF/ELSEIF) will perform a lazy evaluation of the conditional expression, whereas before they fully evaluated the whole expression. - Lazy evaluation means that the expression will be evaluated one piece (or, one subexpression) at a time. At each stage, if it's sure that the whole expression value (true or false) won't change + Lazy evaluation means that the expression will be evaluated one piece (or, one subexpression) at a time. At each stage, if it's certain that the whole expression value (true or false) won't change even if new subexpressions will be evaluated, the evaluation will stop there. Example: IF ( && ( == 1)) Won't return an error if item's LINK is invalid or not set, because the evaluation will only halt to . @@ -52,9 +52,9 @@ Changes to script parsing: Using ISVALID to check an object validity/existence is now the preferred way (instead of checking its value with magic numbers like 0 or -1/0FFFFFFFF). - Now TRY can be used to explicitly "TRY" to execute a command or set a property on an object which may or may not exist. If the object doesn't exist, no operation is done and no error is returned. It can't be used to TRY to retrieve a value (like ). - Its use is DISCOURAGED but sometimes it might be an easy way to execute trivial operations without a lot of if/endif clauses. + Its use is DISCOURAGED, but sometimes it might be an easy way to execute trivial operations without a lot of if/endif clauses. This is a way to explicitly allow what Sphere already did by default in the past (not checking if the object is valid and not returning an error), - which is something that only sometimes we expect and we want, most of the times it caused obscure bugs. + which is something that only sometimes we expect and we want, most of the time it caused obscure bugs. Changes to script keywords: - CAN property is no more RW, but now it's read-only. Use CANMASK to change it on a per-char basis. diff --git a/src/common/CCacheableScriptFile.h b/src/common/CCacheableScriptFile.h index 3075f5da0..2931c0eab 100644 --- a/src/common/CCacheableScriptFile.h +++ b/src/common/CCacheableScriptFile.h @@ -37,10 +37,10 @@ public: virtual int GetPosition() const override; protected: virtual tchar * _ReadString(tchar *pBuffer, int sizemax) override; public: virtual tchar * ReadString(tchar *pBuffer, int sizemax) override; -protected: +protected: void _dupeFrom(CCacheableScriptFile *other); void dupeFrom(CCacheableScriptFile *other); - + protected: bool _HasCache() const; public: bool HasCache() const; diff --git a/src/common/CDataBase.cpp b/src/common/CDataBase.cpp index 27eab2e56..f3f199138 100644 --- a/src/common/CDataBase.cpp +++ b/src/common/CDataBase.cpp @@ -90,10 +90,10 @@ bool CDataBase::query(const char *query, CVarDefMap & mapQueryResult) ADDTOCALLSTACK("CDataBase::query"); mapQueryResult.Clear(); mapQueryResult.SetNumNew("NUMROWS", 0); - + if ( !isConnected() ) return false; - + int result; MYSQL_RES * m_res = nullptr; const char * myErr = nullptr; @@ -105,7 +105,7 @@ bool CDataBase::query(const char *query, CVarDefMap & mapQueryResult) */ SimpleThreadLock lock(m_connectionMutex); result = mysql_query(_myData, query); - + if ( result == 0 ) { m_res = mysql_store_result(_myData); @@ -117,15 +117,15 @@ bool CDataBase::query(const char *query, CVarDefMap & mapQueryResult) myErr = mysql_error(_myData); } } - + if ( m_res != nullptr ) { MYSQL_FIELD * fields = mysql_fetch_fields(m_res); int num_fields = mysql_num_fields(m_res); - + mapQueryResult.SetNum("NUMROWS", mysql_num_rows(m_res)); mapQueryResult.SetNum("NUMCOLS", num_fields); - + char key[12]; char **trow = nullptr; int rownum = 0; @@ -138,13 +138,13 @@ bool CDataBase::query(const char *query, CVarDefMap & mapQueryResult) char *z = trow[i]; if (z == nullptr) //We need to check if the row empty, and return char 0 as return, because SetStr clean up \0 chars from vardef and causes the Sphere crash. z = ∅ - + if ( !rownum ) { mapQueryResult.SetStr(Str_FromI_Fast(i, key, sizeof(key), 10), true, z); mapQueryResult.SetStr(fields[i].name, true, z); } - + snprintf(zStore, Str_TempLength(), "%d.%d", rownum, i); mapQueryResult.SetStr(zStore, true, z); snprintf(zStore, Str_TempLength(), "%d.%s", rownum, fields[i].name); @@ -159,10 +159,10 @@ bool CDataBase::query(const char *query, CVarDefMap & mapQueryResult) { g_Log.Event(LOGM_NOCONTEXT|LOGL_ERROR, "MariaDB query \"%s\" failed due to \"%s\"\n", query, ( *myErr ? myErr : "unknown reason")); } - + if (( result == CR_SERVER_GONE_ERROR ) || ( result == CR_SERVER_LOST )) Close(); - + return false; } diff --git a/src/common/CExpression.h b/src/common/CExpression.h index 84708cc4d..f2d7b0dfd 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -228,7 +228,7 @@ class CExpression int GetRangeVals(lpctstr& refStrExpr, int64* piVals, int iMaxQty, bool fNoWarn = false); int64 GetRangeNumber(lpctstr& refStrExpr); // Evaluate a { } range CSString GetRangeString(lpctstr& refStrExpr); // STRRANDRANGE - + // Strict G++ Prototyping produces an error when not casting char*& to const char*& // So this is a rather lazy and const-UNsafe workaround inline llong GetSingle(lptstr &refArgs) { diff --git a/src/common/CLocalVarsExtra.cpp b/src/common/CLocalVarsExtra.cpp index 38f9e5d7b..7e6f6494b 100644 --- a/src/common/CLocalVarsExtra.cpp +++ b/src/common/CLocalVarsExtra.cpp @@ -33,7 +33,7 @@ bool CLocalFloatVars::Set( const char* VarName, const char* VarValue ) return Insert(VarName, VarValue, true); } -bool CLocalFloatVars::Insert( const char* VarName, const char* VarValue, bool fForceSet) +bool CLocalFloatVars::Insert( const char* VarName, const char* VarValue, bool fForceSet) { ADDTOCALLSTACK("CLocalFloatVars::Insert"); if (!VarValue || !VarName) @@ -119,4 +119,4 @@ bool CLocalObjMap::Insert( ushort Number, CObjBase * pObj, bool fForceSet ) void CLocalObjMap::Clear() { m_ObjMap.clear(); -} \ No newline at end of file +} diff --git a/src/common/CSFileObjContainer.cpp b/src/common/CSFileObjContainer.cpp index 788c89216..bf2bdbd60 100644 --- a/src/common/CSFileObjContainer.cpp +++ b/src/common/CSFileObjContainer.cpp @@ -265,7 +265,7 @@ bool CSFileObjContainer::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsol return pObj->r_WriteVal(ptcKey, sVal, pSrc); } } - + return false; } diff --git a/src/common/CScriptObj.cpp b/src/common/CScriptObj.cpp index 80ae041fe..0b3697587 100644 --- a/src/common/CScriptObj.cpp +++ b/src/common/CScriptObj.cpp @@ -1038,7 +1038,7 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc else if (iValue == iValueEnd) sVal.Format(ppCmd[iValue - 1]); else - { + { sVal.Add(ppCmd[iValue - 1]); for (int64 i = iValue + 1 ; i <= iValueEnd; ++i) { @@ -1782,7 +1782,6 @@ enum SK_TYPE : int }; - lpctstr const CScriptObj::sm_szScriptKeys[SK_QTY+1] = { "BEGIN", diff --git a/src/common/CServerMap.cpp b/src/common/CServerMap.cpp index ef51f140c..7a34074e5 100644 --- a/src/common/CServerMap.cpp +++ b/src/common/CServerMap.cpp @@ -16,7 +16,6 @@ #include "../sphere/threads.h" - ////////////////////////////////////////////////////////////////// // -CServerMapBlockingState diff --git a/src/common/CUID.h b/src/common/CUID.h index 770486eaf..8e80006e8 100644 --- a/src/common/CUID.h +++ b/src/common/CUID.h @@ -152,7 +152,7 @@ class CUID // A unique system serial id. 4 bytes long inline CChar* CharFind(bool fInvalidateBeingDeleted = false) const noexcept{ return CharFindFromUID(m_dwInternalVal, fInvalidateBeingDeleted); } - + }; static constexpr CUID kUIDUninitialized(UID_UNUSED); diff --git a/src/common/CUOInstall.h b/src/common/CUOInstall.h index 11328ffd4..b5e3195d4 100644 --- a/src/common/CUOInstall.h +++ b/src/common/CUOInstall.h @@ -79,7 +79,7 @@ extern struct CUOInstall bool ReadMulIndex(CSFile &file, dword id, CUOIndexRec &Index); bool ReadMulData(CSFile &file, const CUOIndexRec &Index, void * pData); - + public: CUOInstall(); @@ -105,7 +105,7 @@ extern class CVerDataMul void Unload(); void Load( CSFile & file ); bool FindVerDataBlock( VERFILE_TYPE type, dword id, CUOIndexRec & Index ) const; - + public: CVerDataMul(); ~CVerDataMul(); diff --git a/src/common/CVarDefMap.cpp b/src/common/CVarDefMap.cpp index ce930ea0b..6f878e5a4 100644 --- a/src/common/CVarDefMap.cpp +++ b/src/common/CVarDefMap.cpp @@ -62,7 +62,7 @@ bool CVarDefContNum::r_WriteVal( lpctstr pKey, CSString & sVal, CTextConsole * p } CVarDefCont * CVarDefContNum::CopySelf() const -{ +{ return new CVarDefContNum( GetKey(), m_iVal ); } @@ -74,7 +74,7 @@ CVarDefCont * CVarDefContNum::CopySelf() const * ***************************************************************************/ -CVarDefContStr::CVarDefContStr( lpctstr ptcKey, lpctstr pszVal ) : m_sKey( ptcKey ), m_sVal( pszVal ) +CVarDefContStr::CVarDefContStr( lpctstr ptcKey, lpctstr pszVal ) : m_sKey( ptcKey ), m_sVal( pszVal ) { } @@ -88,7 +88,7 @@ int64 CVarDefContStr::GetValNum() const return( Exp_Get64Val(pszStr) ); } -void CVarDefContStr::SetValStr( lpctstr pszVal ) +void CVarDefContStr::SetValStr( lpctstr pszVal ) { const size_t uiLen = strlen(pszVal); if (uiLen <= SCRIPT_MAX_LINE_LEN/2) @@ -111,9 +111,9 @@ bool CVarDefContStr::r_WriteVal( lpctstr pKey, CSString & sVal, CTextConsole * p return true; } -CVarDefCont * CVarDefContStr::CopySelf() const -{ - return new CVarDefContStr( GetKey(), m_sVal ); +CVarDefCont * CVarDefContStr::CopySelf() const +{ + return new CVarDefContStr( GetKey(), m_sVal ); } @@ -142,11 +142,11 @@ lpctstr CVarDefMap::FindValStr( lpctstr pVal ) const for ( const CVarDefCont * pVarBase : m_Container ) { ASSERT( pVarBase ); - + const CVarDefContStr * pVarStr = dynamic_cast ( pVarBase ); if ( pVarStr == nullptr ) continue; - + if ( ! strcmpi( pVal, pVarStr->GetValStr())) return pVarBase->GetKey(); } @@ -505,7 +505,7 @@ CVarDefCont * CVarDefMap::GetKey( lpctstr ptcKey ) const if ( ptcKey ) { const size_t idx = m_Container.find_predicate(ptcKey, VarDefCompare); - + if ( idx != sl::scont_bad_index() ) pReturn = m_Container[idx]; } @@ -668,7 +668,7 @@ void CVarDefMap::r_WritePrefix( CScript & s, lpctstr ptcPrefix, lpctstr ptcKeyEx const lpctstr ptcKey = pVar->GetKey(); if ( fHasExclude && !strcmpi(ptcKeyExclude, ptcKey)) continue; - + const CVarDefContNum * pVarNum = dynamic_cast(pVar); _WritePrefix(ptcKey); lpctstr ptcVal = pVar->GetValStr(); diff --git a/src/common/CVarDefMap.h b/src/common/CVarDefMap.h index 5f5bcc039..4db54f7ae 100644 --- a/src/common/CVarDefMap.h +++ b/src/common/CVarDefMap.h @@ -100,7 +100,7 @@ class CVarDefContStr : public CVarDefCont void SetValStr( lpctstr pszVal ); inline virtual lpctstr GetValStr() const override { - return m_sVal.GetBuffer(); + return m_sVal.GetBuffer(); } virtual int64 GetValNum() const override; virtual CVarDefCont * CopySelf() const override; diff --git a/src/common/ListDefContMap.cpp b/src/common/ListDefContMap.cpp index fafb64911..53d1fe750 100644 --- a/src/common/ListDefContMap.cpp +++ b/src/common/ListDefContMap.cpp @@ -59,7 +59,7 @@ bool CListDefContNum::r_WriteVal( lpctstr pKey, CSString & sVal, CTextConsole * } CListDefContElem * CListDefContNum::CopySelf() const -{ +{ return new CListDefContNum( GetKey(), m_iVal ); } @@ -70,7 +70,7 @@ CListDefContElem * CListDefContNum::CopySelf() const * * ***************************************************************************/ -CListDefContStr::CListDefContStr( lpctstr ptcKey, lpctstr pszVal ) : CListDefContElem( ptcKey ), m_sVal( pszVal ) +CListDefContStr::CListDefContStr( lpctstr ptcKey, lpctstr pszVal ) : CListDefContElem( ptcKey ), m_sVal( pszVal ) { } @@ -84,8 +84,8 @@ int64 CListDefContStr::GetValNum() const return( Exp_Get64Val(pszStr) ); } -void CListDefContStr::SetValStr( lpctstr pszVal ) -{ +void CListDefContStr::SetValStr( lpctstr pszVal ) +{ m_sVal.Copy( pszVal ); } @@ -104,9 +104,9 @@ bool CListDefContStr::r_WriteVal( lpctstr pKey, CSString & sVal, CTextConsole * return true; } -CListDefContElem * CListDefContStr::CopySelf() const -{ - return new CListDefContStr( GetKey(), m_sVal ); +CListDefContElem * CListDefContStr::CopySelf() const +{ + return new CListDefContStr( GetKey(), m_sVal ); } /*************************************************************************** @@ -116,12 +116,12 @@ CListDefContElem * CListDefContStr::CopySelf() const * * ***************************************************************************/ -CListDefCont::CListDefCont( lpctstr ptcKey ) : m_Key( ptcKey ) -{ +CListDefCont::CListDefCont( lpctstr ptcKey ) : m_Key( ptcKey ) +{ } void CListDefCont::SetKey( lpctstr ptcKey ) -{ +{ m_Key = ptcKey; } @@ -135,7 +135,7 @@ CListDefContElem* CListDefCont::GetAt(size_t nIndex) const /* DefList::const_iterator it = m_listElements.begin(); std::advance(it, nIndex); - + if (it != m_listElements.end()) return (*it); return nullptr; @@ -444,7 +444,7 @@ CListDefCont* CListDefCont::CopySelf() ADDTOCALLSTACK("CListDefCont::CopySelf"); if (m_listElements.empty()) return nullptr; - + CListDefCont* pNewList = new CListDefCont(m_Key.GetBuffer()); if ( !pNewList ) return nullptr; @@ -704,7 +704,7 @@ CListDefCont* CListDefMap::AddList(lpctstr ptcKey) { ADDTOCALLSTACK("CListDefMap::AddList"); CListDefCont* pListBase = GetKey(ptcKey); - + if ( !pListBase && ptcKey && *ptcKey ) { pListBase = new CListDefCont(ptcKey); @@ -821,7 +821,7 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) DeleteKey(ppCmds[0]); pListBase = nullptr; } - + // Append: Sets each as a new element in LIST.xxx if ( !pListBase ) { @@ -848,7 +848,7 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) } else if ( !strnicmp(ppCmds[1], "sort", 4) ) { - // Re-orders LIST.xxx according to . (possible values are: no args , i , asc , iasc , desc , idesc) + // Re-orders LIST.xxx according to . (possible values are: no args , i , asc , iasc , desc , idesc) if ( !pListBase ) return false; diff --git a/src/common/ListDefContMap.h b/src/common/ListDefContMap.h index a294da027..a2053be3c 100644 --- a/src/common/ListDefContMap.h +++ b/src/common/ListDefContMap.h @@ -91,7 +91,7 @@ class CListDefContStr: public CListDefContElem public: inline lpctstr GetValStr() const { - return m_sVal.GetBuffer(); + return m_sVal.GetBuffer(); } void SetValStr( lpctstr pszVal ); int64 GetValNum() const; diff --git a/src/common/basic_threading.h b/src/common/basic_threading.h index cf2a913bc..b4d2b0055 100644 --- a/src/common/basic_threading.h +++ b/src/common/basic_threading.h @@ -120,7 +120,7 @@ namespace sl template class GuardedAccess { -public: +public: using Mutex = MT_DEFAULT_CMUTEX_TYPE; using SharedLock = std::shared_lock; using UniqueLock = std::unique_lock; diff --git a/src/common/crypto/CCrypto.cpp b/src/common/crypto/CCrypto.cpp index 19e9b6a62..eef0b3ef1 100644 --- a/src/common/crypto/CCrypto.cpp +++ b/src/common/crypto/CCrypto.cpp @@ -17,7 +17,6 @@ extern "C" { #include "CMD5.h" - // =============================================================================================================== // --------------------------------------------------------------------------------------------------------------- // =============================================================================================================== diff --git a/src/common/crypto/CCrypto.h b/src/common/crypto/CCrypto.h index dc45657bf..9e90a9c30 100644 --- a/src/common/crypto/CCrypto.h +++ b/src/common/crypto/CCrypto.h @@ -45,7 +45,7 @@ struct CCrypto dword m_CryptMaskHi; dword m_CryptMaskLo; dword m_seed; // seed ip we got from the client. - + CONNECT_TYPE m_ConnectType; CCryptoKeysHolder::CCryptoKey m_Key; // crypt key used by this CClient @@ -62,8 +62,8 @@ struct CCrypto void InitTwoFish(); bool DecryptTwoFish( byte * pOutput, const byte * pInput, size_t outLen, size_t inLen ); // --------------- EOF TwoFish ---------------------------- - - + + // -------------- Blow Fish ------------------------------ private: static bool sm_fBFishTablesReady; @@ -134,7 +134,7 @@ struct CCrypto bool LoginCryptStart( dword dwIP, const byte * pEvent, uint inLen ); bool GameCryptStart( dword dwIP, const byte * pEvent, uint inLen ); bool RelayGameCryptStart( byte * pOutput, const byte * pInput, uint outLen, uint inLen ); - + }; #endif // _INC_CCRYPTO_H diff --git a/src/common/crypto/CCryptoLogin.cpp b/src/common/crypto/CCryptoLogin.cpp index 248d5b71b..a1a01392b 100644 --- a/src/common/crypto/CCryptoLogin.cpp +++ b/src/common/crypto/CCryptoLogin.cpp @@ -59,7 +59,7 @@ bool CCrypto::DecryptLogin( byte * pOutput, const byte * pInput, size_t outLen, + (MaskLo * m_MasterLo) - (m_CryptMaskHi * m_CryptMaskHi * 0x4c3a1353) + 0x16ef783f; - + // Old formula could cause undefined behavior /* m_CryptMaskHi = diff --git a/src/common/resource/CResourceHash.cpp b/src/common/resource/CResourceHash.cpp index 8af792106..8deee8bd7 100644 --- a/src/common/resource/CResourceHash.cpp +++ b/src/common/resource/CResourceHash.cpp @@ -51,7 +51,7 @@ int CResourceHashArray::_compare(std::unique_ptr const& pObjStored void CResourceHash::AddSortKey(CResourceID const& rid, CResourceDef* pNew) { ASSERT(rid.GetResPage() <= RES_PAGE_MAX); // RES_PAGE_ANY can be used only for search, you can't insert a rid with this special page - + auto& destArray = m_Array[GetHashArray(rid)]; ASSERT(destArray.find_sorted(rid) == sl::scont_bad_index()); diff --git a/src/common/resource/CResourceLink.h b/src/common/resource/CResourceLink.h index 90124e963..ae8f4b6b4 100644 --- a/src/common/resource/CResourceLink.h +++ b/src/common/resource/CResourceLink.h @@ -43,7 +43,7 @@ class CResourceLink : public CResourceDef { ++_dwRefInstances; } - + void DelRefInstance(); bool IsLinked() const; // been loaded from the scripts ? diff --git a/src/common/resource/CResourceSortedArrays.h b/src/common/resource/CResourceSortedArrays.h index 95b6daf70..618ece4c7 100644 --- a/src/common/resource/CResourceSortedArrays.h +++ b/src/common/resource/CResourceSortedArrays.h @@ -85,7 +85,7 @@ template class CObjUniquePtrNameSortVector final : public sl::unique_ptr_sorted_vector<_ObjType, CObjUniquePtrNameVectorSorter<_ObjType>> { public: - //static const char *m_sClassName; + //static const char *m_sClassName; inline size_t find_sorted(lpctstr ptcKey) const noexcept { return this->find_predicate(ptcKey, CObjUniquePtrNameVectorSorter<_ObjType>::_compare); } inline bool ContainsKey(lpctstr ptcKey) const noexcept { return (sl::scont_bad_index() != this->find_sorted(ptcKey)); } }; diff --git a/src/common/sphere_library/CSFileList.cpp b/src/common/sphere_library/CSFileList.cpp index 997540b25..f3578fb41 100644 --- a/src/common/sphere_library/CSFileList.cpp +++ b/src/common/sphere_library/CSFileList.cpp @@ -83,7 +83,7 @@ int CSFileList::ReadDir( lpctstr pszFileDir, bool bShowError ) break; } } - + DIR* dirp = opendir(szFileDir); struct dirent * fileinfo = nullptr; diff --git a/src/common/sphere_library/CSObjCont.h b/src/common/sphere_library/CSObjCont.h index 7b22c42a4..4ce52d75a 100644 --- a/src/common/sphere_library/CSObjCont.h +++ b/src/common/sphere_library/CSObjCont.h @@ -40,7 +40,6 @@ class cont_reverse_iterate }; - /* CSObjCont */ #define BASECONT std::vector diff --git a/src/common/sphere_library/CSRand.h b/src/common/sphere_library/CSRand.h index e8695dba3..ee3d37116 100644 --- a/src/common/sphere_library/CSRand.h +++ b/src/common/sphere_library/CSRand.h @@ -29,7 +29,7 @@ extern struct CSRand private: // Implementations, using the standard random generator. - + // Integer numbers static int32 genRandInt32(int32 min, int32 max); static int64 genRandInt64(int64 min, int64 max); diff --git a/src/common/sphere_library/CSTypedArray.h b/src/common/sphere_library/CSTypedArray.h index 6e1c54c92..a4b1143a8 100644 --- a/src/common/sphere_library/CSTypedArray.h +++ b/src/common/sphere_library/CSTypedArray.h @@ -81,7 +81,6 @@ class CSTypedArray : public std::vector }; - /* Template methods (inlined or not) are defined here */ // CSTypedArray:: Constructors, Destructor, Assign operator. diff --git a/src/common/sphere_library/sptr.h b/src/common/sphere_library/sptr.h index ce9b396cc..1319afaf0 100644 --- a/src/common/sphere_library/sptr.h +++ b/src/common/sphere_library/sptr.h @@ -125,7 +125,6 @@ namespace sl } - /* Disable unwanted behavior of object_ptr */ // Construct an object_ptr from a raw pointer @@ -142,7 +141,6 @@ namespace sl }; - /* raw_ptr_view */ // It's a NON-OWNING "smart" pointer. It stores a NON-OWNED pointer. diff --git a/src/common/sphere_library/ssorted_vector.h b/src/common/sphere_library/ssorted_vector.h index 443040ab3..8495971be 100644 --- a/src/common/sphere_library/ssorted_vector.h +++ b/src/common/sphere_library/ssorted_vector.h @@ -12,7 +12,7 @@ namespace sl size_t scont_bad_index() noexcept { return size_t(-1); } - + template > class sorted_vector : public std::vector<_Type> { @@ -187,5 +187,4 @@ namespace sl } - #endif // _INC_SORTED_VECTOR_H diff --git a/src/common/sphere_library/sstringobjs.h b/src/common/sphere_library/sstringobjs.h index b14c8ad67..b6ab44dfd 100644 --- a/src/common/sphere_library/sstringobjs.h +++ b/src/common/sphere_library/sstringobjs.h @@ -123,7 +123,6 @@ class HeapString : public AbstractString */ - // Temporary string implementation. Works with thread-safe string // To create such string: // TemporaryString str; diff --git a/src/common/sphere_library/stypecast.h b/src/common/sphere_library/stypecast.h index 2a2d28360..278cb03d8 100644 --- a/src/common/sphere_library/stypecast.h +++ b/src/common/sphere_library/stypecast.h @@ -748,6 +748,4 @@ template int32 i32_from_usize_checked(T, bool) = delete; // disable template int64 i64_from_usize_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument - - #endif // _INC_STYPECAST_H diff --git a/src/common/sqlite/SQLite.cpp b/src/common/sqlite/SQLite.cpp index 63fc1fe07..e4d7d93ab 100644 --- a/src/common/sqlite/SQLite.cpp +++ b/src/common/sqlite/SQLite.cpp @@ -240,7 +240,7 @@ int CSQLite::ImportDB(lpctstr strInFileName) iErr = sqlite3_exec( in_db, UTF8MBSTR(pcQuery), nullptr, nullptr, &pcErrMsg ); if (iErr != SQLITE_OK) goto clean_and_ret; - + // Copy the table schema: how? // End @@ -468,13 +468,13 @@ bool CSQLite::r_Verb(CScript & s, CTextConsole * pSrc) { case LDBOV_CLOSE: if ( _fInMemory ) - return false; + return false; Close(); break; case LDBOV_CONNECT: if ( _fInMemory ) - return false; + return false; Open(s.GetArgStr()); break; diff --git a/src/game/CPathFinder.cpp b/src/game/CPathFinder.cpp index 5ef3fe530..733b4717a 100644 --- a/src/game/CPathFinder.cpp +++ b/src/game/CPathFinder.cpp @@ -47,7 +47,7 @@ CPathFinderPoint::CPathFinderPoint() : } /* -CPathFinderPoint::CPathFinderPoint(const CPointMap& pt) : +CPathFinderPoint::CPathFinderPoint(const CPointMap& pt) : _Parent(nullptr), _Walkable(false), _FValue(0), _GValue(0), _HValue(0) { //ADDTOCALLSTACK("CPathFinderPoint::CPathFinderPoint"); @@ -128,7 +128,7 @@ bool CPathFinder::FindPath() //A* algorithm // Take the point with the lowest FValue CPathFinderPoint *Current = *m_Opened.begin(); - + if ( Current == End ) { // Arrived to destination: reconstruct path and save it @@ -163,7 +163,7 @@ bool CPathFinder::FindPath() //A* algorithm */ // Binary (pre-sorted) search in our sorted_vector if (m_Closed.find(Cell) != sl::scont_bad_index()) - continue; + continue; ASSERT(Cell->_Walkable); // Linear search diff --git a/src/game/CPathFinder.h b/src/game/CPathFinder.h index 7ac8d3ae1..65b76f631 100644 --- a/src/game/CPathFinder.h +++ b/src/game/CPathFinder.h @@ -95,5 +95,4 @@ class CPathFinder }; - #endif // _INC_PATHFINDER_H diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 9b0d4670a..1ecf2ada5 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -1001,7 +1001,6 @@ bool CRegionWorld::r_LoadVal( CScript &s ) } - void CRegionWorld::r_WriteModified( CScript &s ) { ADDTOCALLSTACK("CRegionWorld::r_WriteModified"); diff --git a/src/game/CResourceCalc.cpp b/src/game/CResourceCalc.cpp index f9987ea3f..d1e7851f2 100644 --- a/src/game/CResourceCalc.cpp +++ b/src/game/CResourceCalc.cpp @@ -16,8 +16,8 @@ int CServerConfig::Calc_MaxCarryWeight( const CChar * pChar ) const ADDTOCALLSTACK("CServerConfig::Calc_MaxCarryWeight"); // How much weight can i carry before i can carry no more. (and move at all) // Amount of weight that can be carried Max: - // based on str - // RETURN: + // based on str + // RETURN: // Weight in tenths of stones i should be able to carry. ASSERT(pChar); @@ -50,7 +50,7 @@ int CServerConfig::Calc_CombatAttackSpeed( const CChar * pChar, const CItem * pW if ( pWeapon ) // If we have a weapon, base speed should match weapon's value. iBaseSpeed = pWeapon->GetSpeed(); int iSwingSpeed = 100; - + switch ( g_Cfg.m_iCombatSpeedEra ) { case 0: @@ -362,7 +362,7 @@ int CServerConfig::Calc_KarmaKill( CChar * pKill, NOTO_TYPE NotoThem ) if ( iKarmaChange < 0 ) iKarmaChange = 0; } - + // Check if the victim is a PC, then higher gain/loss. if ( pKill->m_pPlayer ) { @@ -431,16 +431,16 @@ int CServerConfig::Calc_StealingItem( CChar * pCharThief, CItem * pItem, CChar * int iDexMark = pCharMark->Stat_GetAdjusted(STAT_DEX); int iSkillMark = pCharMark->Skill_GetAdjusted( SKILL_STEALING ); int iWeightItem = pItem->GetWeight(); - + // int iDifficulty = iDexMark/2 + (iSkillMark/5) + g_Rand.GetVal(iDexMark/2) + IMulDivLL( iWeightItem, 4, WEIGHT_UNITS ); // Melt mod: int iDifficulty = (iSkillMark/5) + g_Rand.GetVal(iDexMark/2) + IMulDiv( iWeightItem, 4, WEIGHT_UNITS ); - + if ( pItem->IsItemEquipped()) iDifficulty += iDexMark/2 + pCharMark->Stat_GetAdjusted(STAT_INT); // This is REALLY HARD to do. if ( pCharThief->IsStatFlag( STATF_WAR )) // all keyed up. iDifficulty += g_Rand.GetVal( iDexMark/2 ); - + // return( iDifficulty ); // Melt mod: return (iDifficulty / 2); @@ -546,17 +546,17 @@ ushort CServerConfig::Calc_SpellManaCost(CChar* pCharCaster, const CSpellDef* pS ushort iCost = (ushort)pSpell->m_wManaUse; if (iLowerManaCost != 0) //LowerManaCost can be negative, and thus increasing the mana cost! iCost = (ushort)(iCost - ((iCost * iLowerManaCost) / 100)); - + if ( fScroll ) return iCost / 2; //spells cast from scrolls consume half of the mana. - + return iCost; } size_t CServerConfig::Calc_SpellReagentsConsume(CChar* pCharCaster, const CSpellDef* pSpell, CObjBase* pObj, bool fTest) { ADDTOCALLSTACK("CServerConfig::Calc_SpellReagentsConsume"); - + ASSERT(pCharCaster); ASSERT(pSpell); @@ -588,7 +588,7 @@ ushort CServerConfig::Calc_SpellTithingCost(CChar* pCharCaster, const CSpellDef* //Check for tithing points. if (g_Cfg.m_fReagentsRequired && !pCharCaster->m_pNPC && (pObj == pCharCaster)) { - + const CCPropsChar* pCCPChar = pCharCaster->GetComponentProps(); const CCPropsChar* pBaseCCPChar = pCharCaster->Base_GetDef()->GetComponentProps(); const int iLowerReagentCost = (int)pCharCaster->GetPropNum(pCCPChar, PROPCH_LOWERREAGENTCOST, pBaseCCPChar); //Also used for reducing Tithing points. @@ -627,7 +627,7 @@ bool CServerConfig::Calc_CurePoisonChance(const CItem* pPoison, int iCureLevel, if (!iPoisonLevel) //Lesser Poison (iPoisonLevel 0) is always cured no matter the potion or spell/skill level value return true; - //Cure Chance taken from: + //Cure Chance taken from: if (iCureLevel < 410) //Lesser Cure Potion or our healing/veterinary/magery skill is less than 41.0 https://www.uoguide.com/Lesser_Cure_Potion { switch (iPoisonLevel) @@ -646,7 +646,7 @@ bool CServerConfig::Calc_CurePoisonChance(const CItem* pPoison, int iCureLevel, break; } } - else if (iCureLevel < 1010) //Cure Potion or our healing/veterinary/magery skill is between 41.0 and 100.9 https://www.uoguide.com/Cure_Potion + else if (iCureLevel < 1010) //Cure Potion or our healing/veterinary/magery skill is between 41.0 and 100.9 https://www.uoguide.com/Cure_Potion { switch (iPoisonLevel) { diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index f5591a2ef..e4b63f234 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -150,7 +150,6 @@ void CItemsList::AddItemToSector( CItem * pItem ) } - ////////////////////////////////////////////////////////////////// // -CSectorBase diff --git a/src/game/CServerConfig.h b/src/game/CServerConfig.h index 7f5d3b0e5..499b6ef93 100644 --- a/src/game/CServerConfig.h +++ b/src/game/CServerConfig.h @@ -1152,7 +1152,6 @@ typedef std::map KRGumpsMap; } g_Cfg; - #define IsSetEF(ef) ((g_Cfg._uiExperimentalFlags & ef) != 0) #define IsSetOF(of) ((g_Cfg._uiOptionFlags & of) != 0) #define IsSetCombatFlags(cf) ((g_Cfg.m_iCombatFlags & cf) != 0) diff --git a/src/game/CServerDef.h b/src/game/CServerDef.h index 120c5332f..3619d4c00 100644 --- a/src/game/CServerDef.h +++ b/src/game/CServerDef.h @@ -78,7 +78,7 @@ class CServerDef : public CScriptObj public: lpctstr GetStatus() const noexcept; - + size_t StatGet( SERV_STAT_TYPE i ) const; void StatInc( SERV_STAT_TYPE i ); void StatDec( SERV_STAT_TYPE i ); diff --git a/src/game/CStartLoc.h b/src/game/CStartLoc.h index 3321017ab..b455b17ad 100644 --- a/src/game/CStartLoc.h +++ b/src/game/CStartLoc.h @@ -19,7 +19,7 @@ class CStartLoc // The start locations for creating a new char. m_sArea(pszArea), iClilocDescription(1149559) {} ~CStartLoc() noexcept = default; - + CStartLoc(const CStartLoc& copy) = delete; CStartLoc& operator=(const CStartLoc& other) = delete; }; diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index 4642c673c..c830233dc 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -216,7 +216,6 @@ static void ReportGarbageCollection(CObjBase * pObj, int iResultCode) } - ////////////////////////////////////////////////////////////////// // -CWorldThread diff --git a/src/game/CWorldCache.h b/src/game/CWorldCache.h index 6d3f3e384..0c2bb2bf7 100644 --- a/src/game/CWorldCache.h +++ b/src/game/CWorldCache.h @@ -13,7 +13,7 @@ class CWorldCache friend class CWorldMap; int64 _iTimeLastMapBlockCacheCheck; - + using MapBlockCacheCont = std::unique_ptr; // Info about a single map block using MapBlockCache = std::unique_ptr; // An element per each map block MapBlockCache _mapBlocks[MAP_SUPPORTED_QTY]; // An element per each map diff --git a/src/game/CWorldGameTime.cpp b/src/game/CWorldGameTime.cpp index caf99086f..248b196e0 100644 --- a/src/game/CWorldGameTime.cpp +++ b/src/game/CWorldGameTime.cpp @@ -22,7 +22,7 @@ int64 CWorldGameTime::GetCurrentTimeInGameMinutes( int64 basetime ) noexcept // // 8 real world seconds = 1 game minute. // 1 real minute = 7.5 game minutes // 3.2 hours = 1 game day. - + return ( basetime / g_Cfg.m_iGameMinuteLength ); } @@ -45,7 +45,7 @@ int64 CWorldGameTime::GetNextNewMoon( bool fMoonIndex ) // static // Get the game time when this cycle will start int64 iNewStart = (int64)(iNextMonth - (double)(iNextMonth % iSynodic)); return iNewStart * g_Cfg.m_iGameMinuteLength; - + } uint CWorldGameTime::GetMoonPhase(bool fMoonIndex) // static diff --git a/src/game/CWorldGameTime.h b/src/game/CWorldGameTime.h index 1832fc980..9637fd0ab 100644 --- a/src/game/CWorldGameTime.h +++ b/src/game/CWorldGameTime.h @@ -19,7 +19,7 @@ class CWorldGameTime static int64 GetCurrentTimeInGameMinutes(int64 basetime) noexcept; // return game world minutes static int64 GetCurrentTimeInGameMinutes() noexcept; - + static uint GetMoonPhase( bool fMoonIndex = false ); static int64 GetNextNewMoon( bool fMoonIndex ); }; diff --git a/src/game/CWorldImport.cpp b/src/game/CWorldImport.cpp index c8e74c450..821ac1ffc 100644 --- a/src/game/CWorldImport.cpp +++ b/src/game/CWorldImport.cpp @@ -836,7 +836,6 @@ bool CWorld::Import( lpctstr pszFilename, const CChar * pSrc, word wModeFlags, i } - bool CWorld::DumpAreas( CTextConsole * pSrc, lpctstr pszFilename ) { ADDTOCALLSTACK("CWorld::DumpAreas"); @@ -866,7 +865,6 @@ bool CWorld::DumpAreas( CTextConsole * pSrc, lpctstr pszFilename ) } - bool CWorld::Export( lpctstr pszFilename, const CChar * pSrc, word wModeFlags, int iDist, short dx, short dy ) { ADDTOCALLSTACK("CWorld::Export"); diff --git a/src/game/CWorldMap.cpp b/src/game/CWorldMap.cpp index 99d11bae5..c152466fb 100644 --- a/src/game/CWorldMap.cpp +++ b/src/game/CWorldMap.cpp @@ -221,7 +221,6 @@ IT_TYPE CWorldMap::GetTerrainItemType(dword dwTerrainIndex) // static } - ////////////////////////////////////////////////////////////////// // Map reading and blocking. diff --git a/src/game/CWorldSearch.cpp b/src/game/CWorldSearch.cpp index 1665f9c08..c5f91836b 100644 --- a/src/game/CWorldSearch.cpp +++ b/src/game/CWorldSearch.cpp @@ -187,7 +187,7 @@ CItem* CWorldSearch::GetItem() { ASSERT(_eSearchType == ws_search_e::None); _eSearchType = ws_search_e::Items; - + LoadSectorObjs(_pSector->m_Items); } else diff --git a/src/game/CWorldTickingList.cpp b/src/game/CWorldTickingList.cpp index f36177415..63ebb224f 100644 --- a/src/game/CWorldTickingList.cpp +++ b/src/game/CWorldTickingList.cpp @@ -4,7 +4,6 @@ #include "CWorldTicker.h" - void CWorldTickingList::AddObjSingle(int64 iTimeout, CTimedObject* pObj, bool fForce) // static { //The lock on pObj should already be acquired by CTimedObject::SetTimeout diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index fa121591f..fbf6e0c51 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -3360,7 +3360,6 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo } - bool CChar::r_LoadVal( CScript & s ) { ADDTOCALLSTACK("CChar::r_LoadVal"); diff --git a/src/game/chars/CCharMemory.cpp b/src/game/chars/CCharMemory.cpp index e823b16ee..f18b8dc8e 100644 --- a/src/game/chars/CCharMemory.cpp +++ b/src/game/chars/CCharMemory.cpp @@ -133,7 +133,7 @@ bool CChar::Memory_UpdateFlags(CItemMemory * pMemory) else iCheckTime = 20 * 60 * MSECS_PER_SEC; - pMemory->SetTimeout(iCheckTime); // update its decay time. + pMemory->SetTimeout(iCheckTime); // update its decay time. CChar * pCharLink = pMemory->m_uidLink.CharFind(); if (pCharLink) { diff --git a/src/game/chars/CCharNPCAct_Magic.cpp b/src/game/chars/CCharNPCAct_Magic.cpp index 79e809b52..f1d3513e1 100644 --- a/src/game/chars/CCharNPCAct_Magic.cpp +++ b/src/game/chars/CCharNPCAct_Magic.cpp @@ -143,8 +143,8 @@ void CChar::NPC_AddSpellsFromBook(CItem * pBook) } } -// cast a spell if i can ? -// or i can throw or use archery ? +// Cast a spell if I can? +// Or I can throw or use archery? // RETURN: // false = revert to melee type fighting. bool CChar::NPC_FightMagery(CChar * pChar) @@ -152,7 +152,7 @@ bool CChar::NPC_FightMagery(CChar * pChar) ADDTOCALLSTACK("CChar::NPC_FightMagery"); ASSERT(m_pNPC); - if (!NPC_FightMayCast(false)) // not checking skill here since it will do a search later and it's an expensive function. + if (!NPC_FightMayCast(false)) // Not checking skill here since it will do a search later, and it's an expensive function. { return false; } @@ -162,7 +162,7 @@ bool CChar::NPC_FightMagery(CChar * pChar) CObjBase * pTarg = pChar; if (pWand) { - // If the item is really a wand and have it charges it's a valid wand, if not ... we get rid of it. + // If the item is really a wand and have its charges it's a valid wand, if not ... we get rid of it. if (pWand->GetType() != IT_WAND || pWand->m_itWeapon.m_spellcharges <= 0 || !pWand->IsAttr(ATTR_MAGIC)) pWand = nullptr; } @@ -193,13 +193,13 @@ bool CChar::NPC_FightMagery(CChar * pChar) { if (iDist < 4 || iDist > 8) // Here is fine? NPC_Act_Follow(false, g_Rand.GetVal(3) + 2, true); - + return true; } return false; } - // We have the total count of spells inside iSpellCount, so we use 'iRandSpell' to store a rand representing the spell that will be casted + // We have the total count of spells inside iSpellCount, so we use 'iRandSpell' to store a rand representing the spell that will be cast. uchar iRandSpell = (uchar)(g_Rand.GetVal2(0, iSpellCount - 1)); //Spells are being stored using a vector, so it's assumed to be zero-based. bool bSpellSuccess = false, bWandUse = false, bIgnoreAITargetChoice = false; int iHealThreshold = g_Cfg.m_iNPCHealthreshold; @@ -212,7 +212,7 @@ bool CChar::NPC_FightMagery(CChar * pChar) while( iRandSpell < iSpellCount || bWandUse ) { SPELL_TYPE spell = SPELL_NONE; - + if (!bWandUse) spell = m_pNPC->Spells_GetAt(iRandSpell); else @@ -247,7 +247,7 @@ bool CChar::NPC_FightMagery(CChar * pChar) break; } iRandSpell++; - + } if (!bSpellSuccess) return false; @@ -283,7 +283,7 @@ bool CChar::NPC_FightCast(CObjBase * &pTarg, CObjBase * pSrc, SPELL_TYPE &spell, return false; int iSkillReq = 0; - + if (!pSpellDef->GetPrimarySkill(&skill, &iSkillReq)) skill = SKILL_MAGERY; @@ -338,7 +338,7 @@ bool CChar::NPC_FightCast(CObjBase * &pTarg, CObjBase * pSrc, SPELL_TYPE &spell, } } - // i cannot cast this on self. ok, then friends only + // I cannot cast this on self. ok, then friends only. if (pSpellDef->IsSpellType(SPELLFLAG_TARG_NOSELF)) { pFriend[0] = pFriend[1]; diff --git a/src/game/chars/CCharNPCPet.cpp b/src/game/chars/CCharNPCPet.cpp index 8a870208a..371659d30 100644 --- a/src/game/chars/CCharNPCPet.cpp +++ b/src/game/chars/CCharNPCPet.cpp @@ -23,7 +23,7 @@ void CChar::NPC_OnPetCommand( bool fSuccess, CChar * pMaster ) if ( !CanSee( pMaster ) ) return; - // i take a command from my master. + // I take a command from my master. if ( NPC_CanSpeak() ) Speak( fSuccess ? g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_SUCCESS ) : g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_FAILURE ) ); else @@ -412,14 +412,14 @@ bool CChar::NPC_OnHearPetCmdTarg( int iCmd, CChar *pSrc, CObjBase *pObj, const C case PC_TRANSFER: // Can't transfer ownership of a conjured monster, it can be controlled only by its summoner if (IsStatFlag(STATF_CONJURED)) - { + { pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_TRANSFER_SUMMONED)); return false; } break; default: { - // All others commands are avaible only to pet owner + // All others commands are available only to pet owner. if ( !NPC_IsOwnedBy(pSrc, true) ) return false; } @@ -642,7 +642,7 @@ bool CChar::NPC_CheckHirelingStatus() { ADDTOCALLSTACK("CChar::NPC_CheckHirelingStatus"); ASSERT(m_pNPC); - // Am i happy at the moment ? + // Am I happy at the moment? // If not then free myself. // // RETURN: @@ -762,7 +762,7 @@ bool CChar::NPC_OnHirePay( CChar * pCharSrc, CItemMemory * pMemory, CItem * pGol // Put all my loot cash away. ContentConsume( CResourceID(RES_TYPEDEF,IT_GOLD), INT32_MAX, 0 ); - // Mark all my stuff ATTR_OWNED - i won't give it away. + // Mark all my stuff ATTR_OWNED - I won't give it away. ContentAttrMod( ATTR_OWNED, true ); NPC_PetSetOwner( pCharSrc ); @@ -791,7 +791,7 @@ bool CChar::NPC_OnHireHear( CChar * pCharSrc ) { if ( pMemory->IsMemoryTypes(MEMORY_IPET|MEMORY_FRIEND)) { - // Next gold i get goes toward hire. + // Next gold I get goes toward hire. pMemory->m_itEqMemory.m_Action = NPC_MEM_ACT_SPEAK_HIRE; NPC_OnHirePayMore( nullptr, uiWage, false ); return true; @@ -826,7 +826,7 @@ bool CChar::NPC_SetVendorPrice( CItem * pItem, int iPrice ) ASSERT(m_pNPC); // player vendors. // CLIMODE_PROMPT_VENDOR_PRICE - // This does not check who is setting the price if if it is valid for them to do so. + // This does not check who is setting the price if it is valid for them to do so. if ( ! NPC_IsVendor()) return false; @@ -901,7 +901,7 @@ void CChar::NPC_PetDesert() ASSERT(m_pNPC); // If the monster has brain_berserk (i.e. energy vortex): when the player summons it, his CurFollower property rises. // If the master attacks the berserk pet, the pet deserts him and the master's CurFollower goes down. If we don't prevent - // berserk monsters to desert the master, he can do this trick to summon a lot of energy vortexes without any cost to his followers property. + // berserk monsters to desert the master, he can do this trick to summon a lot of energy vortexes without any cost to his followers' property. if (m_pNPC->m_Brain == NPCBRAIN_BERSERK) return; @@ -922,6 +922,6 @@ void CChar::NPC_PetDesert() snprintf(pszMsg, Str_TempLength(), g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_DECIDE_MASTER), GetName()); Speak(pszMsg); - // free to do as i wish ! + // Free to do as I wish! NPC_PetRelease(); } diff --git a/src/game/chars/CCharPlayer.h b/src/game/chars/CCharPlayer.h index cac375775..d3447300b 100644 --- a/src/game/chars/CCharPlayer.h +++ b/src/game/chars/CCharPlayer.h @@ -84,7 +84,7 @@ struct CCharPlayer void r_WriteChar( CChar * pChar, CScript & s ); bool r_WriteVal( CChar * pChar, lpctstr ptcKey, CSString & s ); bool r_LoadVal( CChar * pChar, CScript & s ); - + public: CCharPlayer( CChar * pChar, CAccount * pAccount ); diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index d80cd1ce8..d7aa5ee7b 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -3410,7 +3410,6 @@ void CChar::Spell_CastFail(bool fAbort) } - } int CChar::Spell_CastStart() diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index b54da41f0..b0bc769ba 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -151,7 +151,7 @@ CItemContainer *CChar::GetBank( LAYER_TYPE layer ) layer = LAYER_BANKBOX; break; } - + CItem *pItemTest = LayerFind(layer); CItemContainer *pBankBox = dynamic_cast(pItemTest); if ( pBankBox ) @@ -565,7 +565,7 @@ NPCBRAIN_TYPE CChar::GetNPCBrainAuto() const noexcept //ADDTOCALLSTACK("CChar::GetNPCBrainAuto"); // Auto-detect the brain const CREID_TYPE id = GetDispID(); - + switch (id) { //TODO: add other dragons @@ -686,7 +686,7 @@ byte CChar::GetModeFlag( const CClient *pViewer ) const mode |= CHARMODE_WAR; uint64 iFlags = STATF_SLEEPING; - + //When you want change the color of character anim, you must evitate to send CHARMODE_INVIS because the anim will automaticly be grey //Here we check if it's define on the ini that you need override the color if ( !g_Cfg.m_iColorInvis ) //Serv.ColorInvis @@ -695,7 +695,7 @@ byte CChar::GetModeFlag( const CClient *pViewer ) const iFlags |= STATF_HIDDEN; if ( !g_Cfg.m_iColorInvisSpell ) //serv.ColorInvisSpell iFlags |= STATF_INVISIBLE; - + if ( IsStatFlag(iFlags) ) // Checking if I have any of these settings enabled on the ini and I have any of them, if so ... CHARMODE_INVIS is set and color applied. mode |= CHARMODE_INVIS; //When sending CHARMODE_INVIS state to client, your character anim are grey @@ -1347,7 +1347,7 @@ bool CChar::CanTouch( const CObjBase *pObj ) default: break; } - + if ( !fDeathImmune && IsStatFlag(STATF_FREEZE) ) { if ( !fFreezeImmune && !pItem->IsAttr(ATTR_CANUSE_PARALYZED) ) @@ -1494,7 +1494,7 @@ bool CChar::CanHear( const CObjBaseTemplate *pSrc, TALKMODE_TYPE mode ) const else { pSrcRegion = dynamic_cast(pSrc->GetTopPoint().GetRegion(REGION_TYPE_MULTI|REGION_TYPE_AREA)); - } + } if ( !pSrcRegion || !m_pArea ) // should not happen really. return false; @@ -1561,7 +1561,7 @@ bool CChar::CanHear( const CObjBaseTemplate *pSrc, TALKMODE_TYPE mode ) const if ( m_pArea == pSrcRegion )// same region is always ok. return true; - // Different region (which can be a multi or an areadef). + // Different region (which can be a multi or an areadef). if ( IsSetOF(OF_NoHouseMuteSpeech) ) return true; @@ -1934,7 +1934,7 @@ bool CChar::CanStandAt(CPointMap *ptDest, const CRegion* pArea, uint64 uiMyMovem } else if (uiMapPointMovementFlags & CAN_I_CLIMB) { - // If dwBlockFlags & CAN_I_CLIMB, then it's a "climbable" item (and i can climb it, + // If dwBlockFlags & CAN_I_CLIMB, then it's a "climbable" item (and i can climb it, // since i don't have CAN_I_CLIMB in uiBlockedBy) } else diff --git a/src/game/chars/CStoneMember.cpp b/src/game/chars/CStoneMember.cpp index d7401a285..55f5b90de 100644 --- a/src/game/chars/CStoneMember.cpp +++ b/src/game/chars/CStoneMember.cpp @@ -236,7 +236,7 @@ bool CStoneMember::r_LoadVal( CScript & s ) // Load an item Script default: return false; } - } + } else if ( GetLinkUID().IsItem() ) { switch ( iIndex ) diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index e16ff2ee5..985cf9667 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -592,7 +592,6 @@ void CClient::Event_Item_Drop( CUID uidItem, CPointMap pt, CUID uidOn, uchar gri } - void CClient::Event_Skill_Use( SKILL_TYPE skill ) // Skill is clicked on the skill list { ADDTOCALLSTACK("CClient::Event_Skill_Use"); @@ -725,7 +724,6 @@ void CClient::Event_Skill_Use( SKILL_TYPE skill ) // Skill is clicked on the ski } - bool CClient::Event_CheckWalkBuffer(byte rawdir) { ADDTOCALLSTACK("CClient::Event_CheckWalkBuffer"); @@ -1613,7 +1611,6 @@ void CClient::Event_Profile( byte fWriteMode, CUID uid, lpctstr pszProfile, int } - void CClient::Event_MailMsg( CUID uid1, CUID uid2 ) { ADDTOCALLSTACK("CClient::Event_MailMsg"); @@ -1647,7 +1644,6 @@ void CClient::Event_MailMsg( CUID uid1, CUID uid2 ) } - void CClient::Event_ToolTip( CUID uid ) { ADDTOCALLSTACK("CClient::Event_ToolTip"); diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index a633c6557..f7ae016c4 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -2646,7 +2646,7 @@ void CClient::addGlobalChatStatusToggle() } tchar* pszXML = Str_GetTemp(); - sprintf(pszXML, "", + sprintf(pszXML, "", CGlobalChatChanMember::GetJID(), static_cast(CSTime::GetCurrentTime().GetTime()), m_pChar->GetName(), iShow); CGlobalChatChanMember::SetVisible(static_cast(iShow)); @@ -2881,7 +2881,7 @@ byte CClient::Setup_Start( CChar * pChar ) // Send character startup stuff to pl /* * // If ever we want to change how timers are suspended... - * + * if (m_pChar->IsPlayer()) { // When a character logs out, its timer and its contents' timers has to freeze. @@ -2968,7 +2968,7 @@ byte CClient::Setup_Delete( dword iSlot ) // Deletion of character } } - + if (pChar->Delete()) // Do the scripts allow to delete the char? { @@ -2983,7 +2983,7 @@ byte CClient::Setup_Delete( dword iSlot ) // Deletion of character { return PacketDeleteError::InvalidRequest; } - + } byte CClient::Setup_ListReq( const char * pszAccName, const char * pszPassword, bool fTest ) diff --git a/src/game/clients/CParty.cpp b/src/game/clients/CParty.cpp index 6f2b7fbac..b97a5d0b0 100644 --- a/src/game/clients/CParty.cpp +++ b/src/game/clients/CParty.cpp @@ -453,7 +453,7 @@ bool CPartyDef::AcceptEvent( CChar *pCharAccept, CUID uidInviter, bool bForced, else return false; } - + if (IsTrigUsed(TRIGGER_PARTYADD)) { if ( pCharAccept->OnTrigger(CTRIG_PartyAdd, CScriptTriggerArgsPtr{}, pCharInviter) == TRIGRET_RET_TRUE ) @@ -553,7 +553,7 @@ bool CPartyDef::r_GetRef( lpctstr &ptcKey, CScriptObj *&pRef ) } bool CPartyDef::r_LoadVal( CScript &s ) -{ +{ ADDTOCALLSTACK("CPartyDef::r_LoadVal"); EXC_TRY("LoadVal"); lpctstr ptcKey = s.GetKey(); @@ -587,7 +587,7 @@ bool CPartyDef::r_LoadVal( CScript &s ) lpctstr ptcArg = s.GetArgStr(&fQuoted); m_TagDefs.SetStr(ptcKey, fQuoted, ptcArg, fZero); } break; - + default: return false; } @@ -752,7 +752,7 @@ bool CPartyDef::r_Verb( CScript &s, CTextConsole *pSrc ) if ( pCharMaster && !fForced ) pCharMaster->SetKeyNum("PARTY_LASTINVITE", toAdd.GetObjUID()); - + return CPartyDef::AcceptEvent(pCharAdd, GetMaster(), fForced); } break; @@ -884,10 +884,10 @@ bool CPartyDef::r_Verb( CScript &s, CTextConsole *pSrc ) } bool CPartyDef::r_Load( CScript &s ) -{ +{ ADDTOCALLSTACK("CPartyDef::r_Load"); UnreferencedParameter(s); - return false; + return false; } lpctstr CPartyDef::GetDefStr( lpctstr ptcKey, bool fZero ) const diff --git a/src/game/components/CCChampion.cpp b/src/game/components/CCChampion.cpp index 5e94660a3..4c3a45feb 100644 --- a/src/game/components/CCChampion.cpp +++ b/src/game/components/CCChampion.cpp @@ -22,7 +22,7 @@ #include "../CWorldGameTime.h" #include "CCChampion.h" // predef header. #include -#include +#include #define CANDLESNEXTRED 4 #define MAXSPAWN 2400 diff --git a/src/game/components/CCChampion.h b/src/game/components/CCChampion.h index 661840406..2a6940c09 100644 --- a/src/game/components/CCChampion.h +++ b/src/game/components/CCChampion.h @@ -326,7 +326,6 @@ class CCChampion : public CComponent }; - /** * @brief Storing information from scripted [CHAMPION ] resource */ diff --git a/src/game/game_enums.h b/src/game/game_enums.h index 637174639..3aaea7e3d 100644 --- a/src/game/game_enums.h +++ b/src/game/game_enums.h @@ -143,7 +143,6 @@ enum CLIMODE_TYPE // What mode is the client to server connection in ? (waiting }; - ////////////////////////////////////////////////////////////////////////// // Buff Icons diff --git a/src/game/items/CItemCorpse.cpp b/src/game/items/CItemCorpse.cpp index a5fa00a7f..4a435e5d3 100644 --- a/src/game/items/CItemCorpse.cpp +++ b/src/game/items/CItemCorpse.cpp @@ -38,7 +38,7 @@ bool CItemCorpse::IsCorpseResurrectable(CChar * pCharHealer, CChar * pCharGhost) { return false; } - + //Check if the ghost is visible when targetting the corpse. if (pCharGhost->IsStatFlag(STATF_INSUBSTANTIAL)) { @@ -217,7 +217,7 @@ CItemCorpse * CChar::MakeCorpse( bool fFrontFall ) if ( !(uiFlags & DEATH_NOLOOTDROP) ) // move non-newbie contents of the pack to corpse DropAll( pCorpse ); - + if (iDecayTimer != -1) { pCorpse->MoveToDecay(GetTopPoint(), iDecayTimer); diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index d110173a2..2a86871f4 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -139,9 +139,9 @@ CItemMulti::~CItemMulti() bool CItemMulti::Delete(bool fForce) { - + RemoveAllComponents(); - + const CChar* pOwner = GetOwner().CharFind(); if (pOwner && pOwner->m_pPlayer) // If pOwner is null it means we are redeeming the multi or we manually added a multi. In the first case DelMulti is already called in the Redeed multi method. { @@ -149,7 +149,7 @@ bool CItemMulti::Delete(bool fForce) if (pMultiStorage) pMultiStorage->DelMulti(GetUID()); } - + //return CObjBase::Delete(fForce); return CItem::Delete(fForce); } @@ -3640,7 +3640,7 @@ void CMultiStorage::DelHouse(const CUID& uidHouse) { return; } - + if (_lHouses.find(uidHouse) != _lHouses.end()) { CItemMulti *pMulti = static_cast(uidHouse.ItemFind()); diff --git a/src/game/items/CItemStone.cpp b/src/game/items/CItemStone.cpp index e50802ead..aacbe0af1 100644 --- a/src/game/items/CItemStone.cpp +++ b/src/game/items/CItemStone.cpp @@ -231,7 +231,7 @@ bool CItemStone::r_GetRef( lpctstr & ptcKey, CScriptObj * & pRef ) if ( nNumber == i ) { - pRef = pMember; + pRef = pMember; return true; } @@ -253,7 +253,7 @@ bool CItemStone::r_GetRef( lpctstr & ptcKey, CScriptObj * & pRef ) CStoneMember * pMemberGuild = GetMember( pMemberChar ); if ( pMemberGuild ) { - pRef = pMemberGuild; + pRef = pMemberGuild; return true; } } @@ -271,7 +271,7 @@ bool CItemStone::r_GetRef( lpctstr & ptcKey, CScriptObj * & pRef ) for ( int i = 0; pMember != nullptr; pMember = pMember->GetNext() ) { - if ( pMember->GetLinkUID().IsChar() ) + if ( pMember->GetLinkUID().IsChar() ) continue; if ( nNumber == i ) @@ -298,7 +298,7 @@ bool CItemStone::r_GetRef( lpctstr & ptcKey, CScriptObj * & pRef ) CStoneMember * pGuild = GetMember( pMemberGuild ); if ( pGuild ) { - pRef = pGuild; + pRef = pGuild; return true; } } @@ -439,7 +439,7 @@ bool CItemStone::r_LoadVal( CScript & s ) // Load an item Script m_sCharter[i] = s.GetArgStr(); return true; } - + return CItem::r_LoadVal(s); EXC_CATCH; @@ -487,7 +487,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr { for (; pMember != nullptr; pMember = pMember->GetNext()) { - if (!pMember->GetLinkUID().IsChar()) + if (!pMember->GetLinkUID().IsChar()) continue; ++i; @@ -505,12 +505,12 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr for ( int i = 0 ; pMember != nullptr; pMember = pMember->GetNext() ) { - if (!pMember->GetLinkUID().IsChar()) + if (!pMember->GetLinkUID().IsChar()) continue; - + if ( nNumber == i ) { - if (!pszCmd[0]) + if (!pszCmd[0]) return true; return pMember->r_WriteVal(pszCmd, sVal, pSrc); @@ -575,7 +575,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr { for (; pMember != nullptr; pMember = pMember->GetNext()) { - if (pMember->GetLinkUID().IsChar()) + if (pMember->GetLinkUID().IsChar()) continue; i++; @@ -593,12 +593,12 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr for ( int i = 0 ; pMember != nullptr; pMember = pMember->GetNext() ) { - if (pMember->GetLinkUID().IsChar()) + if (pMember->GetLinkUID().IsChar()) continue; - + if ( nNumber == i ) { - if (!pszCmd[0]) + if (!pszCmd[0]) return true; return pMember->r_WriteVal(pszCmd, sVal, pSrc); @@ -720,7 +720,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr sVal = pResult ? pResult->GetValStr() : ""; } return true; - + case STC_MASTER: { CChar * pMaster = GetMaster(); @@ -729,7 +729,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr : g_ExprGlobals.mtEngineLockedReader()->m_VarDefs.GetKeyStr("STONECONFIG_VARIOUSNAME_PENDVOTE"); } return true; - + case STC_MASTERGENDERTITLE: { CChar * pMaster = GetMaster(); @@ -742,14 +742,14 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr : "STONECONFIG_VARIOUSNAME_MASTERGENDERMALE"); } return true; - + case STC_MASTERTITLE: { CStoneMember * pMember = GetMasterMember(); sVal = (pMember) ? pMember->GetTitle() : ""; } return true; - + case STC_MASTERUID: { CChar * pMaster = GetMaster(); @@ -759,7 +759,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr sVal.FormatHex( (dword) 0 ); } return true; - + default: return (fNoCallParent ? false : CItem::r_WriteVal( ptcKey, sVal, pSrc )); } @@ -1293,7 +1293,7 @@ bool CItemStone::CheckValidMember( CStoneMember * pMember ) } // just delete this member. (it is mislinked) - DEBUG_ERR(( "Stone UID=0%x has mislinked member uid=0%x\n", + DEBUG_ERR(( "Stone UID=0%x has mislinked member uid=0%x\n", (dword) GetUID(), (dword) pMember->GetLinkUID())); return false; } diff --git a/src/game/items/CItemVendable.cpp b/src/game/items/CItemVendable.cpp index 05ea51752..11c02d8cd 100644 --- a/src/game/items/CItemVendable.cpp +++ b/src/game/items/CItemVendable.cpp @@ -201,7 +201,7 @@ dword CItemVendable::GetVendorPrice( int iConvertFactor , bool forselling ) if ( llPrice <= 0 ) // No price/overrride.value set, we use the value of item. { - + if ( IsType(IT_DEED) ) { // Deeds just represent the item they are deeding. @@ -212,9 +212,9 @@ dword CItemVendable::GetVendorPrice( int iConvertFactor , bool forselling ) else pItemDef = Item_GetDef(); - llPrice = pItemDef->GetMakeValue(GetQuality()); //If value is a range(ex:10,20), value change depending quality + llPrice = pItemDef->GetMakeValue(GetQuality()); //If value is a range(ex:10,20), value change depending quality } - + llPrice += IMulDivLL(llPrice, maximum(iConvertFactor, -100), 100); if ( llPrice > UINT32_MAX ) return UINT32_MAX; diff --git a/src/game/items/CItemVendable.h b/src/game/items/CItemVendable.h index babb652ab..2d46d10dd 100644 --- a/src/game/items/CItemVendable.h +++ b/src/game/items/CItemVendable.h @@ -31,7 +31,7 @@ class CItemVendable : public CItem void SetPlayerVendorPrice( dword dwVal ); dword GetBasePrice() const; - dword GetVendorPrice( int iConvertFactor , bool forselling); + dword GetVendorPrice( int iConvertFactor , bool forselling); bool IsValidSaleItem( bool fBuyFromVendor ) const; bool IsValidNPCSaleItem() const; diff --git a/src/game/uo_files/CUOMapList.h b/src/game/uo_files/CUOMapList.h index 334c42968..aa1457c71 100644 --- a/src/game/uo_files/CUOMapList.h +++ b/src/game/uo_files/CUOMapList.h @@ -95,7 +95,7 @@ extern class CUOMapList int GetMapFileNum(int map) const; int GetMapID(int map) const; - + ///@} } g_MapList; diff --git a/src/game/uo_files/CUOMobtypes.h b/src/game/uo_files/CUOMobtypes.h index d4514bddf..19449d2ff 100644 --- a/src/game/uo_files/CUOMobtypes.h +++ b/src/game/uo_files/CUOMobtypes.h @@ -46,7 +46,4 @@ class CUOMobTypes }; - - - #endif // _INC_CUOMOBTYPES_H diff --git a/src/network/CIPHistoryManager.h b/src/network/CIPHistoryManager.h index 0658a2a78..a12d22834 100644 --- a/src/network/CIPHistoryManager.h +++ b/src/network/CIPHistoryManager.h @@ -38,7 +38,6 @@ struct HistoryIP typedef std::deque IPHistoryList; - /*************************************************************************** * * diff --git a/src/network/CNetState.cpp b/src/network/CNetState.cpp index eec21d7fb..21b7b167f 100644 --- a/src/network/CNetState.cpp +++ b/src/network/CNetState.cpp @@ -200,7 +200,7 @@ void CNetState::init(SOCKET socket, CSocketAddress addr) // Disable NAGLE algorythm for data compression/coalescing. // Send as fast as we can. we handle packing ourselves. - + int iSockFlag = 1; iSockRet = m_socket.SetSockOpt(TCP_NODELAY, &iSockFlag, sizeof(iSockFlag), IPPROTO_TCP); CheckReportNetAPIErr(iSockRet, "NetState::init.TCP_NODELAY"); diff --git a/src/network/CNetworkOutput.cpp b/src/network/CNetworkOutput.cpp index 445c24df1..67a908bb3 100644 --- a/src/network/CNetworkOutput.cpp +++ b/src/network/CNetworkOutput.cpp @@ -48,7 +48,6 @@ static void CALLBACK SendCompleted_Winsock(DWORD dwError, DWORD cbTransferred, L #endif - CNetworkOutput::CNetworkOutput() : m_thread(nullptr) { m_encryptBuffer = new byte[MAX_BUFFER]; diff --git a/src/network/CNetworkOutput.h b/src/network/CNetworkOutput.h index c4cabbfe7..217b44c63 100644 --- a/src/network/CNetworkOutput.h +++ b/src/network/CNetworkOutput.h @@ -56,5 +56,4 @@ class CNetworkOutput }; - #endif // _INC_NETWORKOUTPUT_H diff --git a/src/network/CSocket.cpp b/src/network/CSocket.cpp index 9014f36bc..a59c7ee3d 100644 --- a/src/network/CSocket.cpp +++ b/src/network/CSocket.cpp @@ -366,7 +366,7 @@ SOCKET CSocket::Accept( struct sockaddr_in * pSockAddrIn ) const SOCKET CSocket::Accept( CSocketAddress & SockAddr ) const { - // RETURN: Error = hSocketClient < 0 || hSocketClient == INVALID_SOCKET + // RETURN: Error = hSocketClient < 0 || hSocketClient == INVALID_SOCKET struct sockaddr_in SockAddrIn; SOCKET hSocket = Accept( &SockAddrIn ); SockAddr.SetAddrPort( SockAddrIn ); @@ -452,7 +452,7 @@ int CSocket::GetSockOpt( int nOptionName, void * optval, int * poptlen, int nLev // RETURN: length sent return( WSASend( m_hSocket, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpOverlapped, lpCompletionRoutine )); } - + void CSocket::ClearAsync() { // TO BE CALLED IN CClient destructor !!! diff --git a/src/network/CSocket.h b/src/network/CSocket.h index 1dc7f9c6e..242714617 100644 --- a/src/network/CSocket.h +++ b/src/network/CSocket.h @@ -1,6 +1,6 @@ /** * @file CSocket.h -* +* */ #ifndef _INC_CSOCKET_H @@ -10,7 +10,7 @@ #ifdef _WIN32 #include // this needs to be included after common.h, which sets some defines and then includes windows.h, since winsock2.h needs windows.h typedef int socklen_t; -#else +#else // else assume LINUX #include #include @@ -85,7 +85,7 @@ struct CSocketAddress : public CSocketAddressIP explicit CSocketAddress(const sockaddr_in & SockAddrIn); explicit CSocketAddress(const CSocketAddressIP&) = delete; explicit CSocketAddress(CSocketAddressIP&) = delete; - + bool operator==( const CSocketAddress & SockAddr ) const; bool operator==( const struct sockaddr_in & SockAddrIn ) const; CSocketAddress& operator = (const struct sockaddr_in& SockAddrIn); @@ -107,9 +107,9 @@ class CSocket { private: SOCKET m_hSocket; // socket connect handle - + void Clear(); - + public: static const char *m_sClassName; diff --git a/src/network/linuxev.cpp b/src/network/linuxev.cpp index c946fcfe5..cb0f70f99 100644 --- a/src/network/linuxev.cpp +++ b/src/network/linuxev.cpp @@ -11,7 +11,7 @@ /* { ev_io_stop(loop, w); - + if ( !g_Serv.IsLoading() ) { if ( revents & EV_READ ) @@ -21,7 +21,7 @@ g_NetworkManager.acceptNewConnection(); } } - + ev_io_start(loop, w); } */ @@ -33,18 +33,18 @@ static void socketslave_cb(struct ev_loop *loop, struct ev_io *watcher, int reve // libev could call this function aliasing ev_io as a ev_watcher. // ev_watcher is a "parent" struct of ev_io, they share the first member variables. // it's a evil trick, but does the job since C doesn't have struct inheritance - + ev_io_stop(loop, watcher); CNetState* state = reinterpret_cast( watcher->data ); - + if ( !g_Serv.IsLoading() ) { if ( revents & EV_READ ) - { + { // This happens when the client socket is readable (i can try to retrieve data), this does NOT mean // that i have data to read. It might also mean that i have done writing to the socket? // g_NetworkOut.onAsyncSendComplete(state); - } + } else if ( revents & EV_WRITE ) { CNetworkThread* thread = state->getParentThread(); @@ -52,7 +52,7 @@ static void socketslave_cb(struct ev_loop *loop, struct ev_io *watcher, int reve thread->onAsyncSendComplete(state, true); // we can send (again) data } } - + if (state->isSendingAsync()) { ev_io_start(loop, watcher); @@ -93,7 +93,7 @@ static void periodic_cb(struct ev_loop* /*loop*/, ev_periodic* /*w*/, int /*reve } void LinuxEv::tick() -{ +{ /* A flags value of EVRUN_NOWAIT will look for new events, will handle those events and any already outstanding ones, @@ -111,7 +111,7 @@ void LinuxEv::tick() // This periodic timer keeps awake the event loop. We could have used ev_ref but it had its problems... // Don't ask me why (maybe i don't get how this actually should work, and this is only a workaround), // but if we rely on ev_ref to increase the event loop reference counter to keep it alive without this periodic timer/callback, - // the loop will ignore the io_collect_interval. Moreover, it will make the polling backend in use (like most frequently epoll) wait the maximum + // the loop will ignore the io_collect_interval. Moreover, it will make the polling backend in use (like most frequently epoll) wait the maximum // time (MAX_BLOCKTIME in ev.c, circa 60 seconds) to collect incoming data, only then the callback will be called. So each batch of packets // would be processed every 60 seconds... struct ev_periodic periodic_check; @@ -139,7 +139,7 @@ void LinuxEv::registerClient(CNetState * state, EventsID eventCheck) ADDTOCALLSTACK("LinuxEv::registerClient"); ASSERT(state != nullptr); - + memset(state->iocb(), 0, sizeof(ev_io)); // Right now we support only async writing to the socket. @@ -159,7 +159,7 @@ void LinuxEv::registerClient(CNetState * state, EventsID eventCheck) state->setSendingAsync(true); // set it true: when the socket will be again writable (so we have finished writing data in it) // socketslave_cb will be called (even immediately after this function if we aren't actually sending data) // and it will report that we are not sending async data anymore. - + ev_io_start(m_eventLoop, state->iocb()); } @@ -169,7 +169,7 @@ void LinuxEv::unregisterClient(CNetState * state) ASSERT(state != nullptr); state->setSendingAsync(false); - + ev_io_stop(m_eventLoop, state->iocb()); } @@ -203,7 +203,7 @@ void LinuxEv::registerMainsocket() #else ev_io_init(&m_watchMainsock, socketmain_cb, g_Serv.m_SocketMain.GetSocket(), EV_READ); #endif - ev_io_start(m_eventLoop, &m_watchMainsock); + ev_io_start(m_eventLoop, &m_watchMainsock); */ } diff --git a/src/network/linuxev.h b/src/network/linuxev.h index bdc1cbcd5..746673b4b 100644 --- a/src/network/linuxev.h +++ b/src/network/linuxev.h @@ -11,10 +11,10 @@ #include #include "../common/sphere_library/smutex.h" #include "../sphere/threads.h" - + class CClient; class CNetState; - + class LinuxEv : public AbstractSphereThread { public: @@ -24,34 +24,34 @@ None = 0, Read = 1, Write = 2, - + Error = 0x80000000 }; - + private: struct ev_loop * m_eventLoop; // struct ev_io m_watchMainsock; // Watcher for Sphere's socket, to accept incoming connections (async read). - + public: LinuxEv(void); virtual ~LinuxEv(void); LinuxEv(const LinuxEv& copy) = delete; LinuxEv& operator=(const LinuxEv& other) = delete; - + public: virtual void onStart() override; virtual void tick() override; virtual void waitForClose() override; - + private: void forceClientevent(CNetState *, EventsID); - + public: void printInitInfo(); void forceClientread(CNetState *); void forceClientwrite(CNetState *); - // -------------------------------------- + // -------------------------------------- void registerClient(CNetState *, EventsID); void unregisterClient(CNetState *); // -------------------------------------- diff --git a/src/network/packet.cpp b/src/network/packet.cpp index f524c263f..066756233 100644 --- a/src/network/packet.cpp +++ b/src/network/packet.cpp @@ -83,7 +83,6 @@ void xRecordPacket(const CClient* client, Packet* packet, lpctstr heading) #endif //defined(_PACKETDUMP) || defined(_DUMPSUPPORT) - Packet::Packet(uint size) : m_buffer(nullptr) { m_expectedLength = size; diff --git a/src/network/packet.h b/src/network/packet.h index 6ba324726..3e2877e99 100644 --- a/src/network/packet.h +++ b/src/network/packet.h @@ -25,7 +25,6 @@ class Packet; #endif - class CNetState; class SimplePacketTransaction; class AbstractString; @@ -282,7 +281,6 @@ class ExtendedPacketTransaction : public PacketTransaction }; - /*************************************************************************** * * diff --git a/src/network/receive.cpp b/src/network/receive.cpp index 990984165..2a4d7a078 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -4745,7 +4745,6 @@ bool PacketUltimaStoreButton::onReceive(CNetState *net) } - /*************************************************************************** * * diff --git a/src/sphere/ntservice.h b/src/sphere/ntservice.h index 124ffa518..33fc6eab2 100644 --- a/src/sphere/ntservice.h +++ b/src/sphere/ntservice.h @@ -17,7 +17,6 @@ #endif // NON_MSVC_COMPILER - extern class CNTService { private: From 37bffb315cbb97ae0cfd3345bc4d480743f49987 Mon Sep 17 00:00:00 2001 From: David Kindl Date: Wed, 28 May 2025 11:22:55 +0200 Subject: [PATCH 038/112] Add missing parameter descriptions --- src/common/sphere_library/CSString.h | 1 + src/common/sphere_library/sstringobjs.h | 2 +- src/game/CBase.h | 2 ++ src/game/CEntity.h | 1 + src/game/CObjBase.h | 1 + src/game/chars/CChar.h | 2 ++ src/game/items/CItemMulti.h | 11 +++++++++++ src/game/items/CItemMultiCustom.h | 1 + 8 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/common/sphere_library/CSString.h b/src/common/sphere_library/CSString.h index cc79b186e..5d414ad05 100644 --- a/src/common/sphere_library/CSString.h +++ b/src/common/sphere_library/CSString.h @@ -151,6 +151,7 @@ class CSString * If the new length is bigger than the current length, alloc memory for the string and copy. * If DEBUG_STRINGS setted, update statistical information (reallocs count, total memory allocated). * @param iLen new length of the string. + * @param fPreciseSize If the length is bigger, should the size be the same? * @return the new length of the CSString. */ int Resize(int iLen, bool fPreciseSize = false); diff --git a/src/common/sphere_library/sstringobjs.h b/src/common/sphere_library/sstringobjs.h index b6ab44dfd..3a01fc718 100644 --- a/src/common/sphere_library/sstringobjs.h +++ b/src/common/sphere_library/sstringobjs.h @@ -149,7 +149,7 @@ class TemporaryString : public AbstractString * @brief "Copy" constructor. * * @param pStr string to copy. - * #param iLen max number of chars (single-byte) to copy. + * @param uiLen max number of chars (single-byte) to copy. */ TemporaryString(lpctstr pStr, size_t uiLen); diff --git a/src/game/CBase.h b/src/game/CBase.h index 2093e72e6..ee92b929d 100644 --- a/src/game/CBase.h +++ b/src/game/CBase.h @@ -179,6 +179,7 @@ struct CBaseBaseDef : public CResourceLink, public CEntityProps * @param ptcKey The key. * @param iVal Zero-based index of the value. * @param fDeleteZero true to zero. + * @param fWarnOverwrite Log warning to console. */ void SetDefNum(lpctstr ptcKey, int64 iVal, bool fDeleteZero = true, bool fWarnOverwrite = true) { @@ -191,6 +192,7 @@ struct CBaseBaseDef : public CResourceLink, public CEntityProps * @param pszVal The value. * @param fQuoted true if quoted. * @param fDeleteZero true to zero. + * @param fWarnOverwrite Log warning to console. */ void SetDefStr(lpctstr ptcKey, lpctstr pszVal, bool fQuoted = false, bool fDeleteZero = true, bool fWarnOverwrite = true) { diff --git a/src/game/CEntity.h b/src/game/CEntity.h index 5409d9a47..560db6c9f 100644 --- a/src/game/CEntity.h +++ b/src/game/CEntity.h @@ -109,6 +109,7 @@ class CEntity * Executes a command (eg: dupe, bounce...) * * @param s the container with the keys and values to execute. + * @param pSrc Source of the command. * @return true if there was a key which could be executed. */ bool r_Verb(CScript & s, CTextConsole * pSrc); ///< Execute command from script. diff --git a/src/game/CObjBase.h b/src/game/CObjBase.h index 7b917f397..6fd3a6713 100644 --- a/src/game/CObjBase.h +++ b/src/game/CObjBase.h @@ -889,6 +889,7 @@ public: virtual bool IsDeleted() const override; * @param iSkillLevel Zero-based index of the skill level. * @param [in,out] pSourceItem If non-null, source item. * @param fReflecting true to reflecting. + * @param iDuration Duration of the spell effect (default is instant). * * @return true if it succeeds, false if it fails. */ diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index ce6109ae5..31b4d45fe 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -647,6 +647,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * @param iKarmaChange Amount of karma to change, can be positive and negative. * @param iBottom is the lower value you can have for this execution. * @param fMessage show message to the char or not. + * @param pNPC The character whose fame is changing. */ void Noto_Karma( int iKarmaChange, int iBottom = INT32_MIN, bool fMessage = false, CChar* pNPC = nullptr ); @@ -656,6 +657,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; * Used to increase/decrease Fame, it fires @FameChange trigger. * Can't exceed g_Cfg.m_iMaxFame and can't be lower than 0. * @param iFameChange is the amount of fame to change over the current one. + * @param pNPC The character whose fame is changing. */ void Noto_Fame( int iFameChange, CChar* pNPC = nullptr ); diff --git a/src/game/items/CItemMulti.h b/src/game/items/CItemMulti.h index 8e5cb122e..41695861f 100644 --- a/src/game/items/CItemMulti.h +++ b/src/game/items/CItemMulti.h @@ -204,6 +204,7 @@ class CItemMulti : public CItem, public CCMultiMovable /** * @brief Deletes a coowner to the _lCoowners list. * @param uidCoowner the coowner + * @param fRemoveFromList Whether owner should be deleted from list. */ void DeleteCoowner(const CUID& uidCoowner, bool fRemoveFromList); /** @@ -227,6 +228,7 @@ class CItemMulti : public CItem, public CCMultiMovable /** * @brief Deletes a friend to the _lFriends list. * @param uidFriend the friend + * @param fRemoveFromList Whether friend should be deleted from list. */ void DeleteFriend(const CUID& uidFriend, bool fRemoveFromList); /** @@ -250,6 +252,7 @@ class CItemMulti : public CItem, public CCMultiMovable /** * @brief Deletes a char from the _lBans list. * @param uidBan the char. + * @param fRemoveFromList Whether character should be deleted from list. */ void DeleteBan(const CUID& uidBan, bool fRemoveFromList); /** @@ -275,6 +278,7 @@ class CItemMulti : public CItem, public CCMultiMovable * * Note: This removes the char from the list, but won't prevent it from enter like a Ban. * @param uidAccess the char. + * @param fRemoveFromList Whether character should be deleted from list. */ void DeleteAccess(const CUID& uidAccess, bool fRemoveFromList); /** @@ -408,6 +412,7 @@ class CItemMulti : public CItem, public CCMultiMovable /** * @brief Removes a CMultiComponent from the components list. * @param uidComponent the component. + * @param fRemoveFromList Whether component should be deleted from list. */ virtual void DeleteComponent(const CUID& uidComponent, bool fRemoveFromList); /** @@ -507,6 +512,7 @@ class CItemMulti : public CItem, public CCMultiMovable /** * @brief Unlocks an item and remove it from the Lockdowns list. * @param uidItem the item. + * @param fRemoveFromList Whether item should be deleted from list. */ void UnlockItem(const CUID& uidItem, bool fRemoveFromList); void UnlockAllItems(); @@ -529,6 +535,7 @@ class CItemMulti : public CItem, public CCMultiMovable /** * @brief Releases a container and removes it from the containers list. * @param uidContainer the container. + * @param fRemoveFromList Whether container should be deleted from list. */ void Release(const CUID& uidContainer, bool fRemoveFromList); /** @@ -556,6 +563,7 @@ class CItemMulti : public CItem, public CCMultiMovable /** * @brief Removes a char from the vendors list. * @param uidVendor the vendor + * @param fRemoveFromList Whether vendor should be deleted from list. */ void DeleteVendor(const CUID& uidVendor, bool fRemoveFromList); /** @@ -681,6 +689,7 @@ class CMultiStorage /** * @brief Adds a multi * @param uidMulti the multi + * @param ePriv Access type for the multi (like ownership, ban etc.) */ void AddMulti(const CUID& uidMulti, HOUSE_PRIV ePriv); /** @@ -697,6 +706,7 @@ class CMultiStorage /** * @brief Adds a house multi. * @param uidHouse the house. + * @param ePriv Access type for the house (like ownership, ban etc.) */ void AddHouse(const CUID& uidHouse, HOUSE_PRIV ePriv); /** @@ -744,6 +754,7 @@ class CMultiStorage /** * @brief Adds a ship. * @param uidShip the ship. + * @param ePriv Access type for the ship (like ownership, ban etc.) */ void AddShip(const CUID& uidShip, HOUSE_PRIV ePriv); /** diff --git a/src/game/items/CItemMultiCustom.h b/src/game/items/CItemMultiCustom.h index 144b5fd25..b7e83cfcd 100644 --- a/src/game/items/CItemMultiCustom.h +++ b/src/game/items/CItemMultiCustom.h @@ -69,6 +69,7 @@ class CItemMultiCustom : public CItemMulti /** * @brief Removes a CMultiComponent from the components list. * @param uidComponent the component. + * @param fRemoveFromList Whether component should be deleted from list. */ virtual void DeleteComponent(const CUID& uidComponent, bool fRemoveFromList) override final; From 7bd0e6f372afdb895ba61b053aa93733b68fefcd Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 28 May 2025 20:16:59 +0200 Subject: [PATCH 039/112] Fixed some regressions. - Objects not going to sleep with the sector. - Crash (nullptr dereference) when creating a multi. - Sector size -1 not falling back to default value. --- src/common/CExpression.cpp | 65 ++++++---- src/common/CExpression.h | 2 +- src/common/CUOInstall.cpp | 10 +- src/common/common.h | 50 ++++++-- src/game/CContainer.cpp | 2 +- src/game/CObjBase.cpp | 39 ++++-- src/game/CObjBase.h | 8 +- src/game/CSector.cpp | 10 +- src/game/CTimedFunction.cpp | 4 +- src/game/CTimedFunction.h | 4 +- src/game/CTimedObject.cpp | 10 +- src/game/CTimedObject.h | 4 +- src/game/CWorldTicker.cpp | 6 +- src/game/chars/CChar.h | 2 +- src/game/chars/CCharAct.cpp | 10 +- src/game/clients/CClientTarg.cpp | 8 +- src/game/items/CItem.cpp | 16 +-- src/game/items/CItem.h | 2 +- src/game/items/CItemMulti.cpp | 2 +- src/game/uo_files/CUOMapList.cpp | 211 +++++++++++++++++-------------- src/game/uo_files/CUOMapList.h | 37 +++--- 21 files changed, 288 insertions(+), 214 deletions(-) diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index 4a697d425..74cce009b 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -1908,18 +1908,20 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) return 0; } - tchar pToParse[THREAD_STRING_LENGTH]; + tchar ptcToParse[THREAD_STRING_LENGTH]; if (iQty == 1) // It's just a simple value { ASSERT(pElementsStart[0] != nullptr); // Copy the value in a new string - const size_t iToParseLen = (pElementsStart[0][1] - pElementsStart[0][0]); - memcpy((void*)pToParse, pElementsStart[0][0], iToParseLen * sizeof(tchar)); - pToParse[iToParseLen] = '\0'; + const size_t uiToParseLen = std::min( + ptrdiff_t(THREAD_STRING_LENGTH-1), + ptrdiff_t(pElementsStart[0][1] - pElementsStart[0][0])); + memcpy((void*)ptcToParse, pElementsStart[0][0], uiToParseLen * sizeof(tchar)); + ptcToParse[uiToParseLen] = '\0'; - lptstr pToParseCasted = static_cast(pToParse); + lptstr pToParseCasted = static_cast(ptcToParse); return GetSingle(pToParseCasted); } @@ -1929,19 +1931,23 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) ASSERT(pElementsStart[1] != nullptr); // Copy the first element in a new string - size_t iToParseLen = (pElementsStart[0][1] - pElementsStart[0][0]); - memcpy((void*)pToParse, pElementsStart[0][0], iToParseLen * sizeof(tchar)); - pToParse[iToParseLen] = '\0'; + size_t uiToParseLen = std::min( + ptrdiff_t(THREAD_STRING_LENGTH-1), + ptrdiff_t(pElementsStart[0][1] - pElementsStart[0][0])); + memcpy((void*)ptcToParse, pElementsStart[0][0], uiToParseLen * sizeof(tchar)); + ptcToParse[uiToParseLen] = '\0'; - lptstr pToParseCasted = static_cast(pToParse); + lptstr pToParseCasted = static_cast(ptcToParse); llong llValFirst = GetSingle(pToParseCasted); // Copy the second element in a new string - iToParseLen = (pElementsStart[1][1] - pElementsStart[1][0]); - memcpy((void*)pToParse, pElementsStart[1][0], iToParseLen * sizeof(tchar)); - pToParse[iToParseLen] = '\0'; + uiToParseLen = std::min( + ptrdiff_t(THREAD_STRING_LENGTH-1), + ptrdiff_t(pElementsStart[1][1] - pElementsStart[1][0])); + memcpy((void*)ptcToParse, pElementsStart[1][0], uiToParseLen * sizeof(tchar)); + ptcToParse[uiToParseLen] = '\0'; - pToParseCasted = static_cast(pToParse); + pToParseCasted = static_cast(ptcToParse); llong llValSecond = GetSingle(pToParseCasted); if (llValSecond < llValFirst) // the first value has to be < than the second before passing it to g_Rand.GetLLVal2 @@ -1962,11 +1968,13 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) // break; // Shouldn't really happen... // Copy the weight element in a new string - const size_t iToParseLen = (pElementsStart[i][1] - pElementsStart[i][0]); - memcpy((void*)pToParse, pElementsStart[i][0], iToParseLen * sizeof(tchar)); - pToParse[iToParseLen] = '\0'; + const size_t uiToParseLen = std::min( + ptrdiff_t(THREAD_STRING_LENGTH-1), + ptrdiff_t(pElementsStart[i][1] - pElementsStart[i][0])); + memcpy((void*)ptcToParse, pElementsStart[i][0], uiToParseLen * sizeof(tchar)); + ptcToParse[uiToParseLen] = '\0'; - lptstr pToParseCasted = static_cast(pToParse); + lptstr pToParseCasted = static_cast(ptcToParse); llWeights[i] = GetSingle(pToParseCasted); // GetSingle changes the pointer value, so i need to work with a copy if ( ! llWeights[i] ) // having a weight of 0 is very strange ! @@ -1988,15 +1996,16 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) ASSERT(i < iQty); i -= 1; // pick the value instead of the weight - const size_t iToParseLen = (pElementsStart[i][1] - pElementsStart[i][0]); - // Copy the value element in a new string ASSERT(nullptr != pElementsStart[i][0]); - memcpy((void*)pToParse, pElementsStart[i][0], iToParseLen * sizeof(tchar)); - pToParse[iToParseLen] = '\0'; - - lptstr pToParseCasted = static_cast(pToParse); - return GetSingle(pToParseCasted); + const size_t uiToParseLen = std::min( + ptrdiff_t(THREAD_STRING_LENGTH-1), + ptrdiff_t(pElementsStart[i][1] - pElementsStart[i][0])); + memcpy((void*)ptcToParse, pElementsStart[i][0], uiToParseLen * sizeof(tchar)); + ptcToParse[uiToParseLen] = '\0'; + + lptstr ptcToParseCasted = static_cast(ptcToParse); + return GetSingle(ptcToParseCasted); } CSString CExpression::GetRangeString(lpctstr & refStrExpr) @@ -2017,7 +2026,7 @@ CSString CExpression::GetRangeString(lpctstr & refStrExpr) if (iQty == 1) // It's just a simple value { ASSERT(pElementsStart[0] != nullptr); - const int iToParseLen = int(pElementsStart[0][1] - pElementsStart[0][0]); + const int iToParseLen = int(ptrdiff_t(pElementsStart[0][1] - pElementsStart[0][0])); return CSString(pElementsStart[0][0], iToParseLen - 1); } @@ -2038,7 +2047,7 @@ CSString CExpression::GetRangeString(lpctstr & refStrExpr) // break; // Shouldn't really happen... // Copy the weight element in a new string - const size_t iToParseLen = (pElementsStart[i][1] - pElementsStart[i][0]); + const size_t iToParseLen = ptrdiff_t(pElementsStart[i][1] - pElementsStart[i][0]); memcpy((void*)pToParse, pElementsStart[i][0], iToParseLen * sizeof(tchar)); pToParse[iToParseLen] = '\0'; lptstr pToParseCasted = reinterpret_cast(pToParse); @@ -2068,7 +2077,7 @@ CSString CExpression::GetRangeString(lpctstr & refStrExpr) ASSERT(i < iQty); i -= 1; // pick the value instead of the weight - const int iToParseLen = int(pElementsStart[i][1] - pElementsStart[i][0]); + const int iToParseLen = int(ptrdiff_t(pElementsStart[i][1] - pElementsStart[i][0])); return CSString(pElementsStart[i][0], iToParseLen); } @@ -2171,7 +2180,7 @@ bool CExpression::EvaluateConditionalWhole(lptstr ptcExpr, CScriptExprContext& r pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); lptstr ptcExprDbg = ptcExpr; - const auto pSubexprArena = GetConditionalSubexpressions(ptcExprDbg, _pBufs.get()->m_poolCScriptExprSubStates); // number of arguments + const auto pSubexprArena = GetConditionalSubexpressions(ptcExprDbg, _pBufs.get()->m_poolCScriptExprSubStatesPool); // number of arguments const uint uiQty = pSubexprArena->m_uiQty; CScriptSubExprState* parsingSubexprsStates = pSubexprArena->m_subexprs; diff --git a/src/common/CExpression.h b/src/common/CExpression.h index f2d7b0dfd..5abf2e45b 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -210,7 +210,7 @@ class CExpression static constexpr uint sm_subexpr_pool_size = 1'000; static constexpr bool sm_allow_fallback_objects = false; using CSubExprStatesArenaPool_t = sl::ObjectPool; - CSubExprStatesArenaPool_t m_poolCScriptExprSubStates; + CSubExprStatesArenaPool_t m_poolCScriptExprSubStatesPool; }; std::unique_ptr _pBufs; diff --git a/src/common/CUOInstall.cpp b/src/common/CUOInstall.cpp index 816c53d00..6a588389a 100644 --- a/src/common/CUOInstall.cpp +++ b/src/common/CUOInstall.cpp @@ -172,7 +172,7 @@ lpctstr CUOInstall::GetBaseFileName( VERFILE_TYPE i ) // static nullptr, "tiledata.mul", // Data about tiles in ART. name and flags, etc "animdata.mul", // - "hues.mul" // the 16 bit color pallete we use for everything. + "hues.mul" // the 16 bit color palette we use for everything. }; return ( i<0 || i>=VERFILE_QTY ) ? nullptr : sm_szFileNames[i]; @@ -266,10 +266,10 @@ VERFILE_TYPE CUOInstall::OpenFiles( ullong ullMask ) { if (g_MapList.IsInitialized(m) || (m == 0)) //Need at least a minimum of map0... (Ben) { - const int index = g_MapList.m_mapGeoData.maps[m].num; + const int index = g_MapList.m_mapGeoData.maps[m].iNum; if (index == -1) { - g_MapList.m_mapGeoData.maps[m].enabled = false; + g_MapList.m_mapGeoData.maps[m].fEnabled = false; continue; } @@ -406,9 +406,9 @@ VERFILE_TYPE CUOInstall::OpenFiles( ullong ullMask ) m_Statics[index].Close(); if (index == 1 && m_Maps[0].IsFileOpen()) - g_MapList.m_mapGeoData.maps[m].num = 0; + g_MapList.m_mapGeoData.maps[m].iNum = 0; else - g_MapList.m_mapGeoData.maps[m].id = 0; + g_MapList.m_mapGeoData.maps[m].iId = 0; } // mapdif and mapdifl are not required, but if one exists so should diff --git a/src/common/common.h b/src/common/common.h index 8fecc2308..0dad50d12 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -144,7 +144,8 @@ //#define IsNegative(c) (((c) < 0) ? 1 : 0) template -constexpr bool IsNegative(T val) noexcept { +[[nodiscard]] constexpr + bool IsNegative(T val) noexcept { return (val < 0); } @@ -156,74 +157,96 @@ inline constexpr word dword_low_word(dword in) noexcept { } //#define HIWORD(l) ((word)((dword)(l) >> 16)) -inline constexpr word dword_hi_word(dword in) noexcept { +[[nodiscard]] inline constexpr + word dword_hi_word(dword in) noexcept { return (in >> 16); } //#define LOBYTE(w) ((byte)((dword)(w) & 0xff)) -inline constexpr byte word_low_byte(word in) noexcept { +[[nodiscard]] inline constexpr + byte word_low_byte(word in) noexcept { return (in & 0xFF); } //#define HIBYTE(w) ((byte)((dword)(w) >> 8)) -inline constexpr byte word_hi_byte(word in) noexcept { +[[nodiscard]] inline constexpr + byte word_hi_byte(word in) noexcept { return (in >> 8); } //#define MAKEWORD(low,high) ((word)(((byte)(low))|(((word)((byte)(high)))<<8))) -inline constexpr word make_word(byte low, byte high) noexcept { +[[nodiscard]] inline constexpr + word make_word(byte low, byte high) noexcept { return (word)low | ((word)high << 8); } //#define make_dword(low, high) ((dword)(((word)low) | (((dword)((word)high)) << 16))) -inline constexpr dword make_dword(word low, word high) noexcept { +[[nodiscard]] inline constexpr + dword make_dword(word low, word high) noexcept { return (dword)low | ((dword)high << 16); } //#define IMulDiv(a,b,c) (((((int)(a)*(int)(b)) + (int)(c / 2)) / (int)(c)) - (IsNegative((int)(a)*(int)(b)))) -constexpr int IMulDiv(const int a, const int b, const int c) noexcept +[[nodiscard]] constexpr + int IMulDiv(const int a, const int b, const int c) noexcept { const int ab = a*b; return ((ab + (c/2)) / c) - IsNegative(ab); } -constexpr uint UIMulDiv(const uint a, const uint b, const uint c) noexcept +[[nodiscard]] constexpr + uint UIMulDiv(const uint a, const uint b, const uint c) noexcept { const int ab = a * b; return ((ab + (c / 2)) / c) - IsNegative(ab); } //#define IMulDivLL(a,b,c) (((((llong)(a)*(llong)(b)) + (llong)(c / 2)) / (llong)(c)) - (IsNegative((llong)(a)*(llong)(b)))) -constexpr llong IMulDivLL(const llong a, const llong b, const llong c) noexcept +[[nodiscard]] constexpr + llong IMulDivLL(const llong a, const llong b, const llong c) noexcept { const llong ab = a*b; return ((ab + (c/2)) / c) - IsNegative(ab); } -constexpr realtype IMulDivRT(const realtype a, const realtype b, const realtype c) noexcept +[[nodiscard]] constexpr + realtype IMulDivRT(const realtype a, const realtype b, const realtype c) noexcept { const realtype ab = a*b; return ((ab + (c/2)) / c) - IsNegative(ab); } //#define IMulDivDown(a,b,c) (((a)*(b))/(c)) -constexpr int IMulDivDown(const int a, const int b, const int c) noexcept +[[nodiscard]] constexpr + int IMulDivDown(const int a, const int b, const int c) noexcept { return (a*b)/c; } -constexpr llong IMulDivDownLL(const llong a, const llong b, const llong c) noexcept +[[nodiscard]] constexpr + llong IMulDivDownLL(const llong a, const llong b, const llong c) noexcept { return (a*b)/c; } //#define sign(n) (((n) < 0) ? -1 : (((n) > 0) ? 1 : 0)) template +[[nodiscard]] constexpr T sign(const T n) noexcept { static_assert(std::is_arithmetic::value, "Invalid data type."); return ( (n < 0) ? -1 : ((n > 0) ? 1 : 0) ); } +[[nodiscard]] inline constexpr bool IsPowerOfTwo(unsigned int n) noexcept +{ + // n & (n - 1): This expression removes the lowest set bit in n. + // For powers of two, which have exactly one bit set (e.g., 2 is 10 in binary, 4 is 100, etc.), + // subtracting one yields a number where all lower bits are set to 1 (e.g., 2 - 1 = 1, 4 - 1 = 3), and the bitwise AND of these two numbers results in 0. + return n != 0 && (n & (n - 1)) == 0; +} +[[nodiscard]] inline constexpr bool IsPowerOfTwo(unsigned short n) noexcept { return n != 0 && (n & (n - 1)) == 0; } +[[nodiscard]] inline constexpr bool IsPowerOfTwo(unsigned char n) noexcept { return n != 0 && (n & (n - 1)) == 0; } + #define minimum(x,y) ((x)<(y)?(x):(y)) // NOT to be used with functions! Store the result of the function in a variable first, otherwise the function will be executed twice! #define maximum(x,y) ((x)>(y)?(x):(y)) // NOT to be used with functions! Store the result of the function in a variable first, otherwise the function will be executed twice! #define medium(x,y,z) ((x)>(y)?(x):((z)<(y)?(z):(y))) // NOT to be used with functions! Store the result of the function in a variable first, otherwise the function will be executed twice! @@ -258,6 +281,7 @@ constexpr T saturating_sub(T a, T b) noexcept { // Ensure that a constexpr value or a generic expression is evaluated at compile time. // Constexpr values are constants and cannot be mutated in the code. template +[[nodiscard]] consteval T as_consteval(T&& val_) noexcept { return val_; } @@ -269,7 +293,7 @@ consteval T as_consteval(T&& val_) noexcept { */ #undef UNREFERENCED_PARAMETER template -constexpr void UnreferencedParameter(T const&) noexcept { +constexpr void UnreferencedParameter([[maybe_unused]] T const& ) noexcept { ; } diff --git a/src/game/CContainer.cpp b/src/game/CContainer.cpp index f5f61e8f0..b8b46e048 100644 --- a/src/game/CContainer.cpp +++ b/src/game/CContainer.cpp @@ -35,7 +35,7 @@ void CContainer::_GoSleep() { CItem* pItem = static_cast(pObjRec); //std::unique_lock lock(pItem->MT_CMUTEX); - if (!pItem->CanTick()) + if (!pItem->TickableState()) { pItem->GoSleep(); } diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index 7a48b42c4..f117b1a4f 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -3273,30 +3273,47 @@ void CObjBase::_GoSleep() */ } -bool CObjBase::_CanTick() const +bool CObjBase::_TickableState() const { //ADDTOCALLSTACK_DEBUG("CObjBase::_CanTick"); // Called very frequently. // This doesn't check the sector sleeping status, it's only about this object. - EXC_TRY("Can tick?"); + //EXC_TRY("Able to tick?"); // Directly call the method specifying the belonging class, to avoid the overhead of vtable lookup under the hood. - bool fCanTick = !CTimedObject::_IsSleeping(); - if (!fCanTick) + return !CTimedObject::_IsSleeping(); + + //EXC_CATCH; + return false; +} + +std::optional CObjBase::_TickableStateOverride() const +{ + if (Can(CAN_O_NOSLEEP)) { - // Try to call the Can method the less often possible. - // - // This should happen only if the item was manually put to sleep. - // CAN_O_NOSLEEP items should not be put to sleep by the source. - fCanTick = Can(CAN_O_NOSLEEP); + // CAN_O_NOSLEEP items should not be put to sleep by the source. + // SECF_NoSleep is a property of the sector, not of the item, so it's managed in the sector code. + return true; // Override: i should never sleep. } + // No override. Do the default thing. + return std::nullopt; +} - return fCanTick; +bool CObjBase::_CanTick(bool fParentGoingToSleep) const +{ + EXC_TRY("Can tick?"); - EXC_CATCH; + const bool fTickable = _TickableState(); + const std::optional fOverriding = _TickableStateOverride(); + if (fParentGoingToSleep && (!fTickable || !fOverriding.value_or(false))) + return false; + return fTickable; + + EXC_CATCH; return false; } + void CObjBase::ResendTooltip(bool fSendFull, bool fUseCache) { ADDTOCALLSTACK("CObjBase::ResendTooltip"); diff --git a/src/game/CObjBase.h b/src/game/CObjBase.h index 6fd3a6713..017724708 100644 --- a/src/game/CObjBase.h +++ b/src/game/CObjBase.h @@ -922,8 +922,12 @@ public: virtual bool IsDeleted() const override; */ virtual void OnTickStatusUpdate(); - virtual bool _CanTick() const override; - //virtual bool CanTick(bool fParentGoingToSleep = false) const override; // Not needed: the right virtual is called by CTimedObj::_CanTick. + virtual bool _TickableState() const override; + //virtual bool TickableState() const override; // Not needed: the right virtual is called by CTimedObj::_CanTick. + + std::optional _TickableStateOverride() const; + + bool _CanTick(bool fParentGoingToSleep = false) const; public: diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index f9b556da4..1eb91ced9 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -186,7 +186,7 @@ void CSector::_GoSleep() for (CSObjContRec* pObjRec : m_Chars_Active) { CChar* pChar = static_cast(pObjRec); - const bool fCanTick = pChar->CanTick(); + const bool fCanTick = pChar->_CanTick(true); ASSERT(!pChar->IsDisconnected()); if (!fCanTick) pChar->GoSleep(); @@ -196,7 +196,7 @@ void CSector::_GoSleep() for (CSObjContRec* pObjRec : m_Chars_Disconnect) { CChar* pChar = static_cast(pObjRec); - const bool fCanTick = pChar->CanTick(); + const bool fCanTick = pChar->_CanTick(true); ASSERT(pChar->IsDisconnected()); if (!fCanTick) pChar->GoSleep(); @@ -206,7 +206,7 @@ void CSector::_GoSleep() for (CSObjContRec* pObjRec : m_Items) { CItem* pItem = static_cast(pObjRec); - const bool fCanTick = pItem->CanTick(); + const bool fCanTick = pItem->_CanTick(true); if (!fCanTick) pItem->GoSleep(); } @@ -962,7 +962,7 @@ void CSector::MoveItemToSector( CItem * pItem ) { if (_CanSleep(true)) { - if (!pItem->CanTick()) + if (!pItem->TickableState()) pItem->GoSleep(); } else @@ -1025,7 +1025,7 @@ bool CSector::MoveCharToSector( CChar * pChar ) } else if (!pChar->IsSleeping()) // An NPC entered, but the sector is sleeping { - if (!pChar->CanTick()) + if (!pChar->TickableState()) pChar->GoSleep(); // then make the NPC sleep too. } } diff --git a/src/game/CTimedFunction.cpp b/src/game/CTimedFunction.cpp index cb89e434a..aed86c8b6 100644 --- a/src/game/CTimedFunction.cpp +++ b/src/game/CTimedFunction.cpp @@ -26,12 +26,12 @@ bool CTimedFunction::IsDeleted() const // virtual } -bool CTimedFunction::_CanTick() const // virtual +bool CTimedFunction::_TickableState() const // virtual { return true; } -bool CTimedFunction::CanTick() const // virtual +bool CTimedFunction::TickableState() const // virtual { return true; } diff --git a/src/game/CTimedFunction.h b/src/game/CTimedFunction.h index 1d21f3c0e..93933d38a 100644 --- a/src/game/CTimedFunction.h +++ b/src/game/CTimedFunction.h @@ -35,8 +35,8 @@ class CTimedFunction : public CTimedObject, CSObjContRec return _ptcCommand; } -protected: virtual bool _CanTick() const override; -public: virtual bool CanTick() const override; +protected: virtual bool _TickableState() const override; +public: virtual bool TickableState() const override; protected: virtual bool _OnTick() override; public: virtual bool OnTick() override; diff --git a/src/game/CTimedObject.cpp b/src/game/CTimedObject.cpp index 245e9a446..aef9a92e4 100644 --- a/src/game/CTimedObject.cpp +++ b/src/game/CTimedObject.cpp @@ -37,16 +37,16 @@ void CTimedObject::_GoAwake() _fIsSleeping = false; } -bool CTimedObject::_CanTick() const +bool CTimedObject::_TickableState() const { - //ADDTOCALLSTACK_DEBUG("CTimedObject::_CanTick"); + //ADDTOCALLSTACK_DEBUG("CTimedObject::_TickableState"); return !_IsSleeping(); } -bool CTimedObject::CanTick() const +bool CTimedObject::TickableState() const { - //ADDTOCALLSTACK_DEBUG("CTimedObject::CanTick"); - MT_ENGINE_SHARED_LOCK_RETURN(_CanTick()); + //ADDTOCALLSTACK_DEBUG("CTimedObject::_TickableState"); + MT_ENGINE_SHARED_LOCK_RETURN(_TickableState()); } bool CTimedObject::OnTick() diff --git a/src/game/CTimedObject.h b/src/game/CTimedObject.h index 0ee67ce91..6b73d1be2 100644 --- a/src/game/CTimedObject.h +++ b/src/game/CTimedObject.h @@ -65,8 +65,8 @@ public: PROFILE_TYPE GetProfileType() const noexcept; /** * @brief Determine if the object is in a "tickable" state. */ -protected: virtual bool _CanTick() const; // TODO: locks need to be extended to derived classes -public: virtual bool CanTick() const; +protected: virtual bool _TickableState() const; // TODO: locks need to be extended to derived classes +public: virtual bool TickableState() const; /** * @brief Executes the tick action. diff --git a/src/game/CWorldTicker.cpp b/src/game/CWorldTicker.cpp index 31049f182..3b6b9b756 100644 --- a/src/game/CWorldTicker.cpp +++ b/src/game/CWorldTicker.cpp @@ -442,7 +442,7 @@ bool CWorldTicker::AddTimedObject(const int64 iTimeout, CTimedObject* pTimedObje } else { - fCanTick = pTimedObject->_CanTick(); + fCanTick = pTimedObject->_TickableState(); /* if (!fCanTick) { @@ -1094,7 +1094,7 @@ void CWorldTicker::ProcessTimedObjects() ++it, ++uiProgressive) { CTimedObject* pTimedObj = it->second; - if (!pTimedObj->_IsTimerSet() || !pTimedObj->_CanTick()) + if (!pTimedObj->_IsTimerSet() || !pTimedObj->_TickableState()) continue; //if (pTimedObj->_GetTimeoutRaw() > _iCurTickStartTime) @@ -1337,7 +1337,7 @@ void CWorldTicker::ProcessCharPeriodicTicks() { ASSERT(it->first != 0); CChar* pChar = it->second; - if (!pChar->_CanTick() || pChar->_IsBeingDeleted()) + if (!pChar->_TickableState() || pChar->_IsBeingDeleted()) continue; _vPeriodicCharsTicksBuffer.emplace_back(pChar); diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index 31b4d45fe..6e48a255d 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -1380,7 +1380,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; bool IsPeriodicTickPending() const; - virtual bool _CanTick() const override final; + virtual bool _TickableState() const override final; protected: virtual bool _OnTick() override final; // _OnTick timeout for skills, AI, etc. //public: virtual bool _OnTick() override final; diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index dc0317036..7a5e6db86 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -5813,10 +5813,10 @@ void CChar::OnTickSkill() EXC_CATCHSUB("Skill tick"); } -bool CChar::_CanTick() const +bool CChar::_TickableState() const { - //ADDTOCALLSTACK_DEBUG("CChar::_CanTick"); - EXC_TRY("Can tick?"); + //ADDTOCALLSTACK_DEBUG("CChar::_TickableState"); + EXC_TRY("Able to tick?"); if (IsDisconnected()) { @@ -5827,7 +5827,7 @@ bool CChar::_CanTick() const return false; } - return CObjBase::_CanTick(); + return CObjBase::_TickableState(); EXC_CATCH; @@ -5885,7 +5885,7 @@ bool CChar::_OnTick() return true; } - if (!_CanTick()) + if (!_TickableState()) { // It can happen that i'm in the ticking list, but for various reasons right now i'm in a non-tickable state. // Among the reasons why i can't tick, though, there cannot be being in a sleeping state: when a char goes into sleeping state diff --git a/src/game/clients/CClientTarg.cpp b/src/game/clients/CClientTarg.cpp index a4e79563d..c864f2ed9 100644 --- a/src/game/clients/CClientTarg.cpp +++ b/src/game/clients/CClientTarg.cpp @@ -285,7 +285,7 @@ bool CClient::OnTarg_UnExtract( CObjBase * pObj, const CPointMap & pt ) // result of the MULTI command. // Break a multi out of the multi.txt files and turn it into items. - if ( !pt.GetRegion(REGION_TYPE_AREA) ) + if ( !pt.GetRegion(REGION_TYPE_AREA) ) //TODO: add err message return false; CScript s; // It is not really a valid script type file. @@ -332,7 +332,7 @@ bool CClient::OnTarg_Char_Add( CObjBase * pObj, const CPointMap & pt ) ASSERT(m_pChar); if ( !pt.GetRegion(REGION_TYPE_AREA) ) - return false; + return false; //TODO: add err message if ( pObj && pObj->IsItemInContainer() ) return false; @@ -358,7 +358,7 @@ bool CClient::OnTarg_Item_Add( CObjBase * pObj, CPointMap & pt ) // m_tmAdd.m_id = item id ASSERT(m_pChar); - if ( !pt.GetRegion(REGION_TYPE_AREA) ) + if ( !pt.GetRegion(REGION_TYPE_AREA) ) //TODO: add err message return false; if ( pObj && pObj->IsItemInContainer() ) return false; @@ -1658,7 +1658,7 @@ CItem * CClient::OnTarg_Use_Multi(const CItemBase * pItemDef, CPointMap & pt, CI ADDTOCALLSTACK("CClient::OnTarg_Use_Multi"); // Might be a IT_MULTI or it might not. place it anyhow. - if ((pItemDef == nullptr) || !pt.GetRegion(REGION_TYPE_AREA)) + if ((pItemDef == nullptr) || !pt.GetRegion(REGION_TYPE_AREA)) //TODO: add err message return nullptr; return CItemMulti::Multi_Create(GetChar(), pItemDef, pt, pDeed); diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 49ef73008..87c00016d 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -6156,10 +6156,10 @@ bool CItem::_CanHoldTimer() const return true; } -bool CItem::_CanTick() const +bool CItem::_TickableState() const { - //ADDTOCALLSTACK_DEBUG("CItem::_CanTick"); - EXC_TRY("Can tick?"); + //ADDTOCALLSTACK_DEBUG("CItem::_TickableState"); + EXC_TRY("Able to tick?"); const CObjBase* pCont = GetContainer(); const bool fIgnoreCont = (HAS_FLAGS_STRICT(g_Cfg.m_uiItemTimers, ITEM_CANTIMER_IN_CONTAINER) || Can(CAN_I_TIMER_CONTAINED)); @@ -6179,23 +6179,23 @@ bool CItem::_CanTick() const return false; } - return CObjBase::_CanTick(); + return CObjBase::_TickableState(); } if (IsAttr(ATTR_DECAY) && !pCont) { // If pCont is not a CObjBase, it will most probably be a CSector. Decaying items won't go to sleep. - return CObjBase::_CanTick(); + return CObjBase::_TickableState(); } const bool fCharCont = pCont && pCont->IsChar(); - if (fCharCont && !pCont->CanTick()) + if (fCharCont && !pCont->TickableState()) { // Is it equipped on a Char? return false; } - return CObjBase::_CanTick(); + return CObjBase::_TickableState(); EXC_CATCH; @@ -6215,7 +6215,7 @@ bool CItem::_OnTick() if (!_IsSleeping()) { - if (!_CanTick()) + if (!_TickableState()) { const CSector* pSector = GetTopSector(); // It prints an error if it belongs to an invalid sector. if (pSector && pSector->IsSleeping()) diff --git a/src/game/items/CItem.h b/src/game/items/CItem.h index 638ea60be..20bf07403 100644 --- a/src/game/items/CItem.h +++ b/src/game/items/CItem.h @@ -608,7 +608,7 @@ class CItem : public CObjBase public: virtual bool _OnTick() override; - virtual bool _CanTick() const override; + virtual bool _TickableState() const override; bool _CanHoldTimer() const; virtual void DupeCopy( const CObjBase * pItem ) override; diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index 2a86871f4..a57f35ca0 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -3784,7 +3784,7 @@ void CMultiStorage::AddShip(const CUID& uidShip, HOUSE_PRIV ePriv) const CItemShip* pShip = static_cast(uidShip.ItemFind()); TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; - CScriptTriggerArgsPtr pScriptArgs; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); pScriptArgs->m_iN1 = pShip->GetMultiCount(); pScriptArgs->m_iN2 = ePriv; pScriptArgs->m_iN3 = (ePriv == HOUSE_PRIV::HP_OWNER) ? 1 : 0; diff --git a/src/game/uo_files/CUOMapList.cpp b/src/game/uo_files/CUOMapList.cpp index 4da2329d4..112de2551 100644 --- a/src/game/uo_files/CUOMapList.cpp +++ b/src/game/uo_files/CUOMapList.cpp @@ -41,13 +41,13 @@ void CUOMapList::Clear() noexcept // Unused /* -void CUOMapList::ResetMap(uint map, ushort maxx, ushort maxy, ushort sectorsize, ushort realmapnum, ushort mapid) +void CUOMapList::ResetMap(uint map, ushort maxx, ushort maxy, ushort iSectorSize, ushort realmapnum, ushort mapid) { MapGeoData& map_data = m_mapGeoData.maps[map]; - map_data.sizex = maxx; - map_data.sizey = maxy; - map_data.sectorsize = sectorsize; - map_data.num = realmapnum; + map_data.uiSizeX = maxx; + map_data.uiSizeY = maxy; + map_data.iSectorSize = iSectorSize; + map_data.iNum = realmapnum; map_data.id = mapid; } */ @@ -63,13 +63,13 @@ void CUOMapList::Init() for ( int i = 0; i < MAP_SUPPORTED_QTY; ++i ) { MapGeoData& map_data = m_mapGeoData.maps[i]; - if ( map_data.enabled ) // map marked as available. check whatever it's possible + if ( map_data.fEnabled ) // map marked as available. check whatever it's possible { // check coordinates first - if ( map_data.num == (ushort)-1 ) - map_data.enabled = false; - else if ( map_data.sizex <= 0 || map_data.sizey <= 0 || map_data.sectorsize <= 0 ) - map_data.enabled = DetectMapSize(i); + if ( map_data.iNum == -1 ) + map_data.fEnabled = false; + else if ( map_data.uiSizeX <= 0 || map_data.uiSizeY <= 0 || map_data.iSectorSize <= 0 ) + map_data.fEnabled = DetectMapSize(i); } } } @@ -83,57 +83,74 @@ bool CUOMapList::Load(int map, char *args) } MapGeoData& map_data = m_mapGeoData.maps[map]; - if ( false == map_data.initialized ) // disable double intialization + if ( false == map_data.fInitialized ) // disable double intialization { tchar * ppCmd[5]; // maxx,maxy,sectorsize,mapnum[like 0 for map0/statics0/staidx0],mapid size_t iCount = Str_ParseCmds(args, ppCmd, ARRAY_COUNT(ppCmd), ","); if ( iCount <= 0 ) // simple MAPX= same as disabling the map { - map_data.enabled = false; + map_data.fEnabled = false; + map_data.fInitialized = true; + return true; } - else + + int maxx = 0, maxy = 0, sectorsize = 0, realmapnum = 0, mapid = -1; + if ( ppCmd[0] ) maxx = atoi(ppCmd[0]); + if ( ppCmd[1] ) maxy = atoi(ppCmd[1]); + if ( ppCmd[2] ) sectorsize = atoi(ppCmd[2]); + if ( ppCmd[3] ) realmapnum = atoi(ppCmd[3]); + if ( ppCmd[4] ) mapid = atoi(ppCmd[4]); + + // zero settings of anything except the real map num means + if ( maxx ) // skipping the argument { - int maxx = 0, maxy = 0, sectorsize = 0, realmapnum = 0, mapid = -1; - if ( ppCmd[0] ) maxx = atoi(ppCmd[0]); - if ( ppCmd[1] ) maxy = atoi(ppCmd[1]); - if ( ppCmd[2] ) sectorsize = atoi(ppCmd[2]); - if ( ppCmd[3] ) realmapnum = atoi(ppCmd[3]); - if ( ppCmd[4] ) mapid = atoi(ppCmd[4]); - - // zero settings of anything except the real map num means - if ( maxx ) // skipping the argument + if (( maxx < 8 ) || ( maxx % 8 )) { - if (( maxx < 8 ) || ( maxx % 8 )) - { - g_Log.EventError("MAP%d: X coord must be multiple of 8 (%d is invalid, %d is still effective).\n", - map, maxx, map_data.sizex); - } - else - map_data.sizex = (ushort)std::clamp(maxx, 0, (int)UINT16_MAX); + g_Log.EventError("MAP%d: X coord must be multiple of 8 (%d is invalid, %d is still effective).\n", + map, maxx, map_data.uiSizeX); } - if ( maxy ) + else + map_data.uiSizeX = (ushort)std::clamp(maxx, 0, (int)UINT16_MAX); + } + if ( maxy ) + { + if (( maxy < 8 ) || ( maxy % 8 )) { - if (( maxy < 8 ) || ( maxy % 8 )) - { - g_Log.EventError("MAP%d: Y coord must be multiple of 8 (%d is invalid, %d is still effective).\n", - map, maxy, map_data.sizey); - } - else - map_data.sizey = (ushort)std::clamp(maxy, 0, (int)UINT16_MAX); + g_Log.EventError("MAP%d: Y coord must be multiple of 8 (%d is invalid, %d is still effective).\n", + map, maxy, map_data.uiSizeY); } - if ( sectorsize > 0 ) - map_data.sectorsize = (ushort)sectorsize; - if ( realmapnum >= 0 ) - map_data.num = (ushort)realmapnum; - if ( mapid >= 0 ) - map_data.id = (ushort)mapid; else - map_data.id = (ushort)std::clamp(map, 0, (int)UINT16_MAX); + map_data.uiSizeY = (ushort)std::clamp(maxy, 0, (int)UINT16_MAX); } - - map_data.initialized = true; + if ( sectorsize > 0 ) + { + if (!IsPowerOfTwo((ushort)sectorsize)) + { + g_Log.EventError("MAP%d: Invalid SectorSize (%d) is not a power of 2.\n", + map, sectorsize); + } + else + { + map_data.iSectorSize = (int16)sectorsize; + + /* + // Calc sector_shift (log2(iSectorSize)). We use this to do a bit shift instead of a division + // when we do sector calculations (that is, very often). + uint32 sector_shift = 0; + for (int size = sectorsize; size > 1; size >>= 1) + ++sector_shift; + map_data.uiSectorDivShift = (uint16)sector_shift; + */ + } + } + if ( realmapnum >= 0 ) + map_data.iNum = (int16)realmapnum; + if ( mapid >= 0 ) + map_data.iId = (int16)mapid; } + + map_data.fInitialized = true; return true; } @@ -142,10 +159,10 @@ bool CUOMapList::Load(int map, char *args) bool CUOMapList::DetectMapSize(int map) // it sets also the default sector size, if not specified in the ini (<= 0) { - if (m_mapGeoData.maps[map].initialized == false ) + if (m_mapGeoData.maps[map].fInitialized == false ) return false; - const int index = m_mapGeoData.maps[map].num; + const int index = m_mapGeoData.maps[map].iNum; if ( index < 0 ) return false; @@ -172,58 +189,58 @@ bool CUOMapList::DetectMapSize(int map) // it sets also the default sector size, //g_Install.m_Maps[index].GetLength() == 89923808) // (UOP packed) { // ML+ map - if (map_data.sizex <= 0) - map_data.sizex = 7168; - if (map_data.sizey <= 0) - map_data.sizey = 4096; + if (map_data.uiSizeX <= 0) + map_data.uiSizeX = 7168; + if (map_data.uiSizeY <= 0) + map_data.uiSizeY = 4096; } else { // Pre ML map - if (map_data.sizex <= 0) - map_data.sizex = 6144; - if (map_data.sizey <= 0) - map_data.sizey = 4096; + if (map_data.uiSizeX <= 0) + map_data.uiSizeX = 6144; + if (map_data.uiSizeY <= 0) + map_data.uiSizeY = 4096; } - if (map_data.sectorsize <= 0) - map_data.sectorsize = SECTORSIZE_DEFAULT; + if (map_data.iSectorSize <= 0) + map_data.iSectorSize = SECTORSIZE_DEFAULT; break; case 2: // map2.mul - if (map_data.sizex <= 0) - map_data.sizex = 2304; - if (map_data.sizey <= 0) - map_data.sizey = 1600; - if (map_data.sectorsize <= 0) - map_data.sectorsize = SECTORSIZE_DEFAULT; + if (map_data.uiSizeX <= 0) + map_data.uiSizeX = 2304; + if (map_data.uiSizeY <= 0) + map_data.uiSizeY = 1600; + if (map_data.iSectorSize <= 0) + map_data.iSectorSize = SECTORSIZE_DEFAULT; break; case 3: // map3.mul - if (map_data.sizex <= 0) - map_data.sizex = 2560; - if (map_data.sizey <= 0) - map_data.sizey = 2048; - if (map_data.sectorsize <= 0) - map_data.sectorsize = SECTORSIZE_DEFAULT; + if (map_data.uiSizeX <= 0) + map_data.uiSizeX = 2560; + if (map_data.uiSizeY <= 0) + map_data.uiSizeY = 2048; + if (map_data.iSectorSize <= 0) + map_data.iSectorSize = SECTORSIZE_DEFAULT; break; case 4: // map4.mul - if (map_data.sizex <= 0) - map_data.sizex = 1448; - if (map_data.sizey <= 0) - map_data.sizey = 1448; - if (map_data.sectorsize <= 0) - map_data.sectorsize = SECTORSIZE_DEFAULT; + if (map_data.uiSizeX <= 0) + map_data.uiSizeX = 1448; + if (map_data.uiSizeY <= 0) + map_data.uiSizeY = 1448; + if (map_data.iSectorSize <= 0) + map_data.iSectorSize = SECTORSIZE_DEFAULT; break; case 5: // map5.mul - if (map_data.sizex <= 0) - map_data.sizex = 1280; - if (map_data.sizey <= 0) - map_data.sizey = 4096; - if (map_data.sectorsize <= 0) - map_data.sectorsize = SECTORSIZE_DEFAULT; + if (map_data.uiSizeX <= 0) + map_data.uiSizeX = 1280; + if (map_data.uiSizeY <= 0) + map_data.uiSizeY = 4096; + if (map_data.iSectorSize <= 0) + map_data.iSectorSize = SECTORSIZE_DEFAULT; break; default: @@ -232,27 +249,27 @@ bool CUOMapList::DetectMapSize(int map) // it sets also the default sector size, break; } - return (map_data.sizex > 0 && map_data.sizey > 0 && map_data.sectorsize > 0); + return (map_data.uiSizeX > 0 && map_data.uiSizeY > 0 && map_data.iSectorSize > 0); } bool CUOMapList::IsMapSupported(int map) const noexcept { if (( map < 0 ) || ( map >= MAP_SUPPORTED_QTY)) return false; - return m_mapGeoData.maps[map].enabled; + return m_mapGeoData.maps[map].fEnabled; } bool CUOMapList::IsInitialized(int map) const { ASSERT(IsMapSupported(map)); - return m_mapGeoData.maps[map].initialized; + return m_mapGeoData.maps[map].fInitialized; } int CUOMapList::GetSectorSize(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].sectorsize > 0); - return m_mapGeoData.maps[map].sectorsize; + ASSERT(m_mapGeoData.maps[map].iSectorSize > 0); + return m_mapGeoData.maps[map].iSectorSize; } int CUOMapList::CalcSectorQty(int map) const @@ -263,7 +280,7 @@ int CUOMapList::CalcSectorQty(int map) const int CUOMapList::CalcSectorCols(int map) const { ASSERT(IsMapSupported(map)); - const int a = m_mapGeoData.maps[map].sizex; + const int a = m_mapGeoData.maps[map].uiSizeX; const int b = GetSectorSize(map); // ceil division: some maps may not have x or y size perfectly dividable by 64 (default sector size), // still we need to make room even for sectors with a smaller number of usable tiles @@ -273,7 +290,7 @@ int CUOMapList::CalcSectorCols(int map) const int CUOMapList::CalcSectorRows(int map) const { ASSERT(IsMapSupported(map)); - const int a = m_mapGeoData.maps[map].sizey; + const int a = m_mapGeoData.maps[map].uiSizeY; const int b = GetSectorSize(map); // ceil division: some maps may not have x or y size perfectly dividable by 64 (default sector size), // still we need to make room even for sectors with a smaller number of usable tiles @@ -283,27 +300,27 @@ int CUOMapList::CalcSectorRows(int map) const int CUOMapList::GetMapCenterX(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].sizex != (ushort)-1); - return (m_mapGeoData.maps[map].sizex / 2); + ASSERT(m_mapGeoData.maps[map].uiSizeX != (ushort)-1); + return (m_mapGeoData.maps[map].uiSizeX / 2); } int CUOMapList::GetMapCenterY(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].sizey != (ushort)-1); - return (m_mapGeoData.maps[map].sizey / 2); + ASSERT(m_mapGeoData.maps[map].uiSizeY != (ushort)-1); + return (m_mapGeoData.maps[map].uiSizeY / 2); } int CUOMapList::GetMapFileNum(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].num != (ushort)-1); - return m_mapGeoData.maps[map].num; + ASSERT(m_mapGeoData.maps[map].iNum != -1); + return m_mapGeoData.maps[map].iNum; } int CUOMapList::GetMapID(int map) const { ASSERT(IsMapSupported(map)); - ASSERT(m_mapGeoData.maps[map].id != (ushort)-1); - return m_mapGeoData.maps[map].id; + ASSERT(m_mapGeoData.maps[map].iId != -1); + return m_mapGeoData.maps[map].iId; } diff --git a/src/game/uo_files/CUOMapList.h b/src/game/uo_files/CUOMapList.h index aa1457c71..a54803999 100644 --- a/src/game/uo_files/CUOMapList.h +++ b/src/game/uo_files/CUOMapList.h @@ -23,24 +23,27 @@ extern class CUOMapList protected: struct MapGeoData { - uint16 num; // real map number (0 for 0 and 1, 2 for 2, and so on) - file name - uint16 id; // map id used by the client - uint16 sizex; - uint16 sizey; - uint16 sectorsize; - bool enabled; // supported map? - bool initialized; - + int16 iNum; // real map number (0 for 0 and 1, 2 for 2, and so on) - file name + int16 iId; // map id used by the client + uint16 uiSizeX; + uint16 uiSizeY; + int16 iSectorSize; + //uint16 uiSectorDivShift; + bool fEnabled; // supported map? + bool fInitialized; + + [[nodiscard]] static constexpr MapGeoData invalid() noexcept { return { - .num = (uint16)-1, - .id = (uint16)-1, - .sizex = (uint16)-1, - .sizey = (uint16)-1, - .sectorsize = (uint16)-1, - .enabled = true, - .initialized = false + .iNum = -1, + .iId = -1, + .uiSizeX = (uint16)-1, + .uiSizeY = (uint16)-1, + .iSectorSize = -1, + //.uiSectorDivShift = 0, + .fEnabled = true, + .fInitialized = false }; } }; @@ -107,7 +110,7 @@ inline uint16 CUOMapList::GetMapSizeX(int map) const noexcept // Used by CPointBase::IsValidXY() and IsValidPoint(), which is called a LOT //ASSERT(IsMapSupported(map)); //ASSERT(m_sizex[map] != -1); - return m_mapGeoData.maps[map].sizex; + return m_mapGeoData.maps[map].uiSizeX; } inline uint16 CUOMapList::GetMapSizeY(int map) const noexcept @@ -115,7 +118,7 @@ inline uint16 CUOMapList::GetMapSizeY(int map) const noexcept // Used by CPointBase::IsValidXY() and IsValidPoint(), which is called a LOT //ASSERT(IsMapSupported(map)); //ASSERT(m_sizey[map] != -1); - return m_mapGeoData.maps[map].sizey; + return m_mapGeoData.maps[map].uiSizeY; } #endif //_INC_CUOMAPLIST_H From 39e2d5b2883f5d8026d547cda0c9a1a93ff5dc9f Mon Sep 17 00:00:00 2001 From: David Kindl Date: Fri, 30 May 2025 11:05:47 +0200 Subject: [PATCH 040/112] Fix issue with dclicking items on ground with return 1 in equip test disappearing --- src/game/chars/CCharAct.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 7a5e6db86..e509d49bf 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -3308,12 +3308,12 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) if (IsTrigUsed(TRIGGER_EQUIPTEST) || IsTrigUsed(TRIGGER_ITEMEQUIPTEST)) { // Swap the layer for real one, because if we don't use dclick for equip, real layer gets rewritten with LAYER_DRAGGING. - pItem->SetEquipLayer(layer); + pItem->SetContainedLayer(layer); if (pItem->OnTrigger(ITRIG_EQUIPTEST, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE) { // Reset layer to the original value, that item doesn't get misplaced. - pItem->SetEquipLayer(requestedLayer); + pItem->SetContainedLayer(requestedLayer); // since this trigger is called also when creating an item via ITEM=, if the created item has a RETURN 1 in @EquipTest // (or if the NPC has a RETURN 1 in @ItemEquipTest), the item will be created but not placed in the world. @@ -3326,7 +3326,7 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) } // Reset layer to the original value. - pItem->SetEquipLayer(requestedLayer); + pItem->SetContainedLayer(requestedLayer); if (pItem->IsDeleted()) return false; From 5ad8a310746b04b31c311d78c551de719f5b0318 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 1 Jun 2025 12:53:04 +0200 Subject: [PATCH 041/112] CSFile: de-virtualized and/or inlined some simple and hot methods. --- src/common/CScript.cpp | 2 +- src/common/resource/CResourceLock.cpp | 2 +- src/common/sphere_library/CSFile.cpp | 17 +++------- src/common/sphere_library/CSFile.h | 42 +++++++++++++++++++----- src/common/sphere_library/CSFileText.cpp | 5 +-- src/common/sphere_library/CSFileText.h | 8 +---- 6 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/common/CScript.cpp b/src/common/CScript.cpp index a8249516f..6c6e2cc43 100644 --- a/src/common/CScript.cpp +++ b/src/common/CScript.cpp @@ -550,7 +550,7 @@ bool CScript::FindTextHeader( lpctstr pszName ) // Find a section in the current ADDTOCALLSTACK("CScript::FindTextHeader"); // RETURN: false = EOF reached. ASSERT(pszName); - ASSERT( ! IsBinaryMode()); + ASSERT(!_IsBinaryMode()); SeekToBegin(); diff --git a/src/common/resource/CResourceLock.cpp b/src/common/resource/CResourceLock.cpp index 16e4cdd29..e9a8d835b 100644 --- a/src/common/resource/CResourceLock.cpp +++ b/src/common/resource/CResourceLock.cpp @@ -75,7 +75,7 @@ bool CResourceLock::_ReadTextLine( bool fRemoveBlanks ) // Read a line from the EXC_TRY("_ReadTextLine"); ASSERT(m_pLock); - ASSERT( ! IsBinaryMode() ); + ASSERT( ! _IsBinaryMode() ); tchar* ptcBuf = _GetKeyBufferRaw(SCRIPT_MAX_LINE_LEN); while ( CCacheableScriptFile::_ReadString( ptcBuf, SCRIPT_MAX_LINE_LEN )) diff --git a/src/common/sphere_library/CSFile.cpp b/src/common/sphere_library/CSFile.cpp index 3794f943a..6e16f4c9d 100644 --- a/src/common/sphere_library/CSFile.cpp +++ b/src/common/sphere_library/CSFile.cpp @@ -9,12 +9,14 @@ #include #endif -// CSFile:: Constructors, Destructor, Asign operator. +// CSFile:: Constructors, Destructor, Assign operator. CSFile::CSFile() { _fileDescriptor = _kInvalidFD; _uiMode = 0; + if (!_fBinaryMode.has_value()) + _fBinaryMode = true; } CSFile::~CSFile() @@ -464,28 +466,17 @@ CSString CSFile::GetMergedFileName( lpctstr pszBase, lpctstr pszName ) // static // CSFile:: Mode operations. -uint CSFile::_GetFullMode() const -{ - return _uiMode; -} + uint CSFile::GetFullMode() const { MT_SHARED_LOCK_RETURN(this, _uiMode); } -uint CSFile::_GetMode() const -{ - return (_uiMode & 0x0FFFFFFF); -} uint CSFile::GetMode() const { MT_SHARED_LOCK_RETURN(this, _uiMode & 0x0FFFFFFF); } -bool CSFile::_IsWriteMode() const -{ - return (_uiMode & OF_WRITE); -} bool CSFile::IsWriteMode() const { MT_SHARED_LOCK_RETURN(this, _uiMode & OF_WRITE); diff --git a/src/common/sphere_library/CSFile.h b/src/common/sphere_library/CSFile.h index db7e5cbb1..d03a23db2 100644 --- a/src/common/sphere_library/CSFile.h +++ b/src/common/sphere_library/CSFile.h @@ -39,7 +39,7 @@ class CSFile public: static const char * m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ CSFile(); @@ -202,27 +202,27 @@ public: lpctstr GetFileTitle() const; * @brief Get open mode (full). * @return full mode flags. */ -protected: uint _GetFullMode() const; -public: uint GetFullMode() const; +protected: inline uint _GetFullMode() const noexcept; +public: uint GetFullMode() const; /** * @brief Get open mode (OF_NONCRIT, OF_TEXT, OF_BINARY, OF_DEFAULTMODE) * * Get rid of OF_NONCRIT type flags * @return mode flags. */ -protected: uint _GetMode() const; -public: uint GetMode() const; +protected: inline uint _GetMode() const noexcept; +public: uint GetMode() const; /** * @brief Check if file es in binary mode. * @return Always true (this method is virtual). */ - virtual bool IsBinaryMode() const { return true; } + inline bool _IsBinaryMode() const; /** * @brief Check if file is open for write. * @return true if file is open for write, false otherwise. */ -protected: bool _IsWriteMode() const; -public: bool IsWriteMode() const; +protected: inline bool _IsWriteMode() const noexcept; +public: bool IsWriteMode() const; ///@} protected: @@ -233,9 +233,10 @@ public: bool IsWriteMode() const; #endif const file_descriptor_t _kInvalidFD = (file_descriptor_t)-1; -protected: CSString _strFileName; // File name (with path). uint _uiMode; // MMSYSTEM may use 32 bit flags. + std::optional _fBinaryMode; + public: file_descriptor_t _fileDescriptor; // File descriptor (POSIX, int) or HANDLE (Windows, size of a pointer). @@ -247,4 +248,27 @@ public: bool IsWriteMode() const; }; +// Inline methods + +uint CSFile::_GetFullMode() const noexcept +{ + return _uiMode; +} + +uint CSFile::_GetMode() const noexcept +{ + return (_uiMode & 0x0FFFFFFF); +} + +bool CSFile::_IsWriteMode() const noexcept +{ + return (_uiMode & OF_WRITE); +} + +bool CSFile::_IsBinaryMode() const +{ + return _fBinaryMode.value_or(true); // It must always have a value actually. +} + + #endif // _INC_CSFILE_H diff --git a/src/common/sphere_library/CSFileText.cpp b/src/common/sphere_library/CSFileText.cpp index 4ebc6e641..8b2767d95 100644 --- a/src/common/sphere_library/CSFileText.cpp +++ b/src/common/sphere_library/CSFileText.cpp @@ -5,7 +5,7 @@ #include // for _get_osfhandle (used by STDFUNC_FILENO) #endif -// CSFileText:: Constructors, Destructor, Asign operator. +// CSFileText:: Constructors, Destructor, Assign operator. const char* CSFileText::m_sClassName = "CSFileText"; @@ -16,6 +16,7 @@ CSFileText::CSFileText() #ifdef _WIN32 _fNoBuffer = false; #endif + _fBinaryMode = false; } CSFileText::~CSFileText() @@ -294,7 +295,7 @@ lpctstr CSFileText::_GetModeStr() const ADDTOCALLSTACK("CSFileText::_GetModeStr"); // end of line translation is crap. ftell and fseek don't work correctly when you use it. // fopen() args - if ( IsBinaryMode()) + if ( _IsBinaryMode()) return ( _IsWriteMode() ? "wb" : "rb" ); if ( _GetMode() & OF_READWRITE ) return "a+b"; diff --git a/src/common/sphere_library/CSFileText.h b/src/common/sphere_library/CSFileText.h index b8ae6f4c3..288a3dbfd 100644 --- a/src/common/sphere_library/CSFileText.h +++ b/src/common/sphere_library/CSFileText.h @@ -15,7 +15,7 @@ class CSFileText : public CSFile public: static const char *m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ public: @@ -139,12 +139,6 @@ public: bool WriteString(lpctstr pStr); * @return string that describes the open mode. */ lpctstr _GetModeStr() const; -public: - /** - * @brief Check if file is open in binary mode. - * @return false always. - */ - virtual bool IsBinaryMode() const override { return false; }; ///@} public: FILE * _pStream; // The current open script type file. From 17b07459a325d40e4fb7a03b28bd936187f70bf1 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 1 Jun 2025 12:53:36 +0200 Subject: [PATCH 042/112] Fixed some typos in comments. --- src/common/sphere_library/CSMemBlock.cpp | 4 ++-- src/common/sphere_library/CSMemBlock.h | 4 ++-- src/common/sphere_library/CSObjArray.h | 2 +- src/common/sphere_library/CSObjCont.h | 2 +- src/common/sphere_library/CSObjContRec.h | 2 +- src/common/sphere_library/CSObjList.h | 2 +- src/common/sphere_library/CSObjListRec.h | 2 +- src/common/sphere_library/CSObjSortArray.h | 2 +- src/common/sphere_library/CSPtrTypeArray.h | 2 +- src/common/sphere_library/CSQueue.cpp | 2 +- src/common/sphere_library/CSQueue.h | 2 +- src/common/sphere_library/CSString.h | 2 +- src/common/sphere_library/CSTypedArray.h | 2 +- src/common/sphere_library/smutex.cpp | 2 +- src/common/sphere_library/smutex.h | 6 +++--- src/common/sphere_library/sresetevents.cpp | 2 +- src/common/sphere_library/sresetevents.h | 4 ++-- src/game/uo_files/CUOMapList.cpp | 2 +- src/game/uo_files/CUOMapList.h | 2 +- 19 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/common/sphere_library/CSMemBlock.cpp b/src/common/sphere_library/CSMemBlock.cpp index 3e5647050..f9ca9adae 100644 --- a/src/common/sphere_library/CSMemBlock.cpp +++ b/src/common/sphere_library/CSMemBlock.cpp @@ -1,7 +1,7 @@ #include #include "CSMemBlock.h" -// CSMemBlock:: Constructors, Destructor, Asign operator. +// CSMemBlock:: Constructors, Destructor, Assign operator. CSMemBlock::CSMemBlock() { @@ -47,7 +47,7 @@ void CSMemBlock::MemLink( byte * pData ) } -// CSMemLenBlock:: Constructors, Destructor, Asign operator. +// CSMemLenBlock:: Constructors, Destructor, Assign operator. CSMemLenBlock::CSMemLenBlock() : m_uiLength(0) diff --git a/src/common/sphere_library/CSMemBlock.h b/src/common/sphere_library/CSMemBlock.h index 17a043983..e7f3f6484 100644 --- a/src/common/sphere_library/CSMemBlock.h +++ b/src/common/sphere_library/CSMemBlock.h @@ -13,7 +13,7 @@ class CSMemBlock { public: - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ CSMemBlock(); @@ -78,7 +78,7 @@ class CSMemBlock class CSMemLenBlock : public CSMemBlock { public: - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ CSMemLenBlock(); diff --git a/src/common/sphere_library/CSObjArray.h b/src/common/sphere_library/CSObjArray.h index 4e3f21d26..7bd856ce0 100644 --- a/src/common/sphere_library/CSObjArray.h +++ b/src/common/sphere_library/CSObjArray.h @@ -20,7 +20,7 @@ class CSObjArray : public CSPtrTypeArray public: static const char *m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ public: diff --git a/src/common/sphere_library/CSObjCont.h b/src/common/sphere_library/CSObjCont.h index 4ce52d75a..8f882b441 100644 --- a/src/common/sphere_library/CSObjCont.h +++ b/src/common/sphere_library/CSObjCont.h @@ -53,7 +53,7 @@ class CSObjCont friend class CSObjContRec; static const char * m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ /** diff --git a/src/common/sphere_library/CSObjContRec.h b/src/common/sphere_library/CSObjContRec.h index 83bd81c52..f91300567 100644 --- a/src/common/sphere_library/CSObjContRec.h +++ b/src/common/sphere_library/CSObjContRec.h @@ -20,7 +20,7 @@ class CSObjContRec friend class CSObjCont; static const char * m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ /** diff --git a/src/common/sphere_library/CSObjList.h b/src/common/sphere_library/CSObjList.h index dc0401953..0e24390aa 100644 --- a/src/common/sphere_library/CSObjList.h +++ b/src/common/sphere_library/CSObjList.h @@ -15,7 +15,7 @@ class CSObjList friend class CSObjListRec; static const char * m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ /** diff --git a/src/common/sphere_library/CSObjListRec.h b/src/common/sphere_library/CSObjListRec.h index 819a07141..7d42b2943 100644 --- a/src/common/sphere_library/CSObjListRec.h +++ b/src/common/sphere_library/CSObjListRec.h @@ -20,7 +20,7 @@ class CSObjListRec friend class CSObjList; static const char * m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ /** diff --git a/src/common/sphere_library/CSObjSortArray.h b/src/common/sphere_library/CSObjSortArray.h index 3ed38b3f5..042d6778e 100644 --- a/src/common/sphere_library/CSObjSortArray.h +++ b/src/common/sphere_library/CSObjSortArray.h @@ -14,7 +14,7 @@ template class CSObjSortArray : public CSObjArray { - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ public: diff --git a/src/common/sphere_library/CSPtrTypeArray.h b/src/common/sphere_library/CSPtrTypeArray.h index 95f1181b0..9c7548318 100644 --- a/src/common/sphere_library/CSPtrTypeArray.h +++ b/src/common/sphere_library/CSPtrTypeArray.h @@ -14,7 +14,7 @@ class CSPtrTypeArray : public CSTypedArray public: static const char * m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ CSPtrTypeArray() = default; diff --git a/src/common/sphere_library/CSQueue.cpp b/src/common/sphere_library/CSQueue.cpp index 1901def49..18c409aa8 100644 --- a/src/common/sphere_library/CSQueue.cpp +++ b/src/common/sphere_library/CSQueue.cpp @@ -5,7 +5,7 @@ #include #include "CSQueue.h" -// CSQueueBytes:: Constructors, Destructor, Asign operator. +// CSQueueBytes:: Constructors, Destructor, Assign operator. CSQueueBytes::CSQueueBytes() noexcept : m_iDataQty(0) diff --git a/src/common/sphere_library/CSQueue.h b/src/common/sphere_library/CSQueue.h index c0d47a9b7..b1b7ff783 100644 --- a/src/common/sphere_library/CSQueue.h +++ b/src/common/sphere_library/CSQueue.h @@ -15,7 +15,7 @@ class CSQueueBytes public: static const char * m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ CSQueueBytes() noexcept; diff --git a/src/common/sphere_library/CSString.h b/src/common/sphere_library/CSString.h index 5d414ad05..cca75ed84 100644 --- a/src/common/sphere_library/CSString.h +++ b/src/common/sphere_library/CSString.h @@ -42,7 +42,7 @@ class CSString void InitDefault(); public: - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ diff --git a/src/common/sphere_library/CSTypedArray.h b/src/common/sphere_library/CSTypedArray.h index a4b1143a8..c1653faae 100644 --- a/src/common/sphere_library/CSTypedArray.h +++ b/src/common/sphere_library/CSTypedArray.h @@ -20,7 +20,7 @@ class CSTypedArray : public std::vector public: static const char *m_sClassName; - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ /** diff --git a/src/common/sphere_library/smutex.cpp b/src/common/sphere_library/smutex.cpp index a6faa28e0..98c951145 100644 --- a/src/common/sphere_library/smutex.cpp +++ b/src/common/sphere_library/smutex.cpp @@ -3,7 +3,7 @@ #endif #include "smutex.h" -// SimpleMutex:: Constructors, Destructor, Asign operator. +// SimpleMutex:: Constructors, Destructor, Assign operator. SimpleMutex::SimpleMutex() noexcept { diff --git a/src/common/sphere_library/smutex.h b/src/common/sphere_library/smutex.h index 29192ab41..4534e21ab 100644 --- a/src/common/sphere_library/smutex.h +++ b/src/common/sphere_library/smutex.h @@ -31,7 +31,7 @@ class SimpleMutex #endif public: - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ SimpleMutex() noexcept; @@ -98,7 +98,7 @@ class SimpleMutex class SimpleThreadLock { public: - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ inline explicit SimpleThreadLock(SimpleMutex &mutex) noexcept @@ -140,7 +140,7 @@ class SimpleThreadLock class ManualThreadLock { public: - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ inline ManualThreadLock() noexcept diff --git a/src/common/sphere_library/sresetevents.cpp b/src/common/sphere_library/sresetevents.cpp index bc4d875c3..e7a187b05 100644 --- a/src/common/sphere_library/sresetevents.cpp +++ b/src/common/sphere_library/sresetevents.cpp @@ -89,7 +89,7 @@ void AutoResetEvent::signal() #endif } -// ManualResetEvent:: Constructors, Destructor, Asign operator. +// ManualResetEvent:: Constructors, Destructor, Assign operator. ManualResetEvent::ManualResetEvent() { diff --git a/src/common/sphere_library/sresetevents.h b/src/common/sphere_library/sresetevents.h index 98a25a4d8..f83d856b5 100644 --- a/src/common/sphere_library/sresetevents.h +++ b/src/common/sphere_library/sresetevents.h @@ -24,7 +24,7 @@ class AutoResetEvent static constexpr uint _kiInfinite = 0xffffffff; // Default wait time. #endif - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ AutoResetEvent(); @@ -73,7 +73,7 @@ class ManualResetEvent static constexpr uint _kiInfinite = 0xffffffff; // Default wait time. #endif - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ ManualResetEvent(); diff --git a/src/game/uo_files/CUOMapList.cpp b/src/game/uo_files/CUOMapList.cpp index 112de2551..5e88b7c4f 100644 --- a/src/game/uo_files/CUOMapList.cpp +++ b/src/game/uo_files/CUOMapList.cpp @@ -19,7 +19,7 @@ void CUOMapList::MapGeoDataHolder::clear() noexcept } -// CUOMapList:: Constructors, Destructor, Asign operator. +// CUOMapList:: Constructors, Destructor, Assign operator. CUOMapList::CUOMapList() noexcept { diff --git a/src/game/uo_files/CUOMapList.h b/src/game/uo_files/CUOMapList.h index a54803999..5526e67df 100644 --- a/src/game/uo_files/CUOMapList.h +++ b/src/game/uo_files/CUOMapList.h @@ -59,7 +59,7 @@ extern class CUOMapList CServerMapDiffCollection * m_pMapDiffCollection; public: - /** @name Constructors, Destructor, Asign operator: + /** @name Constructors, Destructor, Assign operator: */ ///@{ CUOMapList() noexcept; From 30ad7649403fd7ae3110c0f89e6e4fe235cc9a33 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 1 Jun 2025 18:57:24 +0200 Subject: [PATCH 043/112] CRect/CUOMapList: more optimizations. --- src/common/CRect.cpp | 40 +++++++++++------ src/game/CRegion.cpp | 8 ++-- src/game/CSector.cpp | 12 +++-- src/game/CSector.h | 2 - src/game/CSectorList.cpp | 76 ++++++++++++++------------------ src/game/CSectorList.h | 34 +++++++------- src/game/CSectorTemplate.cpp | 33 ++++++++------ src/game/CSectorTemplate.h | 5 ++- src/game/CServerConfig.cpp | 11 ++--- src/game/CWorld.cpp | 8 ++-- src/game/CWorldMap.cpp | 2 +- src/game/uo_files/CUOMapList.cpp | 15 +------ src/game/uo_files/CUOMapList.h | 4 +- 13 files changed, 128 insertions(+), 122 deletions(-) diff --git a/src/common/CRect.cpp b/src/common/CRect.cpp index 589fe09ac..0ce482b72 100644 --- a/src/common/CRect.cpp +++ b/src/common/CRect.cpp @@ -284,37 +284,49 @@ CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that ma // RETURN: nullptr = no more // Align new rect. - const CSectorList* pSectors = CSectorList::Get(); + const CSectorList *pSectors = CSectorList::Get(); + const MapSectorsData& sd = pSectors->GetMapSectorData(m_map); + const int iSectorSize = sd.iSectorSize; + const int iSectorCols = sd.iSectorColumns; + const uint uiSectorShift = sd.uiSectorDivShift; - const int iSectorSize = pSectors->GetSectorSize(m_map); CRectMap rect; - rect.m_left = m_left & ~(iSectorSize-1); - rect.m_right = ( m_right | (iSectorSize-1)) + 1; + rect.m_left = m_left & ~(iSectorSize-1); // aligns the left boundary down to the nearest multiple of iSectorSize. + rect.m_right = ( m_right | (iSectorSize-1)) + 1; // rounds the right boundary up to cover the full last sector. rect.m_top = m_top & ~(iSectorSize-1); rect.m_bottom = ( m_bottom | (iSectorSize-1)) + 1; rect.m_map = m_map; rect.NormalizeRectMax(); - const int iSectorCols = pSectors->GetSectorCols(m_map); - const int width = (rect.GetWidth()) / iSectorSize; - const int height = (rect.GetHeight()) / iSectorSize; + //const int width = (rect.GetWidth()) / iSectorSize; + //const int height = (rect.GetHeight()) / iSectorSize; + const int width = rect.GetWidth() >> uiSectorShift; + const int height = rect.GetHeight() >> uiSectorShift; + #ifdef _DEBUG ASSERT(width <= iSectorCols); - const int iSectorRows = pSectors->GetSectorRows(m_map); + const int iSectorRows = sd.iSectorRows; ASSERT(height <= iSectorRows); #endif + /* const int iBase = (( rect.m_top / iSectorSize) * iSectorCols) + ( rect.m_left / iSectorSize ); - if ( i >= ( height * width )) - { - if ( ! i ) - return pSectors->GetSector(m_map, iBase); - return nullptr; - } + return i ? nullptr : pSectors->GetSector(m_map, iBase); const int indexoffset = (( i / width ) * iSectorCols) + ( i % width ); return pSectors->GetSector(m_map, iBase + indexoffset); + */ + + const int baseRow = rect.m_top >> uiSectorShift; + const int baseCol = rect.m_left >> uiSectorShift; + const int iBase = (baseRow * iSectorCols) + baseCol; + + if (i >= (height * width)) + return i ? nullptr : pSectors->GetSector(m_map, iBase); + + const int indexoffset = ((i / width) * iSectorCols) + (i % width); + return pSectors->GetSector(m_map, iBase + indexoffset); } diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 1ecf2ada5..e2a9fc9e6 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -18,9 +18,9 @@ CRegion::CRegion( CResourceID rid, lpctstr pszName ) : CResourceDef( rid ) { - ADDTOCALLSTACK("CRegion::CRegion()"); - m_dwFlags = 0; - m_dwModifiedFlags = 0; + ADDTOCALLSTACK("CRegion::CRegion"); + m_dwFlags = 0; + m_dwModifiedFlags = 0; m_iLinkedSectors = 0; if ( pszName ) SetName( pszName ); @@ -74,7 +74,7 @@ bool CRegion::RealizeRegion() // Attach to all sectors that i overlap. ASSERT( m_iLinkedSectors == 0 ); const CSectorList* pSectors = CSectorList::Get(); - for ( int i = 0, iMax = pSectors->GetSectorQty(m_pt.m_map); i < iMax; ++i ) + for ( int i = 0, iMax = pSectors->GetMapSectorData(m_pt.m_map).iSectorQty; i < iMax; ++i ) { CSector *pSector = pSectors->GetSector(m_pt.m_map, i); diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index 1eb91ced9..50ff21de3 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -16,6 +16,10 @@ #include "triggers.h" #include "CSector.h" + +#define SECTOR_TICKING_PERIOD 30 * 1000 // Every 30 seconds. + + ////////////////////////////////////////////////////////////////// // -CSector @@ -263,6 +267,7 @@ void CSector::_GoAwake() * of NPCs being stop until you enter the sector, or all the spawns * generating NPCs at once. */ + // Using the 'static' keyword isn't thread safe. We assume that we are managing sectors only on a single thread... static CSector *pCentral = nullptr; // do this only for the awaken sector if (!pCentral) { @@ -619,12 +624,13 @@ int CSector::GetLocalTime() const } else { + const MapSectorsData& sd = pSectors->GetMapSectorData(pt.m_map); + // Time difference between adjacent sectors in minutes - const int iSectorTimeDiff = (24*60) / pSectors->GetSectorCols(pt.m_map); + const int iSectorTimeDiff = (24*60) / sd.iSectorColumns; // Calculate the # of columns between here and Castle Britannia ( x = 1400 ) - //int iSectorOffset = ( pt.m_x / g_MapList.GetX(pt.m_map) ) - ( (24*60) / g_MapList.GetSectorSize(pt.m_map)); - const int iSectorOffset = ( pt.m_x / pSectors->GetSectorSize(pt.m_map)); + const int iSectorOffset = ( pt.m_x / sd.iSectorSize); // Calculate the time offset from global time const int iTimeOffset = iSectorOffset * iSectorTimeDiff; diff --git a/src/game/CSector.h b/src/game/CSector.h index 396e522a8..48f718c89 100644 --- a/src/game/CSector.h +++ b/src/game/CSector.h @@ -11,8 +11,6 @@ #include "CSectorTemplate.h" #include "CTimedObject.h" -#define SECTOR_TICKING_PERIOD 30 * 1000 // Every 30 seconds. - class CChar; class CItemStone; diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index d99514400..6dfb93df3 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -24,8 +24,9 @@ void CSectorList::Init() if ( _fInitialized ) // disable changes on-a-fly return; - // Initialize sector data for every map plane - TemporaryString ts; + + // Initialize sector data for every map plane + TemporaryString ts; TemporaryString tsConcat; for (int iMap = 0; iMap < MAP_SUPPORTED_QTY; ++iMap) @@ -38,7 +39,7 @@ void CSectorList::Init() int iSectorIndex = 0; MapSectorsData& sd = _SectorData[iMap]; - sd._iSectorSize = sd._iSectorColumns = sd._iSectorRows = sd._iSectorQty = 0; + sd.iSectorSize = sd.iSectorColumns = sd.iSectorRows = sd.iSectorQty = 0; sd._pSectors.reset(); if (!g_MapList.IsMapSupported(iMap)) @@ -52,10 +53,18 @@ void CSectorList::Init() Str_ConcatLimitNull(tsConcat.buffer(), ts.buffer(), tsConcat.capacity()); sd._pSectors = std::make_unique(iSectorQty); - sd._iSectorSize = g_MapList.GetSectorSize(iMap); - sd._iSectorQty = iSectorQty; - sd._iSectorColumns = iMaxX; - sd._iSectorRows = iMaxY; + sd.iSectorSize = g_MapList.GetSectorSize(iMap); + sd.iSectorQty = iSectorQty; + sd.iSectorColumns = iMaxX; + sd.iSectorRows = iMaxY; + + // Calc sector_shift (log2(iSectorSize)). We use this to do a bit shift instead of a division + // when we do sector calculations (that is, very often). + uint32 sector_shift = 0; + for (uint sz = sd.iSectorSize; sz > 1; sz >>= 1) + ++sector_shift; + sd.uiSectorDivShift = (uint16)sector_shift; + short iSectorX = 0, iSectorY = 0; for (; iSectorIndex < iSectorQty; ++iSectorIndex) @@ -65,6 +74,7 @@ void CSectorList::Init() iSectorX = 0; ++iSectorY; } + CSector* pSector = &(sd._pSectors[iSectorIndex]); ASSERT(pSector); pSector->Init(iSectorIndex, (uchar)iMap, iSectorX, iSectorY); @@ -77,7 +87,7 @@ void CSectorList::Init() if (!sd._pSectors) continue; - for (int iSector = 0; iSector < sd._iSectorQty; ++iSector) + for (int iSector = 0; iSector < sd.iSectorQty; ++iSector) { sd._pSectors[iSector].SetAdjacentSectors(); } @@ -97,7 +107,7 @@ void CSectorList::Close(bool fClosingWorld) continue; // Delete everything in sector - for (int iSector = 0; iSector < sd._iSectorQty; ++iSector) + for (int iSector = 0; iSector < sd.iSectorQty; ++iSector) { sd._pSectors[iSector].Close(fClosingWorld); } @@ -106,62 +116,44 @@ void CSectorList::Close(bool fClosingWorld) } -#define IsMapInvalid(map) ((map < 0) || (map >= MAP_SUPPORTED_QTY)) - -int CSectorList::GetSectorSize(int map) const noexcept +[[nodiscard]] +const MapSectorsData& CSectorList::GetMapSectorData(int map) const { - return (IsMapInvalid(map) ? -1 : _SectorData[map]._iSectorSize); -} - -int CSectorList::GetSectorQty(int map) const noexcept -{ - return (IsMapInvalid(map) ? -1 : _SectorData[map]._iSectorQty); -} - -int CSectorList::GetSectorCols(int map) const noexcept -{ - return (IsMapInvalid(map) ? -1 : _SectorData[map]._iSectorColumns); -} - -int CSectorList::GetSectorRows(int map) const noexcept -{ - return (IsMapInvalid(map) ? -1 : _SectorData[map]._iSectorRows); + DEBUG_ASSERT(g_MapList.IsMapSupported(map)); + return _SectorData[map]; } CSector* CSectorList::GetSector(int map, int index) const noexcept { - if (IsMapInvalid(map) || !_SectorData[map]._pSectors) + if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) return nullptr; const MapSectorsData& sd = _SectorData[map]; - return (index < sd._iSectorQty) ? &(sd._pSectors[index]) : nullptr; + return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; } CSector* CSectorList::GetSector(int map, short x, short y) const noexcept { - if (IsMapInvalid(map) || !_SectorData[map]._pSectors) + if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) return nullptr; const MapSectorsData& sd = _SectorData[map]; - const int xSect = (x / sd._iSectorSize), ySect = (y / sd._iSectorSize); - if ((xSect >= sd._iSectorColumns) || (ySect >= sd._iSectorRows)) + const int xSect = (x / sd.iSectorSize), ySect = (y / sd.iSectorSize); + if ((xSect >= sd.iSectorColumns) || (ySect >= sd.iSectorRows)) return nullptr; - const int index = ((ySect * sd._iSectorColumns) + xSect); - return (index < sd._iSectorQty) ? &(sd._pSectors[index]) : nullptr; + const int index = ((ySect * sd.iSectorColumns) + xSect); + return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; } -#undef IsMapInvalid - - int CSectorList::GetSectorAbsoluteQty() const noexcept { int iCount = 0; for (const MapSectorsData& sd : _SectorData) { if (!sd._pSectors) - continue; + continue; - iCount += sd._iSectorQty; + iCount += sd.iSectorQty; } return iCount; } @@ -174,13 +166,13 @@ CSector* CSectorList::GetSectorAbsolute(int index) noexcept if (!sd._pSectors) continue; - if ((base + sd._iSectorQty) > index) + if ((base + sd.iSectorQty) > index) { const int iSummation = (index - base); return &(sd._pSectors[iSummation]); } - base += sd._iSectorQty; + base += sd.iSectorQty; } return nullptr; } diff --git a/src/game/CSectorList.h b/src/game/CSectorList.h index 7f2bec4d0..278212add 100644 --- a/src/game/CSectorList.h +++ b/src/game/CSectorList.h @@ -10,23 +10,27 @@ #include "CSector.h" +// Sector data for a given map +struct MapSectorsData +{ + friend class CSectorList; + + // Pre-calculated values, for faster retrieval + int iSectorSize; + int iSectorColumns; // how much sectors are in a column (x) in a given map + int iSectorRows; // how much sectors are in a row (y) in a given map + int iSectorQty; // how much sectors are in a map + uint16 uiSectorDivShift; // precalculated value to avoid a division in sector/rect calculations + +private: + std::unique_ptr _pSectors; +}; + class CSectorList { public: static const char* m_sClassName; - // Sector data - struct MapSectorsData - { - std::unique_ptr _pSectors; - - // Pre-calculated values, for faster retrieval - int _iSectorSize; - int _iSectorColumns; // how much sectors are in a column (x) in a given map - int _iSectorRows; // how much sectors are in a row (y) in a given map - int _iSectorQty; // how much sectors are in a map - }; - //std::array _SectorData; // Use plain C-style vectors, to remove even the minimum overhead of std::array methods MapSectorsData _SectorData[MAP_SUPPORTED_QTY]; bool _fInitialized; @@ -44,10 +48,8 @@ class CSectorList void Init(); void Close(bool fClosingWorld); - int GetSectorSize(int map) const noexcept; - int GetSectorQty(int map) const noexcept; - int GetSectorCols(int map) const noexcept; - int GetSectorRows(int map) const noexcept; + [[nodiscard]] + const MapSectorsData& GetMapSectorData(int map) const; CSector* GetSector(int map, int index) const noexcept; // gets sector # from one map CSector* GetSector(int map, short x, short y) const noexcept; diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index e4b63f234..c540194ca 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -156,12 +156,13 @@ void CItemsList::AddItemToSector( CItem * pItem ) void CSectorBase::SetAdjacentSectors() { const CSectorList* pSectors = CSectorList::Get(); + auto const& sd = pSectors->GetMapSectorData(m_map); - const int iMaxX = pSectors->GetSectorCols(m_map); + const int iMaxX = sd.iSectorColumns; ASSERT(iMaxX > 0); - [[maybe_unused]] const int iMaxY = pSectors->GetSectorRows(m_map); + [[maybe_unused]] const int iMaxY = sd.iSectorRows; ASSERT(iMaxY > 0); - const int iMaxSectors = pSectors->GetSectorQty(m_map); + const int iMaxSectors = sd.iSectorQty; // Sectors are layed out in the array horizontally: when the row is complete (X), the subsequent sector is placed in // the column below (Y). @@ -232,7 +233,7 @@ void CSectorBase::Init(int index, uchar map, short x, short y) { g_Log.EventError("Trying to initalize a sector %d in unsupported map #%d. Defaulting to 0,0.\n", index, map); } - else if (( index < 0 ) || ( index >= CSectorList::Get()->GetSectorQty(map) )) + else if (( index < 0 ) || ( index >= CSectorList::Get()->GetMapSectorData(map).iSectorQty )) { m_map = map; g_Log.EventError("Trying to initalize a sector by sector number %d out-of-range for map #%d. Defaulting to 0,%d.\n", index, map, map); @@ -452,13 +453,16 @@ CPointMap CSectorBase::GetBasePoint() const { // ADDTOCALLSTACK_DEBUG("CSectorBase::GetBasePoint"); // It's commented because it's slow and this method is called VERY often! // What is the coord base of this sector. upper left point. - const CSectorList* pSectors = CSectorList::Get(); -#if _DEBUG - DEBUG_ASSERT( (m_index >= 0) && (m_index < pSectors->GetSectorQty(m_map)) ); - // Again this method is called very often, so call the least functions possible and do the minimum amount of checks required -#endif - const int iCols = pSectors->GetSectorCols(m_map); - const int iSize = pSectors->GetSectorSize(m_map); + + // Again this method is called very often, so call the least functions possible and do the minimum amount of checks required + DEBUG_ASSERT(g_MapList.IsMapSupported(m_map)); + + const CSectorList* pSectors = CSectorList::Get(); + auto const& sd = pSectors->GetMapSectorData(m_map); + DEBUG_ASSERT((m_index >= 0) && (m_index < sd.iSectorQty) ); + + const int iCols = sd.iSectorColumns; + const int iSize = sd.iSectorSize; const int iQuot = (m_index % iCols), iRem = (m_index / iCols); // Help the compiler to optimize the division return // Initializer list for CPointMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) @@ -474,8 +478,11 @@ CRectMap CSectorBase::GetRect() const noexcept { //ADDTOCALLSTACK_DEBUG("CSectorBase::GetRect"); // It's commented because it's slow and this method is called VERY often! // Get a rectangle for the sector. - const CPointMap& pt = GetBasePoint(); - const int iSectorSize = CSectorList::Get()->GetSectorSize(pt.m_map); + DEBUG_ASSERT(g_MapList.IsMapSupported(m_map)); + + const CSectorList* pSectors = CSectorList::Get(); + const CPointMap& pt = GetBasePoint(); + const int iSectorSize = pSectors->GetMapSectorData(pt.m_map).iSectorSize; return // Initializer list for CRectMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) { pt.m_x, // left diff --git a/src/game/CSectorTemplate.h b/src/game/CSectorTemplate.h index 8260e669c..8e7cd54c7 100644 --- a/src/game/CSectorTemplate.h +++ b/src/game/CSectorTemplate.h @@ -99,11 +99,12 @@ class CSectorBase // world sector protected: - uchar m_map; // sector map - short _x, _y; // x and y (row and column) of the sector in the map + uint8 m_map; // sector map + int16 _x, _y; // x and y (row and column) of the sector in the map int m_index; // sector index private: + // TODO: store indices instead of pointers, to make the class smaller? CSector* _ppAdjacentSectors[DIR_QTY]; public: diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 0842e31ed..f103edaaf 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -1102,7 +1102,7 @@ bool CServerConfig::r_LoadVal( CScript &s ) { if ( !strnicmp(pszStr, "ALLSECTORS", 10) ) { - const int nSectors = CSectorList::Get()->GetSectorQty(nMapNumber); + const int nSectors = CSectorList::Get()->GetMapSectorData(nMapNumber).iSectorQty; pszStr = s.GetArgRaw(); if ( pszStr && *pszStr ) @@ -1733,15 +1733,16 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * { pszCmd += 7; const CSectorList* pSectors = CSectorList::Get(); + const MapSectorsData& sd = pSectors->GetMapSectorData(iNumber); if (!strnicmp(pszCmd, "SIZE", 4)) - sVal.FormatVal(pSectors->GetSectorSize(iNumber)); + sVal.FormatVal(sd.iSectorSize); else if (!strnicmp(pszCmd, "ROWS", 4)) - sVal.FormatVal(pSectors->GetSectorRows(iNumber)); + sVal.FormatVal(sd.iSectorRows); else if (!strnicmp(pszCmd, "COLS", 4)) - sVal.FormatVal(pSectors->GetSectorCols(iNumber)); + sVal.FormatVal(sd.iSectorColumns); else if (!strnicmp(pszCmd, "QTY", 3)) - sVal.FormatVal(pSectors->GetSectorQty(iNumber)); + sVal.FormatVal(sd.iSectorQty); else return false; } diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index c830233dc..fc5ebe86c 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -1260,7 +1260,7 @@ void CWorld::SaveStatics() if ( !g_MapList.IsMapSupported(m) ) continue; - for (int s = 0, qty = _Sectors.GetSectorQty(m); s < qty; ++s) + for (int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s) { CSector* pSector = _Sectors.GetSector(m, s); if ( !pSector ) @@ -1470,7 +1470,7 @@ bool CWorld::LoadAll() // Load world from script if (!g_MapList.IsMapSupported(m)) continue; - for (int s = 0, qty = _Sectors.GetSectorQty(m); s < qty; ++s) + for (int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s) { EXC_TRYSUB("Load"); @@ -1658,7 +1658,7 @@ void CWorld::RespawnDeadNPCs() if ( !g_MapList.IsMapSupported(m) ) continue; - for (int s = 0, qty = _Sectors.GetSectorQty(m); s < qty; ++s) + for (int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s) { EXC_TRY("OnSector"); @@ -1698,7 +1698,7 @@ void CWorld::Restock() if ( !g_MapList.IsMapSupported(m) ) continue; - for ( int s = 0, qty = _Sectors.GetSectorQty(m); s < qty; ++s ) + for ( int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s ) { EXC_TRY("OnSector"); diff --git a/src/game/CWorldMap.cpp b/src/game/CWorldMap.cpp index c152466fb..8f37a6ee4 100644 --- a/src/game/CWorldMap.cpp +++ b/src/game/CWorldMap.cpp @@ -229,7 +229,7 @@ CSector* CWorldMap::GetSector(int map, int index) noexcept // static { //ADDTOCALLSTACK_DEBUG("CWorldMap::GetSector(index)"); - const int iMapSectorQty = g_World._Sectors.GetSectorQty(map); + const int iMapSectorQty = g_World._Sectors.GetMapSectorData(map).iSectorQty; if (index >= iMapSectorQty) { g_Log.EventError("Unsupported sector #%d for map #%d specified.\n", index, map); diff --git a/src/game/uo_files/CUOMapList.cpp b/src/game/uo_files/CUOMapList.cpp index 5e88b7c4f..922bfd3df 100644 --- a/src/game/uo_files/CUOMapList.cpp +++ b/src/game/uo_files/CUOMapList.cpp @@ -131,18 +131,7 @@ bool CUOMapList::Load(int map, char *args) map, sectorsize); } else - { map_data.iSectorSize = (int16)sectorsize; - - /* - // Calc sector_shift (log2(iSectorSize)). We use this to do a bit shift instead of a division - // when we do sector calculations (that is, very often). - uint32 sector_shift = 0; - for (int size = sectorsize; size > 1; size >>= 1) - ++sector_shift; - map_data.uiSectorDivShift = (uint16)sector_shift; - */ - } } if ( realmapnum >= 0 ) map_data.iNum = (int16)realmapnum; @@ -254,9 +243,7 @@ bool CUOMapList::DetectMapSize(int map) // it sets also the default sector size, bool CUOMapList::IsMapSupported(int map) const noexcept { - if (( map < 0 ) || ( map >= MAP_SUPPORTED_QTY)) - return false; - return m_mapGeoData.maps[map].fEnabled; + return ((map >= 0) && (map < MAP_SUPPORTED_QTY) && m_mapGeoData.maps[map].fEnabled); } bool CUOMapList::IsInitialized(int map) const diff --git a/src/game/uo_files/CUOMapList.h b/src/game/uo_files/CUOMapList.h index 5526e67df..6dfe5090a 100644 --- a/src/game/uo_files/CUOMapList.h +++ b/src/game/uo_files/CUOMapList.h @@ -28,7 +28,6 @@ extern class CUOMapList uint16 uiSizeX; uint16 uiSizeY; int16 iSectorSize; - //uint16 uiSectorDivShift; bool fEnabled; // supported map? bool fInitialized; @@ -41,7 +40,6 @@ extern class CUOMapList .uiSizeX = (uint16)-1, .uiSizeY = (uint16)-1, .iSectorSize = -1, - //.uiSectorDivShift = 0, .fEnabled = true, .fInitialized = false }; @@ -88,9 +86,11 @@ extern class CUOMapList public: bool IsMapSupported(int map) const noexcept; bool IsInitialized(int map) const; + int CalcSectorQty(int map) const; // Use it only when initializing the map sectors! (because it's slower than the Get* method) int CalcSectorCols(int map) const; // Use it only when initializing the map sectors! (because it's slower than the Get* method) int CalcSectorRows(int map) const; // Use it only when initializing the map sectors! (because it's slower than the Get* method) + inline uint16 GetMapSizeX(int map) const noexcept; inline uint16 GetMapSizeY(int map) const noexcept; int GetMapCenterX(int map) const; From 4429a0acee92c644f31997b094c4b21e5e191191 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 2 Jun 2025 17:19:38 +0200 Subject: [PATCH 044/112] sfastmath.h: added more methods. --- src/common/sphere_library/sfastmath.h | 235 +++++++++++++++++++++++++- 1 file changed, 227 insertions(+), 8 deletions(-) diff --git a/src/common/sphere_library/sfastmath.h b/src/common/sphere_library/sfastmath.h index ed14f1f47..7c92de4d9 100644 --- a/src/common/sphere_library/sfastmath.h +++ b/src/common/sphere_library/sfastmath.h @@ -1,21 +1,62 @@ #ifndef _INC_SFASTMATH_H #define _INC_SFASTMATH_H -// Adapted from: https://github.com/ermig1979/Simd/blob/master/src/Simd/SimdMath.h +//#include +//#include +#include +#include -namespace sfmath + +// Most (if not all) of this functions works on the assumption that negative numbers are represented +// using the two's complement. To be fair most common archs use this, so the checks are over, over zealant. + +// Two's complement validation for signed types +template +[[maybe_unused]] +constexpr bool is_twos_complement_signed() +{ + if constexpr (! std::is_signed_v) + return true; // Unsigned types automatically pass + + // Check 1: All-ones pattern should equal -1 + constexpr bool check_all_ones = static_cast(~T(0)) == T(-1); + + // Check 2: Arithmetic right shift preservation + // Shifting a negative value right by 1 must preserve the sign bit (i.e. yield −1) for arithmetic shift. + constexpr bool check_arith_shift = (T(-1) >> 1) == T(-1); + + return check_all_ones && check_arith_shift; +} +static_assert(is_twos_complement_signed()); + + +namespace sl { +namespace fmath +{ + // Adapted from: https://github.com/ermig1979/Simd/blob/master/src/Simd/SimdMath.h - inline int Average(int a, int b) noexcept + template + inline T Average(T a, T b) noexcept { return (a + b + 1) >> 1; } - inline int Average(int a, int b, int c, int d) noexcept + template + inline T Average(T a, T b, T c, T d) noexcept { return (a + b + c + d + 2) >> 2; } + inline float NotF16(float f) noexcept + { + const int i = ~(int&)f; + return (float&)i; + } + + // -- + + /* inline void SortU8(int & a, int & b) noexcept { const int d = a - b; @@ -23,41 +64,219 @@ namespace sfmath b += d & m; a -= d & m; } + */ + + // TODO: return a pair to unpack the return values with a structured binding? + template + inline void sSortPair(T &iMin, T &iMax) noexcept + { + // 1) Compute difference + T d = iMin - iMax; + + // 2) Build mask = ~(d >> (bitwidth-1)): + // - If a>b, d>0 → d>>(W-1) == 0 → mask = ~0 == all-ones + // - If a<=b,d<=0→ d>>(W-1) == -1→ mask = ~(-1) == 0 + + constexpr int W = sizeof(T) * 8 - 1; + const T m = ~(d >> W); + // 3) Conditionally add/subtract the difference + const T t = d & m; + iMax += t; // if a>b: b = b + (a–b) → b = a + iMin -= t; // if a>b: a = a - (a–b) → a = b_old + } + + // TODO: return a pair to unpack the return values with a structured binding? + // Sort two unsigned integers so that a ≤ b, branchlessly + template + inline void uSortPair(T &uiMin, T &uiMax) noexcept + { + // Compute a mask of all-1’s if a < b, else 0 + // (a < b) → 1, negate → T(-1) ; (a < b) → 0, negate → T(0) + const T mask = -T(uiMin < uiMax); + + // XOR-swap trick under mask: + // t = (a ^ b) & mask → t = difference bits if a> 8; return (d & ~m) | (-d & m); } + */ + template + inline T uAbsDiff(T a, T b) noexcept + { + // Use the corresponding signed type. + using SignedT = typename std::make_signed::type; + + // Compute the difference in signed space. + const SignedT diff = static_cast(a) - static_cast(b); + // Compute mask: arithmetic right-shift extracts the sign bit + const int bits = sizeof(SignedT) * 8 - 1; + const SignedT mask = diff >> bits; + + // The standard trick: (diff ^ mask) - mask produces the absolute value. + return static_cast((diff ^ mask) - mask); + } + + template + inline T sAbsDiff(T a, T b) noexcept + { + const T d = a - b; + const T mask = -(d < 0); // 0 if d>=0, -1 if d<0 + return (d ^ mask) - mask; // (d ^ mask) - mask = |d| (absolute value) + } + + /* inline int MaxU8(int a, int b) noexcept { const int d = a - b; const int m = ~(d >> 8); return b + (d & m); } + */ + template + inline T sMax(T a, T b) noexcept + { + const T d = a - b; + const T mask = -(d >= 0); // -1 if a>=b, 0 otherwise + return b + (d & mask); // b + (d & mask) gives max(a,b) + } + /* inline int MinU8(int a, int b) noexcept { const int d = a - b; const int m = ~(d >> 8); return a - (d & m); } + */ + template + inline T sMin(T a, T b) noexcept + { + const T d = a - b; + const T mask = -(d >= 0); // -1 if a>=b, 0 otherwise + return a - (d & mask); // a - (d & mask) gives min(a,b) + } - inline int SaturatedSubtractionU8(int a, int b) noexcept + /* + inline int ZeroSaturatedSubtractionU8(int a, int b) noexcept { const int d = a - b; const int m = ~(d >> 8); return (d & m); } + */ + template + inline T sZeroSatSub(T a, T b) noexcept // clamps negative value to 0 + { + const T d = a - b; + const T mask = -(d >= 0); // -1 if a>=b, 0 otherwise + return d & mask; // (d & mask) clamps negative d to 0 + } - inline float NotF16(float f) noexcept +/* SatSub + Case 1: When a ≥ b (positive difference) + + Example: a = 5, b = 2 → d = 3 + + d = 00000011 (3 in 8-bit) + mask = 00000011 >> 7 = 00000000 (positive→0) + d ^ mask = 00000011 ^ 00000000 = 00000011 + (d ^ mask) - mask = 00000011 - 00000000 = 00000011 (3) + + Case 2: When a < b (negative difference) + + Example: a = 2, b = 5 → d = -3 + + d = 11111101 (-3 in 8-bit two's complement) + mask = 11111101 >> 7 = 11111111 (negative→all 1s) + d ^ mask = 11111101 ^ 11111111 = 00000010 (bit flip) + (d ^ mask) - mask = 00000010 - 11111111 = 00000010 + 1 = 00000011 (3) + + Why it works: When d is negative, XOR with all-1s flips all bits (one's complement), then subtracting -1 adds 1, completing the two's complement negation. +*/ + + // --- + + /* Absolute value */ + + // WARNING: these functions, and even the standard abs(), cannot fully handle the lowest (most negative) value for a signed integer type. + // This is a well-known, inherent limitation of two’s-complement representation. + // Example: int8_t has range –128…127, abs of –128 returns –128 for our signed Abs* (and it's undefined behavior for abs()) and 128 for our Abs*u. + + template + inline T sAbs(T x) noexcept { - const int i = ~(int&)f; - return (float&)i; + // Signed: branchless two’s‐complement abs + // + // mask = x >> (W-1) → 0 for x>=0, -1 for x<0 + // (x ^ mask) - mask → if mask=0: x; if mask=-1: (~x)+1 = -x + constexpr unsigned W = sizeof(T) * 8 - 1; + const T mask = x >> W; + return (x ^ mask) - mask; + } + + /* + inline int8_t sAbs8(int8_t x) noexcept + { + const int8_t mask = x >> 7; + return (x ^ mask) - mask; + } + + inline uint8_t uAbs8(int8_t x) noexcept + { + const uint8_t mask = uint8_t(x) >> 7; + return (uint8_t(x) ^ mask - mask); } + inline int16_t sAbs16(int16_t x) noexcept + { + const int16_t mask = x >> 15; + return (x ^ mask) - mask; + } + + inline uint16_t uAbs16(int16_t x) noexcept + { + const uint16_t mask = uint16_t(x) >> 15; + return (uint16_t(x) ^ mask - mask); + } + + inline int32_t sAbs32(int32_t x) noexcept + { + const int32_t mask = x >> 31;// Arithmetic shift: 0 for x>=0, –1 for x<0 + return (x ^ mask) - mask; // If mask=–1: (~x)–(–1)=–x; else x–0=x + + } + inline uint32_t uAbs32(int32_t x) noexcept + { + const uint32_t mask = uint32_t(x) >> 31; + return (uint32_t(x) ^ mask) - mask; + } + + inline int64_t sAbs64(int64_t x) noexcept + { + const int64_t mask = x >> 63; + return (x ^ mask) - mask; + } + + inline uint64_t uAbs64(int64_t x) noexcept + { + const int64_t mask = x >> 63; + return (uint64_t(x) ^ mask - mask); + } + */ + +} // namespace } // namespace #endif // _INC_SFASTMATH_H From 33c1540c470a49de3e0509829c238b26ad33803c Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 2 Jun 2025 17:23:05 +0200 Subject: [PATCH 045/112] Optimized coords calculations and operations in CRect, CPointBase, CSectorList. --- src/common/CPointBase.cpp | 30 ++++--- src/common/CRect.cpp | 110 +++++++++++++++----------- src/common/CRect.h | 18 ++--- src/common/sphere_library/sfastmath.h | 7 +- src/game/CRegion.cpp | 2 +- src/game/CSectorList.cpp | 41 +++++++--- src/game/CSectorList.h | 4 +- src/game/CSectorTemplate.cpp | 2 +- src/game/CServerConfig.cpp | 6 +- src/game/CWorld.cpp | 8 +- src/game/CWorldMap.cpp | 107 ++++++++++++++++--------- src/game/CWorldMap.h | 10 +-- 12 files changed, 210 insertions(+), 135 deletions(-) diff --git a/src/common/CPointBase.cpp b/src/common/CPointBase.cpp index cb8bccbad..7c1eb3f3d 100644 --- a/src/common/CPointBase.cpp +++ b/src/common/CPointBase.cpp @@ -1,4 +1,5 @@ #include "../common/sphere_library/scontainer_ops.h" +#include "../common/sphere_library/sfastmath.h" //#include "../common/CExpression.h" // included in the precompiled header #include "../game/items/CItem.h" #include "../game/uo_files/CUOMultiItemRec.h" @@ -324,6 +325,13 @@ void CPointBase::ValidatePoint() noexcept const short iMaxX = (short)g_MapList.GetMapSizeX(m_map); const short iMaxY = (short)g_MapList.GetMapSizeY(m_map); + m_x = sl::fmath::sMax(m_x, (short)0); + m_x = (m_x < iMaxX) ? m_x : (iMaxX - 1); + + m_y = sl::fmath::sMax(m_y, (short)0); + m_y = (m_y < iMaxY) ? m_y : (iMaxY - 1); + + /* if ( m_x < 0 ) m_x = 0; if (m_x >= iMaxX) @@ -333,6 +341,7 @@ void CPointBase::ValidatePoint() noexcept m_y = 0; if (m_y >= iMaxY) m_y = iMaxY - 1; + */ } bool CPointBase::IsSame2D( const CPointBase & pt ) const @@ -531,7 +540,12 @@ bool CPointBase::r_WriteVal( lpctstr ptcKey, CSString & sVal ) const if (pMultiItem->m_visible == 0) continue; - const CPointMap ptTest((word)(ptMulti.m_x + pMultiItem->m_dx), (word)(ptMulti.m_y + pMultiItem->m_dy), (char)(ptMulti.m_z + pMultiItem->m_dz), this->m_map); + const CPointMap ptTest( + (word)(ptMulti.m_x + pMultiItem->m_dx), + (word)(ptMulti.m_y + pMultiItem->m_dy), + (char)(ptMulti.m_z + pMultiItem->m_dz), + this->m_map); + if (GetDist(ptTest) > 0) continue; @@ -1062,11 +1076,11 @@ CSector * CPointBase::GetSector() const { g_Log.Event(LOGL_ERROR, "Point(%d,%d): trying to get a sector for point on map #%d out of bounds for this map(%d,%d). Defaulting to sector 0 of the map.\n", m_x, m_y, m_map, g_MapList.GetMapSizeX(m_map), g_MapList.GetMapSizeY(m_map)); - return CWorldMap::GetSector(m_map, 0); + return CWorldMap::GetSectorByIndex(m_map, 0); } // Get the world Sector we are in. - return CWorldMap::GetSector(m_map, m_x, m_y); + return CWorldMap::GetSectorByCoordsUnchecked(m_map, m_x, m_y); } CRegion * CPointBase::GetRegion( dword dwType ) const @@ -1079,10 +1093,7 @@ CRegion * CPointBase::GetRegion( dword dwType ) const return nullptr; const CSector *pSector = GetSector(); - if ( pSector ) - return pSector->GetRegion(*this, dwType); - - return nullptr; + return !pSector ? nullptr : pSector->GetRegion(*this, dwType); } size_t CPointBase::GetRegions( dword dwType, CRegionLinks *pRLinks ) const @@ -1094,10 +1105,7 @@ size_t CPointBase::GetRegions( dword dwType, CRegionLinks *pRLinks ) const return 0; const CSector *pSector = GetSector(); - if ( pSector ) - return pSector->GetRegions(*this, dwType, pRLinks); - - return 0; + return !pSector ? 0 : pSector->GetRegions(*this, dwType, pRLinks); } int CPointBase::GetPointSortIndex() const noexcept diff --git a/src/common/CRect.cpp b/src/common/CRect.cpp index 0ce482b72..92340190d 100644 --- a/src/common/CRect.cpp +++ b/src/common/CRect.cpp @@ -1,5 +1,6 @@ #include "../game/uo_files/CUOMapList.h" #include "../game/CSectorList.h" +#include "sphere_library/sfastmath.h" #include "CLog.h" #include "CRect.h" @@ -65,51 +66,51 @@ void CRect::UnionRect( const CRect & rect ) DEBUG_ERR(("Uniting regions from different maps!\n")); } } -bool CRect::IsInside( const CRect & rect ) const noexcept + +bool CRect::IsInside( int x, int y, int map ) const noexcept +{ + // NON inclusive rect! Is the point in the rectangle ? + return( IsInsideX(x) && IsInsideY(y) && ( m_map == map )); +} + +bool CRect::IsInside(const CRect & rect) const noexcept { // Is &rect inside me ? // ASSUME: Normalized rect - if ( rect.m_map != m_map ) - return false; - if ( rect.m_left < m_left ) - return false; - if ( rect.m_top < m_top ) - return false; - if ( rect.m_right > m_right ) - return false; - if ( rect.m_bottom > m_bottom ) - return false; - return true; + return (rect.m_map == m_map) && + (rect.m_left >= m_left) && + (rect.m_top >= m_top) && + (rect.m_right <= m_right)&& + (rect.m_bottom<= m_bottom); } -bool CRect::IsOverlapped( const CRect & rect ) const noexcept +bool CRect::IsOverlapped(const CRect & rect) const noexcept { // are the 2 rects overlapped at all ? // NON inclusive rect. // ASSUME: Normalized rect - // if ( rect.m_map != m_map ) return false; - if ( rect.m_left >= m_right ) - return false; - if ( rect.m_top >= m_bottom ) - return false; - if ( rect.m_right <= m_left ) - return false; - if ( rect.m_bottom <= m_top ) - return false; - return true; + return (rect.m_left < m_right) && + (rect.m_top < m_bottom) && + (rect.m_right > m_left) && + (rect.m_bottom> m_top); } bool CRect::IsEqual( const CRect & rect ) const noexcept { - return m_left == rect.m_left && - m_top == rect.m_top && - m_right == rect.m_right && - m_bottom == rect.m_bottom && - m_map == rect.m_map; + return m_left == rect.m_left && + m_top == rect.m_top && + m_right == rect.m_right && + m_bottom == rect.m_bottom && + m_map == rect.m_map; } void CRect::NormalizeRect() noexcept { + sl::fmath::sSortPair(m_top, m_bottom); + sl::fmath::sSortPair(m_left, m_right); + m_map = g_MapList.IsMapSupported(m_map) ? m_map : 0; + +/* if ( m_bottom < m_top ) { const int wtmp = m_bottom; @@ -124,6 +125,7 @@ void CRect::NormalizeRect() noexcept } if ( !g_MapList.IsMapSupported(m_map) ) m_map = 0; +*/ } void CRect::SetRect( int left, int top, int right, int bottom, int map ) noexcept @@ -138,6 +140,12 @@ void CRect::SetRect( int left, int top, int right, int bottom, int map ) noexcep void CRect::NormalizeRectMax( int cx, int cy ) noexcept { + m_left = sl::fmath::sMax(0, m_left); + m_top = sl::fmath::sMax(0, m_top); + m_right = sl::fmath::sMin(cx, m_right); + m_bottom = sl::fmath::sMin(cy, m_bottom); + + /* if ( m_left < 0 ) m_left = 0; if ( m_top < 0 ) @@ -146,6 +154,7 @@ void CRect::NormalizeRectMax( int cx, int cy ) noexcept m_right = cx; if ( m_bottom > cy ) m_bottom = cy; + */ } size_t CRect::Read( lpctstr pszVal ) @@ -215,14 +224,14 @@ lpctstr CRect::Write() const return Write( Str_GetTemp(), (uint)Str_TempLength() ); } -CPointBase CRect::GetCenter() const +CPointBase CRect::GetCenter() const noexcept { - CPointBase pt; - pt.m_x = (short)(( m_left + m_right ) / 2); - pt.m_y = (short)((m_top + m_bottom) / 2); - pt.m_z = 0; - pt.m_map = (uchar)(m_map); - return pt; + return CPointBase( + (short)((m_left + m_right) / 2), + (short)((m_top + m_bottom) / 2), + 0, + (uchar)(m_map) + ); } CPointBase CRect::GetRectCorner( DIR_TYPE dir ) const @@ -290,12 +299,12 @@ CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that ma const int iSectorCols = sd.iSectorColumns; const uint uiSectorShift = sd.uiSectorDivShift; - CRectMap rect; - rect.m_left = m_left & ~(iSectorSize-1); // aligns the left boundary down to the nearest multiple of iSectorSize. - rect.m_right = ( m_right | (iSectorSize-1)) + 1; // rounds the right boundary up to cover the full last sector. - rect.m_top = m_top & ~(iSectorSize-1); - rect.m_bottom = ( m_bottom | (iSectorSize-1)) + 1; - rect.m_map = m_map; + CRectMap rect; + rect.m_left = m_left & ~(iSectorSize-1); // aligns the left boundary down to the nearest multiple of iSectorSize. + rect.m_right = (m_right | (iSectorSize-1)) + 1; // rounds the right boundary up to cover the full last sector. + rect.m_top = m_top & ~(iSectorSize-1); + rect.m_bottom = (m_bottom | (iSectorSize-1)) + 1; + rect.m_map = m_map; rect.NormalizeRectMax(); //const int width = (rect.GetWidth()) / iSectorSize; @@ -323,10 +332,10 @@ CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that ma const int iBase = (baseRow * iSectorCols) + baseCol; if (i >= (height * width)) - return i ? nullptr : pSectors->GetSector(m_map, iBase); + return i ? nullptr : pSectors->GetSectorByIndex(m_map, iBase); const int indexoffset = ((i / width) * iSectorCols) + (i % width); - return pSectors->GetSector(m_map, iBase + indexoffset); + return pSectors->GetSectorByIndex(m_map, iBase + indexoffset); } @@ -349,13 +358,22 @@ CRectMap::CRectMap(int left, int top, int right, int bottom, int map) noexcept : bool CRectMap::IsValid() const noexcept { + const int iSizeX = GetWidth(); + const int iSizeY = GetHeight(); + return (iSizeX >= 0 && iSizeX <= g_MapList.GetMapSizeX(m_map)) && + (iSizeY >= 0 && iSizeY <= g_MapList.GetMapSizeY(m_map)); + + /* const int iSizeX = GetWidth(); if ( (iSizeX < 0) || (iSizeX > g_MapList.GetMapSizeX(m_map)) ) return false; const int iSizeY = GetHeight(); - if ( (iSizeY < 0) || (iSizeY > g_MapList.GetMapSizeY(m_map)) ) - return false; - return true; + return !( (iSizeY < 0) || (iSizeY > g_MapList.GetMapSizeY(m_map)) ); + */ + + //if ( (iSizeY < 0) || (iSizeY > g_MapList.GetMapSizeY(m_map)) ) + // return false; + //return true; } void CRectMap::NormalizeRect() noexcept diff --git a/src/common/CRect.h b/src/common/CRect.h index 629cbb479..cd1cb7950 100644 --- a/src/common/CRect.h +++ b/src/common/CRect.h @@ -49,34 +49,30 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) void OffsetRect( int x, int y ); void UnionPoint( int x, int y ); - inline bool IsInsideX( int x ) const + inline bool IsInsideX( int x ) const noexcept { // non-inclusive return( x >= m_left && x < m_right ); } - inline bool IsInsideY( int y ) const + inline bool IsInsideY( int y ) const noexcept { // non-inclusive return( y >= m_top && y < m_bottom ); } - inline bool IsInside( int x, int y, int map ) const - { - // NON inclusive rect! Is the point in the rectangle ? - return( IsInsideX(x) && IsInsideY(y) && ( m_map == map )); - } - inline bool IsInside2d( const CPointBase & pt ) const + inline bool IsInside2d( const CPointBase & pt ) const noexcept { // NON inclusive rect! Is the point in the rectangle ? return( IsInside( pt.m_x, pt.m_y, pt.m_map ) ); } - void UnionRect( const CRect & rect ); - bool IsInside( const CRect & rect ) const noexcept; + bool IsInside( int x, int y, int map ) const noexcept; + bool IsInside( const CRect & rect ) const noexcept; + void UnionRect( const CRect & rect ); bool IsOverlapped( const CRect & rect ) const noexcept; bool IsEqual( const CRect & rect ) const noexcept; virtual void NormalizeRect() noexcept; void NormalizeRectMax( int cx, int cy ) noexcept; - CPointBase GetCenter() const; + CPointBase GetCenter() const noexcept; CPointBase GetRectCorner( DIR_TYPE dir ) const; CSector * GetSector( int i ) const noexcept; // ge all the sectors that make up this rect. diff --git a/src/common/sphere_library/sfastmath.h b/src/common/sphere_library/sfastmath.h index 7c92de4d9..766771249 100644 --- a/src/common/sphere_library/sfastmath.h +++ b/src/common/sphere_library/sfastmath.h @@ -15,7 +15,7 @@ template [[maybe_unused]] constexpr bool is_twos_complement_signed() { - if constexpr (! std::is_signed_v) + if constexpr (std::is_unsigned_v) return true; // Unsigned types automatically pass // Check 1: All-ones pattern should equal -1 @@ -48,11 +48,14 @@ namespace fmath return (a + b + c + d + 2) >> 2; } + // Not likely to be needed at all. If so, we have to disable here the 'Wstrict-aliasing' warning for every compiler. + /* inline float NotF16(float f) noexcept { const int i = ~(int&)f; return (float&)i; } + */ // -- @@ -66,7 +69,6 @@ namespace fmath } */ - // TODO: return a pair to unpack the return values with a structured binding? template inline void sSortPair(T &iMin, T &iMax) noexcept { @@ -86,7 +88,6 @@ namespace fmath iMin -= t; // if a>b: a = a - (a–b) → a = b_old } - // TODO: return a pair to unpack the return values with a structured binding? // Sort two unsigned integers so that a ≤ b, branchlessly template inline void uSortPair(T &uiMin, T &uiMax) noexcept diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index e2a9fc9e6..2ff95c258 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -76,7 +76,7 @@ bool CRegion::RealizeRegion() const CSectorList* pSectors = CSectorList::Get(); for ( int i = 0, iMax = pSectors->GetMapSectorData(m_pt.m_map).iSectorQty; i < iMax; ++i ) { - CSector *pSector = pSectors->GetSector(m_pt.m_map, i); + CSector *pSector = pSectors->GetSectorByIndex(m_pt.m_map, i); if ( pSector && IsOverlapped(pSector->GetRect()) ) { diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index 6dfb93df3..a7ccf9e04 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -123,26 +123,47 @@ const MapSectorsData& CSectorList::GetMapSectorData(int map) const return _SectorData[map]; } -CSector* CSectorList::GetSector(int map, int index) const noexcept +CSector* CSectorList::GetSectorByIndex(int map, int index) const noexcept { - if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) - return nullptr; + // We call this method very often and from places we ASSUME have already checked that the map number is legit. + // Micro-optimization: Skip the function call and the if statement to avoid the possible cost of a branch misprediction. + //if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) + // return nullptr; + const MapSectorsData& sd = _SectorData[map]; return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; } -CSector* CSectorList::GetSector(int map, short x, short y) const noexcept +CSector* CSectorList::GetSectorByCoordsUnchecked(int map, short x, short y) const noexcept { - if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) - return nullptr; + // We call this method very often and from places we ASSUME have already checked that the map number is legit. + // Micro-optimization: Skip the IsMapSupported function call and the if statement to avoid the possible cost of a branch misprediction. + //if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) + // return nullptr; const MapSectorsData& sd = _SectorData[map]; - const int xSect = (x / sd.iSectorSize), ySect = (y / sd.iSectorSize); + + // We know that sector sizes MUST be multiple of 2, so just use bit shifts. + //const int xSectTest = (x / sd.iSectorSize), ySectTest = (y / sd.iSectorSize); + const int xSect = (x >> sd.uiSectorDivShift), ySect = (y >> sd.uiSectorDivShift); + //ASSERT(xSectTest == xSect); + //ASSERT(ySectTest == ySect); + +/* +#ifdef _DEBUG + // We also assume that X and Y are inside map boundaries. if ((xSect >= sd.iSectorColumns) || (ySect >= sd.iSectorRows)) return nullptr; - const int index = ((ySect * sd.iSectorColumns) + xSect); - return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; + const int index = ((ySect * sd.iSectorColumns) + xSect); + return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; +#else +*/ + const int index = ((ySect * sd.iSectorColumns) + xSect); + DEBUG_ASSERT(index < sd.iSectorQty); + return &(sd._pSectors[index]); +//#endif + } int CSectorList::GetSectorAbsoluteQty() const noexcept @@ -164,7 +185,7 @@ CSector* CSectorList::GetSectorAbsolute(int index) noexcept { const MapSectorsData& sd = _SectorData[m]; if (!sd._pSectors) - continue; + continue; // WTF? if ((base + sd.iSectorQty) > index) { diff --git a/src/game/CSectorList.h b/src/game/CSectorList.h index 278212add..a43d7f705 100644 --- a/src/game/CSectorList.h +++ b/src/game/CSectorList.h @@ -51,8 +51,8 @@ class CSectorList [[nodiscard]] const MapSectorsData& GetMapSectorData(int map) const; - CSector* GetSector(int map, int index) const noexcept; // gets sector # from one map - CSector* GetSector(int map, short x, short y) const noexcept; + CSector* GetSectorByIndex(int map, int index) const noexcept; // gets sector # from one map + CSector* GetSectorByCoordsUnchecked(int map, short x, short y) const noexcept; int GetSectorAbsoluteQty() const noexcept; CSector* GetSectorAbsolute(int index) noexcept; diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index c540194ca..6a72d8f24 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -206,7 +206,7 @@ void CSectorBase::SetAdjacentSectors() { continue; } - _ppAdjacentSectors[(DIR_TYPE)i] = pSectors->GetSector(m_map, index); + _ppAdjacentSectors[(DIR_TYPE)i] = pSectors->GetSectorByIndex(m_map, index); } } diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index f103edaaf..86d2cbc02 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -1111,7 +1111,7 @@ bool CServerConfig::r_LoadVal( CScript &s ) script.CopyParseState(s); for (int nIndex = 0; nIndex < nSectors; ++nIndex) { - CSector* pSector = CWorldMap::GetSector(nMapNumber, nIndex); + CSector* pSector = CWorldMap::GetSectorByIndex(nMapNumber, nIndex); ASSERT(pSector); pSector->r_Verb(script, &g_Serv); } @@ -1128,7 +1128,7 @@ bool CServerConfig::r_LoadVal( CScript &s ) pszStr = s.GetArgRaw(); if (pszStr && *pszStr) { - CSector* pSector = CWorldMap::GetSector(nMapNumber, iSecNumber); + CSector* pSector = CWorldMap::GetSectorByIndex(nMapNumber, iSecNumber); if (pSector) { CScript script(pszStr); @@ -1768,7 +1768,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * ptcKey = ptcKey + 6; int iSecNumber = Exp_GetVal(ptcKey); SKIP_SEPARATORS(ptcKey); - CSector* pSector = CWorldMap::GetSector(iMapNumber, iSecNumber); + CSector* pSector = CWorldMap::GetSectorByIndex(iMapNumber, iSecNumber); return !pSector ? false : pSector->r_WriteVal(ptcKey, sVal, pSrc); } } diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index fc5ebe86c..aa55012af 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -1262,7 +1262,7 @@ void CWorld::SaveStatics() for (int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s) { - CSector* pSector = _Sectors.GetSector(m, s); + CSector* pSector = _Sectors.GetSectorByIndex(m, s); if ( !pSector ) continue; @@ -1474,7 +1474,7 @@ bool CWorld::LoadAll() // Load world from script { EXC_TRYSUB("Load"); - CSector* pSector = _Sectors.GetSector(m, s); + CSector* pSector = _Sectors.GetSectorByIndex(m, s); ASSERT(pSector); if (!pSector->IsLightOverriden()) @@ -1662,7 +1662,7 @@ void CWorld::RespawnDeadNPCs() { EXC_TRY("OnSector"); - CSector* pSector = _Sectors.GetSector(m, s); + CSector* pSector = _Sectors.GetSectorByIndex(m, s); ASSERT(pSector); pSector->RespawnDeadNPCs(); @@ -1702,7 +1702,7 @@ void CWorld::Restock() { EXC_TRY("OnSector"); - CSector *pSector = _Sectors.GetSector(m, s); + CSector *pSector = _Sectors.GetSectorByIndex(m, s); ASSERT(pSector); pSector->Restock(); diff --git a/src/game/CWorldMap.cpp b/src/game/CWorldMap.cpp index 8f37a6ee4..2cd569990 100644 --- a/src/game/CWorldMap.cpp +++ b/src/game/CWorldMap.cpp @@ -1,6 +1,7 @@ #include "../common/resource/sections/CItemTypeDef.h" #include "../common/resource/sections/CRandGroupDef.h" #include "../common/resource/sections/CRegionResourceDef.h" +#include "../common/sphere_library/sfastmath.h" //#include "../common/CException.h" // included in the precompiled header //#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h #include "../common/CRect.h" @@ -225,9 +226,9 @@ IT_TYPE CWorldMap::GetTerrainItemType(dword dwTerrainIndex) // static // Map reading and blocking. // gets sector # from one map -CSector* CWorldMap::GetSector(int map, int index) noexcept // static +CSector* CWorldMap::GetSectorByIndex(int map, int index) noexcept // static { - //ADDTOCALLSTACK_DEBUG("CWorldMap::GetSector(index)"); + //ADDTOCALLSTACK_DEBUG("CWorldMap::GetSectorByIndex(index)"); const int iMapSectorQty = g_World._Sectors.GetMapSectorData(map).iSectorQty; if (index >= iMapSectorQty) @@ -236,13 +237,13 @@ CSector* CWorldMap::GetSector(int map, int index) noexcept // static return nullptr; } - return g_World._Sectors.GetSector(map, index); + return g_World._Sectors.GetSectorByIndex(map, index); } -CSector* CWorldMap::GetSector(int map, short x, short y) noexcept // static +CSector* CWorldMap::GetSectorByCoordsUnchecked(int map, short x, short y) noexcept // static { - //ADDTOCALLSTACK_DEBUG("CWorldMap::GetSector(x,y)"); - return g_World._Sectors.GetSector(map, x, y); + //ADDTOCALLSTACK_DEBUG("CWorldMap::GetSectorByCoords(x,y)"); + return g_World._Sectors.GetSectorByCoordsUnchecked(map, x, y); } @@ -302,16 +303,28 @@ std::optional CWorldMap::GetMapMeterAdjusted(const CPointMap& pt) return std::nullopt; CUOMapMeter pMapTop(*pMeter); - const CUOMapMeter pMapLeft(CheckMapTerrain(pMapTop, pt.m_x, pt.m_y + 1, pt.m_map)); + const CUOMapMeter pMapLeft (CheckMapTerrain(pMapTop, pt.m_x, pt.m_y + 1, pt.m_map)); const CUOMapMeter pMapBottom(CheckMapTerrain(pMapTop, pt.m_x + 1, pt.m_y + 1, pt.m_map)); - const CUOMapMeter pMapRight(CheckMapTerrain(pMapTop, pt.m_x + 1, pt.m_y, pt.m_map)); + const CUOMapMeter pMapRight (CheckMapTerrain(pMapTop, pt.m_x + 1, pt.m_y, pt.m_map)); - const short iAverage = GetAreaAverage(pMapTop.m_z, pMapLeft.m_z, pMapBottom.m_z, pMapRight.m_z); - if ((char)abs((short)pMapTop.m_z - (short)pMapBottom.m_z) > (char)abs((short)pMapLeft.m_z - (short)pMapRight.m_z)) - pMapTop.m_z = GetFloorAvarage(pMapLeft.m_z, pMapRight.m_z, iAverage); - else - pMapTop.m_z = GetFloorAvarage(pMapTop.m_z, pMapBottom.m_z, iAverage); - return std::make_optional(pMapTop); + const int iAverage = GetAreaAverageHeight(pMapTop.m_z, pMapLeft.m_z, pMapBottom.m_z, pMapRight.m_z); + //const auto iAbsDiffTopBottom = (char)abs((short)pMapTop.m_z - (short)pMapBottom.m_z); + //const auto iAbsDiffLeftRight = (char)abs((short)pMapLeft.m_z - (short)pMapRight.m_z); + const int iAbsDiffTopBottom = sl::fmath::sAbsDiff((int)pMapTop.m_z, (int)pMapBottom.m_z); + const int iAbsDiffLeftRight = sl::fmath::sAbsDiff((int)pMapLeft.m_z, (int)pMapRight.m_z); + + //if (iAbsDiffTopBottom > iAbsDiffLeftRight) + // pMapTop.m_z = GetFloorAverageHeight(pMapLeft.m_z, pMapRight.m_z, iAverage); + //else + // pMapTop.m_z = GetFloorAverageHeight(pMapTop.m_z, pMapBottom.m_z, iAverage); + + const bool useLeftRight = (iAbsDiffTopBottom > iAbsDiffLeftRight); + const int value1 = useLeftRight ? pMapLeft.m_z : pMapTop.m_z; + const int value2 = useLeftRight ? pMapRight.m_z : pMapBottom.m_z; + + pMapTop.m_z = (char)GetFloorAverageHeight(value1, value2, iAverage); + + return std::make_optional(pMapTop); } bool CWorldMap::IsTypeNear_Top( const CPointMap & pt, IT_TYPE iType, int iDistance ) // static @@ -319,6 +332,7 @@ bool CWorldMap::IsTypeNear_Top( const CPointMap & pt, IT_TYPE iType, int iDistan ADDTOCALLSTACK("CWorldMap::IsTypeNear_Top"); if ( !pt.IsValidPoint() ) return false; + const CPointMap ptn = FindTypeNear_Top( pt, iType, iDistance ); return ptn.IsValidPoint(); } @@ -1497,43 +1511,60 @@ void CWorldMap::GetHeightPoint(const CPointMap & pt, CServerMapBlockingState & b } } -char CWorldMap::GetFloorAvarage(char pPoint1, char pPoint2, short iAverage) +int CWorldMap::GetFloorAverageHeight(int iPoint1, int iPoint2, int iAverage) { - //We can't use char here, because higher points like hills has 64+ heights and adding 64+65 each other exceed char limit and causes returns minus values. - const short pTotal = pPoint1 + pPoint2, pHalf = pTotal / 2, pEven = pTotal % 2, pAverage = iAverage - pHalf; - return static_cast(pHalf + (pEven != 0 && pAverage > 5)); + // We can't use char here, because higher points like hills has 64+ heights and adding 64+65 each other exceed char limit and causes returns minus values. + const int iTotal = iPoint1 + iPoint2; + const int iHalf = iTotal / 2; + + // Leaving the old, less efficient algorithm just because it's more clear. + //const short pEven = pTotal % 2, pAverage = iAverage - pHalf; + //return static_cast(pHalf + (pEven != 0 && pAverage > 5)); + + // If total is odd and (iAverage - half) > 5, round up + // const bool fRoundUp = (iTotal % 2 != 0) && ((iAverage - iHalf) > 5); + // return static_cast(iHalf + (fRoundUp ? 1 : 0)); + + const int iRoundUp = (iTotal & 1 /* odd number */) && ((iAverage - iHalf) > 5); + return iHalf + iRoundUp; } -short CWorldMap::GetAreaAverage(char pTop, char pLeft, char pBottom, char pRight) +int CWorldMap::GetAreaAverageHeight(int iTop, int iLeft, int iBottom, int iRight) { - const short iHighest1 = maximum(pTop, pBottom); - const short iLowest1 = minimum(pTop, pBottom); + // Leaving the old, less efficient algorithm just because it's more clear. + /* + const short iHighest1 = maximum(iTop, iBottom); + const short iLowest1 = minimum(iTop, iBottom); - const short iHighest2 = maximum(pLeft, pRight); - const short iLowest2 = minimum(pLeft, pRight); + const short iHighest2 = maximum(iLeft, iRight); + const short iLowest2 = minimum(iLeft, iRight); return maximum(iHighest1, iHighest2) - minimum(iLowest1, iLowest2); + */ + + sl::fmath::sSortPair(iBottom, iTop); // iTop will be >= iBottom + sl::fmath::sSortPair(iLeft, iRight); // iRight will be >= iLeft + const int iHighest = sl::fmath::sMax(iTop, iRight); + const int iLowest = sl::fmath::sMin(iBottom, iLeft); + + return iHighest - iLowest; } -CUOMapMeter CWorldMap::CheckMapTerrain(CUOMapMeter pDefault, short x, short y, uchar map) +CUOMapMeter CWorldMap::CheckMapTerrain(const CUOMapMeter &meterDefault, short x, short y, uchar map) { - CPointMap pt = { x, y, 0, map }; + const CPointMap pt = { x, y, 0, map }; const CServerMapBlock* pMapBlock = GetMapBlock(pt); if (!pMapBlock) - return pDefault; + return meterDefault; const CUOMapMeter* pMeter = pMapBlock->GetTerrain(UO_BLOCK_OFFSET(pt.m_x), UO_BLOCK_OFFSET(pt.m_y)); - if (!pMeter) - return pDefault; - - if (CUOMapMeter::IsTerrainNull(pMeter->m_wTerrainIndex)) - return pDefault; - else - { - const CUOTerrainInfo land(pMeter->m_wTerrainIndex, false); - if ((land.m_flags & UFLAG1_WATER)) - return pDefault; - } - return *pMeter; + if (!pMeter || CUOMapMeter::IsTerrainNull(pMeter->m_wTerrainIndex)) + return meterDefault; + + const CUOTerrainInfo land(pMeter->m_wTerrainIndex, false); + //if ((land.m_flags & UFLAG1_WATER)) + // return meterDefault; + //return *pMeter; + return (land.m_flags & UFLAG1_WATER) ? meterDefault : *pMeter; } char CWorldMap::GetHeightPoint(const CPointMap & pt, uint64 & uiBlockFlags, bool fHouseCheck) // static diff --git a/src/game/CWorldMap.h b/src/game/CWorldMap.h index 9b22f4140..c58ac7b0c 100644 --- a/src/game/CWorldMap.h +++ b/src/game/CWorldMap.h @@ -27,8 +27,8 @@ class CWorldMap // Sectors - static CSector* GetSector(int map, int index) noexcept; // gets sector # from one map - static CSector* GetSector(int map, short x, short y) noexcept; + static CSector* GetSectorByIndex(int map, int index) noexcept; // gets sector # from one map + static CSector* GetSectorByCoordsUnchecked(int map, short x, short y) noexcept; // Map blocks (for caching) and terrain @@ -37,9 +37,9 @@ class CWorldMap static const CUOMapMeter* GetMapMeter(const CPointMap& pt); // Height of MAP0.MUL at given coordinates static std::optional GetMapMeterAdjusted(const CPointMap& pt); - static CUOMapMeter CheckMapTerrain(CUOMapMeter pDefault, short x, short y, uchar map); - static char GetFloorAvarage(char pPoint1, char pPoint2, short iAverage); - static short GetAreaAverage(char pTop, char pLeft, char pBottom, char pRight); + static CUOMapMeter CheckMapTerrain(const CUOMapMeter& meterDefault, short x, short y, uchar map); + static int GetFloorAverageHeight(int iPoint1, int iPoint2, int iAverage); + static int GetAreaAverageHeight(int iTop, int iLeft, int iBottom, int iRight); static CItemTypeDef* GetTerrainItemTypeDef(dword dwIndex); static IT_TYPE GetTerrainItemType(dword dwIndex); From e5b0aefa83e82d44b762178fb871bb8429fcd84d Mon Sep 17 00:00:00 2001 From: cbnolok Date: Tue, 3 Jun 2025 07:21:54 +0200 Subject: [PATCH 046/112] Removed duplicate thread data for PingServer. --- src/common/basic_threading.h | 21 +++++++++++++-------- src/common/sphere_library/sobjpool.h | 4 ++++ src/network/PingServer.cpp | 4 +--- src/sphere/asyncdb.cpp | 2 +- src/sphere/threads.cpp | 24 ++++++++++++++---------- src/sphere/threads.h | 1 + 6 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/common/basic_threading.h b/src/common/basic_threading.h index b4d2b0055..71bdcf91a 100644 --- a/src/common/basic_threading.h +++ b/src/common/basic_threading.h @@ -140,6 +140,7 @@ class GuardedAccess // ---- Construct underlying T in-place template + [[nodiscard]] explicit GuardedAccess(Args&&... args) : mutex_(), data_(std::forward(args)...) {} @@ -148,6 +149,7 @@ class GuardedAccess class LockedReader { public: + [[nodiscard]] LockedReader(const GuardedAccess& owner) : lock_(owner->pmutex_), ptr_(&owner.data_) {} @@ -162,6 +164,7 @@ class GuardedAccess class MTEngineLockedReader { public: + [[nodiscard]] MTEngineLockedReader(const GuardedAccess& owner) : #if MT_ENGINES @@ -190,6 +193,7 @@ class GuardedAccess class LockedWriter { public: + [[nodiscard]] LockedWriter(GuardedAccess& owner) : lock_(owner->pmutex_), ptr_(&owner.data_) {} @@ -204,6 +208,7 @@ class GuardedAccess class MTEngineLockedWriter { public: + [[nodiscard]] MTEngineLockedWriter(GuardedAccess& owner) : #if MT_ENGINES @@ -234,18 +239,18 @@ class GuardedAccess const T& unsafeReader() const { return data_; } // ---- Lock grabbing ---- - auto getLockShared() const { return SharedLock(mutex_); } - auto getLockUnique() { return UniqueLock(mutex_); } + [[nodiscard]] auto getLockShared() const { return SharedLock(mutex_); } + [[nodiscard]] auto getLockUnique() { return UniqueLock(mutex_); } - auto mtEngineGetLockShared() const { return OptLock(mutex_); } - auto mtEngineGetLockUnique() { return OptLock(mutex_); } + [[nodiscard]] auto mtEngineGetLockShared() const { return OptLock(mutex_); } + [[nodiscard]] auto mtEngineGetLockUnique() { return OptLock(mutex_); } // ---- Accessors ---- - auto lockedReader() const { return LockedReader(*this); } - auto lockedWriter() { return LockedWriter(*this); } + [[nodiscard]] auto lockedReader() const { return LockedReader(*this); } + [[nodiscard]] auto lockedWriter() { return LockedWriter(*this); } - auto mtEngineLockedReader() const { return MTEngineLockedReader(*this); } - auto mtEngineLockedWriter() { return MTEngineLockedWriter(*this); } + [[nodiscard]] auto mtEngineLockedReader() const { return MTEngineLockedReader(*this); } + [[nodiscard]] auto mtEngineLockedWriter() { return MTEngineLockedWriter(*this); } private: mutable Mutex mutex_; diff --git a/src/common/sphere_library/sobjpool.h b/src/common/sphere_library/sobjpool.h index 886352905..bdb1c8378 100644 --- a/src/common/sphere_library/sobjpool.h +++ b/src/common/sphere_library/sobjpool.h @@ -88,6 +88,7 @@ class ObjectPool // Acquires an object and returns it as a unique_ptr. // If no pooled objects are available, either returns a fallback allocation or throws. + [[nodiscard]] UniquePtr_t acquireUnique() { if (!m_freeIndices.empty()) [[likely]] @@ -115,6 +116,7 @@ class ObjectPool // Acquires an object and returns it as a shared_ptr. // If no pooled objects are available, either returns a fallback allocation or throws. + [[nodiscard]] SharedPtr_t acquireShared() { if (!m_freeIndices.empty()) [[likely]] @@ -253,6 +255,7 @@ class TSObjectPool // Acquires an object and returns it as a unique_ptr. // If no pooled objects are available, either returns a fallback allocation or throws. + [[nodiscard]] UniquePtr_t acquireUnique() { std::lock_guard lock(m_mtx); @@ -281,6 +284,7 @@ class TSObjectPool // Acquires an object and returns it as a shared_ptr. // If no pooled objects are available, either returns a fallback allocation or throws. + [[nodiscard]] SharedPtr_t acquireShared() { std::lock_guard lock(m_mtx); diff --git a/src/network/PingServer.cpp b/src/network/PingServer.cpp index 2627c1227..63ef2975a 100644 --- a/src/network/PingServer.cpp +++ b/src/network/PingServer.cpp @@ -3,10 +3,8 @@ #include "PingServer.h" -static PingServer s_PingServer; - // run the thread in RealTime as we need pings to be responded to ASAP -PingServer::PingServer() : AbstractSphereThread("PingServer", ThreadPriority::RealTime) +PingServer::PingServer() : AbstractSphereThread("T_PingServer", ThreadPriority::RealTime) { m_profile.EnableProfile(PROFILE_NETWORK_RX); m_profile.EnableProfile(PROFILE_NETWORK_TX); diff --git a/src/sphere/asyncdb.cpp b/src/sphere/asyncdb.cpp index 3a3c4087d..7eece99e5 100644 --- a/src/sphere/asyncdb.cpp +++ b/src/sphere/asyncdb.cpp @@ -5,7 +5,7 @@ #include "asyncdb.h" -CDataBaseAsyncHelper::CDataBaseAsyncHelper(void) : AbstractSphereThread("AsyncDatabaseHelper", ThreadPriority::Low) +CDataBaseAsyncHelper::CDataBaseAsyncHelper(void) : AbstractSphereThread("T_AsyncDBHelper", ThreadPriority::Low) { } diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index dd0d6f6f8..9793b9306 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -154,7 +154,7 @@ AbstractThread* ThreadHolder::current() noexcept auto thread = static_cast(found->second); - ASSERT(thread->m_threadHolderId != -1); + ASSERT(thread->m_threadHolderId != ThreadHolder::m_kiInvalidThreadID); SphereThreadData *tdata = &(m_threads[thread->m_threadHolderId]); if (tdata->m_closed) [[unlikely]] @@ -204,8 +204,8 @@ void ThreadHolder::push(AbstractThread *thread) noexcept std::unique_lock lock(m_mutex); - ASSERT(thread->m_threadSystemId != 0); - ASSERT(thread->m_threadHolderId == -1); + //ASSERT(thread->m_threadSystemId != 0); + ASSERT(thread->m_threadHolderId == ThreadHolder::m_kiInvalidThreadID); m_threads.emplace_back( SphereThreadData{ thread, false }); thread->m_threadHolderId = m_threadCount; @@ -357,7 +357,7 @@ AbstractThread * ThreadHolder::getThreadAt(size_t at) noexcept int AbstractThread::m_threadsAvailable = 0; AbstractThread::AbstractThread(const char *name, ThreadPriority priority) : - m_threadSystemId(0), m_threadHolderId(-1), + m_threadSystemId(0), m_threadHolderId(ThreadHolder::m_kiInvalidThreadID), _fKeepAliveAtShutdown(false), _fIsClosing(false) { if( AbstractThread::m_threadsAvailable == 0 ) @@ -384,8 +384,10 @@ AbstractThread::AbstractThread(const char *name, ThreadPriority priority) : AbstractThread::~AbstractThread() { #ifdef _DEBUG - fprintf(stdout, "DEBUG: Destroying thread '%s' with ThreadHolder ID %d and system ID %" PRIu64 ".\n", - getName(), m_threadHolderId, (uint64)m_threadSystemId); + fprintf(stdout, "DEBUG: Destroying thread '%s' with ThreadHolder ID %d%s and system ID %" PRIu64 ".\n", + getName(), m_threadHolderId, + (m_threadHolderId == ThreadHolder::m_kiInvalidThreadID) ? " (never started)" : "", + (uint64)m_threadSystemId); fflush(stdout); #endif @@ -976,10 +978,12 @@ void AbstractSphereThread::printStackTrace() noexcept break; lpctstr extra = ""; - if (i == m_iStackUnwindingStackPos) { - extra = "<-- last function call (stack unwinding detected here)"; + if (i == m_iStackUnwindingStackPos) + { + extra = "<-- last tracked function call (stack unwinding detected here)"; } - else if (i == m_iCaughtExceptionStackPos) { + else if (i == m_iCaughtExceptionStackPos) + { if (m_iStackUnwindingStackPos == -1) extra = "<-- exception catch point (below is guessed and could be incorrect!)"; else @@ -990,7 +994,7 @@ void AbstractSphereThread::printStackTrace() noexcept if (origin) { if (m_uisignalExceptionStackUnwinding) - extra = "<-- last function call (stack unwinding began here)"; + extra = "<-- last tracked function call (stack unwinding began here)"; else extra = "<-- exception catch point (below is guessed and could be incorrect!)"; } diff --git a/src/sphere/threads.h b/src/sphere/threads.h index e736dd4f9..3601efe55 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -378,6 +378,7 @@ class ThreadHolder public: static constexpr lpctstr m_sClassName = "ThreadHolder"; + static constexpr int m_kiInvalidThreadID = -1; static ThreadHolder& get() noexcept; From 41a4063577511b60131699dbc49649bfde4c0a69 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Tue, 3 Jun 2025 07:22:53 +0200 Subject: [PATCH 047/112] Fixed false-positive assertion when sending a message to the whole party. --- src/game/clients/CParty.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/game/clients/CParty.cpp b/src/game/clients/CParty.cpp index b97a5d0b0..60c1b7780 100644 --- a/src/game/clients/CParty.cpp +++ b/src/game/clients/CParty.cpp @@ -242,12 +242,9 @@ bool CPartyDef::MessageEvent( CUID uidDst, CUID uidSrc, const nachar *pText, int if ( uidDst.IsValidUID() && !IsInParty(uidDst.CharFind()) ) return false; - CChar *pFrom = uidSrc.CharFind(); - CChar *pTo = nullptr; - if ( uidDst != (dword)0 ) - pTo = uidDst.CharFind(); + CChar *pFrom = uidSrc.CharFind(); ASSERT(pFrom); - ASSERT(pTo); + CChar *pTo = (uidDst == (dword)0) ? nullptr : uidDst.CharFind(); tchar *szText = Str_GetTemp(); CvtNETUTF16ToSystem(szText, MAX_TALK_BUFFER, pText, MAX_TALK_BUFFER); @@ -269,7 +266,10 @@ bool CPartyDef::MessageEvent( CUID uidDst, CUID uidSrc, const nachar *pText, int } if ( g_Log.IsLoggedMask(LOGM_PLAYER_SPEAK) ) - g_Log.Event(LOGM_PLAYER_SPEAK|LOGM_NOCONTEXT, "%x:'%s' Says '%s' in party to '%s'\n", pFrom->GetClientActive()->GetSocketID(), pFrom->GetName(), szText, pTo ? pTo->GetName() : "all"); + g_Log.Event(LOGM_PLAYER_SPEAK|LOGM_NOCONTEXT, + "%x:'%s' Says '%s' in party to '%s'\n", + pFrom->GetClientActive()->GetSocketID(), pFrom->GetName(), szText, + (pTo ? pTo->GetName() : "all")); PacketPartyChat cmd(pFrom, pText); From 8b5bfc091b111d622086b5fecfb85dfb8718bc07 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Tue, 3 Jun 2025 07:26:56 +0200 Subject: [PATCH 048/112] CWorldSearch: minor optimization of search methods. --- src/game/CWorldSearch.cpp | 118 ++++++++++++++++---------------------- src/game/CWorldSearch.h | 14 +++-- 2 files changed, 58 insertions(+), 74 deletions(-) diff --git a/src/game/CWorldSearch.cpp b/src/game/CWorldSearch.cpp index c5f91836b..3e3cccea9 100644 --- a/src/game/CWorldSearch.cpp +++ b/src/game/CWorldSearch.cpp @@ -9,6 +9,7 @@ static constexpr size_t kuiContainerScaleFactor = 2; +// This was done before we added ObjectPool class. class CWorldSearchHolderImpl { static constexpr size_t kuiPreallocateSize = 20; @@ -47,11 +48,12 @@ CSReferenceCounted CWorldSearchHolder::GetInstance(const CPointMap CWorldSearch::CWorldSearch() noexcept : _iDist(0), _fAllShow(false), _fSearchSquare(false), - _eSearchType(ws_search_e::None), _fInertToggle(false), + _fInertToggle(false), _eSearchType(ws_search_e::None), _ppCurContObjs(nullptr), _pObj(nullptr), _uiCurObjIndex(0), _uiObjArrayCapacity(0), _uiObjArraySize(0), _iSectorCur(0), // Get upper left of search rect. - _pSectorBase(nullptr), _pSector(nullptr) + _pSectorBase(nullptr), _pSector(nullptr), + _distanceFunction(nullptr) { } @@ -76,13 +78,10 @@ void CWorldSearch::Reset(const CPointMap& pt, int iDist) //ADDTOCALLSTACK("CWorldSearch::Reset"); // define a search of the world. - _fAllShow = false; - _fSearchSquare = false; + _fAllShow = _fSearchSquare = _fInertToggle = false; _eSearchType = ws_search_e::None; - _fInertToggle = false; _pObj = nullptr; - _uiCurObjIndex = 0; - _uiObjArraySize = 0; + _uiCurObjIndex = _uiObjArraySize = 0; //_uiObjArrayCapacity = 0; // Don't! Recycle the allocated space for _ppCurContObjs. _iSectorCur = 0; // Get upper left of search rect. @@ -95,18 +94,28 @@ void CWorldSearch::Reset(const CPointMap& pt, int iDist) pt.m_x + iDist + 1, pt.m_y + iDist + 1, pt.m_map); + + SetDistanceFunction(); } void CWorldSearch::SetAllShow(bool fView) { //ADDTOCALLSTACK_DEBUG("CWorldSearch::SetAllShow"); - _fAllShow = fView; + if (_fAllShow == fView) + return; + + _fAllShow = fView; + SetDistanceFunction(); } void CWorldSearch::SetSearchSquare(bool fSquareSearch) { //ADDTOCALLSTACK_DEBUG("CWorldSearch::SetSearchSquare"); - _fSearchSquare = fSquareSearch; + if (_fSearchSquare == fSquareSearch) + return; + + _fSearchSquare = fSquareSearch; + SetDistanceFunction(); } void CWorldSearch::RestartSearch() @@ -176,6 +185,24 @@ void CWorldSearch::LoadSectorObjs(CSObjCont const& pSectorObjList) _uiCurObjIndex = 0; } +void CWorldSearch::SetDistanceFunction() +{ + if (_fAllShow) + { + if (_fSearchSquare) + _distanceFunction = &CPointMap::GetDistSightBase; + else + _distanceFunction = &CPointMap::GetDistSight; + } + else + { + if (_fSearchSquare) + _distanceFunction = &CPointMap::GetDistBase; + else + _distanceFunction = &CPointMap::GetDist; + } +} + CItem* CWorldSearch::GetItem() { // This method is called very frequently, ADDTOCALLSTACK unneededly sucks cpu @@ -196,42 +223,18 @@ CItem* CWorldSearch::GetItem() } ASSERT(_eSearchType == ws_search_e::Items); - _pObj = (_uiCurObjIndex >= _uiObjArraySize) ? nullptr : static_cast(_ppCurContObjs[_uiCurObjIndex]); - if (_pObj == nullptr) + if (_uiCurObjIndex >= _uiObjArraySize) { + _pObj = nullptr; if (GetNextSector()) continue; return nullptr; } - const CPointMap& ptObj(_pObj->GetTopPoint()); - if (_fSearchSquare) - { - if (_fAllShow) - { - if (_pt.GetDistSightBase(ptObj) <= _iDist) - return static_cast (_pObj); - } - else - { - if (_pt.GetDistSight(ptObj) <= _iDist) - return static_cast (_pObj); - } - } - else - { - if (_fAllShow) - { - if (_pt.GetDistBase(ptObj) <= _iDist) - return static_cast (_pObj); - } - else - { - if (_pt.GetDist(ptObj) <= _iDist) - return static_cast (_pObj); - } - } + _pObj = static_cast(_ppCurContObjs[_uiCurObjIndex]); + if ((_pt.* _distanceFunction)(_pObj->GetTopPoint()) <= _iDist) + return static_cast (_pObj); } } @@ -256,17 +259,17 @@ CChar* CWorldSearch::GetChar() } ASSERT(_eSearchType == ws_search_e::Chars); - _pObj = (_uiCurObjIndex >= _uiObjArraySize) ? nullptr : static_cast(_ppCurContObjs[_uiCurObjIndex]); - if (_pObj == nullptr) + if (_uiCurObjIndex >= _uiObjArraySize) { + _pObj = nullptr; if (!_fInertToggle && _fAllShow) { _fInertToggle = true; LoadSectorObjs(_pSector->m_Chars_Disconnect); - _pObj = (_uiCurObjIndex >= _uiObjArraySize) ? nullptr : static_cast(_ppCurContObjs[_uiCurObjIndex]); - if (_pObj != nullptr) + if (_uiCurObjIndex < _uiObjArraySize) goto jumpover; + _pObj = nullptr; } if (GetNextSector()) @@ -276,33 +279,8 @@ CChar* CWorldSearch::GetChar() } jumpover: - const CPointMap& ptObj = _pObj->GetTopPoint(); - - if (_fSearchSquare) - { - if (_fAllShow) - { - if (_pt.GetDistSightBase(ptObj) <= _iDist) - return static_cast (_pObj); - } - else - { - if (_pt.GetDistSight(ptObj) <= _iDist) - return static_cast (_pObj); - } - } - else - { - if (_fAllShow) - { - if (_pt.GetDistBase(ptObj) <= _iDist) - return static_cast (_pObj); - } - else - { - if (_pt.GetDist(ptObj) <= _iDist) - return static_cast (_pObj); - } - } + _pObj = static_cast(_ppCurContObjs[_uiCurObjIndex]); + if ((_pt.* _distanceFunction)(_pObj->GetTopPoint()) <= _iDist) + return static_cast (_pObj); } } diff --git a/src/game/CWorldSearch.h b/src/game/CWorldSearch.h index 382207d9b..97f8a4c1e 100644 --- a/src/game/CWorldSearch.h +++ b/src/game/CWorldSearch.h @@ -36,13 +36,13 @@ class CWorldSearch Chars }; - CPointMap _pt; // Base point of our search. - int _iDist; // How far from the point are we interested in + int _iDist; // How far from the point are we interested in + CPointMap _pt; // Base point of our search. bool _fAllShow; // Include Even inert items. bool _fSearchSquare; // Search in a square (uo-sight distance) rather than a circle (standard distance). - ws_search_e _eSearchType; bool _fInertToggle; // We are now doing the inert chars. + ws_search_e _eSearchType; CSObjContRec** _ppCurContObjs; // Array with a copy of the pointers to the objects inside the sector we are in searching right now. CObjBase* _pObj; // The current object of interest. @@ -55,6 +55,10 @@ class CWorldSearch CSector* _pSector; // current Sector CRectMap _rectSector; // A rectangle containing our sectors we can search. + // Pointer to distance function: + // The ::* operator means "pointer to member of CPointMap". + int (CPointMap::* _distanceFunction)(const CPointBase&) const noexcept; + public: static const char* m_sClassName; CWorldSearch(const CWorldSearch& copy) = delete; @@ -65,15 +69,17 @@ class CWorldSearch ~CWorldSearch() noexcept; public: - //static cdrc::rc_ptr GetInstance(const CPointMap& pt, int iDist = 0); void Reset(const CPointMap& pt, int iDist = 0); void SetAllShow(bool fView); void SetSearchSquare(bool fSquareSearch); void RestartSearch(); + CChar* GetChar(); CItem* GetItem(); private: + void SetDistanceFunction(); + bool GetNextSector(); void LoadSectorObjs(CSObjCont const& pSectorObjList); }; From 48b0c1e4ba9024b720bed8f8ef28846ec39cdd87 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 4 Jun 2025 07:13:19 +0200 Subject: [PATCH 049/112] sstring.cpp: Added max str length parameter and renamed Str_Check and Str_CheckName Changed: CSectorList::Get() to return a reference and not a pointer. --- src/common/CCacheableScriptFile.cpp | 2 +- src/common/CRect.cpp | 8 +-- src/common/sphere_library/sstring.cpp | 98 ++++----------------------- src/common/sphere_library/sstring.h | 5 +- src/game/CRegion.cpp | 6 +- src/game/CSector.cpp | 4 +- src/game/CSectorList.cpp | 10 +-- src/game/CSectorList.h | 4 +- src/game/CSectorTemplate.cpp | 16 ++--- src/game/CServerConfig.cpp | 6 +- src/game/chars/CChar.cpp | 3 +- src/game/clients/CAccount.cpp | 2 +- src/game/clients/CClientEvent.cpp | 8 +-- src/game/clients/CClientMsg.cpp | 4 +- src/network/receive.cpp | 8 +-- 15 files changed, 57 insertions(+), 127 deletions(-) diff --git a/src/common/CCacheableScriptFile.cpp b/src/common/CCacheableScriptFile.cpp index 709d31d79..fcc9a236a 100644 --- a/src/common/CCacheableScriptFile.cpp +++ b/src/common/CCacheableScriptFile.cpp @@ -301,7 +301,7 @@ void CCacheableScriptFile::dupeFrom(CCacheableScriptFile *other) bool CCacheableScriptFile::_HasCache() const { - if ((_fileContent == nullptr) /* || _fileContent->empty() Exclude this: might just be an empty file!*/) + if ((_fileContent == nullptr) /* || _fileContent->empty() Exclude this: might just be an empty file, which is legit!*/) return false; return true; } diff --git a/src/common/CRect.cpp b/src/common/CRect.cpp index 92340190d..3ab4a941b 100644 --- a/src/common/CRect.cpp +++ b/src/common/CRect.cpp @@ -293,8 +293,8 @@ CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that ma // RETURN: nullptr = no more // Align new rect. - const CSectorList *pSectors = CSectorList::Get(); - const MapSectorsData& sd = pSectors->GetMapSectorData(m_map); + const CSectorList &pSectors = CSectorList::Get(); + const MapSectorsData& sd = pSectors.GetMapSectorData(m_map); const int iSectorSize = sd.iSectorSize; const int iSectorCols = sd.iSectorColumns; const uint uiSectorShift = sd.uiSectorDivShift; @@ -332,10 +332,10 @@ CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that ma const int iBase = (baseRow * iSectorCols) + baseCol; if (i >= (height * width)) - return i ? nullptr : pSectors->GetSectorByIndex(m_map, iBase); + return i ? nullptr : pSectors.GetSectorByIndex(m_map, iBase); const int indexoffset = ((i / width) * iSectorCols) + (i % width); - return pSectors->GetSectorByIndex(m_map, iBase + indexoffset); + return pSectors.GetSectorByIndex(m_map, iBase + indexoffset); } diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index da7b23e4b..9f03d0e0e 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -332,15 +332,17 @@ tchar* Str_FromInt_Fast(_IntType val, lptstr_restrict out_buf, size_t buf_length // Write digits from back of buffer #define WRITE_OUT(ch) \ - do { \ + { \ if (idx == 0) \ return nullptr; \ out_buf[--idx] = (ch); \ - } while (0) + } size_t idx = buf_length; out_buf[--idx] = '\0'; + // TODO: gracefully handle integer overflows, maybe printing a warning message. + if (fHex) { // Hex path: mask & shift by 4 bits (it's the same as dividing by 16). @@ -1114,83 +1116,6 @@ void Str_EatEndWhitespace(const tchar* const pStrBegin, tchar*& pStrEnd) noexcep } } -/* -void Str_SkipEnclosedAngularBrackets(tchar*& ptcLine) noexcept -{ - // Move past a < > statement. It can have ( ) inside, if it happens, ignore < > characters inside (). - bool fOpenedOneAngular = false; - int iOpenAngular = 0, iOpenCurly = 0; - tchar* ptcTest = ptcLine; - while (const tchar ch = *ptcTest) - { - if (IsWhitespace(ch)) - ; - else if (ch == '(') - ++iOpenCurly; - else if (ch == ')') - --iOpenCurly; - else if (iOpenCurly == 0) - { - if (ch == '<') - { - bool fOperator = false; - if ((ptcTest[1] == '<') && (ptcTest[2] != '\0') && IsWhitespace(ptcTest[2])) - { - // I want a whitespace after the operator and some text after it. - const tchar * ptcOpTest = &(ptcTest[3]); - if (*ptcOpTest != '\0') - { - GETNONWHITESPACE(ptcOpTest); - if (*ptcOpTest != '\0') // There's more text to parse - { - // I guess i have sufficient proof: skip, it's a << operator - fOperator = true; - ptcTest += 2; // Skip the second > and the following whitespace - } - } - } - if (!fOperator) - { - fOpenedOneAngular = true; - ++iOpenAngular; - } - } - else if (ch == '>') - { - bool fOperator = false; - if ((ptcTest[1] == '>') && (ptcTest[2] != '\0') && IsWhitespace(ptcTest[2])) - { - if ((ptcLine == ptcTest) || ((iOpenAngular > 0) && IsWhitespace(*(ptcTest - 1)))) - { - const tchar * ptcOpTest = &(ptcTest[3]); - if (*ptcOpTest != '\0') - { - GETNONWHITESPACE(ptcOpTest); - if (*ptcOpTest != '\0') // There's more text to parse - { - // I guess i have sufficient proof: skip, it's a >> operator - fOperator = true; - ptcTest += 2; // Skip the second > and the following whitespace - } - } - } - } - if (!fOperator) - { - --iOpenAngular; - if (fOpenedOneAngular && !iOpenAngular) - { - ptcLine = ptcTest + 1; - return; - } - } - } - } - ++ptcTest; - } -} -*/ - void Str_SkipEnclosedAngularBrackets(tchar*& ptcLine) noexcept { // Move past a < > statement. It can have ( ) inside, if it happens, ignore < > characters inside (). @@ -1383,27 +1308,30 @@ int FindCAssocRegTableHeadSorted(const tchar * pszFind, const tchar * const* pps return -1; } -bool Str_Check(const tchar * pszIn) noexcept +bool Str_Untrusted_InvalidTermination(const tchar * pszIn, size_t uiMaxAcceptableSize) noexcept { if (pszIn == nullptr) return true; const tchar * p = pszIn; - while (*p != '\0' && (*p != 0x0A) && (*p != 0x0D)) + while ((*p != '\0') && (*p != 0x0A /* '\n' */) && (*p != 0x0D /* '\r' */) + && ((p - pszIn) < ptrdiff_t(uiMaxAcceptableSize))) + { ++p; + } return (*p != '\0'); } -bool Str_CheckName(const tchar * pszIn) noexcept +bool Str_Untrusted_InvalidName(const tchar * pszIn, size_t uiMaxAcceptableSize) noexcept { if (pszIn == nullptr) return true; const tchar * p = pszIn; - while (*p != '\0' && - ( - ((*p >= 'A') && (*p <= 'Z')) || + while (*p != '\0' && ((p - pszIn) < ptrdiff_t(uiMaxAcceptableSize)) + && ( + ((*p >= 'A') && (*p <= 'Z')) || ((*p >= 'a') && (*p <= 'z')) || ((*p >= '0') && (*p <= '9')) || ((*p == ' ') || (*p == '\'') || (*p == '-') || (*p == '.')) diff --git a/src/common/sphere_library/sstring.h b/src/common/sphere_library/sstring.h index a82122734..036fbbb5d 100644 --- a/src/common/sphere_library/sstring.h +++ b/src/common/sphere_library/sstring.h @@ -313,15 +313,16 @@ int FindTableHeadSorted(const tchar * pFind, const tchar * const * ppTable, int /** * @param pszIn string to check. +* @param uiMaxAcceptableSize a string greater than this is invalid. * @return true if string is empty or has '\c' or '\n' characters, false otherwise. */ -bool Str_Check(const tchar * pszIn) noexcept; +bool Str_Untrusted_InvalidTermination(const tchar * pszIn, size_t uiMaxAcceptableSize = SCRIPT_MAX_LINE_LEN) noexcept; /** * @param pszIn string to check. * @return false if string match "[a-zA-Z0-9_- \'\.]+", true otherwise. */ -bool Str_CheckName(const tchar * pszIn) noexcept; +bool Str_Untrusted_InvalidName(const tchar * pszIn, size_t uiMaxAcceptableSize = 100 /* arbitrary */) noexcept; /** * @brief find a substring in a string from an offset. diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 2ff95c258..8179dd220 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -73,10 +73,10 @@ bool CRegion::RealizeRegion() // Attach to all sectors that i overlap. ASSERT( m_iLinkedSectors == 0 ); - const CSectorList* pSectors = CSectorList::Get(); - for ( int i = 0, iMax = pSectors->GetMapSectorData(m_pt.m_map).iSectorQty; i < iMax; ++i ) + const CSectorList& pSectors = CSectorList::Get(); + for ( int i = 0, iMax = pSectors.GetMapSectorData(m_pt.m_map).iSectorQty; i < iMax; ++i ) { - CSector *pSector = pSectors->GetSectorByIndex(m_pt.m_map, i); + CSector *pSector = pSectors.GetSectorByIndex(m_pt.m_map, i); if ( pSector && IsOverlapped(pSector->GetRect()) ) { diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index 50ff21de3..3c14936d2 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -614,7 +614,7 @@ int CSector::GetLocalTime() const { ADDTOCALLSTACK("CSector::GetLocalTime"); // Get local time of the day (in minutes) - const CSectorList* pSectors = CSectorList::Get(); + const CSectorList& pSectors = CSectorList::Get(); const CPointMap& pt(GetBasePoint()); int64 iLocalTime = CWorldGameTime::GetCurrentTimeInGameMinutes(); @@ -624,7 +624,7 @@ int CSector::GetLocalTime() const } else { - const MapSectorsData& sd = pSectors->GetMapSectorData(pt.m_map); + const MapSectorsData& sd = pSectors.GetMapSectorData(pt.m_map); // Time difference between adjacent sectors in minutes const int iSectorTimeDiff = (24*60) / sd.iSectorColumns; diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index a7ccf9e04..b1d5f02ec 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -13,9 +13,9 @@ CSectorList::~CSectorList() Close(true); } -const CSectorList* CSectorList::Get() noexcept // static +const CSectorList& CSectorList::Get() noexcept // static { - return &g_World._Sectors; + return g_World._Sectors; } void CSectorList::Init() @@ -45,9 +45,9 @@ void CSectorList::Init() if (!g_MapList.IsMapSupported(iMap)) continue; - const int iSectorQty = g_MapList.CalcSectorQty(iMap); - const int iMaxX = g_MapList.CalcSectorCols(iMap); - const int iMaxY = g_MapList.CalcSectorRows(iMap); + const int iSectorQty= g_MapList.CalcSectorQty(iMap); + const int iMaxX = g_MapList.CalcSectorCols(iMap); + const int iMaxY = g_MapList.CalcSectorRows(iMap); snprintf(ts.buffer(), ts.capacity(), " map%d=%d", iMap, iSectorQty); Str_ConcatLimitNull(tsConcat.buffer(), ts.buffer(), tsConcat.capacity()); diff --git a/src/game/CSectorList.h b/src/game/CSectorList.h index a43d7f705..df943b854 100644 --- a/src/game/CSectorList.h +++ b/src/game/CSectorList.h @@ -31,7 +31,7 @@ class CSectorList public: static const char* m_sClassName; - //std::array _SectorData; // Use plain C-style vectors, to remove even the minimum overhead of std::array methods + //std::array _SectorData; // Use plain C-style vectors, to remove even the minimal overhead of std::array methods MapSectorsData _SectorData[MAP_SUPPORTED_QTY]; bool _fInitialized; @@ -43,7 +43,7 @@ class CSectorList CSectorList& operator=(const CSectorList& other) = delete; public: - static const CSectorList* Get() noexcept; + static const CSectorList& Get() noexcept; void Init(); void Close(bool fClosingWorld); diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index 6a72d8f24..46fc7fc74 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -155,8 +155,8 @@ void CItemsList::AddItemToSector( CItem * pItem ) void CSectorBase::SetAdjacentSectors() { - const CSectorList* pSectors = CSectorList::Get(); - auto const& sd = pSectors->GetMapSectorData(m_map); + const CSectorList& pSectors = CSectorList::Get(); + auto const& sd = pSectors.GetMapSectorData(m_map); const int iMaxX = sd.iSectorColumns; ASSERT(iMaxX > 0); @@ -206,7 +206,7 @@ void CSectorBase::SetAdjacentSectors() { continue; } - _ppAdjacentSectors[(DIR_TYPE)i] = pSectors->GetSectorByIndex(m_map, index); + _ppAdjacentSectors[(DIR_TYPE)i] = pSectors.GetSectorByIndex(m_map, index); } } @@ -233,7 +233,7 @@ void CSectorBase::Init(int index, uchar map, short x, short y) { g_Log.EventError("Trying to initalize a sector %d in unsupported map #%d. Defaulting to 0,0.\n", index, map); } - else if (( index < 0 ) || ( index >= CSectorList::Get()->GetMapSectorData(map).iSectorQty )) + else if (( index < 0 ) || ( index >= CSectorList::Get().GetMapSectorData(map).iSectorQty )) { m_map = map; g_Log.EventError("Trying to initalize a sector by sector number %d out-of-range for map #%d. Defaulting to 0,%d.\n", index, map, map); @@ -457,8 +457,8 @@ CPointMap CSectorBase::GetBasePoint() const // Again this method is called very often, so call the least functions possible and do the minimum amount of checks required DEBUG_ASSERT(g_MapList.IsMapSupported(m_map)); - const CSectorList* pSectors = CSectorList::Get(); - auto const& sd = pSectors->GetMapSectorData(m_map); + const CSectorList& pSectors = CSectorList::Get(); + auto const& sd = pSectors.GetMapSectorData(m_map); DEBUG_ASSERT((m_index >= 0) && (m_index < sd.iSectorQty) ); const int iCols = sd.iSectorColumns; @@ -480,9 +480,9 @@ CRectMap CSectorBase::GetRect() const noexcept // Get a rectangle for the sector. DEBUG_ASSERT(g_MapList.IsMapSupported(m_map)); - const CSectorList* pSectors = CSectorList::Get(); + const CSectorList& pSectors = CSectorList::Get(); const CPointMap& pt = GetBasePoint(); - const int iSectorSize = pSectors->GetMapSectorData(pt.m_map).iSectorSize; + const int iSectorSize = pSectors.GetMapSectorData(pt.m_map).iSectorSize; return // Initializer list for CRectMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) { pt.m_x, // left diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 86d2cbc02..de4cf768c 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -1102,7 +1102,7 @@ bool CServerConfig::r_LoadVal( CScript &s ) { if ( !strnicmp(pszStr, "ALLSECTORS", 10) ) { - const int nSectors = CSectorList::Get()->GetMapSectorData(nMapNumber).iSectorQty; + const int nSectors = CSectorList::Get().GetMapSectorData(nMapNumber).iSectorQty; pszStr = s.GetArgRaw(); if ( pszStr && *pszStr ) @@ -1732,8 +1732,8 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * else if (!strnicmp(pszCmd, "SECTOR.", 7)) { pszCmd += 7; - const CSectorList* pSectors = CSectorList::Get(); - const MapSectorsData& sd = pSectors->GetMapSectorData(iNumber); + const CSectorList& pSectors = CSectorList::Get(); + const MapSectorsData& sd = pSectors.GetMapSectorData(iNumber); if (!strnicmp(pszCmd, "SIZE", 4)) sVal.FormatVal(sd.iSectorSize); diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index fbf6e0c51..d9a37f778 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -1734,7 +1734,8 @@ void CChar::InitPlayer( CClient *pClient, const char *pszCharname, bool fFemale, tchar *zCharName = Str_GetTemp(); Str_CopyLimitNull(zCharName, pszCharname, MAX_NAME_SIZE); - if ( !strlen(zCharName) || g_Cfg.IsObscene(zCharName) || Str_CheckName(zCharName) ||!strnicmp(zCharName, "lord ", 5) || !strnicmp(zCharName, "lady ", 5) || + if ( !strlen(zCharName) || Str_Untrusted_InvalidName(zCharName) || + g_Cfg.IsObscene(zCharName) ||!strnicmp(zCharName, "lord ", 5) || !strnicmp(zCharName, "lady ", 5) || !strnicmp(zCharName, "seer ", 5) || !strnicmp(zCharName, "gm ", 3) || !strnicmp(zCharName, "admin ", 6) || !strnicmp(zCharName, "counselor ", 10) ) { fNameIsAccepted = false; diff --git a/src/game/clients/CAccount.cpp b/src/game/clients/CAccount.cpp index 786202a45..18aaa58b8 100644 --- a/src/game/clients/CAccount.cpp +++ b/src/game/clients/CAccount.cpp @@ -987,7 +987,7 @@ bool CAccount::SetPassword( lpctstr pszPassword, bool isMD5Hash ) { ADDTOCALLSTACK("CAccount::SetPassword"); - if ( Str_Check( pszPassword ) ) // Prevents exploits + if ( Str_Untrusted_InvalidTermination( pszPassword ) ) // Prevents exploits return false; bool useMD5 = g_Cfg.m_fMd5Passwords; diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index 985cf9667..2e404baca 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -171,7 +171,7 @@ void CClient::Event_Book_Title( CUID uid, lpctstr pszTitle, lpctstr pszAuthor ) if ( !pBook->IsBookWritable() ) return; - if ( Str_Check(pszTitle) || Str_Check(pszAuthor) ) + if ( Str_Untrusted_InvalidTermination(pszTitle) || Str_Untrusted_InvalidTermination(pszAuthor) ) return; pBook->SetName(pszTitle); @@ -1045,7 +1045,7 @@ bool CClient::Event_Command(lpctstr pszCommand, TALKMODE_TYPE mode) return false; if ( pszCommand[0] == 0 ) return true; // should not be said - if ( Str_Check(pszCommand) ) + if ( Str_Untrusted_InvalidTermination(pszCommand) ) return true; // should not be said if ( ((m_pChar->GetDispID() == CREID_EQUIP_GM_ROBE) && (pszCommand[0] == '=')) // WTF? In any case, keep using dispid, or it's bugged when you change character's dispid to c_man_gm. || (pszCommand[0] == g_Cfg.m_cCommandPrefix)) @@ -1671,7 +1671,7 @@ void CClient::Event_PromptResp( lpctstr pszText, size_t len, dword context1, dwo // result of addPrompt tchar szText[MAX_TALK_BUFFER]; - if ( Str_Check( pszText ) ) + if ( Str_Untrusted_InvalidTermination( pszText, MAX_TALK_BUFFER - 1) ) return; CLIMODE_TYPE promptMode = m_Prompt_Mode; @@ -2205,7 +2205,7 @@ bool CClient::Event_SetName( CUID uid, const char * pszCharName ) if (!pChar || !m_pChar) return false; - if ( Str_CheckName(pszCharName) || !strlen(pszCharName) ) + if ( Str_Untrusted_InvalidName(pszCharName) || !strlen(pszCharName) ) return false; // Do we have the right to do this ? diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index f7ae016c4..fe3edde97 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -3188,9 +3188,9 @@ byte CClient::LogIn( lpctstr ptcAccName, lpctstr ptcPassword, CSString & sMsg ) tchar ptcName[ MAX_ACCOUNT_NAME_SIZE ]; - if ( !CAccount::NameStrip(ptcName, ptcAccName) || Str_Check(ptcAccName) ) + if ( !CAccount::NameStrip(ptcName, ptcAccName) || Str_Untrusted_InvalidTermination(ptcAccName) ) return( PacketLoginError::BadAccount ); - else if ( Str_Check(ptcPassword) ) + else if ( Str_Untrusted_InvalidTermination(ptcPassword) ) return( PacketLoginError::BadPassword ); const bool fGuestAccount = ! strnicmp( ptcAccName, "GUEST", 5 ); diff --git a/src/network/receive.cpp b/src/network/receive.cpp index 2a4d7a078..309b0f3c7 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -1047,7 +1047,7 @@ bool PacketBookPageEdit::onReceive(CNetState* net) ASSERT(len > 0); content[--len] = '\0'; - if (Str_Check(content)) + if (Str_Untrusted_InvalidTermination(content, len)) break; // set page content @@ -1234,7 +1234,7 @@ bool PacketBulletinBoardReq::onReceive(CNetState* net) uint lenstr = readByte(); tchar* str = Str_GetTemp(); readStringASCII(str, lenstr, false); - if (Str_Check(str)) + if (Str_Untrusted_InvalidTermination(str, lenstr)) return true; // if @@ -1259,7 +1259,7 @@ bool PacketBulletinBoardReq::onReceive(CNetState* net) { lenstr = readByte(); readStringASCII(str, lenstr, false); - if (Str_Check(str) == false) + if (Str_Untrusted_InvalidTermination(str, lenstr) == false) newMessage->AddPageText(str); } @@ -2486,7 +2486,7 @@ bool PacketClientVersion::onReceive(CNetState* net) tchar* versionStr = Str_GetTemp(); readStringASCII(versionStr, length, false); - if (Str_Check(versionStr)) + if (Str_Untrusted_InvalidTermination(versionStr, length)) return true; if (strstr(versionStr, "UO:3D") != nullptr) From 02a9d9570eaf998e6aa0c58cd336611978332562 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 4 Jun 2025 07:16:23 +0200 Subject: [PATCH 050/112] CompilerFlagsChecker.cmake: disable --icf=safe linker flag if ASan/*San are used (incompatible with lld and/or LLVM implementation) --- cmake/CompilerFlagsChecker.cmake | 38 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/cmake/CompilerFlagsChecker.cmake b/cmake/CompilerFlagsChecker.cmake index 7271a5f8a..f69e64737 100644 --- a/cmake/CompilerFlagsChecker.cmake +++ b/cmake/CompilerFlagsChecker.cmake @@ -1,10 +1,11 @@ include(CheckCXXCompilerFlag) include(CheckLinkerFlag) -message(STATUS "Checking available compiler flags...") +message(STATUS "Checking available compiler and linker flags...") if(NOT MSVC) - message(STATUS "-- Compilation options:") + message(STATUS "-- Linker options:") + # Linker flags. if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") @@ -14,9 +15,9 @@ if(NOT MSVC) check_linker_flag(CXX -Wl,--fatal-warnings LINKER_HAS_FATAL_WARN) endif() check_linker_flag(CXX -Wl,--as-needed LINKER_HAS_AS_NEEDED) - check_linker_flag(CXX -Wl,--icf=safe LINKER_HAS_ICF) # icf = identical code folding (not supported by mold) + message(STATUS "-- Compiler options:") # Compiler option flags. Common to both compilers, but older versions might not support the following. #check_cxx_compiler_flag("" COMP_HAS_) @@ -31,9 +32,9 @@ if(NOT MSVC) if(${USE_ASAN}) # Compiler option flags. Sanitizers related (ASAN). - message(STATUS "-- Compilation options (Address Sanitizer):") + message(STATUS "-- Compiler options (Address Sanitizer):") - set(CMAKE_REQUIRED_LIBRARIES -fsanitize=address) # link against sanitizer lib, being required by the following compilation test + set(CMAKE_REQUIRED_LIBRARIES -fsanitize=address) # link against sanitizer lib, being required by the following compiler flag check_cxx_compiler_flag("-fsanitize=address" COMP_HAS_ASAN) unset(CMAKE_REQUIRED_LIBRARIES) if(NOT COMP_HAS_ASAN) @@ -49,9 +50,9 @@ if(NOT MSVC) if(${USE_UBSAN}) # Compiler option flags. Sanitizers related (UBSAN). - message(STATUS "-- Compilation options (Undefined Behavior Sanitizer):") + message(STATUS "-- Compiler options (Undefined Behavior Sanitizer):") - set(CMAKE_REQUIRED_LIBRARIES -fsanitize=undefined) # link against sanitizer lib, being required by the following compilation test + set(CMAKE_REQUIRED_LIBRARIES -fsanitize=undefined) # link against sanitizer lib, being required by the following compiler flag check_cxx_compiler_flag("-fsanitize=undefined" COMP_HAS_UBSAN) unset(CMAKE_REQUIRED_LIBRARIES) if(NOT COMP_HAS_UBSAN) @@ -59,7 +60,7 @@ if(NOT MSVC) endif() check_cxx_compiler_flag("-fsanitize=float-divide-by-zero" COMP_HAS_FSAN_FLOAT_DIVIDE_BY_ZERO) - check_cxx_compiler_flag("-fsanitize=unsigned-integer-overflow" COMP_HAS_FSAN_UNSIGNED_INTEGER_OVERFLOW) #Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. + #check_cxx_compiler_flag("-fsanitize=unsigned-integer-overflow" COMP_HAS_FSAN_UNSIGNED_INTEGER_OVERFLOW) #Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. #check_cxx_compiler_flag("-fsanitize=implicit-conversion" COMP_HAS_FSAN_IMPLICIT_CONVERSION) #check_cxx_compiler_flag("-fsanitize=local-bounds" COMP_HAS_FSAN_LOCAL_BOUNDS) check_cxx_compiler_flag("-fno-sanitize=enum" COMP_HAS_FSAN_NO_ENUM) @@ -67,9 +68,9 @@ if(NOT MSVC) if(${USE_LSAN}) # Compiler option flags. Sanitizers related (LSAN). - message(STATUS "-- Compilation options (Leak Sanitizer):") + message(STATUS "-- Compiler options (Leak Sanitizer):") - set(CMAKE_REQUIRED_LIBRARIES -fsanitize=leak) # link against sanitizer lib, being required by the following compilation test + set(CMAKE_REQUIRED_LIBRARIES -fsanitize=leak) # link against sanitizer lib, being required by the following compiler flag check_cxx_compiler_flag("-fsanitize=leak" COMP_HAS_LSAN) unset(CMAKE_REQUIRED_LIBRARIES) if(NOT COMP_HAS_LSAN) @@ -79,9 +80,9 @@ if(NOT MSVC) if(${USE_MSAN}) # Compiler option flags. Sanitizers related (UBSAN). - message(STATUS "-- Compilation options (Memory Behavior Sanitizer):") + message(STATUS "-- Compiler options (Memory Behavior Sanitizer):") - set(CMAKE_REQUIRED_LIBRARIES -fsanitize=memory) # link against sanitizer lib, being required by the following compilation test + set(CMAKE_REQUIRED_LIBRARIES -fsanitize=memory) # link against sanitizer lib, being required by the following compiler flag check_cxx_compiler_flag("-fsanitize=memory" COMP_HAS_MSAN) unset(CMAKE_REQUIRED_LIBRARIES) if(NOT COMP_HAS_MSAN) @@ -114,10 +115,15 @@ See comments in the toolchain and: https://github.com/google/sanitizers/wiki/Mem message(FATAL_ERROR "This compiler doesn't support statically linking libasan. Turn off RUNTIME_STATIC_LINK?") endif() endif() + else() + # icf = identical code folding: + # - not supported by mold + # - error when linking with lld and asan or other sanitizers + check_linker_flag(CXX -Wl,--icf=safe LINKER_HAS_ICF) endif() if(USE_COMPILER_HARDENING_OPTIONS) - message(STATUS "-- Compilation options (code hardening):") + message(STATUS "-- Compiler options (code hardening):") # Compiler option flags. Other sanitizers or code hardening. if(NOT USE_ASAN) check_cxx_compiler_flag("-fsanitize=safe-stack" COMP_HAS_F_SANITIZE_SAFE_STACK) # Can't be used with asan! @@ -136,7 +142,7 @@ See comments in the toolchain and: https://github.com/google/sanitizers/wiki/Mem endif() # Compiler warning flags. - message(STATUS "-- Compilation options (warnings):") + message(STATUS "-- Compiler options (warnings):") #check_cxx_compiler_flag("-Wuseless-cast" COMP_HAS_W_USELESS_CAST) check_cxx_compiler_flag("-Wnull-dereference" COMP_HAS_W_NULL_DEREFERENCE) #check_cxx_compiler_flag("-Wconversion" COMP_HAS_W_CONVERSION) # Temporarily disabled. Implicit type conversions that might change a value, such as narrowing conversions. @@ -168,7 +174,7 @@ See comments in the toolchain and: https://github.com/google/sanitizers/wiki/Mem # -Wdate-time # Compiler warning flags. To be disabled. - message(STATUS "-- Compilation options (disable specific warnings):") + message(STATUS "-- Compiler options (disable specific warnings):") check_cxx_compiler_flag("-Wnonnull-compare" COMP_HAS_WNO_NONNULL_COMPARE) check_cxx_compiler_flag("-Wmaybe-uninitialized" COMP_HAS_WNO_MAYBE_UNINIT) check_cxx_compiler_flag("-Wlanguage-extension-token" COMP_HAS_WNO_LANGUAGE_EXTENSION_TOKEN) @@ -178,7 +184,7 @@ See comments in the toolchain and: https://github.com/google/sanitizers/wiki/Mem elseif(MSVC) # Compiler warning flags (MSVC). - message(STATUS "-- Compilation options (MSVC):") + message(STATUS "-- Compiler options (MSVC):") check_cxx_compiler_flag("/W5105" COMP_HAS_W_DEFINE_MACRO_EXPANSION) # False positive warning, maybe even a MSVC bug. #check_cxx_compiler_flag("" COMP_HAS_) From d21afc20e3b88a13b51d1c81df3847a658ed330d Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 4 Jun 2025 07:48:17 +0200 Subject: [PATCH 051/112] Moved some networking headers inclusion from CSocket.h to .cpp. --- src/game/CServer.h | 1 - src/network/CSocket.cpp | 6 +++++- src/network/CSocket.h | 14 ++++++-------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/game/CServer.h b/src/game/CServer.h index cc5d8009a..709920b40 100644 --- a/src/game/CServer.h +++ b/src/game/CServer.h @@ -7,7 +7,6 @@ #define _INC_CSERVER_H #include "../common/sqlite/SQLite.h" -#include "../network/CSocket.h" #include "../common/CSFileObj.h" #include "../common/CTextConsole.h" #include "../common/CDataBase.h" diff --git a/src/network/CSocket.cpp b/src/network/CSocket.cpp index a59c7ee3d..69dfd641f 100644 --- a/src/network/CSocket.cpp +++ b/src/network/CSocket.cpp @@ -1,6 +1,10 @@ #include "CSocket.h" #ifndef _WIN32 - #include +# include +# include +# include +# include +# include #endif #include "../common/sphere_library/sstring.h" #include "../common/CLog.h" diff --git a/src/network/CSocket.h b/src/network/CSocket.h index 242714617..2f0350bcb 100644 --- a/src/network/CSocket.h +++ b/src/network/CSocket.h @@ -6,34 +6,32 @@ #ifndef _INC_CSOCKET_H #define _INC_CSOCKET_H +// TODO: In the different headers and classes, store a pointer to CSocketAddress instead of the class itself. This avoids +// to include CSocket.h. + #include "../common/common.h" #ifdef _WIN32 #include // this needs to be included after common.h, which sets some defines and then includes windows.h, since winsock2.h needs windows.h typedef int socklen_t; #else // else assume LINUX - #include - #include - #include - #include - #include - #include + #include // needed for hostent struct + //#include // included by netdb.h // Compatibility stuff. #define INVALID_SOCKET (SOCKET)(~0) #define SOCKET_ERROR (-1) #define SOCKET int #define TCP_NODELAY 0x0001 - #endif // _WIN32 - #ifdef _WIN32 # define CLOSESOCKET(_x_) { shutdown(_x_, 2); closesocket(_x_); } #else # define CLOSESOCKET(_x_) { shutdown(_x_, 2); close(_x_); } #endif + void AddSocketToSet(fd_set& fds, SOCKET socket, int& count); void CheckReportNetAPIErr(int retval, lpctstr ptcOperation); From e28eca16826e197675ac28b31581a55e65cf4d0f Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 4 Jun 2025 22:41:55 +0200 Subject: [PATCH 052/112] CScript parsing buffer optimization: do not dynamically allocate and resize but use big buffers in a dedicated pool. --- src/common/CExpression.h | 3 ++ src/common/CScript.cpp | 37 +++++++---------- src/common/CScript.h | 9 ++-- src/common/CScriptParserBufs.cpp | 38 +++++++++-------- src/common/CScriptParserBufs.h | 27 ++++++++++-- src/common/resource/CResourceLock.cpp | 4 +- src/common/sphere_library/sobjpool.h | 60 +++++++++++++-------------- src/game/spheresvr.cpp | 10 ++++- 8 files changed, 108 insertions(+), 80 deletions(-) diff --git a/src/common/CExpression.h b/src/common/CExpression.h index 5abf2e45b..e7293d933 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -167,6 +167,9 @@ struct CExprGlobals std::array m_SkillTitles_Generic; public: + CExprGlobals() = default; + ~CExprGlobals() = default; + void UpdateDefMsgDependentData(); lpctstr SkillTitle(SKILL_TYPE skill, uint uiVal) const; diff --git a/src/common/CScript.cpp b/src/common/CScript.cpp index 6c6e2cc43..ed38bb0c7 100644 --- a/src/common/CScript.cpp +++ b/src/common/CScript.cpp @@ -301,35 +301,30 @@ CScriptKey::CScriptKey( tchar * ptcKey, tchar * ptcArg ) : /////////////////////////////////////////////////////////////// // -CScriptKeyAlloc -tchar * CScriptKeyAlloc::_GetKeyBufferRaw( size_t iLen ) +tchar * CScriptKeyAlloc::_GetKeyBufferRaw() { //ADDTOCALLSTACK_DEBUG("CScriptKeyAlloc::_GetKeyBufferRaw"); // iLen = length of the string we want to hold. - if ( iLen > SCRIPT_MAX_LINE_LEN ) - iLen = SCRIPT_MAX_LINE_LEN; - ++iLen; // add null. - - if ( m_Mem.GetDataLength() < iLen ) - m_Mem.Alloc( iLen ); + m_TextBuf = CScriptParserBufs::GetScriptKeyArgBufPtr(); m_pszKey = m_pszArg = GetKeyBuffer(); m_pszKey[0] = '\0'; return m_pszKey; } /* -tchar * CScriptKeyAlloc::GetKeyBufferRaw( size_t iLen ) +tchar * CScriptKeyAlloc::GetKeyBufferRaw() { ADDTOCALLSTACK("CScriptKeyAlloc::GetKeyBufferRaw"); - MT_UNIQUE_LOCK_RETURN(this, CScriptKeyAlloc::_GetKeyBufferRaw(iLen)); + MT_UNIQUE_LOCK_RETURN(this, CScriptKeyAlloc::_GetKeyBufferRaw()); } */ tchar * CScriptKeyAlloc::GetKeyBuffer() { // Get the buffer the key is in. - ASSERT(m_Mem.GetData()); - return reinterpret_cast(m_Mem.GetData()); + ASSERT(m_TextBuf); + return reinterpret_cast(m_TextBuf.get()->data()); } bool CScriptKeyAlloc::ParseKey( lpctstr ptcKey ) @@ -338,17 +333,17 @@ bool CScriptKeyAlloc::ParseKey( lpctstr ptcKey ) // Skip leading white space if ( ! ptcKey ) { - _GetKeyBufferRaw(0); + _GetKeyBufferRaw(); return false; } GETNONWHITESPACE( ptcKey ); - tchar * pBuffer = _GetKeyBufferRaw( strlen( ptcKey )); + tchar * pBuffer = _GetKeyBufferRaw(); ASSERT(pBuffer); - size_t iLen = m_Mem.GetDataLength(); - Str_CopyLimitNull( pBuffer, ptcKey, iLen ); + size_t iLen = sizeof(CScriptKeyArgBuf); + Str_CopyLimitNull( pBuffer, ptcKey, iLen ); Str_Parse( pBuffer, &m_pszArg ); return true; @@ -363,25 +358,25 @@ bool CScriptKeyAlloc::ParseKey( lpctstr ptcKey, lpctstr pszVal ) if ( ! lenkey ) return ParseKey(pszVal); - ASSERT( lenkey < SCRIPT_MAX_LINE_LEN-2 ); + ASSERT( lenkey < sizeof(CScriptKeyArgBuf) - 2 ); size_t lenval = 0; if ( pszVal ) lenval = strlen( pszVal ); - m_pszKey = _GetKeyBufferRaw( lenkey + lenval + 1 ); + m_pszKey = _GetKeyBufferRaw(); if (ptcKey != m_pszKey) { // Invalid key, or not yet inited. - Str_CopyLimitNull(m_pszKey, ptcKey, m_Mem.GetDataLength()); + Str_CopyLimitNull(m_pszKey, ptcKey, sizeof(CScriptKeyArgBuf)); } m_pszArg = m_pszKey + lenkey; if ( pszVal ) { ++m_pszArg; - lenval = m_Mem.GetDataLength(); + lenval = sizeof(CScriptKeyArgBuf); Str_CopyLimitNull( m_pszArg, pszVal, lenval - lenkey ); } @@ -525,8 +520,8 @@ bool CScript::_ReadTextLine( bool fRemoveBlanks ) // Read a line from the opened // ARGS: // fRemoveBlanks = Don't report any blank lines, (just keep reading) - tchar* ptcBuf = _GetKeyBufferRaw(SCRIPT_MAX_LINE_LEN); - while ( CCacheableScriptFile::_ReadString( ptcBuf, SCRIPT_MAX_LINE_LEN )) + tchar* ptcBuf = _GetKeyBufferRaw(); + while ( CCacheableScriptFile::_ReadString( ptcBuf, sizeof(CScriptKeyArgBuf) )) { ++m_iLineNum; if ( fRemoveBlanks ) diff --git a/src/common/CScript.h b/src/common/CScript.h index bb21601a0..c8c2df484 100644 --- a/src/common/CScript.h +++ b/src/common/CScript.h @@ -6,8 +6,8 @@ #ifndef _INC_CSCRIPT_H #define _INC_CSCRIPT_H -#include "sphere_library/CSMemBlock.h" #include "CScriptContexts.h" +#include "CScriptParserBufs.h" #include "CCacheableScriptFile.h" @@ -78,11 +78,12 @@ class CScriptKeyAlloc : public CScriptKey { // Dynamic allocated script key. protected: - CSMemLenBlock m_Mem; // the buffer to hold data read. + static constexpr size_t sm_TextBufMaxSize = sizeof(CScriptKeyArgBuf); + CScriptKeyArgBufPtr m_TextBuf; // the buffer to hold data read. protected: - tchar * _GetKeyBufferRaw( size_t iSize ); - //tchar * GetKeyBufferRaw( size_t iSize ); + tchar * _GetKeyBufferRaw(); + //tchar * GetKeyBufferRaw(); size_t ParseKeyEnd(); public: diff --git a/src/common/CScriptParserBufs.cpp b/src/common/CScriptParserBufs.cpp index 11b5dfec8..402d4f207 100644 --- a/src/common/CScriptParserBufs.cpp +++ b/src/common/CScriptParserBufs.cpp @@ -1,31 +1,16 @@ #include "CScriptParserBufs.h" -#include "CScriptTriggerArgs.h" #include "CLog.h" -#include "sphere_library/sobjpool.h" -struct CScriptParserBufsImpl -{ - inline static CScriptParserBufsImpl& Get() - { - static auto inst = std::make_unique(); - return *inst.get(); - } - - static constexpr bool sm_allow_fallback_objects = true; - - // This is even more expensive to construct, so will definitely benefit a lot from having allocated, cached instances. - sl::ObjectPool - m_poolScriptTriggerArgs; -}; +extern CScriptParserBufs g_ScriptParserBuffers; auto CScriptParserBufs::GetCScriptTriggerArgsPtr() -> CScriptTriggerArgsPtr { - auto& pool = CScriptParserBufsImpl::Get().m_poolScriptTriggerArgs; + auto& pool = g_ScriptParserBuffers.m_poolScriptTriggerArgs; auto ptr = pool.acquireShared(); if (!pool.isFromPool(ptr)) { - static_assert(CScriptParserBufsImpl::sm_allow_fallback_objects); + static_assert(CScriptParserBufs::sm_allow_fallback_objects); g_Log.EventDebug( "Requesting CScriptTriggerArgs from an exhausted pool (max size: %" PRIu32 "). Alive new heap-allocated fallback objects: %" PRIu32 ".\n", pool.sm_pool_size, pool.getFallbackCount()); @@ -34,3 +19,20 @@ auto CScriptParserBufs::GetCScriptTriggerArgsPtr() -> CScriptTriggerArgsPtr ptr->Clear(); return ptr; } + +auto CScriptParserBufs::GetScriptKeyArgBufPtr() -> CScriptKeyArgBufPtr +{ + auto& pool = g_ScriptParserBuffers.m_poolScriptKeyArgBuffers; + auto ptr = pool.acquireUnique(); + if (!pool.isFromPool(ptr)) + { + static_assert(CScriptParserBufs::sm_allow_fallback_objects); + g_Log.EventDebug( + "Requesting CScriptKeyArgBuf from an exhausted pool (max size: %" PRIu32 "). Alive new heap-allocated fallback objects: %" PRIu32 ".\n", + pool.sm_pool_size, pool.getFallbackCount()); + } + + //ptr.get()->fill('\0'); + //memset(ptr.get(), 0, sizeof(CScriptKeyArgBuf)); + return ptr; +} diff --git a/src/common/CScriptParserBufs.h b/src/common/CScriptParserBufs.h index b119073f6..048896241 100644 --- a/src/common/CScriptParserBufs.h +++ b/src/common/CScriptParserBufs.h @@ -1,10 +1,10 @@ #ifndef _INC_CSCRIPTPARSERBUFS_H #define _INC_CSCRIPTPARSERBUFS_H +#include "sphere_library/sobjpool.h" #include "CScriptTriggerArgs.h" -using CScriptTriggerArgsPtr = std::shared_ptr; /* class CScriptTriggerArgsPtr : public std::shared_ptr { @@ -12,14 +12,35 @@ class CScriptTriggerArgsPtr : public std::shared_ptr } */ +using CScriptKeyArgBuf = std::array; -class CScriptParserBufs +struct CScriptParserBufs { CScriptParserBufs() noexcept = default; ~CScriptParserBufs() noexcept = default; + static constexpr bool sm_allow_fallback_objects = true; + + // This is even more expensive to construct, so will definitely benefit a lot from having allocated, cached instances. + using PoolScriptTriggerArgs_t = sl::ObjectPool; + + // Not expensive to construct but frequent allocations and deallocations (like classic strings). + using PoolScriptKeyArgBuf_t = sl::ObjectPool; + + PoolScriptTriggerArgs_t m_poolScriptTriggerArgs; + PoolScriptKeyArgBuf_t m_poolScriptKeyArgBuffers; + + using CScriptTriggerArgsPtr = PoolScriptTriggerArgs_t::SharedPtr_t; + using CScriptKeyArgBufPtr = PoolScriptKeyArgBuf_t::UniquePtr_t; + public: - static CScriptTriggerArgsPtr GetCScriptTriggerArgsPtr(); + [[nodiscard]] static CScriptTriggerArgsPtr GetCScriptTriggerArgsPtr(); + [[nodiscard]] static CScriptKeyArgBufPtr GetScriptKeyArgBufPtr(); }; +using CScriptTriggerArgsPtr = CScriptParserBufs::CScriptTriggerArgsPtr; +using CScriptKeyArgBufPtr = CScriptParserBufs::CScriptKeyArgBufPtr; + + + #endif // _INC_CSCRIPTPARSERBUFS_H diff --git a/src/common/resource/CResourceLock.cpp b/src/common/resource/CResourceLock.cpp index e9a8d835b..07dbd74ab 100644 --- a/src/common/resource/CResourceLock.cpp +++ b/src/common/resource/CResourceLock.cpp @@ -77,8 +77,8 @@ bool CResourceLock::_ReadTextLine( bool fRemoveBlanks ) // Read a line from the ASSERT(m_pLock); ASSERT( ! _IsBinaryMode() ); - tchar* ptcBuf = _GetKeyBufferRaw(SCRIPT_MAX_LINE_LEN); - while ( CCacheableScriptFile::_ReadString( ptcBuf, SCRIPT_MAX_LINE_LEN )) + tchar* ptcBuf = _GetKeyBufferRaw(); + while ( CCacheableScriptFile::_ReadString( ptcBuf, sm_TextBufMaxSize )) { m_pLock->m_iLineNum = ++m_iLineNum; // share this with original open. if ( fRemoveBlanks ) diff --git a/src/common/sphere_library/sobjpool.h b/src/common/sphere_library/sobjpool.h index bdb1c8378..3f44e2b3c 100644 --- a/src/common/sphere_library/sobjpool.h +++ b/src/common/sphere_library/sobjpool.h @@ -20,7 +20,7 @@ namespace sl // - If AllowFallback is true, a new, dynamically allocated object is returned. // - Otherwise, a std::runtime_error is thrown. -template +template // tp: template parameter class ObjectPool { @@ -31,10 +31,10 @@ class ObjectPool static constexpr index_t sm_pool_size = static_cast(tp_pool_size); static constexpr bool sm_allow_fallback = tp_allow_fallback; - // Ensure that T is default constructible and destructible. - static_assert(std::is_default_constructible_v, + // Ensure that PooledObject_t is default constructible and destructible. + static_assert(std::is_default_constructible_v, "ObjectPool requires the type T to be default constructible"); - static_assert(std::is_destructible_v, + static_assert(std::is_destructible_v, "ObjectPool requires the type T to be destructible"); ObjectPool() @@ -59,7 +59,7 @@ class ObjectPool ObjectPool* m_pool; std::optional m_index; // Engaged if pooled - void operator()(T* ptr) const + void operator()(PooledObject_t* ptr) const { if (m_pool) { @@ -84,7 +84,7 @@ class ObjectPool }; // Type alias for unique_ptr using PoolDeleter. - using UniquePtr_t = std::unique_ptr; + using UniquePtr_t = std::unique_ptr; // Acquires an object and returns it as a unique_ptr. // If no pooled objects are available, either returns a fallback allocation or throws. @@ -95,13 +95,13 @@ class ObjectPool { const auto idx = m_freeIndices.top(); m_freeIndices.pop(); - T* ptr = &m_objects[idx]; + PooledObject_t* ptr = &m_objects[idx]; return UniquePtr_t(ptr, PoolDeleter {this, idx}); } else [[unlikely]] if constexpr (sm_allow_fallback) { // Return a fallback (dynamically allocated) object. - T* ptr = new T(); + PooledObject_t* ptr = new PooledObject_t(); m_fallbackObjsCount += 1; return UniquePtr_t(ptr, PoolDeleter {this, std::nullopt}); } @@ -112,7 +112,7 @@ class ObjectPool } // Type alias for shared_ptr. The deleter goes in the constructor, unlike unique_ptr. - using SharedPtr_t = std::shared_ptr; + using SharedPtr_t = std::shared_ptr; // Acquires an object and returns it as a shared_ptr. // If no pooled objects are available, either returns a fallback allocation or throws. @@ -123,15 +123,15 @@ class ObjectPool { const auto idx = m_freeIndices.top(); m_freeIndices.pop(); - T* ptr = &m_objects[idx]; - return std::shared_ptr(ptr, PoolDeleter {this, idx}); + PooledObject_t* ptr = &m_objects[idx]; + return std::shared_ptr(ptr, PoolDeleter {this, idx}); } else [[unlikely]] if constexpr (sm_allow_fallback) { // Return a fallback (dynamically allocated) object. - T* ptr = new T(); + PooledObject_t* ptr = new PooledObject_t(); m_fallbackObjsCount += 1; - return std::shared_ptr(ptr, PoolDeleter {this, std::nullopt}); + return std::shared_ptr(ptr, PoolDeleter {this, std::nullopt}); } else { @@ -156,7 +156,7 @@ class ObjectPool // Static helper method for shared_ptr. [[nodiscard]] - static bool isFromPool(const std::shared_ptr &ptr) + static bool isFromPool(const std::shared_ptr &ptr) { // Does std::get_deleter work only with RTTI enabled? if (auto deleterPtr = std::get_deleter(ptr)) @@ -178,7 +178,7 @@ class ObjectPool } // Pre-constructed objects stored in an array. - std::array m_objects{}; + std::array m_objects{}; // Stack tracked indices of available objects. sl::fixed_comptime_stack m_freeIndices; @@ -188,7 +188,7 @@ class ObjectPool }; -template +template // tp: template parameter // ts: thread safe class TSObjectPool @@ -198,10 +198,10 @@ class TSObjectPool static constexpr index_t sm_pool_size = static_cast(tp_pool_size); static constexpr bool sm_allow_fallback = tp_allow_fallback; - // Ensure that T is default constructible and destructible. - static_assert(std::is_default_constructible_v, + // Ensure that PooledObject_t is default constructible and destructible. + static_assert(std::is_default_constructible_v, "ObjectPool requires the type T to be default constructible"); - static_assert(std::is_destructible_v, + static_assert(std::is_destructible_v, "ObjectPool requires the type T to be destructible"); TSObjectPool() @@ -226,7 +226,7 @@ class TSObjectPool TSObjectPool* m_pool; std::optional m_index; // Engaged if pooled - void operator()(T* ptr) const + void operator()(PooledObject_t* ptr) const { if (m_pool) { @@ -251,7 +251,7 @@ class TSObjectPool }; // Type alias for unique_ptr using PoolDeleter. - using UniquePtr_t = std::unique_ptr; + using UniquePtr_t = std::unique_ptr; // Acquires an object and returns it as a unique_ptr. // If no pooled objects are available, either returns a fallback allocation or throws. @@ -263,13 +263,13 @@ class TSObjectPool { const auto idx = m_freeIndices.top(); m_freeIndices.pop(); - T* ptr = &m_objects[idx]; + PooledObject_t* ptr = &m_objects[idx]; return UniquePtr_t(ptr, PoolDeleter {this, idx}); } else [[unlikely]] if constexpr (sm_allow_fallback) { // Return a fallback (dynamically allocated) object. - T* ptr = new T(); + PooledObject_t* ptr = new PooledObject_t(); m_fallbackObjsCount.fetch_add(1, std::memory_order_relaxed); return UniquePtr_t(ptr, PoolDeleter {this, std::nullopt}); } @@ -280,7 +280,7 @@ class TSObjectPool } // Type alias for shared_ptr. The deleter goes in the constructor, unlike unique_ptr. - using SharedPtr_t = std::shared_ptr; + using SharedPtr_t = std::shared_ptr; // Acquires an object and returns it as a shared_ptr. // If no pooled objects are available, either returns a fallback allocation or throws. @@ -292,15 +292,15 @@ class TSObjectPool { const auto idx = m_freeIndices.top(); m_freeIndices.pop(); - T* ptr = &m_objects[idx]; - return std::shared_ptr(ptr, PoolDeleter {this, idx}); + PooledObject_t* ptr = &m_objects[idx]; + return std::shared_ptr(ptr, PoolDeleter {this, idx}); } else [[unlikely]] if constexpr (sm_allow_fallback) { // Return a fallback (dynamically allocated) object. - T* ptr = new T(); + PooledObject_t* ptr = new PooledObject_t(); m_fallbackObjsCount.fetch_add(1, std::memory_order_relaxed); - return std::shared_ptr(ptr, PoolDeleter {this, std::nullopt}); + return std::shared_ptr(ptr, PoolDeleter {this, std::nullopt}); } else { @@ -325,7 +325,7 @@ class TSObjectPool // Static helper method for shared_ptr. [[nodiscard]] - static bool isFromPool(const std::shared_ptr &ptr) + static bool isFromPool(const std::shared_ptr &ptr) { if (auto deleterPtr = std::get_deleter(ptr)) return deleterPtr->m_index.has_value(); @@ -347,7 +347,7 @@ class TSObjectPool } // Pre-constructed objects stored in an array. - std::array m_objects{}; + std::array m_objects{}; // Stack tracked indices of available objects. sl::fixed_comptime_stack m_freeIndices; diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 27110fdb0..706d2db83 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -15,8 +15,8 @@ #include "../network/linuxev.h" #endif -#include "../common/sphere_library/CSRand.h" #include "../common/CLog.h" +#include "../common/CScriptParserBufs.h" //#include "../common/CException.h" // included in the precompiled header //#include "../common/CExpression.h" // included in the precompiled header #include "../common/CUOInstall.h" @@ -127,6 +127,10 @@ void GlobalInitializer::PeriodicSyncTimeConstants() // static // NOLINTNEXTLINE(clazy-non-pod-global-static) static GlobalInitializer g_GlobalInitializer; +extern CScriptParserBufs g_ScriptParserBuffers; +CScriptParserBufs g_ScriptParserBuffers; + + #ifdef _WIN32 CNTWindow g_NTWindow; #else @@ -159,7 +163,6 @@ CWorld g_World; // the world. (we save this stuff) // Again, game servers stuff. CUOInstall g_Install; CVerDataMul g_VerData; -CSRand g_Rand; // TODO: remove this, since now all the members are static. sl::GuardedAccess // TODO: put inside GuardedAccess also g_Cfg, and slowly also the other stuff? g_ExprGlobals; // Global script variables. CSStringList g_AutoComplete; // auto-complete list @@ -167,8 +170,11 @@ CScriptProfiler g_profiler; // script profiler CUOMapList g_MapList; // global maps information +//-- Threads. + // NOLINTNEXTLINE(clazy-non-pod-global-static) static MainThread g_Main; + // NOLINTNEXTLINE(clazy-non-pod-global-static) static PingServer g_PingServer; From 0467bfcae9dbccd9ba31c2182b1acc124783a722 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 5 Jun 2025 10:49:15 +0200 Subject: [PATCH 053/112] Optimized startup. - Reserved some starting space for RESDEFs, DEFs and ResourceHash to avoid unnecessary early reallocations. - Cached some invariant sector data, instead of generating it on the fly each time. --- src/common/CExpression.cpp | 38 ++++++--- src/common/CExpression.h | 2 +- src/common/CPointBase.h | 2 +- src/common/CRect.h | 20 ++--- src/common/CVarDefMap.cpp | 5 ++ src/common/CVarDefMap.h | 5 +- src/common/resource/CResourceHolder.cpp | 9 ++ src/common/resource/CResourceHolder.h | 2 +- src/game/CSector.cpp | 30 +++++-- src/game/CSector.h | 1 + src/game/CSectorTemplate.cpp | 106 ++++++++---------------- src/game/CSectorTemplate.h | 18 ++-- 12 files changed, 119 insertions(+), 119 deletions(-) diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index 74cce009b..0338df2dc 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -23,6 +23,13 @@ lpctstr const CExprGlobals::sm_szDefMsgNames[DEFMSG_QTY] = #include "../tables/defmessages.tbl" }; +CExprGlobals::CExprGlobals() +{ + m_VarResDefs. Reserve(0x1000); + m_VarDefs. Reserve(0x100); + m_VarGlobals. Reserve(0x10); +} + void CExprGlobals::UpdateDefMsgDependentData() { // Method to be re-evaluated. @@ -654,16 +661,21 @@ llong CExpression::GetSingle(lpctstr & refStrExpr ) refStrExpr += 2; goto try_dec; } + refStrExpr += 1; + + // TODO: gracefully handle integer overflows, maybe printing a warning message. lpctstr pStart = refStrExpr; - llong val = 0; + ullong val = 0; ushort ndigits = 0; while (true) { tchar ch = *refStrExpr; if ( IsDigit(ch) ) + { ch -= '0'; - else + } + else { ch = static_cast(tolower(ch)); if ( ch > 'f' || ch < 'a' ) @@ -675,16 +687,16 @@ llong CExpression::GetSingle(lpctstr & refStrExpr ) } break; } - ch -= 'a' - 10; - ++ ndigits; - } + ch -= 'a' - 10; + } val *= 0x10; - val += ch; + val += ch; ++ refStrExpr; + ++ ndigits; } if (ndigits <= 8) return (llong)(int)val; - return val; + return (llong)val; } /* // We could just use this, but it doesn't "eat" the string pointer. @@ -702,17 +714,19 @@ llong CExpression::GetSingle(lpctstr & refStrExpr ) else if ( refStrExpr[0] == '.' || IsDigit(refStrExpr[0]) ) { // A decimal number + + // TODO: gracefully handle integer overflows, maybe printing a warning message. try_dec: llong iVal = 0; for ( ; ; ++refStrExpr ) - { + { if ( *refStrExpr == '.' ) - continue; // just skip this. + continue; // just skip this. if ( ! IsDigit(*refStrExpr) ) - break; - iVal *= 10; + break; + iVal *= 10; iVal += (llong)(*refStrExpr) - '0'; - } + } return iVal; } else if ( ! _ISCSYMF(refStrExpr[0]) ) diff --git a/src/common/CExpression.h b/src/common/CExpression.h index e7293d933..1b16d6488 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -167,7 +167,7 @@ struct CExprGlobals std::array m_SkillTitles_Generic; public: - CExprGlobals() = default; + CExprGlobals(); ~CExprGlobals() = default; void UpdateDefMsgDependentData(); diff --git a/src/common/CPointBase.h b/src/common/CPointBase.h index 4a9f5bca9..fa8281107 100644 --- a/src/common/CPointBase.h +++ b/src/common/CPointBase.h @@ -65,7 +65,7 @@ struct CPointBase // Non initialized 3d point. [[nodiscard]] int GetDist( const CPointBase & pt ) const noexcept; // Distance between points [[nodiscard]] int GetDistSightBase( const CPointBase & pt ) const noexcept; // Distance between points based on UO sight [[nodiscard]] int GetDistSight( const CPointBase & pt ) const noexcept; // Distance between points based on UO sight - [[nodiscard]] int GetDist3D( const CPointBase & pt ) const noexcept; // 3D Distance between points + [[nodiscard]] int GetDist3D( const CPointBase & pt ) const noexcept; // 3D Distance between points //[[nodiscard]] inline bool IsValidZ() const noexcept { // return ((m_z > -127 /* -UO_SIZE_Z */) && (m_z < 127 /* UO_SIZE_Z */)); diff --git a/src/common/CRect.h b/src/common/CRect.h index cd1cb7950..ff184883f 100644 --- a/src/common/CRect.h +++ b/src/common/CRect.h @@ -23,14 +23,14 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) void SetRectEmpty() noexcept; - CRect() noexcept; - CRect(int left, int top, int right, int bottom, int map) noexcept; - CRect(const CRect&) noexcept = default; - CRect(CRect&&) noexcept = default; + CRect() noexcept; + CRect(int left, int top, int right, int bottom, int map) noexcept; + CRect(const CRect&) noexcept = default; + CRect(CRect&&) noexcept = default; virtual ~CRect() noexcept = default; - CRect& operator = (const CRect&) = default; - const CRect& operator += (const CRect& rect); + CRect& operator = (const CRect&) = default; + const CRect& operator += (const CRect& rect); inline int GetWidth() const noexcept { @@ -87,22 +87,22 @@ struct CRectMap : public CRect { CRectMap() noexcept = default; CRectMap(int left, int top, int right, int bottom, int map) noexcept; - CRectMap(const CRectMap&) noexcept = default; + CRectMap(const CRectMap&) noexcept = default; CRectMap(CRectMap&&) noexcept = default; // special copy constructors, valid because CRectMap hasn't additional members compared to CRect, it only has more methods CRectMap(const CRect& rect) noexcept : CRectMap(static_cast(rect)) {} CRectMap(CRect&& rect) noexcept : CRectMap(static_cast(rect)) {} - CRectMap& operator=(const CRectMap&) noexcept = default; - CRectMap& operator=(const CRect& rect) noexcept + CRectMap& operator=(const CRectMap&) noexcept = default; + CRectMap& operator=(const CRect& rect) noexcept { return CRectMap::operator=(static_cast(rect)); } virtual ~CRectMap() noexcept = default; - bool IsValid() const noexcept; + bool IsValid() const noexcept; virtual void NormalizeRect() noexcept override; void NormalizeRectMax() noexcept; diff --git a/src/common/CVarDefMap.cpp b/src/common/CVarDefMap.cpp index 6f878e5a4..b7a223a96 100644 --- a/src/common/CVarDefMap.cpp +++ b/src/common/CVarDefMap.cpp @@ -322,6 +322,11 @@ size_t CVarDefMap::GetCount() const noexcept return m_Container.size(); } +void CVarDefMap::Reserve(size_t uiSize) +{ + m_Container.reserve(uiSize); +} + CVarDefContNum* CVarDefMap::SetNumNew( lpctstr pszName, int64 iVal ) { ADDTOCALLSTACK_DEBUG("CVarDefMap::SetNumNew"); diff --git a/src/common/CVarDefMap.h b/src/common/CVarDefMap.h index 4db54f7ae..93bc6c2ea 100644 --- a/src/common/CVarDefMap.h +++ b/src/common/CVarDefMap.h @@ -142,14 +142,13 @@ class CVarDefMap bool CompareAll( const CVarDefMap * pArray ); void Clear(); size_t GetCount() const noexcept; + void Reserve(size_t uiSize); public: CVarDefMap() = default; ~CVarDefMap(); CVarDefMap & operator = ( const CVarDefMap & array ); - -private: - CVarDefMap(const CVarDefMap& copy); + CVarDefMap(const CVarDefMap& copy) = delete; public: lpctstr FindValNum( int64 iVal ) const; diff --git a/src/common/resource/CResourceHolder.cpp b/src/common/resource/CResourceHolder.cpp index 2777cddb1..b19209848 100644 --- a/src/common/resource/CResourceHolder.cpp +++ b/src/common/resource/CResourceHolder.cpp @@ -294,6 +294,15 @@ lpctstr CResourceHolder::GetName() const return "CFG"; } +CResourceHolder::CResourceHolder() +{ + // Avoid unnecessary continuous growing re-allocations at startup, just start with some preallocated space. + for (auto& arr : m_ResHash.m_Array) + { + arr.reserve(0x100); + } +} + CResourceScript * CResourceHolder::GetResourceFile( size_t i ) { if ( ! m_ResourceFiles.IsValidIndex(i) ) diff --git a/src/common/resource/CResourceHolder.h b/src/common/resource/CResourceHolder.h index 003554723..add29c467 100644 --- a/src/common/resource/CResourceHolder.h +++ b/src/common/resource/CResourceHolder.h @@ -63,7 +63,7 @@ class CResourceHolder : public CScriptObj public: lpctstr GetName() const; - CResourceHolder() = default; + CResourceHolder(); virtual ~CResourceHolder() = default; CResourceHolder(const CResourceHolder& copy) = delete; diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index 3c14936d2..198438060 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -433,7 +433,8 @@ void CSector::r_Write() ADDTOCALLSTACK_DEBUG("CSector::r_Write"); if ( m_fSaveParity == g_World.m_fSaveParity ) return; // already saved. - CPointMap pt = GetBasePoint(); + + CPointMap const& pt = m_BasePoint; m_fSaveParity = g_World.m_fSaveParity; bool fHeaderCreated = false; @@ -615,7 +616,7 @@ int CSector::GetLocalTime() const ADDTOCALLSTACK("CSector::GetLocalTime"); // Get local time of the day (in minutes) const CSectorList& pSectors = CSectorList::Get(); - const CPointMap& pt(GetBasePoint()); + const CPointMap& pt = m_BasePoint; int64 iLocalTime = CWorldGameTime::GetCurrentTimeInGameMinutes(); if ( !g_Cfg.m_fAllowLightOverride ) @@ -834,7 +835,7 @@ void CSector::SetLight( int light ) void CSector::SetDefaultWeatherChance() { ADDTOCALLSTACK("CSector::SetDefaultWeatherChance"); - CPointMap pt = GetBasePoint(); + CPointMap const& pt = m_BasePoint; byte iPercent = (byte)(IMulDiv( pt.m_y, 100, g_MapList.GetMapSizeY(pt.m_map) )); // 100 = south if ( iPercent < 50 ) { @@ -943,6 +944,17 @@ void CSector::SetWeatherChance( bool fRain, int iChance ) SetWeather( GetWeatherCalc()); } +bool CSector::IsInDungeon() const +{ + ADDTOCALLSTACK("CSector::IsInDungeon"); + // What part of the maps are filled with dungeons. + // Used for light / weather calcs. + CRegion *pRegion = GetRegion(m_BasePoint, REGION_TYPE_AREA); + + return ( pRegion && pRegion->IsFlag(REGION_FLAG_UNDERGROUND) ); +} + + void CSector::OnHearItem( CChar * pChar, lpctstr pszText ) { ADDTOCALLSTACK("CSector::OnHearItem"); @@ -1110,7 +1122,7 @@ void CSector::RespawnDeadNPCs() { ADDTOCALLSTACK("CSector::RespawnDeadNPCs"); // skip sectors in unsupported maps - if ( !g_MapList.IsMapSupported(m_map) ) + if ( !g_MapList.IsMapSupported(m_BasePoint.m_map) ) return; // Respawn dead NPCs @@ -1189,7 +1201,7 @@ bool CSector::_OnTick() */ // do not tick sectors on maps not supported by server - if ( !g_MapList.IsMapSupported(m_map) ) + if ( !g_MapList.IsMapSupported(m_BasePoint.m_map) ) return true; EXC_TRY("_OnTick"); @@ -1325,7 +1337,7 @@ bool CSector::_OnTick() EXC_CATCHSUB("Sector"); EXC_DEBUGSUB_START; - CPointMap pt = GetBasePoint(); + CPointMap const& pt = m_BasePoint; g_Log.EventDebug("#0 char 0%x '%s'\n", (dword)(pChar->GetUID()), pChar->GetName()); g_Log.EventDebug("#0 sector #%d [%d,%d,%d,%d]\n", GetIndex(), pt.m_x, pt.m_y, pt.m_z, pt.m_map); EXC_DEBUGSUB_END; @@ -1336,7 +1348,7 @@ bool CSector::_OnTick() _SetTimeout(SECTOR_TICKING_PERIOD); // Sector is Awake, make it tick after 30 seconds. EXC_DEBUG_START; - const CPointMap pt = GetBasePoint(); + CPointMap const& pt = m_BasePoint; g_Log.EventError("#4 sector #%d [%hd,%hd,%hhd,%hhu]\n", GetIndex(), pt.m_x, pt.m_y, pt.m_z, pt.m_map); EXC_DEBUG_END; return true; @@ -1410,7 +1422,7 @@ bool CSector::CheckItemComplexity() const noexcept const size_t uiCount = GetItemComplexity(); if (uiCount > g_Cfg.m_iMaxSectorComplexity) { - g_Log.Event(LOGL_WARN, "%" PRIuSIZE_T " items at %s. Sector too complex!\n", uiCount, GetBasePoint().WriteUsed()); + g_Log.Event(LOGL_WARN, "%" PRIuSIZE_T " items at %s. Sector too complex!\n", uiCount, m_BasePoint.WriteUsed()); return true; } return false; @@ -1461,7 +1473,7 @@ bool CSector::CheckCharComplexity() const noexcept const size_t uiCount = GetCharComplexity(); if (uiCount > g_Cfg.m_iMaxCharComplexity) { - g_Log.Event(LOGL_WARN, "%" PRIuSIZE_T " chars at %s. Sector too complex!\n", uiCount, GetBasePoint().WriteUsed()); + g_Log.Event(LOGL_WARN, "%" PRIuSIZE_T " chars at %s. Sector too complex!\n", uiCount, m_BasePoint.WriteUsed()); return true; } return false; diff --git a/src/game/CSector.h b/src/game/CSector.h index 48f718c89..4469c9012 100644 --- a/src/game/CSector.h +++ b/src/game/CSector.h @@ -39,6 +39,7 @@ class CSector : public CScriptObj, public CSectorBase, public CTimedObject // sq void SetLightNow( bool fFlash = false ); bool IsMoonVisible( uint iPhase, int iLocalTime ) const; void SetDefaultWeatherChance(); + bool IsInDungeon() const; public: CSector(); diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index 46fc7fc74..9e7eddc81 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -156,7 +156,7 @@ void CItemsList::AddItemToSector( CItem * pItem ) void CSectorBase::SetAdjacentSectors() { const CSectorList& pSectors = CSectorList::Get(); - auto const& sd = pSectors.GetMapSectorData(m_map); + auto const& sd = pSectors.GetMapSectorData(m_BasePoint.m_map); const int iMaxX = sd.iSectorColumns; ASSERT(iMaxX > 0); @@ -197,8 +197,8 @@ void CSectorBase::SetAdjacentSectors() for (int i = 0; i < (int)DIR_QTY; ++i) { // out of bounds checks - const int iAdjX = _x + _xyDir[i].x; - const int iAdjY = _y + _xyDir[i].y; + const int iAdjX = m_BasePoint.m_x + _xyDir[i].x; + const int iAdjY = m_BasePoint.m_y + _xyDir[i].y; int index = m_index; index += ((iAdjY * iMaxX) + iAdjX); @@ -206,7 +206,7 @@ void CSectorBase::SetAdjacentSectors() { continue; } - _ppAdjacentSectors[(DIR_TYPE)i] = pSectors.GetSectorByIndex(m_map, index); + _ppAdjacentSectors[(DIR_TYPE)i] = pSectors.GetSectorByIndex(m_BasePoint.m_map, index); } } @@ -219,10 +219,8 @@ CSector *CSectorBase::_GetAdjacentSector(DIR_TYPE dir) const CSectorBase::CSectorBase() : _ppAdjacentSectors{{}} { - m_map = 0; m_index = 0; m_dwFlags = 0; - _x = _y = -1; //memset(_ppAdjacentSectors, 0, DIR_QTY * sizeof(_ppAdjacentSectors)); } @@ -232,30 +230,41 @@ void CSectorBase::Init(int index, uchar map, short x, short y) if (!g_MapList.IsMapSupported(map) || !g_MapList.IsInitialized(map)) { g_Log.EventError("Trying to initalize a sector %d in unsupported map #%d. Defaulting to 0,0.\n", index, map); + return; } - else if (( index < 0 ) || ( index >= CSectorList::Get().GetMapSectorData(map).iSectorQty )) + if (( index < 0 ) || ( index >= CSectorList::Get().GetMapSectorData(map).iSectorQty )) { - m_map = map; + m_BasePoint.m_map = map; g_Log.EventError("Trying to initalize a sector by sector number %d out-of-range for map #%d. Defaulting to 0,%d.\n", index, map, map); + return; } - else - { - ASSERT(x >= 0 && y >= 0); - m_index = index; - m_map = map; - _x = x; - _y = y; - } -} -bool CSectorBase::IsInDungeon() const -{ - ADDTOCALLSTACK("CSectorBase::IsInDungeon"); - // What part of the maps are filled with dungeons. - // Used for light / weather calcs. - CRegion *pRegion = GetRegion(GetBasePoint(), REGION_TYPE_AREA); + ASSERT(x >= 0 && y >= 0); + m_index = index; - return ( pRegion && pRegion->IsFlag(REGION_FLAG_UNDERGROUND) ); + // Set BasePoint. + auto const& sd = CSectorList::Get().GetMapSectorData(map); + DEBUG_ASSERT((m_index >= 0) && (m_index < sd.iSectorQty) ); + + const int iCols = sd.iSectorColumns, iSize = sd.iSectorSize; + const int iQuot = (m_index % iCols), iRem = (m_index / iCols); // Help the compiler to optimize the division + m_BasePoint = // Initializer list for CPointMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) + { + (short)(iQuot * iSize), // x + (short)(iRem * iSize), // y + 0, // z + (uint8)map // m + }; + + // Set MapRect. + m_MapRect = // Initializer list for CRectMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) + CRectMap { + m_BasePoint.m_x, // left + m_BasePoint.m_y, // yop + m_BasePoint.m_x + iSize, // right: East + m_BasePoint.m_y + iSize, // bottom: South + m_BasePoint.m_map // map + }; } CRegion * CSectorBase::GetRegion( const CPointBase & pt, dword dwType ) const @@ -443,52 +452,3 @@ bool CSectorBase::AddTeleport( CTeleport * pTeleport ) m_Teleports.AddSortKey( pTeleport, pTeleport->GetPointSortIndex()); return true; } - -bool CSectorBase::IsFlagSet( dword dwFlag ) const noexcept -{ - return (( m_dwFlags & dwFlag) ? true : false ); -} - -CPointMap CSectorBase::GetBasePoint() const -{ - // ADDTOCALLSTACK_DEBUG("CSectorBase::GetBasePoint"); // It's commented because it's slow and this method is called VERY often! - // What is the coord base of this sector. upper left point. - - // Again this method is called very often, so call the least functions possible and do the minimum amount of checks required - DEBUG_ASSERT(g_MapList.IsMapSupported(m_map)); - - const CSectorList& pSectors = CSectorList::Get(); - auto const& sd = pSectors.GetMapSectorData(m_map); - DEBUG_ASSERT((m_index >= 0) && (m_index < sd.iSectorQty) ); - - const int iCols = sd.iSectorColumns; - const int iSize = sd.iSectorSize; - - const int iQuot = (m_index % iCols), iRem = (m_index / iCols); // Help the compiler to optimize the division - return // Initializer list for CPointMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) - { - (short)(iQuot * iSize), // x - (short)(iRem * iSize), // y - 0, // z - m_map // m - }; -} - -CRectMap CSectorBase::GetRect() const noexcept -{ - //ADDTOCALLSTACK_DEBUG("CSectorBase::GetRect"); // It's commented because it's slow and this method is called VERY often! - // Get a rectangle for the sector. - DEBUG_ASSERT(g_MapList.IsMapSupported(m_map)); - - const CSectorList& pSectors = CSectorList::Get(); - const CPointMap& pt = GetBasePoint(); - const int iSectorSize = pSectors.GetMapSectorData(pt.m_map).iSectorSize; - return // Initializer list for CRectMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) - { - pt.m_x, // left - pt.m_y, // yop - pt.m_x + iSectorSize, // right: East - pt.m_y + iSectorSize, // bottom: South - pt.m_map // map - }; -} diff --git a/src/game/CSectorTemplate.h b/src/game/CSectorTemplate.h index 8e7cd54c7..f117c1878 100644 --- a/src/game/CSectorTemplate.h +++ b/src/game/CSectorTemplate.h @@ -99,9 +99,9 @@ class CSectorBase // world sector protected: - uint8 m_map; // sector map - int16 _x, _y; // x and y (row and column) of the sector in the map - int m_index; // sector index + int m_index; // Sector index in the 'sector grid' + CPointBase m_BasePoint; // Sector coordinates in the 'sector grid'. + CRectMap m_MapRect; // Map area inside this sector. private: // TODO: store indices instead of pointers, to make the class smaller? @@ -137,11 +137,9 @@ class CSectorBase // world sector virtual void Init(int index, uchar map, short x, short y); // Location map units. - int GetIndex() const noexcept { return m_index; } - int GetMap() const noexcept { return m_map; } - CPointMap GetBasePoint() const; - CRectMap GetRect() const noexcept; - bool IsInDungeon() const; + int GetIndex() const noexcept { return m_index; } + int GetMap() const noexcept { return m_BasePoint.m_map; } + const CRectMap& GetRect() const noexcept { return m_MapRect; } // CRegion CRegion * GetRegion( const CPointBase & pt, dword dwType ) const; @@ -154,7 +152,9 @@ class CSectorBase // world sector CTeleport * GetTeleport( const CPointMap & pt ) const; bool AddTeleport( CTeleport * pTeleport ); - bool IsFlagSet( dword dwFlag ) const noexcept; + bool IsFlagSet( dword dwFlag ) const noexcept { + return (m_dwFlags & dwFlag); + } #define SECF_NoSleep 0x00000001 #define SECF_InstaSleep 0x00000002 From daefcd1eeaf8023c7ffc41a037ff2b82a0ad3c2d Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 5 Jun 2025 13:14:30 +0200 Subject: [PATCH 054/112] Optimized startup (at least x2 in the debug build). Also: - Added support for delayed sorting of CResourceHash. - Moved some CResourceHolder methods to CServerConfig, instead of having spaghetti code calling virtuals from both classes. --- src/common/CCacheableScriptFile.cpp | 13 +- src/common/resource/CResourceHash.cpp | 36 +++ src/common/resource/CResourceHash.h | 6 + src/common/resource/CResourceHolder.cpp | 218 +---------------- src/common/resource/CResourceHolder.h | 12 - src/common/resource/CResourceScript.cpp | 4 +- src/common/sphere_library/CSFile.cpp | 10 +- src/common/sphere_library/ssorted_vector.h | 15 ++ src/game/CRegionBase.cpp | 23 +- src/game/CServerConfig.cpp | 263 +++++++++++++++++++-- src/game/CServerConfig.h | 22 +- src/game/CWorld.cpp | 2 +- src/game/CWorldImport.cpp | 2 +- 13 files changed, 359 insertions(+), 267 deletions(-) diff --git a/src/common/CCacheableScriptFile.cpp b/src/common/CCacheableScriptFile.cpp index fcc9a236a..9d92061d9 100644 --- a/src/common/CCacheableScriptFile.cpp +++ b/src/common/CCacheableScriptFile.cpp @@ -163,7 +163,9 @@ bool CCacheableScriptFile::_Open(lpctstr ptcFilename, uint uiModeFlags) // go past the end of this substring, there should be the delimiter/newline/etc, just skip it. lpctstr str_end = &str_start[len_to_copy]; _SkipOneEndline(str_end); + _fileContent->emplace_back(str_start, len_to_copy); + iStrLen += (str_end - len_to_copy - str_start); fFirstLine = false; @@ -235,7 +237,8 @@ bool CCacheableScriptFile::_IsEOF() const if ( _useDefaultFile() ) return CSFileText::_IsEOF(); - return (_fileContent->empty() || ((uint)_iCurrentLine == _fileContent->size()) ); + const size_t uiFileSize = _fileContent->size(); // might be faster than empty() on some implementations + return (!uiFileSize || ((uint)_iCurrentLine == uiFileSize) ); } bool CCacheableScriptFile::IsEOF() const { @@ -256,15 +259,17 @@ tchar * CCacheableScriptFile::_ReadString(tchar *pBuffer, int sizemax) //*pBuffer = '\0'; ASSERT(_fileContent); - if (_fileContent->empty() || ((uint)_iCurrentLine >= _fileContent->size())) + size_t uiSize = _fileContent->size(); // might be faster than empty() on some implementations + if (!uiSize || ((uint)_iCurrentLine >= uiSize)) return nullptr; const std::string_view cur_line = (*_fileContent)[_iCurrentLine]; + uiSize = cur_line.size(); ++_iCurrentLine; - if (cur_line.empty()) + if (!uiSize) return pBuffer; - size_t bytes_to_copy = cur_line.size(); + size_t bytes_to_copy = uiSize; if (bytes_to_copy >= size_t(sizemax)) bytes_to_copy = size_t(sizemax) - 1; //if (!cur_line.empty()) diff --git a/src/common/resource/CResourceHash.cpp b/src/common/resource/CResourceHash.cpp index 8deee8bd7..1a9e7d5c2 100644 --- a/src/common/resource/CResourceHash.cpp +++ b/src/common/resource/CResourceHash.cpp @@ -3,6 +3,7 @@ * */ +#include "../CLog.h" #include "CResourceDef.h" #include "CResourceHash.h" @@ -48,6 +49,23 @@ int CResourceHashArray::_compare(std::unique_ptr const& pObjStored return -1; } +void CResourceHashArray::ManualSort() +{ + auto sorter = CResourceHashArraySorter(); + std::sort(this->begin(), this->end(), sorter); + auto it = this->cbegin(); + const auto itEnd = this->cend(); + while (true) + { + it = std::adjacent_find(it, itEnd); + if (it == itEnd) + break; + + const CResourceDef * pRes = it->get(); + g_Log.EventError("Duplicated CResourceDef '%s'.\n", pRes->GetResourceName()); + } +} + void CResourceHash::AddSortKey(CResourceID const& rid, CResourceDef* pNew) { ASSERT(rid.GetResPage() <= RES_PAGE_MAX); // RES_PAGE_ANY can be used only for search, you can't insert a rid with this special page @@ -58,6 +76,24 @@ void CResourceHash::AddSortKey(CResourceID const& rid, CResourceDef* pNew) destArray.emplace(pNew); } +void CResourceHash::AddUnsortedKey(CResourceID const& rid, CResourceDef* pNew) +{ + ASSERT(rid.GetResPage() <= RES_PAGE_MAX); // RES_PAGE_ANY can be used only for search, you can't insert a rid with this special page + + auto& destArray = m_Array[GetHashArray(rid)]; + + //ASSERT(destArray.find_sorted(rid) == sl::scont_bad_index()); + destArray.emplace_unsorted(pNew); +} + +void CResourceHash::SortStep() +{ + for (auto& arr : m_Array) + { + arr.ManualSort(); + } +} + void CResourceHash::SetAt(CResourceID const& rid, size_t index, CResourceDef* pNew) { ASSERT(rid.GetResPage() <= RES_PAGE_MAX); // RES_PAGE_ANY can be used only for search, you can't insert a rid with this special page diff --git a/src/common/resource/CResourceHash.h b/src/common/resource/CResourceHash.h index 6c6262e59..4b378c5f3 100644 --- a/src/common/resource/CResourceHash.h +++ b/src/common/resource/CResourceHash.h @@ -28,6 +28,8 @@ class CResourceHashArray : public sl::unique_ptr_sorted_vectorfind_predicate(rid, _compare); } //inline bool Contains(CResourceID const& rid) const { return (sl::scont_bad_index() != this->find_sorted(rid)); } }; @@ -62,6 +64,10 @@ struct CResourceHash } void AddSortKey(CResourceID const& rid, CResourceDef* pNew); + + void AddUnsortedKey(CResourceID const& rid, CResourceDef* pNew); + void SortStep(); + void SetAt(CResourceID const& rid, size_t index, CResourceDef* pNew); //void ReplaceRid(CResourceID const& ridOld, CResourceDef* pNew); }; diff --git a/src/common/resource/CResourceHolder.cpp b/src/common/resource/CResourceHolder.cpp index b19209848..a13c41123 100644 --- a/src/common/resource/CResourceHolder.cpp +++ b/src/common/resource/CResourceHolder.cpp @@ -1,11 +1,10 @@ -#include "../sphere_library/CSFileList.h" //#include "../CException.h" // included in the precompiled header #include "../CExpression.h" // included in the precompiled header #include "../CLog.h" #include "CResourceHolder.h" #include "CResourceHash.h" -#include "CResourceScript.h" +//#include "CResourceScript.h" //*************************************************** @@ -76,215 +75,7 @@ lpctstr const CResourceHolder::sm_szResourceBlocks[RES_QTY] = // static }; -//********************************************************* -// Resource Files - -CResourceScript * CResourceHolder::FindResourceFile( lpctstr pszPath ) -{ - ADDTOCALLSTACK("CResourceHolder::FindResourceFile"); - // Just match the titles ( not the whole path) - - lpctstr pszTitle = CScript::GetFilesTitle( pszPath ); - - for ( size_t i = 0; ; ++i ) - { - CResourceScript * pResFile = GetResourceFile(i); - if ( pResFile == nullptr ) - break; - lpctstr pszTitle2 = pResFile->GetFileTitle(); - if ( ! strcmpi( pszTitle2, pszTitle )) - return pResFile; - } - return nullptr; -} - -CResourceScript * CResourceHolder::AddResourceFile( lpctstr pszName ) -{ - ADDTOCALLSTACK("CResourceHolder::AddResourceFile"); - ASSERT(pszName != nullptr); - // Is this really just a dir name ? - - if (strlen(pszName) >= SPHERE_MAX_PATH) - throw CSError(LOGL_ERROR, 0, "Filename too long!"); - - tchar szName[SPHERE_MAX_PATH]; - Str_CopyLimitNull(szName, pszName, sizeof(szName)); - - tchar szTitle[SPHERE_MAX_PATH]; - lpctstr ptcTitle = CScript::GetFilesTitle(szName); - ASSERT_ALWAYS(strlen(ptcTitle) < sizeof(szTitle)); - Str_CopyLimitNull(szTitle, ptcTitle, sizeof(szTitle)); - - if ( szTitle[0] == '\0' ) - { - AddResourceDir( pszName ); - return nullptr; - } - - lpctstr pszExt = CScript::GetFilesExt( szTitle ); - if ( pszExt == nullptr ) - { - // No file extension provided, so append .scp to the filename - Str_ConcatLimitNull( szName, SPHERE_SCRIPT_EXT, sizeof(szName) ); - Str_ConcatLimitNull( szTitle, SPHERE_SCRIPT_EXT, sizeof(szTitle) ); - } - - if ( ! strnicmp( szTitle, SPHERE_FILE "tables", strlen(SPHERE_FILE "tables"))) - { - // Don't dupe this. - return nullptr; - } - - // Try to prevent dupes - CResourceScript * pNewRes = FindResourceFile(szTitle); - if ( pNewRes ) - return pNewRes; - - // Find correct path - pNewRes = new CResourceScript(); - if (! OpenResourceFind(static_cast(*pNewRes), szName)) - { - delete pNewRes; - return nullptr; - } - - m_ResourceFiles.emplace_back(pNewRes); - pNewRes->m_iResourceFileIndex = int(m_ResourceFiles.size() -1); - return pNewRes; -} - -void CResourceHolder::AddResourceDir( lpctstr pszDirName ) -{ - ADDTOCALLSTACK("CResourceHolder::AddResourceDir"); - if ( pszDirName[0] == '\0' ) - return; - - CSString sFilePath = CSFile::GetMergedFileName( pszDirName, "*" SPHERE_SCRIPT_EXT ); - - CSFileList filelist; - int iRet = filelist.ReadDir( sFilePath, false ); - if ( iRet < 0 ) - { - // also check script file path - sFilePath = CSFile::GetMergedFileName(m_sSCPBaseDir, sFilePath.GetBuffer()); - - iRet = filelist.ReadDir( sFilePath, true ); - if ( iRet < 0 ) - { - DEBUG_ERR(( "DirList=%d for '%s'\n", iRet, pszDirName )); - return; - } - } - - if ( iRet <= 0 ) // no files here. - return; - - CSStringListRec * psFile = filelist.GetHead(), *psFileNext = nullptr; - for ( ; psFile; psFile = psFileNext ) - { - psFileNext = psFile->GetNext(); - sFilePath = CSFile::GetMergedFileName( pszDirName, *psFile ); - AddResourceFile( sFilePath ); - } -} - -void CResourceHolder::LoadResourcesOpen( CScript * pScript ) -{ - ADDTOCALLSTACK("CResourceHolder::LoadResourcesOpen"); - // Load an already open resource file. - - ASSERT(pScript); - ASSERT( pScript->HasCache() ); - - int iSections = 0; - while ( pScript->FindNextSection() ) - { - LoadResourceSection( pScript ); - ++iSections; - } - if ( ! iSections ) - DEBUG_WARN(( "No resource sections in '%s'\n", pScript->GetFilePath())); -} - -bool CResourceHolder::LoadResources( CResourceScript * pScript ) -{ - ADDTOCALLSTACK("CResourceHolder::LoadResources"); - // Open the file then load it. - if ( pScript == nullptr ) - return false; - - if ( ! pScript->Open()) - { - g_Log.Event(LOGL_CRIT|LOGM_INIT, "[RESOURCES] '%s' not found...\n", pScript->GetFilePath()); - return false; - } - - g_Log.Event(LOGM_INIT, "Loading %s\n", pScript->GetFilePath()); - - LoadResourcesOpen( pScript ); - pScript->Close(); - pScript->CloseForce(); - return true; -} - -CResourceScript * CResourceHolder::LoadResourcesAdd( lpctstr pszNewFileName ) -{ - ADDTOCALLSTACK("CResourceHolder::LoadResourcesAdd"); - // Make sure this is added to my list of resource files - // And load it now. - - CResourceScript * pScript = AddResourceFile( pszNewFileName ); - if ( ! LoadResources(pScript) ) - return nullptr; - return pScript; -} - -bool CResourceHolder::OpenResourceFind( CScript &s, lpctstr pszFilename, bool fCritical ) -{ - ADDTOCALLSTACK("CResourceHolder::OpenResourceFind"); - // Open a single resource script file. - // Look in the specified path. - - if ( pszFilename == nullptr ) - pszFilename = s.GetFilePath(); - - // search the local dir or full path first. - if (CSFile::FileExists(pszFilename)) - { - if (s.Open(pszFilename, OF_READ | OF_NONCRIT)) - return true; - if (!fCritical) - return false; - } - - // next, check the script file path - CSString sPathName = CSFile::GetMergedFileName( m_sSCPBaseDir, pszFilename ); - if (CSFile::FileExists(sPathName)) - { - if (s.Open(sPathName, OF_READ | OF_NONCRIT)) - return true; - } - - // finally, strip the directory and re-check script file path - lpctstr pszTitle = CSFile::GetFilesTitle(pszFilename); - sPathName = CSFile::GetMergedFileName( m_sSCPBaseDir, pszTitle ); - if (CSFile::FileExists(sPathName)) - { - return s.Open(sPathName, OF_READ); - } - - g_Log.Event(LOGM_INIT|LOGL_ERROR, "Can't find file '%s' in any of the expected paths!.\n", pszFilename); - return false; -} - -bool CResourceHolder::LoadResourceSection( CScript * pScript ) -{ - ADDTOCALLSTACK("CResourceHolder::LoadResourceSection"); - UnreferencedParameter(pScript); - // Just stub this out for others for now. - return false; -} //********************************************************* // Resource Section Definitions @@ -303,12 +94,7 @@ CResourceHolder::CResourceHolder() } } -CResourceScript * CResourceHolder::GetResourceFile( size_t i ) -{ - if ( ! m_ResourceFiles.IsValidIndex(i) ) - return nullptr; // All resource files we need to get blocks from later. - return m_ResourceFiles[i]; -} + CResourceID CResourceHolder::ResourceGetID_EatStr(RES_TYPE restype, lpctstr &ptcName, word wPage, bool fCanFail) { diff --git a/src/common/resource/CResourceHolder.h b/src/common/resource/CResourceHolder.h index add29c467..69db54cad 100644 --- a/src/common/resource/CResourceHolder.h +++ b/src/common/resource/CResourceHolder.h @@ -36,21 +36,9 @@ class CResourceHolder : public CScriptObj sl::smart_ptr_view ResourceGetDefRefByName(RES_TYPE restype, lpctstr pszName, word wPage = 0); CResourceDef* ResourceGetDefByName(RES_TYPE restype, lpctstr pszName, word wPage = 0); - CResourceScript * AddResourceFile( lpctstr pszName ); - void AddResourceDir( lpctstr pszDirName ); - public: - CResourceScript * FindResourceFile( lpctstr pszTitle ); - CResourceScript * LoadResourcesAdd( lpctstr pszNewName ); - - void LoadResourcesOpen( CScript * pScript ); - bool LoadResources( CResourceScript * pScript ); static lpctstr GetResourceBlockName( RES_TYPE restype ); - virtual bool OpenResourceFind( CScript &s, lpctstr pszFilename, bool fCritical = true ); - virtual bool LoadResourceSection( CScript * pScript ) = 0; - - CResourceScript * GetResourceFile( size_t i ); CResourceID ResourceGetID_EatStr( RES_TYPE restype, lpctstr &pszName, word wPage = 0, bool fCanFail = false ); // this moves forward (changes!) the ptcName pointer! CResourceID ResourceGetID( RES_TYPE restype, lpctstr ptcName, word wPage = 0, bool fCanFail = false); CResourceID ResourceGetIDType( RES_TYPE restype, lpctstr pszName, word wPage = 0 ); diff --git a/src/common/resource/CResourceScript.cpp b/src/common/resource/CResourceScript.cpp index 88460eabc..f290f0bbb 100644 --- a/src/common/resource/CResourceScript.cpp +++ b/src/common/resource/CResourceScript.cpp @@ -55,7 +55,7 @@ void CResourceScript::ReSync() _fCacheToBeUpdated = true; if ( !Open() ) return; - g_Cfg.LoadResourcesOpen( this ); + g_Cfg.LoadResourcesOpen( this, true ); Close(); } @@ -75,7 +75,7 @@ bool CResourceScript::Open( lpctstr pszFilename, uint wFlags ) { // what should we do about it ? reload it of course ! g_Serv.SetServerMode(SERVMODE_ResyncLoad); - g_Cfg.LoadResourcesOpen( this ); + g_Cfg.LoadResourcesOpen( this, true ); g_Serv.SetServerMode(SERVMODE_Run); } } diff --git a/src/common/sphere_library/CSFile.cpp b/src/common/sphere_library/CSFile.cpp index 6e16f4c9d..cdc994d35 100644 --- a/src/common/sphere_library/CSFile.cpp +++ b/src/common/sphere_library/CSFile.cpp @@ -379,7 +379,7 @@ bool CSFile::Write(const void* pData, int iLength) lpctstr CSFile::GetFilesTitle( lpctstr pszPath ) // static { - ADDTOCALLSTACK("CSFile::GetFilesTitle"); + //ADDTOCALLSTACK("CSFile::GetFilesTitle"); // strrchr size_t len = strlen(pszPath); while ( len > 0 ) @@ -396,18 +396,18 @@ lpctstr CSFile::GetFilesTitle( lpctstr pszPath ) // static lpctstr CSFile::_GetFileTitle() const { - ADDTOCALLSTACK("CFile::_GetFileTitle"); + //ADDTOCALLSTACK("CFile::_GetFileTitle"); return CSFile::GetFilesTitle(_strFileName.GetBuffer()); } lpctstr CSFile::GetFileTitle() const { - ADDTOCALLSTACK("CFile::GetFileTitle"); + //ADDTOCALLSTACK("CFile::GetFileTitle"); return CSFile::GetFilesTitle( GetFilePath() ); } lpctstr CSFile::GetFilesExt( lpctstr pszName ) // static { - ADDTOCALLSTACK("CSFile::GetFilesExt"); + //ADDTOCALLSTACK("CSFile::GetFilesExt"); // get the EXTension including the . size_t len = strlen( pszName ); while ( len > 0 ) @@ -425,7 +425,7 @@ lpctstr CSFile::GetFilesExt( lpctstr pszName ) // static lpctstr CSFile::GetFileExt() const { - ADDTOCALLSTACK("CSFile::GetFileExt"); + //ADDTOCALLSTACK("CSFile::GetFileExt"); // get the EXTension including the . return GetFilesExt(GetFilePath()); } diff --git a/src/common/sphere_library/ssorted_vector.h b/src/common/sphere_library/ssorted_vector.h index 8495971be..8a3806ef8 100644 --- a/src/common/sphere_library/ssorted_vector.h +++ b/src/common/sphere_library/ssorted_vector.h @@ -65,6 +65,14 @@ namespace sl return base_type::emplace(base_type::begin() + _pos, std::move(_obj)); } + template + _Type& emplace_unsorted(_ValType&&... value) + { + //_Type * _obj = new _Type(std::forward<_ValType>(value)...); + _Type _obj(std::forward<_ValType>(value)...); + return base_type::emplace_back(std::move(_obj)); + } + inline iterator insert(_Type const& value) { return this->emplace(value); } @@ -73,6 +81,13 @@ namespace sl } //iterator insert(const size_Typepe _Count, const _Typepe& value) = delete; + inline _Type& insert_unsorted(_Type const& value) { + return this->emplace_unsorted(value); + } + inline _Type& insert_unsorted(_Type&& value) { + return this->emplace_unsorted(std::move(value)); + } + inline void remove_index(const size_t index) { base_type::erase(base_type::begin() + index); } diff --git a/src/game/CRegionBase.cpp b/src/game/CRegionBase.cpp index d04911925..c8e3f4728 100644 --- a/src/game/CRegionBase.cpp +++ b/src/game/CRegionBase.cpp @@ -114,13 +114,32 @@ bool CRegionBase::IsOverlapped( const CRectMap & rect ) const noexcept if ( !m_rectUnion.IsOverlapped(rect) ) return false; - const size_t iQty = m_Rects.size(); + const uint iQty = (uint)m_Rects.size(); if ( iQty <= 0 ) return true; - for ( size_t i = 0; i < iQty; ++i ) + + const int left = rect.m_left; + const int right = rect.m_right; + const int top = rect.m_top; + const int bottom = rect.m_bottom; + + for ( uint i = 0; i < iQty; ++i ) { + CRect const& r = m_Rects[i]; + + // Avoid the cost of the IsOverlapped function call and just slap the content here. + // Also, reorder conditions to prioritize early exits for non-overlapping rectangles. + if ( + (right > r.m_left) && // Left edge of rect is to the left of this rect's right edge + (r.m_right > left) && // Right edge of rect is to the right of this rect's left edge + (bottom > r.m_top) && // Top edge of rect is above this rect's bottom edge + (r.m_bottom > top) // Bottom edge of rect is below this rect's top edge + ) + return true; + /* if ( rect.IsOverlapped(m_Rects[i])) return true; + */ } return false; } diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index de4cf768c..e3687392f 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -4,6 +4,7 @@ #include "../common/resource/sections/CRandGroupDef.h" #include "../common/resource/sections/CRegionResourceDef.h" #include "../common/resource/sections/CResourceNamedDef.h" +#include "../common/sphere_library/CSFileList.h" #include "../common/sphere_library/CSRand.h" //#include "../common/CException.h" // included in the precompiled header //#include "../common/CExpression.h" // included in the precompiled header @@ -3010,7 +3011,216 @@ uint CServerConfig::GetPacketFlag( bool bCharlist, RESDISPLAY_VERSION res, uchar //************************************************************* -bool CServerConfig::LoadResourceSection( CScript * pScript ) +CResourceScript * CServerConfig::GetResourceFile( size_t i ) +{ + if ( ! m_ResourceFiles.IsValidIndex(i) ) + return nullptr; // All resource files we need to get blocks from later. + return m_ResourceFiles[i]; +} + +CResourceScript * CServerConfig::FindResourceFile( lpctstr pszPath ) +{ + ADDTOCALLSTACK("CResourceHolder::FindResourceFile"); + // Just match the titles ( not the whole path) + + lpctstr pszTitle = CScript::GetFilesTitle( pszPath ); + + for ( size_t i = 0; ; ++i ) + { + CResourceScript * pResFile = GetResourceFile(i); + if ( pResFile == nullptr ) + break; + lpctstr pszTitle2 = pResFile->GetFileTitle(); + if ( ! strcmpi( pszTitle2, pszTitle )) + return pResFile; + } + return nullptr; +} + +bool CServerConfig::OpenResourceFind( CScript &s, lpctstr pszFilename, bool fCritical ) +{ + ADDTOCALLSTACK("CServerConfig::OpenResourceFind"); + // Open a single resource script file. + // Look in the specified path. + + if ( pszFilename == nullptr ) + pszFilename = s.GetFilePath(); + + // search the local dir or full path first. + if (CSFile::FileExists(pszFilename)) + { + if (s.Open(pszFilename, OF_READ | OF_NONCRIT)) + return true; + if (!fCritical) + return false; + } + + // next, check the script file path + CSString sPathName = CSFile::GetMergedFileName( m_sSCPBaseDir, pszFilename ); + if (CSFile::FileExists(sPathName)) + { + if (s.Open(sPathName, OF_READ | OF_NONCRIT)) + return true; + } + + // finally, strip the directory and re-check script file path + lpctstr pszTitle = CSFile::GetFilesTitle(pszFilename); + sPathName = CSFile::GetMergedFileName( m_sSCPBaseDir, pszTitle ); + if (CSFile::FileExists(sPathName)) + { + return s.Open(sPathName, OF_READ); + } + + g_Log.Event(LOGM_INIT|LOGL_ERROR, "Can't find file '%s' in any of the expected paths!.\n", pszFilename); + return false; +} + + +CResourceScript * CServerConfig::AddResourceFile( lpctstr pszName ) +{ + ADDTOCALLSTACK("CResourceHolder::AddResourceFile"); + ASSERT(pszName != nullptr); + // Is this really just a dir name ? + + if (strlen(pszName) >= SPHERE_MAX_PATH) + throw CSError(LOGL_ERROR, 0, "Filename too long!"); + + tchar szName[SPHERE_MAX_PATH]; + Str_CopyLimitNull(szName, pszName, sizeof(szName)); + + tchar szTitle[SPHERE_MAX_PATH]; + lpctstr ptcTitle = CScript::GetFilesTitle(szName); + ASSERT_ALWAYS(strlen(ptcTitle) < sizeof(szTitle)); + Str_CopyLimitNull(szTitle, ptcTitle, sizeof(szTitle)); + + if ( szTitle[0] == '\0' ) + { + AddResourceDir( pszName ); + return nullptr; + } + + lpctstr pszExt = CScript::GetFilesExt( szTitle ); + if ( pszExt == nullptr ) + { + // No file extension provided, so append .scp to the filename + Str_ConcatLimitNull( szName, SPHERE_SCRIPT_EXT, sizeof(szName) ); + Str_ConcatLimitNull( szTitle, SPHERE_SCRIPT_EXT, sizeof(szTitle) ); + } + + if ( ! strnicmp( szTitle, SPHERE_FILE "tables", strlen(SPHERE_FILE "tables"))) + { + // Don't dupe this. + return nullptr; + } + + // Try to prevent dupes + CResourceScript * pNewRes = FindResourceFile(szTitle); + if ( pNewRes ) + return pNewRes; + + // Find correct path + + + pNewRes = new CResourceScript(); + if (! OpenResourceFind(static_cast(*pNewRes), szName)) + { + delete pNewRes; + return nullptr; + } + + m_ResourceFiles.emplace_back(pNewRes); + pNewRes->m_iResourceFileIndex = int(m_ResourceFiles.size() -1); + return pNewRes; +} + +void CServerConfig::AddResourceDir( lpctstr pszDirName ) +{ + ADDTOCALLSTACK("CServerConfig::AddResourceDir"); + if ( pszDirName[0] == '\0' ) + return; + + CSString sFilePath = CSFile::GetMergedFileName( pszDirName, "*" SPHERE_SCRIPT_EXT ); + + CSFileList filelist; + int iRet = filelist.ReadDir( sFilePath, false ); + if ( iRet < 0 ) + { + // also check script file path + sFilePath = CSFile::GetMergedFileName(m_sSCPBaseDir, sFilePath.GetBuffer()); + + iRet = filelist.ReadDir( sFilePath, true ); + if ( iRet < 0 ) + { + DEBUG_ERR(( "DirList=%d for '%s'\n", iRet, pszDirName )); + return; + } + } + + if ( iRet <= 0 ) // no files here. + return; + + CSStringListRec * psFile = filelist.GetHead(), *psFileNext = nullptr; + for ( ; psFile; psFile = psFileNext ) + { + psFileNext = psFile->GetNext(); + sFilePath = CSFile::GetMergedFileName( pszDirName, *psFile ); + AddResourceFile( sFilePath ); + } +} + +bool CServerConfig::LoadResources( CResourceScript * pScript, bool fAddSorted ) +{ + ADDTOCALLSTACK("CServerConfig::LoadResources"); + // Open the file then load it. + if ( pScript == nullptr ) + return false; + + if ( ! pScript->Open()) + { + g_Log.Event(LOGL_CRIT|LOGM_INIT, "[RESOURCES] '%s' not found...\n", pScript->GetFilePath()); + return false; + } + + g_Log.Event(LOGM_INIT, "Loading %s\n", pScript->GetFilePath()); + + LoadResourcesOpen( pScript, fAddSorted ); + pScript->Close(); + pScript->CloseForce(); + return true; +} + +CResourceScript * CServerConfig::LoadResourcesAdd( lpctstr pszNewFileName ) +{ + ADDTOCALLSTACK("CServerConfig::LoadResourcesAdd"); + // Make sure this is added to my list of resource files + // And load it now. + + CResourceScript * pScript = AddResourceFile( pszNewFileName ); + if ( ! LoadResources(pScript, true) ) + return nullptr; + return pScript; +} + +void CServerConfig::LoadResourcesOpen( CScript * pScript, bool fAddSorted ) +{ + ADDTOCALLSTACK("CServerConfig::LoadResourcesOpen"); + // Load an already open resource file. + + ASSERT(pScript); + ASSERT( pScript->HasCache() ); + + int iSections = 0; + while ( pScript->FindNextSection() ) + { + LoadResourceSection( pScript, fAddSorted ); + ++iSections; + } + + if ( ! iSections ) + DEBUG_WARN(( "No resource sections in '%s'\n", pScript->GetFilePath())); +} + +bool CServerConfig::LoadResourceSection( CScript * pScript, bool fInsertSorted ) { ADDTOCALLSTACK("CServerConfig::LoadResourceSection"); // Index or read any resource sections we know how to handle. @@ -3131,6 +3341,9 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) pListBase->r_LoadVal(pScript->GetArgStr()); } + auto _resHashAddFunction = fInsertSorted ? &CResourceHash::AddSortKey : &CResourceHash::AddUnsortedKey; + #define RESHASH_ADD (m_ResHash.* _resHashAddFunction) + switch ( restype ) { case RES_SPHERE: @@ -3436,7 +3649,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) CResourceScript* pLinkResScript = dynamic_cast(pScript); if (pLinkResScript != nullptr) pNewLink->SetLink(pLinkResScript); // So later i can retrieve m_iResourceFileIndex and m_iLineNum from the CResourceScript - m_ResHash.AddSortKey( rid, pNewLink ); + RESHASH_ADD( rid, pNewLink ); } ASSERT(pScript); @@ -3472,7 +3685,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) CResourceScript* pLinkResScript = dynamic_cast(pScript); if (pLinkResScript != nullptr) pNewLink->SetLink(pLinkResScript); // So later i can retrieve m_iResourceFileIndex and m_iLineNum from the CResourceScript - m_ResHash.AddSortKey( rid, pNewLink ); + RESHASH_ADD( rid, pNewLink ); } break; case RES_DIALOG: @@ -3490,7 +3703,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) CResourceScript* pLinkResScript = dynamic_cast(pScript); if (pLinkResScript != nullptr) pNewLink->SetLink(pLinkResScript); // So later i can retrieve m_iResourceFileIndex and m_iLineNum from the CResourceScript - m_ResHash.AddSortKey( rid, pNewLink ); + RESHASH_ADD( rid, pNewLink ); } break; @@ -3509,7 +3722,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) CResourceScript* pLinkResScript = dynamic_cast(pScript); if (pLinkResScript != nullptr) pNewLink->SetLink(pLinkResScript); // So later i can retrieve m_iResourceFileIndex and m_iLineNum from the CResourceScript - m_ResHash.AddSortKey( rid, pNewLink ); + RESHASH_ADD( rid, pNewLink ); } { CScriptLineContext LineContext = pScript->GetContext(); @@ -3541,7 +3754,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) { pNewDef = pRegion; ASSERT(pNewDef); - m_ResHash.AddSortKey( rid, pRegion ); + RESHASH_ADD( rid, pRegion ); // if it's old style but has a defname, it's already set via r_Load, // so this will do nothing, which is good // if ( !fNewStyleDef ) @@ -3573,7 +3786,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) } else { - m_ResHash.AddSortKey( rid, pRegion ); + RESHASH_ADD( rid, pRegion ); // if it's old style but has a defname, it's already set via r_Load, // so this will do nothing, which is good // if ( !fNewStyleDef ) @@ -3598,7 +3811,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) CResourceScript* pLinkResScript = dynamic_cast(pScript); if (pLinkResScript != nullptr) pNewLink->SetLink(pLinkResScript); // So later i can retrieve m_iResourceFileIndex and m_iLineNum from the CResourceScript - m_ResHash.AddSortKey( rid, pNewLink ); + RESHASH_ADD( rid, pNewLink ); } { CScriptLineContext LineContext = pScript->GetContext(); @@ -3623,7 +3836,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) CResourceScript* pLinkResScript = dynamic_cast(pScript); if (pLinkResScript != nullptr) pNewLink->SetLink(pLinkResScript); // So later i can retrieve m_iResourceFileIndex and m_iLineNum from the CResourceScript - m_ResHash.AddSortKey(rid, pNewLink); + RESHASH_ADD(rid, pNewLink); } } { @@ -3647,7 +3860,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) CResourceScript* pLinkResScript = dynamic_cast(pScript); if (pLinkResScript != nullptr) pNewLink->SetLink(pLinkResScript); // So later i can retrieve m_iResourceFileIndex and m_iLineNum from the CResourceScript - m_ResHash.AddSortKey( rid, pNewLink ); + RESHASH_ADD( rid, pNewLink ); } { CScriptLineContext LineContext = pScript->GetContext(); @@ -3683,7 +3896,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) CResourceScript* pLinkResScript = dynamic_cast(pScript); if (pLinkResScript != nullptr) pNewLink->SetLink(pLinkResScript); // So later i can retrieve m_iResourceFileIndex and m_iLineNum from the CResourceScript - m_ResHash.AddSortKey( rid, pNewLink ); + RESHASH_ADD( rid, pNewLink ); } break; @@ -3809,7 +4022,7 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) pResDef = new CItemTypeDef( ridnew ); ASSERT(pResDef); pResDef->SetResourceName( ptcName ); - m_ResHash.AddSortKey( ridnew, pResDef ); + RESHASH_ADD( ridnew, pResDef ); } } @@ -4119,8 +4332,7 @@ CResourceID CServerConfig::ResourceGetNewID( RES_TYPE restype, lpctstr pszName, break; } - - int iIndex; + int iIndex; if ( pszName ) { if ( pszName[0] == '\0' ) // absence of resourceid = index 0 @@ -4666,7 +4878,7 @@ bool CServerConfig::LoadIni(bool fTest) return false; } - LoadResourcesOpen(&m_scpIni); + LoadResourcesOpen(&m_scpIni, true); m_scpIni.Close(); m_scpIni.CloseForce(); @@ -4700,7 +4912,7 @@ bool CServerConfig::LoadCryptIni( void ) return false; } - LoadResourcesOpen(&m_scpCryptIni); + LoadResourcesOpen(&m_scpCryptIni, true); m_scpCryptIni.Close(); m_scpCryptIni.CloseForce(); @@ -4822,7 +5034,7 @@ bool CServerConfig::Load( bool fResync ) } g_Log.Event(LOGL_EVENT|LOGM_INIT, "Loading table definitions file (" SPHERE_FILE "tables" SPHERE_SCRIPT_EXT ")...\n"); - LoadResourcesOpen(&m_scpTables); + LoadResourcesOpen(&m_scpTables, true); m_scpTables.Close(); } else @@ -4857,14 +5069,23 @@ bool CServerConfig::Load( bool fResync ) if ( !pResFile ) break; - if ( !fResync ) - LoadResources( pResFile ); - else + if ( !fResync ) + { + // It's the startup load, sort everything just once at the end? + // TODO: not a good idea for now, because we might reference a resource while loading another resource, + // at the current state. + // Also, is it worth it by a performance cost? It looks like the greatest part of the cpu time is consumed in unique_ptr moving. + LoadResources( pResFile, true /*false*/ ); + } + else pResFile->ReSync(); g_Serv.PrintPercent( (size_t)(j + 1), count); } + //if (!fResync) + // m_ResHash.SortStep(); + g_ExprGlobals.mtEngineLockedWriter()->UpdateDefMsgDependentData(); // Now that we have parsed every script, we can end the configuration of some resources... @@ -4920,7 +5141,7 @@ bool CServerConfig::Load( bool fResync ) // must have at least 1 skill class. CSkillClassDef * pSkillClass = new CSkillClassDef( CResourceID( RES_SKILLCLASS )); ASSERT(pSkillClass); - m_ResHash.AddSortKey( CResourceID( RES_SKILLCLASS, 0 ), pSkillClass ); + m_ResHash.AddSortKey( CResourceID( RES_SKILLCLASS, 0 ), pSkillClass ); } if ( !fResync ) diff --git a/src/game/CServerConfig.h b/src/game/CServerConfig.h index 499b6ef93..534b6b677 100644 --- a/src/game/CServerConfig.h +++ b/src/game/CServerConfig.h @@ -654,7 +654,6 @@ extern class CServerConfig : public CResourceHolder CResourceHashArray m_WebPages; // These can be linked back to the script. private: - CResourceID ResourceGetNewID( RES_TYPE restype, lpctstr pszName, CVarDefContNum ** ppVarNum, bool fNewStyleDef ); char m_iniDirectory[SPHERE_MAX_PATH]; public: @@ -665,6 +664,12 @@ extern class CServerConfig : public CResourceHolder CServerConfig(const CServerConfig& copy) = delete; CServerConfig& operator=(const CServerConfig& other) = delete; +private: + CResourceID ResourceGetNewID( RES_TYPE restype, lpctstr pszName, CVarDefContNum ** ppVarNum, bool fNewStyleDef ); + + CResourceScript * AddResourceFile( lpctstr pszName ); + void AddResourceDir( lpctstr pszDirName ); + public: virtual bool r_LoadVal( CScript &s ) override; virtual bool r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc = nullptr, bool fNoCallParent = false, bool fNoCallChildren = false ) override; @@ -706,14 +711,25 @@ extern class CServerConfig : public CResourceHolder void Unload( bool fResync ); void _OnTick( bool fNow ); + // TODO: add doxygen-style comments + CResourceScript * FindResourceFile( lpctstr pszTitle ); + CResourceScript * LoadResourcesAdd( lpctstr pszNewName ); + + void LoadResourcesOpen( CScript * pScript, bool fAddSorted ); + bool LoadResources(CResourceScript * pScript, bool fAddSorted); + + CResourceScript * GetResourceFile( size_t i ); + + bool OpenResourceFind( CScript &s, lpctstr pszFilename, bool fCritical = true ); + /** * @brief Loads resource section ([SKILL ], [SPELL ], [CHARDEF ]...). * - * @param [in,out] pScript If non-null, the script. + * @param pScript to be loaded. * * @return true if it succeeds, false if it fails. */ - virtual bool LoadResourceSection( CScript * pScript ) override; + bool LoadResourceSection( CScript * pScript, bool fInsertSorted); /** * @brief Sort all spells in order. diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index aa55012af..b74ab9f51 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -1333,7 +1333,7 @@ bool CWorld::LoadFile( lpctstr pszLoadName, bool fError ) // Load world from scr try { - g_Cfg.LoadResourceSection(&s); + g_Cfg.LoadResourceSection(&s, true); // pass true, it shouldn't really touch g_Cfg.m_ResHash } catch ( const CSError& e ) { diff --git a/src/game/CWorldImport.cpp b/src/game/CWorldImport.cpp index 821ac1ffc..92bb658b7 100644 --- a/src/game/CWorldImport.cpp +++ b/src/game/CWorldImport.cpp @@ -249,7 +249,7 @@ bool CImportFile::ImportSCP( CScript & s, word wModeFlags ) CheckLast(); if ( s.IsSectionType( "ACCOUNT" )) { - g_Cfg.LoadResourceSection( &s ); + g_Cfg.LoadResourceSection( &s, true ); continue; } else if ( s.IsSectionType( "WORLDCHAR" ) || s.IsSectionType("WC")) From 20c97293aefd28ac3b632518faf515b02b90b97e Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 5 Jun 2025 13:31:20 +0200 Subject: [PATCH 055/112] Removed assertion in CChar::_OnTick and added debug message to monitor Chars unable to tick but placed in the ticking list. --- src/common/sphere_library/ssorted_vector.h | 31 +++++++++++----------- src/game/chars/CCharAct.cpp | 10 +++++-- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/common/sphere_library/ssorted_vector.h b/src/common/sphere_library/ssorted_vector.h index 8a3806ef8..731d0683e 100644 --- a/src/common/sphere_library/ssorted_vector.h +++ b/src/common/sphere_library/ssorted_vector.h @@ -8,11 +8,12 @@ // Sphere library namespace sl { - consteval - size_t scont_bad_index() noexcept { + consteval size_t scont_bad_index() noexcept { return size_t(-1); } + // -- + template > class sorted_vector : public std::vector<_Type> { @@ -49,14 +50,14 @@ namespace sl void push_back(const bool& value) = delete; template - iterator emplace(const_iterator itWhere, _ValType&&... value) = delete; + constexpr iterator emplace(const_iterator itWhere, _ValType&&... value) = delete; - iterator insert(const_iterator itWhere, _Type const& value) = delete; - iterator insert(const_iterator itWhere, _Type&& value) = delete; - iterator insert(const_iterator itWhere, const size_t _Count, _Type const& value) = delete; + constexpr iterator insert(const_iterator itWhere, _Type const& value) = delete; + constexpr iterator insert(const_iterator itWhere, _Type&& value) = delete; + constexpr iterator insert(const_iterator itWhere, const size_t _Count, _Type const& value) = delete; template - iterator emplace(_ValType&&... value) + constexpr iterator emplace(_ValType&&... value) { //_Type * _obj = new _Type(std::forward<_ValType>(value)...); _Type _obj(std::forward<_ValType>(value)...); @@ -66,30 +67,30 @@ namespace sl } template - _Type& emplace_unsorted(_ValType&&... value) + constexpr _Type& emplace_unsorted(_ValType&&... value) { //_Type * _obj = new _Type(std::forward<_ValType>(value)...); _Type _obj(std::forward<_ValType>(value)...); return base_type::emplace_back(std::move(_obj)); } - inline iterator insert(_Type const& value) { + constexpr inline iterator insert(_Type const& value) { return this->emplace(value); } - inline iterator insert(_Type&& value) { + constexpr inline iterator insert(_Type&& value) { return this->emplace(std::move(value)); } - //iterator insert(const size_Typepe _Count, const _Typepe& value) = delete; + //constexpr iterator insert(const size_Typepe _Count, const _Typepe& value) = delete; - inline _Type& insert_unsorted(_Type const& value) { + constexpr inline _Type& insert_unsorted(_Type const& value) { return this->emplace_unsorted(value); } - inline _Type& insert_unsorted(_Type&& value) { + constexpr inline _Type& insert_unsorted(_Type&& value) { return this->emplace_unsorted(std::move(value)); } - inline void remove_index(const size_t index) { - base_type::erase(base_type::begin() + index); + constexpr inline void remove_index(const size_t index) { + base_type::erase(base_type::cbegin() + index); } diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index e509d49bf..33d79d3ac 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -5892,17 +5892,23 @@ bool CChar::_OnTick() // it should also be removed from the list (it happens in _GoSleep()). ASSERT(!_IsSleeping()); +//#ifdef _DEBUG + g_Log.EventDebug("[Temporary msg] Char '%s' (UID=0x%" PRIx32 ") at P=%s can't tick but is in the ticking list.\n", + GetName(), GetUID().GetObjUID(), GetTopPoint().WriteUsed()); +//#endif + if (GetTopSector()->IsSleeping() && !g_Rand.Get16ValFast(15)) { // Do not make the char sleep right when it enters a sleeping sector. Doing this // will lead to an accumulation of npcs at the edge of the new sector. + g_Log.EventDebug("Sent to sleep (random), to be awaken alongside its sector.\n"); + _SetTimeout(1); //Make it tick after sector's awakening. _GoSleep(); - return true; } + return true; } - ASSERT(!_IsSleeping()); EXC_SET_BLOCK("Components Tick"); /* From 6996ab45f3dffb0142a3e23175f67a102702d633 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 5 Jun 2025 13:52:34 +0200 Subject: [PATCH 056/112] More detailed debug message --- src/game/chars/CCharAct.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 33d79d3ac..462871d28 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -5885,30 +5885,43 @@ bool CChar::_OnTick() return true; } - if (!_TickableState()) + const bool fTickableState = _TickableState(); + const bool fSleeping = _IsSleeping(); + +//#ifdef _DEBUG + if (!fTickableState || fSleeping) + { + g_Log.EventDebug("[Temporary msg] Char '%s' (UID=0x%" PRIx32 ") at P=%s is in the ticking list with unusual TickableState=%d, SleepingState=%d.\n", + GetName(), GetUID().GetObjUID(), + GetTopPoint().WriteUsed(), + (int)fTickableState, (int)fSleeping + ); + } +//#endif + + if (!fTickableState) { // It can happen that i'm in the ticking list, but for various reasons right now i'm in a non-tickable state. // Among the reasons why i can't tick, though, there cannot be being in a sleeping state: when a char goes into sleeping state // it should also be removed from the list (it happens in _GoSleep()). - ASSERT(!_IsSleeping()); - -//#ifdef _DEBUG - g_Log.EventDebug("[Temporary msg] Char '%s' (UID=0x%" PRIx32 ") at P=%s can't tick but is in the ticking list.\n", - GetName(), GetUID().GetObjUID(), GetTopPoint().WriteUsed()); -//#endif + ASSERT(!fSleeping); if (GetTopSector()->IsSleeping() && !g_Rand.Get16ValFast(15)) { // Do not make the char sleep right when it enters a sleeping sector. Doing this // will lead to an accumulation of npcs at the edge of the new sector. +//#ifdef _DEBUG g_Log.EventDebug("Sent to sleep (random), to be awaken alongside its sector.\n"); +//#endif _SetTimeout(1); //Make it tick after sector's awakening. _GoSleep(); } return true; } + if (fSleeping) + return true; EXC_SET_BLOCK("Components Tick"); /* From 17a1cb2d7d2b3b41cdf9f9675cf2384ec498af50 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 5 Jun 2025 20:10:54 +0200 Subject: [PATCH 057/112] Fixed regression for hex number parsing, added unit tests for number parsing. --- src/common/CExpression.cpp | 73 ++++++---- src/common/CExpression.h | 2 +- tests/CMakeSources.cmake | 2 + tests/src/t_num_parsing.cpp | 281 ++++++++++++++++++++++++++++++++++++ 4 files changed, 331 insertions(+), 27 deletions(-) create mode 100644 tests/src/t_num_parsing.cpp diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index 0338df2dc..aa12d3f05 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -661,42 +661,51 @@ llong CExpression::GetSingle(lpctstr & refStrExpr ) refStrExpr += 2; goto try_dec; } - refStrExpr += 1; - // TODO: gracefully handle integer overflows, maybe printing a warning message. + // Skip the leading '0' + refStrExpr += 1; - lpctstr pStart = refStrExpr; - ullong val = 0; - ushort ndigits = 0; + uint64 uiVal = 0; + uint uiDigits = 0; while (true) { tchar ch = *refStrExpr; if ( IsDigit(ch) ) { ch -= '0'; + ++ uiDigits; } else { ch = static_cast(tolower(ch)); if ( ch > 'f' || ch < 'a' ) { - if ( ch == '.' && pStart[0] != '0' ) // ok i'm confused. it must be decimal. + if ( ch == '.' && ptcStartingString[0] != '0' ) // ok i'm confused. it must be decimal. { - refStrExpr = pStart; + refStrExpr = ptcStartingString; goto try_dec; } break; } - ch -= 'a' - 10; + ch -= 'a' - 10; + ++ uiDigits; + } + + if (uiDigits > 16) + { + g_Log.EventWarn("Hexadecimal value parsing will overflow: %s.\n", ptcStartingString); + return -1; } - val *= 0x10; - val += ch; + + uiVal = (uiVal << 4ull) | ch; // Equivalent to 'val *= 0x10; val += ch;' + //val *= 0x10; + //val += ch; + ++ refStrExpr; - ++ ndigits; - } - if (ndigits <= 8) - return (llong)(int)val; - return (llong)val; + } + if (uiDigits <= 8) + return (int64)(int32)uiVal; + return (int64)uiVal; } /* // We could just use this, but it doesn't "eat" the string pointer. @@ -714,20 +723,32 @@ llong CExpression::GetSingle(lpctstr & refStrExpr ) else if ( refStrExpr[0] == '.' || IsDigit(refStrExpr[0]) ) { // A decimal number - - // TODO: gracefully handle integer overflows, maybe printing a warning message. try_dec: - llong iVal = 0; - for ( ; ; ++refStrExpr ) - { - if ( *refStrExpr == '.' ) + constexpr int64 kiMaxBeforeMul = (INT64_MAX - 9) / 10; + + int64 iVal = 0; + for ( ; ; ++refStrExpr ) + { + int c = *refStrExpr; // character + if ( c == '.' ) continue; // just skip this. - if ( ! IsDigit(*refStrExpr) ) + if ( ! IsDigit(c) ) break; + + c = c - '0'; // digit + if (iVal > kiMaxBeforeMul) + { + if ((iVal > (INT64_MAX - c) / 10)) + { + g_Log.EventWarn("Decimal value parsing will overflow: %s.\n", ptcStartingString); + return -1; + } + } + iVal *= 10; - iVal += (llong)(*refStrExpr) - '0'; - } - return iVal; + iVal += (llong)(*refStrExpr) - '0'; + } + return iVal; } else if ( ! _ISCSYMF(refStrExpr[0]) ) { @@ -746,7 +767,7 @@ else if ( ! _ISCSYMF(refStrExpr[0]) ) case '+': ++refStrExpr; break; - case '-': + case '-': ++refStrExpr; return -GetSingle( refStrExpr ); case '~': // Bitwise not. diff --git a/src/common/CExpression.h b/src/common/CExpression.h index 1b16d6488..58f7bad71 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -217,7 +217,7 @@ class CExpression }; std::unique_ptr _pBufs; - short _iGetVal_Reentrant; + int _iGetVal_Reentrant; public: static const char *m_sClassName; diff --git a/tests/CMakeSources.cmake b/tests/CMakeSources.cmake index 0181ef5b6..4ff975819 100644 --- a/tests/CMakeSources.cmake +++ b/tests/CMakeSources.cmake @@ -1,5 +1,7 @@ set(SPHERE_TESTS_SOURCES + tests/src/t_num_parsing.cpp tests/src/t_CPointBase.cpp + #tests/src/t_CUOClientVersion.cpp ) source_group(tests FILES ${SPHERE_TESTS_SOURCES}) diff --git a/tests/src/t_num_parsing.cpp b/tests/src/t_num_parsing.cpp new file mode 100644 index 000000000..11fc9def8 --- /dev/null +++ b/tests/src/t_num_parsing.cpp @@ -0,0 +1,281 @@ +#include +#include "../../src/common/CExpression.h" +#include // toupper + +#ifndef MSVC_COMPILER +#define ATTR_NO_UBSAN __attribute__((no_sanitize("undefined"))) +#else +#define ATTR_NO_UBSAN __declspec(no_sanitize_address) +#endif + + +namespace { namespace ref_impl +{ + + // From 0.56d, known to be faulty... Copying it here just for historical reasons. + int ahextoi(lpctstr pszArgs) // convert hex string to int + { + // Unfortunately the library func can't handle UINT_MAX + //tchar *pszEnd; return strtol(s, &pszEnd, 16); + + if ( !pszArgs || !*pszArgs ) + return 0; + + GETNONWHITESPACE(pszArgs); + + bool fHex = false; + if ( *pszArgs == '0' ) + { + if ( *++pszArgs != '.' ) + fHex = true; + --pszArgs; + } + + int iVal = 0; + for (;;) + { + tchar ch = static_cast(toupper(*pszArgs)); + if ( IsDigit(ch) ) + ch -= '0'; + else if ( fHex && (ch >= 'A') && (ch <= 'F') ) + ch -= 'A' - 10; + else if ( !fHex && (ch == '.') ) + { + ++pszArgs; + continue; + } + else + break; + + iVal *= (fHex ? 0x10 : 10); + iVal += ch; + ++pszArgs; + } + return iVal; + } + + // From 0.56d, known to be faulty... Copying it here just for historical reasons. + int64 ahextoi64(lpctstr pszArgs) // convert hex string to int64 + { + if ( !pszArgs || !*pszArgs ) + return 0; + + GETNONWHITESPACE(pszArgs); + + bool fHex = false; + if ( *pszArgs == '0' ) + { + if ( *++pszArgs != '.' ) + fHex = true; + --pszArgs; + } + + int64 iVal = 0; + for (;;) + { + tchar ch = static_cast(toupper(*pszArgs)); + if ( IsDigit(ch) ) + ch -= '0'; + else if ( fHex && (ch >= 'A') && (ch <= 'F') ) + ch -= 'A' - 10; + else if ( !fHex && (ch == '.') ) + { + ++pszArgs; + continue; + } + else + break; + + iVal *= (fHex ? 0x10 : 10); + iVal += ch; + ++pszArgs; + } + return iVal; + } + + + // Part of CExpression::GetSingle that parses numerical strings. + int64 GetSingle_num(lpctstr &pszArgs) ATTR_NO_UBSAN + { + // Parse just a single expression without any operators or ranges. + GETNONWHITESPACE(pszArgs); + + if ( pszArgs[0] == '.' ) + ++pszArgs; + + if ( pszArgs[0] == '0' ) // leading '0' = hex value + { + // Hex value + if ( pszArgs[1] == '.' ) // leading '0.' = decimal value + { + pszArgs += 2; + goto try_dec; + } + + lpctstr pStart = pszArgs; + int64 iVal = 0; + for (;;) + { + tchar ch = *pszArgs; + if ( IsDigit(ch) ) + ch -= '0'; + else + { + ch = static_cast(tolower(ch)); + if ( (ch > 'f') || (ch < 'a') ) + { + if ( (ch == '.') && (pStart[0] != '0') ) // ok I'm confused, it must be decimal + { + pszArgs = pStart; + goto try_dec; + } + break; + } + ch -= 'a' - 10; + } + iVal *= 0x10; + iVal += ch; + ++pszArgs; + } + return iVal; + } + else if ( (pszArgs[0] == '.') || IsDigit(pszArgs[0]) ) + { + // Decimal value + try_dec: + int64 iVal = 0; + for ( ; ; ++pszArgs ) + { + if ( *pszArgs == '.' ) + continue; // just skip this + if ( !IsDigit(*pszArgs) ) + break; + iVal *= 10; + iVal += static_cast(*pszArgs) - '0'; + } + return iVal; + } + else if ( ! _ISCSYMF(pszArgs[0]) ) + { + //#pragma region maths // MSVC specific + // some sort of math op ? + + switch ( pszArgs[0] ) + { + /* + case '{': + ++pszArgs; + return GetRangeNumber( pszArgs ); + case '[': + case '(': // Parse out a sub expression. + ++pszArgs; + return GetVal( pszArgs ); + */ + case '+': + ++pszArgs; + break; + case '-': + ++pszArgs; + return -GetSingle_num( pszArgs ); + case '~': // Bitwise not. + ++pszArgs; + return ~GetSingle_num( pszArgs ); + case '!': // boolean not. + ++pszArgs; + if ( pszArgs[0] == '=' ) // odd condition such as (!=x) which is always true of course. + { + ++pszArgs; // so just skip it. and compare it to 0 + return GetSingle_num( pszArgs ); + } + return !GetSingle_num( pszArgs ); + case ';': // seperate field. + case ',': // seperate field. + case '\0': + return 0; + } + } + throw std::runtime_error("reached an unexpected point?"); + } +}} + + +TEST_CASE("Numerical string parsing")\ +// clazy:exclude=non-pod-global-static +{ + lpctstr in_str_consumable; + llong out_cur_impl, out_ref_impl; + + SUBCASE("Decimal integer") + { + constexpr std::pair test_data_ref_ok[] + { + {"0", 0}, + {"-1", -1}, + {"100", 100}, + {"2147483647", INT32_MAX}, + {"-2147483647", -INT32_MAX}, + {"-2147483648", INT32_MIN}, + {"4294967295", UINT32_MAX}, + {"9223372036854775807", INT64_MAX}, + {"-9223372036854775807", -INT64_MAX}, + //{"-9223372036854775808", INT64_MIN} + }; + + for (auto const& p : test_data_ref_ok) + { + in_str_consumable = p.first, out_cur_impl = Exp_GetLLVal(in_str_consumable); + CHECK_EQ(out_cur_impl, p.second); + + in_str_consumable = p.first, out_ref_impl = ref_impl::GetSingle_num(in_str_consumable); + CHECK_EQ(out_cur_impl, out_ref_impl); + } + + // With this data the old reference implementation failed + constexpr std::pair test_data_ref_fail[] + { + {"9223372036854775808", /* INT64_MAX + 1 */ -1} // Overflow + }; + + for (auto const& p : test_data_ref_fail) + { + in_str_consumable = p.first, out_cur_impl = Exp_GetLLVal(in_str_consumable); + CHECK_EQ(out_cur_impl, p.second); + } + + } + + SUBCASE("Hexadecimal") + { + constexpr std::pair test_data_ref_ok[] + { + {"0", 0}, + {"07FFFFFFF", INT32_MAX}, + {"0100000000", 0x100000000}, + {"07FFFFFFFFFFFFFFF", 0x7FFFFFFFFFFFFFFF /* INT64_MAX */}, + {"0FFFFFFFFFFFFFFFF", -1}, + {"0FFFFFFFFFFFFFFFE", -2} + }; + + for (auto const& p : test_data_ref_ok) + { + in_str_consumable = p.first, out_cur_impl = Exp_GetLLVal(in_str_consumable); + CHECK_EQ(out_cur_impl, p.second); + + in_str_consumable = p.first, out_ref_impl = ref_impl::GetSingle_num(in_str_consumable); + CHECK_EQ(out_cur_impl, out_ref_impl); + } + + // With this data the old reference implementation failed + constexpr std::pair test_data_ref_fail[] + { + {"0FFFFFFFF", -1}, // This is a SphereServer quirk for historical reasons. + {"0FFFFFFFFFFFFFFFF1", -1} // Overflow + }; + + for (auto const& p : test_data_ref_fail) + { + in_str_consumable = p.first, out_cur_impl = Exp_GetLLVal(in_str_consumable); + CHECK_EQ(out_cur_impl, p.second); + } + } +} From d5cb18b1866e4a8a7e53f224f068a80e68e55c95 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 8 Jun 2025 16:14:40 +0200 Subject: [PATCH 058/112] Other minor optimizations --- src/common/CRect.h | 24 ++++++++--------- src/common/resource/CResourceHash.h | 4 +-- src/game/CRegionBase.cpp | 40 +++++++++++++++++++---------- src/game/CSectorList.cpp | 3 ++- src/game/CSectorTemplate.h | 2 +- tests/src/t_num_parsing.cpp | 2 +- 6 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/common/CRect.h b/src/common/CRect.h index ff184883f..534d10da4 100644 --- a/src/common/CRect.h +++ b/src/common/CRect.h @@ -25,23 +25,23 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) CRect() noexcept; CRect(int left, int top, int right, int bottom, int map) noexcept; - CRect(const CRect&) noexcept = default; - CRect(CRect&&) noexcept = default; + constexpr CRect(const CRect&) noexcept = default; + constexpr CRect(CRect&&) noexcept = default; virtual ~CRect() noexcept = default; CRect& operator = (const CRect&) = default; const CRect& operator += (const CRect& rect); - inline int GetWidth() const noexcept + constexpr inline int GetWidth() const noexcept { return( m_right - m_left ); } - inline int GetHeight() const noexcept + constexpr inline int GetHeight() const noexcept { return( m_bottom - m_top ); } - inline bool IsRectEmpty() const noexcept + constexpr inline bool IsRectEmpty() const noexcept { return( m_left >= m_right || m_top >= m_bottom ); } @@ -49,11 +49,11 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) void OffsetRect( int x, int y ); void UnionPoint( int x, int y ); - inline bool IsInsideX( int x ) const noexcept + constexpr inline bool IsInsideX( int x ) const noexcept { // non-inclusive return( x >= m_left && x < m_right ); } - inline bool IsInsideY( int y ) const noexcept + constexpr inline bool IsInsideY( int y ) const noexcept { // non-inclusive return( y >= m_top && y < m_bottom ); } @@ -86,13 +86,13 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) struct CRectMap : public CRect { CRectMap() noexcept = default; - CRectMap(int left, int top, int right, int bottom, int map) noexcept; - CRectMap(const CRectMap&) noexcept = default; - CRectMap(CRectMap&&) noexcept = default; + CRectMap(int left, int top, int right, int bottom, int map) noexcept; + constexpr CRectMap(const CRectMap&) noexcept = default; + constexpr CRectMap(CRectMap&&) noexcept = default; // special copy constructors, valid because CRectMap hasn't additional members compared to CRect, it only has more methods - CRectMap(const CRect& rect) noexcept : CRectMap(static_cast(rect)) {} - CRectMap(CRect&& rect) noexcept : CRectMap(static_cast(rect)) {} + constexpr CRectMap(const CRect& rect) noexcept : CRectMap(static_cast(rect)) {} + constexpr CRectMap(CRect&& rect) noexcept : CRectMap(static_cast(rect)) {} CRectMap& operator=(const CRectMap&) noexcept = default; CRectMap& operator=(const CRect& rect) noexcept diff --git a/src/common/resource/CResourceHash.h b/src/common/resource/CResourceHash.h index 4b378c5f3..67b99d495 100644 --- a/src/common/resource/CResourceHash.h +++ b/src/common/resource/CResourceHash.h @@ -44,7 +44,7 @@ struct CResourceHash CResourceHash& operator=(const CResourceHash&) = delete; private: - inline uint GetHashArray(const CResourceID& rid) const + constexpr inline uint GetHashArray(const CResourceID& rid) const { return (rid.GetResIndex() & 0x0F); } @@ -54,7 +54,7 @@ struct CResourceHash { return m_Array[GetHashArray(rid)].find_sorted(rid); } - inline sl::smart_ptr_view GetSmartPtrViewAt(const CResourceID& rid, size_t index) const + constexpr inline sl::smart_ptr_view GetSmartPtrViewAt(const CResourceID& rid, size_t index) const { return sl::smart_ptr_view(m_Array[GetHashArray(rid)][index]); } diff --git a/src/game/CRegionBase.cpp b/src/game/CRegionBase.cpp index c8e3f4728..6364ac578 100644 --- a/src/game/CRegionBase.cpp +++ b/src/game/CRegionBase.cpp @@ -110,14 +110,31 @@ bool CRegionBase::AddRegionRect( const CRectMap & rect ) bool CRegionBase::IsOverlapped( const CRectMap & rect ) const noexcept { // ADDTOCALLSTACK("CRegionBase::IsOverlapped"); // It's called very frequently on server startup, so avoid this call - // Does the region overlap this rectangle. - if ( !m_rectUnion.IsOverlapped(rect) ) - return false; + // Does the region overlap this rectangle. + + /* + const int left = rect.m_left; + const int right = rect.m_right; + const int top = rect.m_top; + const int bottom = rect.m_bottom; + if ( + (right <= m_rectUnion.m_left) && // Left edge of rect is to the left of this rect's right edge + (m_rectUnion.m_right <= left) && // Right edge of rect is to the right of this rect's left edge + (bottom <= m_rectUnion.m_top) && // Top edge of rect is above this rect's bottom edge + (m_rectUnion.m_bottom <= top) // Bottom edge of rect is below this rect's top edge + ) + return false; + */ + + if ( !m_rectUnion.IsOverlapped(rect) ) + return false; const uint iQty = (uint)m_Rects.size(); if ( iQty <= 0 ) return true; + // Avoid the cost of the IsOverlapped function call and just slap the content here. + // Also, reorder conditions to prioritize early exits for non-overlapping rectangles. const int left = rect.m_left; const int right = rect.m_right; const int top = rect.m_top; @@ -126,20 +143,15 @@ bool CRegionBase::IsOverlapped( const CRectMap & rect ) const noexcept for ( uint i = 0; i < iQty; ++i ) { CRect const& r = m_Rects[i]; - - // Avoid the cost of the IsOverlapped function call and just slap the content here. - // Also, reorder conditions to prioritize early exits for non-overlapping rectangles. if ( - (right > r.m_left) && // Left edge of rect is to the left of this rect's right edge - (r.m_right > left) && // Right edge of rect is to the right of this rect's left edge - (bottom > r.m_top) && // Top edge of rect is above this rect's bottom edge - (r.m_bottom > top) // Bottom edge of rect is below this rect's top edge + (right > r.m_left) && // Left edge of rect is to the left of this rect's right edge + (r.m_right > left) && // Right edge of rect is to the right of this rect's left edge + (bottom > r.m_top) && // Top edge of rect is above this rect's bottom edge + (r.m_bottom > top) // Bottom edge of rect is below this rect's top edge ) return true; - /* - if ( rect.IsOverlapped(m_Rects[i])) - return true; - */ + //if ( rect.IsOverlapped(m_Rects[i])) + // return true; } return false; } diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index b1d5f02ec..b629de25a 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -131,7 +131,8 @@ CSector* CSectorList::GetSectorByIndex(int map, int index) const noexcept // return nullptr; const MapSectorsData& sd = _SectorData[map]; - return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; + //return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; + return (index < sd.iSectorQty) ? &(sd._pSectors.get()[index]) : nullptr; } CSector* CSectorList::GetSectorByCoordsUnchecked(int map, short x, short y) const noexcept diff --git a/src/game/CSectorTemplate.h b/src/game/CSectorTemplate.h index f117c1878..2f552385f 100644 --- a/src/game/CSectorTemplate.h +++ b/src/game/CSectorTemplate.h @@ -139,7 +139,7 @@ class CSectorBase // world sector // Location map units. int GetIndex() const noexcept { return m_index; } int GetMap() const noexcept { return m_BasePoint.m_map; } - const CRectMap& GetRect() const noexcept { return m_MapRect; } + constexpr inline const CRectMap& GetRect() const noexcept { return m_MapRect; } // CRegion CRegion * GetRegion( const CPointBase & pt, dword dwType ) const; diff --git a/tests/src/t_num_parsing.cpp b/tests/src/t_num_parsing.cpp index 11fc9def8..c26151a4b 100644 --- a/tests/src/t_num_parsing.cpp +++ b/tests/src/t_num_parsing.cpp @@ -5,7 +5,7 @@ #ifndef MSVC_COMPILER #define ATTR_NO_UBSAN __attribute__((no_sanitize("undefined"))) #else -#define ATTR_NO_UBSAN __declspec(no_sanitize_address) +#define ATTR_NO_UBSAN //__declspec(no_sanitize_address) #endif From d218743802040b3e81fc7984dfae606157c7ed1b Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 9 Jun 2025 13:52:02 +0200 Subject: [PATCH 059/112] send.cpp/h: cleaned includes in .h, moved away some methods implementation to .cpp. --- src/network/receive.h | 217 +++++++++++++++++++++--------------------- src/network/send.cpp | 116 ++++++++++++++++++++++ src/network/send.h | 184 +++++++++++------------------------ 3 files changed, 283 insertions(+), 234 deletions(-) diff --git a/src/network/receive.h b/src/network/receive.h index f464c8a6e..92b5e1fc4 100644 --- a/src/network/receive.h +++ b/src/network/receive.h @@ -25,7 +25,7 @@ class PacketUnknown : public Packet { public: PacketUnknown(uint size = 0); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -39,9 +39,10 @@ class PacketCreate : public Packet { public: PacketCreate(uint size = 104); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; protected: + // TODO: pack this abomination of arguments list in a struct bool doCreate(CNetState* net, lpctstr charname, bool bFemale, RACE_TYPE rtRace, ushort wStr, ushort wDex, ushort wInt, PROFESSION_TYPE prProf, SKILL_TYPE skSkill1, ushort uiSkillVal1, SKILL_TYPE skSkill2, ushort uiSkillVal2, SKILL_TYPE skSkill3, ushort uiSkillVal3, SKILL_TYPE skSkill4, ushort uiSkillVal4, HUE_TYPE wSkinHue, ITEMID_TYPE idHair, HUE_TYPE wHairHue, ITEMID_TYPE idBeard, HUE_TYPE wBeardHue, HUE_TYPE wShirtHue, HUE_TYPE wPantsHue, ITEMID_TYPE idFace, @@ -59,7 +60,7 @@ class PacketMovementReq : public Packet { public: PacketMovementReq(uint size = 7); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -73,7 +74,7 @@ class PacketSpeakReq : public Packet { public: PacketSpeakReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -87,7 +88,7 @@ class PacketAttackReq : public Packet { public: PacketAttackReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -101,7 +102,7 @@ class PacketDoubleClick : public Packet { public: PacketDoubleClick(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -115,7 +116,7 @@ class PacketItemPickupReq : public Packet { public: PacketItemPickupReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -129,8 +130,8 @@ class PacketItemDropReq : public Packet { public: PacketItemDropReq(); - virtual uint getExpectedLength(CNetState* client, Packet* packet); - virtual bool onReceive(CNetState* net); + virtual uint getExpectedLength(CNetState* client, Packet* packet) override; + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -144,7 +145,7 @@ class PacketSingleClick : public Packet { public: PacketSingleClick(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -158,7 +159,7 @@ class PacketTextCommand : public Packet { public: PacketTextCommand(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -172,7 +173,7 @@ class PacketItemEquipReq : public Packet { public: PacketItemEquipReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -186,7 +187,7 @@ class PacketResynchronize : public Packet { public: PacketResynchronize(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -207,7 +208,7 @@ class PacketDeathStatus : public Packet }; PacketDeathStatus(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -221,7 +222,7 @@ class PacketObjStatusReq : public Packet { public: PacketObjStatusReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -235,7 +236,7 @@ class PacketSkillLockChange : public Packet { public: PacketSkillLockChange(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -256,7 +257,7 @@ class PacketVendorBuyReq : public Packet { public: PacketVendorBuyReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -271,7 +272,7 @@ class PacketStaticUpdate : public Packet { public: PacketStaticUpdate(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -285,7 +286,7 @@ class PacketMapEdit : public Packet { public: PacketMapEdit(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -299,7 +300,7 @@ class PacketCharPlay : public Packet { public: PacketCharPlay(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -313,7 +314,7 @@ class PacketBookPageEdit : public Packet { public: PacketBookPageEdit(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -327,7 +328,7 @@ class PacketTarget : public Packet { public: PacketTarget(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -341,7 +342,7 @@ class PacketSecureTradeReq : public Packet { public: PacketSecureTradeReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -355,7 +356,7 @@ class PacketBulletinBoardReq : public Packet { public: PacketBulletinBoardReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -369,7 +370,7 @@ class PacketWarModeReq : public Packet { public: PacketWarModeReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -383,7 +384,7 @@ class PacketPingReq : public Packet { public: PacketPingReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -397,7 +398,7 @@ class PacketCharRename : public Packet { public: PacketCharRename(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -411,7 +412,7 @@ class PacketMenuChoice : public Packet { public: PacketMenuChoice(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -425,7 +426,7 @@ class PacketServersReq : public Packet { public: PacketServersReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -439,7 +440,7 @@ class PacketCharDelete : public Packet { public: PacketCharDelete(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -453,7 +454,7 @@ class PacketCreateNew : public PacketCreate { public: PacketCreateNew(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -467,7 +468,7 @@ class PacketCharListReq : public Packet { public: PacketCharListReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -481,7 +482,7 @@ class PacketBookHeaderEdit : public Packet { public: PacketBookHeaderEdit(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -495,7 +496,7 @@ class PacketDyeObject : public Packet { public: PacketDyeObject(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -509,7 +510,7 @@ class PacketAllNamesReq : public Packet { public: PacketAllNamesReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -523,7 +524,7 @@ class PacketPromptResponse : public Packet { public: PacketPromptResponse(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -537,7 +538,7 @@ class PacketHelpPageReq : public Packet { public: PacketHelpPageReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -551,7 +552,7 @@ class PacketVendorSellReq : public Packet { public: PacketVendorSellReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -565,7 +566,7 @@ class PacketServerSelect : public Packet { public: PacketServerSelect(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -579,7 +580,7 @@ class PacketSystemInfo : public Packet { public: PacketSystemInfo(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -593,7 +594,7 @@ class PacketTipReq : public Packet { public: PacketTipReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -607,7 +608,7 @@ class PacketGumpValueInputResponse : public Packet { public: PacketGumpValueInputResponse(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -621,7 +622,7 @@ class PacketSpeakReqUNICODE : public Packet { public: PacketSpeakReqUNICODE(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -635,7 +636,7 @@ class PacketGumpDialogRet : public Packet { public: PacketGumpDialogRet(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -649,7 +650,7 @@ class PacketChatCommand : public Packet { public: PacketChatCommand(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -663,7 +664,7 @@ class PacketChatButton : public Packet { public: PacketChatButton(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -677,7 +678,7 @@ class PacketToolTipReq : public Packet { public: PacketToolTipReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -691,7 +692,7 @@ class PacketProfileReq : public Packet { public: PacketProfileReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -705,7 +706,7 @@ class PacketMailMessage : public Packet { public: PacketMailMessage(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -719,7 +720,7 @@ class PacketClientVersion : public Packet { public: PacketClientVersion(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -747,7 +748,7 @@ class PacketExtendedCommand : public Packet { public: PacketExtendedCommand(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -761,7 +762,7 @@ class PacketScreenSize : public Packet { public: PacketScreenSize(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -775,7 +776,7 @@ class PacketPartyMessage : public Packet { public: PacketPartyMessage(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -789,7 +790,7 @@ class PacketArrowClick : public Packet { public: PacketArrowClick(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -803,7 +804,7 @@ class PacketWrestleDisarm : public Packet { public: PacketWrestleDisarm(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -817,7 +818,7 @@ class PacketWrestleStun : public Packet { public: PacketWrestleStun(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -831,7 +832,7 @@ class PacketLanguage : public Packet { public: PacketLanguage(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -845,7 +846,7 @@ class PacketStatusClose : public Packet { public: PacketStatusClose(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -859,7 +860,7 @@ class PacketAnimationReq : public Packet { public: PacketAnimationReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -873,7 +874,7 @@ class PacketClientInfo : public Packet { public: PacketClientInfo(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -887,7 +888,7 @@ class PacketAosTooltipInfo : public Packet { public: PacketAosTooltipInfo(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -901,7 +902,7 @@ class PacketPopupReq : public Packet { public: PacketPopupReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -915,7 +916,7 @@ class PacketPopupSelect : public Packet { public: PacketPopupSelect(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -929,7 +930,7 @@ class PacketChangeStatLock : public Packet { public: PacketChangeStatLock(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -943,7 +944,7 @@ class PacketSpellSelect : public Packet { public: PacketSpellSelect(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -957,7 +958,7 @@ class PacketHouseDesignReq : public Packet { public: PacketHouseDesignReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -971,7 +972,7 @@ class PacketAntiCheat : public Packet { public: PacketAntiCheat(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -985,7 +986,7 @@ class PacketBandageMacro : public Packet { public: PacketBandageMacro(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -999,7 +1000,7 @@ class PacketTargetedSkill : public Packet { public: PacketTargetedSkill(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1013,7 +1014,7 @@ class PacketGargoyleFly : public Packet { public: PacketGargoyleFly(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1027,7 +1028,7 @@ class PacketWheelBoatMove : public Packet { public: PacketWheelBoatMove(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1041,7 +1042,7 @@ class PacketPromptResponseUnicode : public Packet { public: PacketPromptResponseUnicode(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1055,7 +1056,7 @@ class PacketViewRange : public Packet { public: PacketViewRange(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1069,7 +1070,7 @@ class PacketLogout : public Packet { public: PacketLogout(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1083,7 +1084,7 @@ class PacketBookHeaderEditNew : public Packet { public: PacketBookHeaderEditNew(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1097,7 +1098,7 @@ class PacketAOSTooltipReq : public Packet { public: PacketAOSTooltipReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1111,7 +1112,7 @@ class PacketEncodedCommand : public Packet { public: PacketEncodedCommand(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1125,7 +1126,7 @@ class PacketHouseDesignBackup : public Packet { public: PacketHouseDesignBackup(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1139,7 +1140,7 @@ class PacketHouseDesignRestore : public Packet { public: PacketHouseDesignRestore(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1153,7 +1154,7 @@ class PacketHouseDesignCommit : public Packet { public: PacketHouseDesignCommit(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1167,7 +1168,7 @@ class PacketHouseDesignDestroyItem : public Packet { public: PacketHouseDesignDestroyItem(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1181,7 +1182,7 @@ class PacketHouseDesignPlaceItem : public Packet { public: PacketHouseDesignPlaceItem(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1195,7 +1196,7 @@ class PacketHouseDesignExit : public Packet { public: PacketHouseDesignExit(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1209,7 +1210,7 @@ class PacketHouseDesignPlaceStair : public Packet { public: PacketHouseDesignPlaceStair(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1223,7 +1224,7 @@ class PacketHouseDesignSync : public Packet { public: PacketHouseDesignSync(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1237,7 +1238,7 @@ class PacketHouseDesignClear : public Packet { public: PacketHouseDesignClear(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1251,7 +1252,7 @@ class PacketHouseDesignSwitch : public Packet { public: PacketHouseDesignSwitch(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1265,7 +1266,7 @@ class PacketHouseDesignPlaceRoof : public Packet { public: PacketHouseDesignPlaceRoof(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1279,7 +1280,7 @@ class PacketHouseDesignDestroyRoof : public Packet { public: PacketHouseDesignDestroyRoof(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1293,7 +1294,7 @@ class PacketSpecialMove : public Packet { public: PacketSpecialMove(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1307,7 +1308,7 @@ class PacketHouseDesignRevert : public Packet { public: PacketHouseDesignRevert(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1321,7 +1322,7 @@ class PacketEquipLastWeapon : public Packet { public: PacketEquipLastWeapon(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1335,7 +1336,7 @@ class PacketGuildButton : public Packet { public: PacketGuildButton(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1349,7 +1350,7 @@ class PacketQuestButton : public Packet { public: PacketQuestButton(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1363,7 +1364,7 @@ class PacketHardwareInfo : public Packet { public: PacketHardwareInfo(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1377,7 +1378,7 @@ class PacketBugReport : public Packet { public: PacketBugReport(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1391,7 +1392,7 @@ class PacketClientType : public Packet { public: PacketClientType(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1405,7 +1406,7 @@ class PacketRemoveUIHighlight : public Packet { public: PacketRemoveUIHighlight(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1419,7 +1420,7 @@ class PacketUseHotbar : public Packet { public: PacketUseHotbar(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1433,7 +1434,7 @@ class PacketEquipItemMacro : public Packet { public: PacketEquipItemMacro(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1447,7 +1448,7 @@ class PacketUnEquipItemMacro : public Packet { public: PacketUnEquipItemMacro(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1461,7 +1462,7 @@ class PacketMovementReqNew : public Packet { public: PacketMovementReqNew(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1475,7 +1476,7 @@ class PacketTimeSyncRequest : public Packet { public: PacketTimeSyncRequest(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1489,7 +1490,7 @@ class PacketCrashReport : public Packet { public: PacketCrashReport(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1503,7 +1504,7 @@ class PacketCreateHS : public PacketCreate { public: PacketCreateHS(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1517,7 +1518,7 @@ class PacketGlobalChatReq : public Packet { public: PacketGlobalChatReq(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; /*************************************************************************** @@ -1531,7 +1532,7 @@ class PacketUltimaStoreButton : public Packet { public: PacketUltimaStoreButton(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; @@ -1546,7 +1547,7 @@ class PacketPublicHouseContent : public Packet { public: PacketPublicHouseContent(); - virtual bool onReceive(CNetState* net); + virtual bool onReceive(CNetState* net) override; }; #endif // _INC_RECEIVE_H diff --git a/src/network/send.cpp b/src/network/send.cpp index 832a05d5f..cc5486344 100644 --- a/src/network/send.cpp +++ b/src/network/send.cpp @@ -17,7 +17,9 @@ #include "../game/items/CItemVendable.h" #include "../game/uo_files/uofiles_enums_creid.h" #include "../game/CServer.h" +#include "../game/CServerConfig.h" #include "../game/CWorldGameTime.h" +#include "CNetState.h" #include "CNetworkManager.h" #include "send.h" @@ -119,6 +121,11 @@ PacketCombatDamage::PacketCombatDamage(const CClient* target, word damage, CUID push(target); } +bool PacketCombatDamage::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_NEWDAMAGE); +} + /*************************************************************************** * @@ -406,6 +413,11 @@ bool PacketHealthBarUpdateNew::onSend(const CClient* client) return client->CanSee(m_character.CharFind()); } +bool PacketHealthBarUpdateNew::CanSendTo(const CNetState* state) // static +{ + return state->isClientEnhanced(); +} + /*************************************************************************** * * @@ -439,6 +451,11 @@ bool PacketHealthBarUpdate::onSend(const CClient* client) return client->CanSee(m_character.CharFind()); } +bool PacketHealthBarUpdate::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_SA) || state->isClientKR(); +} + /*************************************************************************** * @@ -1014,6 +1031,11 @@ PacketDropAccepted::PacketDropAccepted(const CClient* target) : PacketSend(XCMD_ push(target); } +bool PacketDropAccepted::CanSendTo(const CNetState* state) // static +{ + return state->isClientKR(); +} + /*************************************************************************** * @@ -2538,6 +2560,11 @@ PacketChangeCharacter::PacketChangeCharacter(CClient* target) : PacketSend(XCMD_ push(target); } +bool PacketChangeCharacter::CanSendTo(const CNetState* state) // static +{ + return !(state->isClientKR() || state->isClientEnhanced()); +} + /*************************************************************************** * @@ -4083,6 +4110,11 @@ bool PacketPropertyListVersionOld::onSend(const CClient* client) return true; } +bool PacketPropertyListVersionOld::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_TOOLTIP); +} + /*************************************************************************** * @@ -4313,6 +4345,10 @@ PacketStatLocks::PacketStatLocks(const CClient* target, const CChar* character) push(target); } +bool PacketStatLocks::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_STATLOCKS); +} /*************************************************************************** * @@ -4356,6 +4392,11 @@ PacketSpellbookContent::PacketSpellbookContent(const CClient* target, const CIte push(target); } +bool PacketSpellbookContent::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_SPELLBOOK); +} + /*************************************************************************** * @@ -4396,6 +4437,11 @@ PacketHouseBeginCustomise::PacketHouseBeginCustomise(const CClient* target, cons push(target); } +bool PacketHouseBeginCustomise::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_CUSTOMMULTI) || state->isClientKR() || state->isClientEnhanced(); +} + /*************************************************************************** * @@ -4440,6 +4486,10 @@ PacketCombatDamageOld::PacketCombatDamageOld(const CClient* target, byte damage, push(target); } +bool PacketCombatDamageOld::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_DAMAGE); +} /*************************************************************************** * @@ -4656,6 +4706,10 @@ PacketDisplayBookNew::PacketDisplayBookNew(const CClient* target, CItem* book) : push(target); } +bool PacketDisplayBookNew::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_NEWBOOK) || state->isClientKR() || state->isClientEnhanced(); +} /*************************************************************************** * @@ -4731,6 +4785,11 @@ bool PacketPropertyList::hasExpired(int64 iTimeout) const return (m_time + iTimeout) < CWorldGameTime::GetCurrentTime().GetTimeRaw(); } +bool PacketPropertyList::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_TOOLTIP); +} + /*************************************************************************** * @@ -4902,6 +4961,11 @@ void PacketHouseDesign::finalise(void) seek(endPosition); } +bool PacketHouseDesign::CanSendToClient(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_CUSTOMMULTI) || state->isClientKR() || state->isClientEnhanced(); +} + /*************************************************************************** * @@ -4941,6 +5005,11 @@ bool PacketPropertyListVersion::onSend(const CClient* client) return true; } +bool PacketPropertyListVersion::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_TOOLTIPHASH); +} + /*************************************************************************** * @@ -5026,6 +5095,11 @@ PacketBuff::PacketBuff(const CClient* target, const BUFF_ICONS iconId) : PacketS push(target); } +bool PacketBuff::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_BUFFS); +} + /*************************************************************************** * * @@ -5083,6 +5157,11 @@ PacketWaypointAdd::PacketWaypointAdd(const CClient *target, CObjBase *object, MA push(target); } +bool PacketWaypointAdd::CanSendTo(const CNetState *state) // static +{ + return state->isClientVersionNumber(MINCLIVER_MAPWAYPOINT) || state->isClientKR() || state->isClientEnhanced(); +} + /*************************************************************************** * * @@ -5102,6 +5181,11 @@ PacketWaypointRemove::PacketWaypointRemove(const CClient *target, CObjBase *obje push(target); } +bool PacketWaypointRemove::CanSendTo(const CNetState *state) // static +{ + return state->isClientVersionNumber(MINCLIVER_MAPWAYPOINT) || state->isClientKR() || state->isClientEnhanced(); +} + /*************************************************************************** * * @@ -5118,6 +5202,12 @@ PacketToggleHotbar::PacketToggleHotbar(const CClient* target, bool enable) : Pac push(target); } +bool PacketToggleHotbar::CanSendTo(const CNetState* state) // static +{ + return state->isClientKR(); +} + + /*************************************************************************** * * @@ -5142,6 +5232,11 @@ PacketTimeSyncResponse::PacketTimeSyncResponse(const CClient* target) : PacketSe push(target); } +bool PacketTimeSyncResponse::CanSendTo(const CNetState* state) //static +{ + return state->isClientVersionNumber(MINCLIVER_SA) || state->isClientEnhanced() || state->isClientKR(); +} + /*************************************************************************** * @@ -5243,6 +5338,11 @@ PacketItemWorldNew::PacketItemWorldNew(const CClient* target, const CChar* mobil push(target); } +bool PacketItemWorldNew::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_SA) || state->isClientEnhanced(); +} + /*************************************************************************** * * @@ -5281,6 +5381,11 @@ PacketDisplayMapNew::PacketDisplayMapNew(const CClient* target, const CItemMap* push(target); } +bool PacketDisplayMapNew::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_NEWMAPDISPLAY) || state->isClientEnhanced(); +} + /*************************************************************************** * @@ -5410,6 +5515,12 @@ PacketContainer::PacketContainer(const CClient* target, CObjBase** objects, uint push(target); } +bool PacketContainer::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_HS); +} + + /*************************************************************************** * * @@ -5433,3 +5544,8 @@ PacketGlobalChat::PacketGlobalChat(const CClient* target, byte unknown, byte act trim(); push(target); } + +bool PacketGlobalChat::CanSendTo(const CNetState* state) // static +{ + return state->isClientVersionNumber(MINCLIVER_GLOBALCHAT); +} diff --git a/src/network/send.h b/src/network/send.h index 3223d007e..2af34a892 100644 --- a/src/network/send.h +++ b/src/network/send.h @@ -10,17 +10,15 @@ #include "../common/CUID.h" #include "../common/CLanguageID.h" #include "../common/sphereproto.h" -#include "../common/CRect.h" #include "../game/game_enums.h" -#include "../game/CServerConfig.h" -#include "CNetState.h" #include "packet.h" -#include struct CMenuItem; +class CNetState; class CItemMap; class CObjBaseTemplate; +class CRectMap; class CSpellDef; class CItemContainer; class CItemMessage; @@ -94,11 +92,8 @@ class PacketCombatDamage : public PacketSend public: PacketCombatDamage(const CClient* target, word damage, CUID defender); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_NEWDAMAGE); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -138,13 +133,10 @@ class PacketHealthBarUpdateNew : public PacketSend PacketHealthBarUpdateNew(const CClient* target, const CChar* character); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientEnhanced(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -168,13 +160,10 @@ class PacketHealthBarUpdate : public PacketSend PacketHealthBarUpdate(const CClient* target, const CChar* character); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_SA) || state->isClientKR(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -197,7 +186,7 @@ class PacketItemWorld : public PacketSend static void adjustItemData(const CClient* target, const CItem* item, ITEMID_TYPE &id, HUE_TYPE &hue, word &amount, DIR_TYPE &dir, byte &flags, byte &light); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; }; /*************************************************************************** @@ -290,7 +279,7 @@ class PacketDragAnimation : public PacketSend public: PacketDragAnimation(const CChar* source, const CItem* item, const CObjBase* container, const CPointMap* pt); - virtual bool canSendTo(const CNetState* state) const; + virtual bool canSendTo(const CNetState* state) const override; }; /*************************************************************************** @@ -308,7 +297,7 @@ class PacketContainerOpen : public PacketSend public: PacketContainerOpen(const CClient* target, const CObjBase* container, GUMP_TYPE gump); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; }; /*************************************************************************** @@ -328,7 +317,7 @@ class PacketItemContainer : public PacketSend PacketItemContainer(const CItem* spellbook, const CSpellDef* spell); void completeForTarget(const CClient* target, const CItem* spellbook); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; }; /*************************************************************************** @@ -379,11 +368,8 @@ class PacketDropAccepted : public PacketSend public: PacketDropAccepted(const CClient* target); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientKR(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -475,7 +461,7 @@ class PacketItemContents : public PacketSend PacketItemContents(CClient* target, const CItemContainer* container, bool fIsShop, bool fFilterLayers); // standard content PacketItemContents(const CClient* target, const CItem* spellbook); // spellbook spells PacketItemContents(const CClient* target, const CItemContainer* spellbook); // custom spellbook spells - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; }; /*************************************************************************** @@ -821,7 +807,7 @@ class PacketCharacter : public PacketSend public: PacketCharacter(CClient* target, const CChar* character); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; }; /*************************************************************************** @@ -849,11 +835,8 @@ class PacketChangeCharacter : public PacketSend public: PacketChangeCharacter(CClient* target); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return !(state->isClientKR() || state->isClientEnhanced()); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -965,7 +948,7 @@ class PacketCorpseEquipment : public PacketSend public: PacketCorpseEquipment(CClient* target, const CItemContainer* corpse); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; }; /*************************************************************************** @@ -1450,13 +1433,10 @@ class PacketPropertyListVersionOld : public PacketExtended public: PacketPropertyListVersionOld(const CClient* target, const CObjBase* object, dword version); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_TOOLTIP); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1569,11 +1549,8 @@ class PacketStatLocks : public PacketExtended public: PacketStatLocks(const CClient* target, const CChar* character); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_STATLOCKS); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1601,11 +1578,8 @@ class PacketSpellbookContent : public PacketExtended public: PacketSpellbookContent(const CClient* target, const CItem* spellbook, word offset); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_SPELLBOOK); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1633,11 +1607,8 @@ class PacketHouseBeginCustomise : public PacketExtended public: PacketHouseBeginCustomise(const CClient* target, const CItemMultiCustom* house); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_CUSTOMMULTI) || state->isClientKR() || state->isClientEnhanced(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1665,11 +1636,8 @@ class PacketCombatDamageOld : public PacketExtended public: PacketCombatDamageOld(const CClient* target, byte damage, CUID defender); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_DAMAGE); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1749,11 +1717,8 @@ class PacketDisplayBookNew : public PacketSend public: PacketDisplayBookNew(const CClient* target, CItem* book); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_NEWBOOK) || state->isClientKR() || state->isClientEnhanced(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1774,7 +1739,7 @@ class PacketPropertyList : public PacketSend public: PacketPropertyList(const CObjBase* object, dword version, const std::vector> &data); PacketPropertyList(const CClient* target, const PacketPropertyList* other); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; inline CUID getObject(void) const { return m_object; } inline dword getVersion(void) const { return m_version; } @@ -1783,8 +1748,8 @@ class PacketPropertyList : public PacketSend bool hasExpired(int64 iTimeout) const; - virtual inline bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static inline bool CanSendTo(const CNetState* state) { return state->isClientVersionNumber(MINCLIVER_TOOLTIP); } + virtual inline bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1829,11 +1794,8 @@ class PacketHouseDesign : public PacketSend void flushStairData(void); void finalise(void); - virtual bool canSendTo(const CNetState* state) const override { return CanSendToClient(state); } - static bool CanSendToClient(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_CUSTOMMULTI) || state->isClientKR() || state->isClientEnhanced(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendToClient(state); } + static bool CanSendToClient(const CNetState* state); }; /*************************************************************************** @@ -1850,13 +1812,10 @@ class PacketPropertyListVersion : public PacketSend public: PacketPropertyListVersion(const CClient* target, const CObjBase* object, dword version); - virtual bool onSend(const CClient* client); + virtual bool onSend(const CClient* client) override; - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_TOOLTIPHASH); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1872,11 +1831,8 @@ class PacketBuff : public PacketSend PacketBuff(const CClient* target, const BUFF_ICONS iconId, const dword clilocOne, const dword clilocTwo, const word durationSeconds, lpctstr* args, uint argCount); // add buff PacketBuff(const CClient* target, const BUFF_ICONS iconId); // remove buff - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_BUFFS); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1905,10 +1861,7 @@ class PacketWaypointAdd : public PacketSend PacketWaypointAdd(const CClient *target, CObjBase *object, MAPWAYPOINT_TYPE type); virtual bool canSendTo(const CNetState *state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState *state) - { - return state->isClientVersionNumber(MINCLIVER_MAPWAYPOINT) || state->isClientKR() || state->isClientEnhanced(); - } + static bool CanSendTo(const CNetState *state); }; /*************************************************************************** @@ -1924,10 +1877,7 @@ class PacketWaypointRemove : public PacketSend PacketWaypointRemove(const CClient *target, CObjBase *object); virtual bool canSendTo(const CNetState *state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState *state) - { - return state->isClientVersionNumber(MINCLIVER_MAPWAYPOINT) || state->isClientKR() || state->isClientEnhanced(); - } + static bool CanSendTo(const CNetState *state); }; /*************************************************************************** @@ -1942,11 +1892,8 @@ class PacketToggleHotbar : public PacketSend public: PacketToggleHotbar(const CClient* target, bool enable); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientKR(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1961,11 +1908,8 @@ class PacketTimeSyncResponse : public PacketSend public: PacketTimeSyncResponse(const CClient* target); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_SA) || state->isClientEnhanced() || state->isClientKR(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -1992,11 +1936,8 @@ class PacketItemWorldNew : public PacketItemWorld PacketItemWorldNew(const CClient* target, const CItem* item); PacketItemWorldNew(const CClient* target, const CChar* mobile); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_SA) || state->isClientEnhanced(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -2011,11 +1952,8 @@ class PacketDisplayMapNew : public PacketSend public: PacketDisplayMapNew(const CClient* target, const CItemMap* map, const CRectMap& rect); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_NEWMAPDISPLAY) || state->isClientEnhanced(); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -2043,11 +1981,8 @@ class PacketContainer : public PacketSend public: PacketContainer(const CClient* target, CObjBase** objects, uint objectCount); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_HS); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; /*************************************************************************** @@ -2078,11 +2013,8 @@ class PacketGlobalChat : public PacketSend PacketGlobalChat(const CClient* target, byte unknown, byte action, byte stanza, lpctstr xml); - virtual bool canSendTo(const CNetState* state) const { return CanSendTo(state); } - static bool CanSendTo(const CNetState* state) - { - return state->isClientVersionNumber(MINCLIVER_GLOBALCHAT); - } + virtual bool canSendTo(const CNetState* state) const override { return CanSendTo(state); } + static bool CanSendTo(const CNetState* state); }; #ifdef __clang__ From 42a329abd6839bc019e8bfeadaffc1bfab33b233 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 11 Jun 2025 12:41:45 +0200 Subject: [PATCH 060/112] Fixed CUOClientVersion implementation. Expanded Str_To* functions by having a uiStopAtLen parameter, in order to make it able to parse substrings. If the param is 0, just parse it until the end as always. --- src/common/CExpression.cpp | 26 +++--- src/common/CUOClientVersion.cpp | 123 ++++++++++++++++++-------- src/common/CUOClientVersion.h | 23 ++--- src/common/crypto/CCryptoKeyCalc.cpp | 2 +- src/common/sphere_library/sstring.cpp | 43 +++++---- src/common/sphere_library/sstring.h | 33 +++---- src/game/spheresvr.cpp | 17 ++-- src/network/packet.h | 5 +- src/network/send.h | 1 - tests/CMakeSources.cmake | 2 +- 10 files changed, 168 insertions(+), 107 deletions(-) diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index aa12d3f05..a5b9a3a56 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -1951,8 +1951,8 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) // Copy the value in a new string const size_t uiToParseLen = std::min( - ptrdiff_t(THREAD_STRING_LENGTH-1), - ptrdiff_t(pElementsStart[0][1] - pElementsStart[0][0])); + size_t(THREAD_STRING_LENGTH-1), + size_t(pElementsStart[0][1] - pElementsStart[0][0])); memcpy((void*)ptcToParse, pElementsStart[0][0], uiToParseLen * sizeof(tchar)); ptcToParse[uiToParseLen] = '\0'; @@ -1967,8 +1967,8 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) // Copy the first element in a new string size_t uiToParseLen = std::min( - ptrdiff_t(THREAD_STRING_LENGTH-1), - ptrdiff_t(pElementsStart[0][1] - pElementsStart[0][0])); + size_t(THREAD_STRING_LENGTH-1), + size_t(pElementsStart[0][1] - pElementsStart[0][0])); memcpy((void*)ptcToParse, pElementsStart[0][0], uiToParseLen * sizeof(tchar)); ptcToParse[uiToParseLen] = '\0'; @@ -1977,8 +1977,8 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) // Copy the second element in a new string uiToParseLen = std::min( - ptrdiff_t(THREAD_STRING_LENGTH-1), - ptrdiff_t(pElementsStart[1][1] - pElementsStart[1][0])); + size_t(THREAD_STRING_LENGTH-1), + size_t(pElementsStart[1][1] - pElementsStart[1][0])); memcpy((void*)ptcToParse, pElementsStart[1][0], uiToParseLen * sizeof(tchar)); ptcToParse[uiToParseLen] = '\0'; @@ -2004,8 +2004,8 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) // Copy the weight element in a new string const size_t uiToParseLen = std::min( - ptrdiff_t(THREAD_STRING_LENGTH-1), - ptrdiff_t(pElementsStart[i][1] - pElementsStart[i][0])); + size_t(THREAD_STRING_LENGTH-1), + size_t(pElementsStart[i][1] - pElementsStart[i][0])); memcpy((void*)ptcToParse, pElementsStart[i][0], uiToParseLen * sizeof(tchar)); ptcToParse[uiToParseLen] = '\0'; @@ -2034,8 +2034,8 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) // Copy the value element in a new string ASSERT(nullptr != pElementsStart[i][0]); const size_t uiToParseLen = std::min( - ptrdiff_t(THREAD_STRING_LENGTH-1), - ptrdiff_t(pElementsStart[i][1] - pElementsStart[i][0])); + size_t(THREAD_STRING_LENGTH-1), + size_t(pElementsStart[i][1] - pElementsStart[i][0])); memcpy((void*)ptcToParse, pElementsStart[i][0], uiToParseLen * sizeof(tchar)); ptcToParse[uiToParseLen] = '\0'; @@ -2061,7 +2061,7 @@ CSString CExpression::GetRangeString(lpctstr & refStrExpr) if (iQty == 1) // It's just a simple value { ASSERT(pElementsStart[0] != nullptr); - const int iToParseLen = int(ptrdiff_t(pElementsStart[0][1] - pElementsStart[0][0])); + const int iToParseLen = int(size_t(pElementsStart[0][1] - pElementsStart[0][0])); return CSString(pElementsStart[0][0], iToParseLen - 1); } @@ -2082,7 +2082,7 @@ CSString CExpression::GetRangeString(lpctstr & refStrExpr) // break; // Shouldn't really happen... // Copy the weight element in a new string - const size_t iToParseLen = ptrdiff_t(pElementsStart[i][1] - pElementsStart[i][0]); + const size_t iToParseLen = size_t(pElementsStart[i][1] - pElementsStart[i][0]); memcpy((void*)pToParse, pElementsStart[i][0], iToParseLen * sizeof(tchar)); pToParse[iToParseLen] = '\0'; lptstr pToParseCasted = reinterpret_cast(pToParse); @@ -2112,7 +2112,7 @@ CSString CExpression::GetRangeString(lpctstr & refStrExpr) ASSERT(i < iQty); i -= 1; // pick the value instead of the weight - const int iToParseLen = int(ptrdiff_t(pElementsStart[i][1] - pElementsStart[i][0])); + const int iToParseLen = int(size_t(pElementsStart[i][1] - pElementsStart[i][0])); return CSString(pElementsStart[i][0], iToParseLen); } diff --git a/src/common/CUOClientVersion.cpp b/src/common/CUOClientVersion.cpp index ca2256160..49df19943 100644 --- a/src/common/CUOClientVersion.cpp +++ b/src/common/CUOClientVersion.cpp @@ -18,12 +18,19 @@ uint CUOClientVersion::GetLegacyVersionNumber() const noexcept uint factor_revision = 100; uint factor_minor = 100'00; uint factor_major = 100'00'00; - if (m_revision > 100) + + if (m_build >= 100) { + factor_revision *= 10; factor_minor *= 10; factor_major *= 10; } - if (m_minor > 100) + if (m_revision >= 100) + { + factor_minor *= 10; + factor_major *= 10; + } + if (m_minor >= 100) { factor_major *= 10; } @@ -38,24 +45,36 @@ uint CUOClientVersion::GetLegacyVersionNumber() const noexcept std::string CUOClientVersion::GetVersionString() const noexcept { + if (m_major > 1000 || m_minor > 1000 || m_revision > 1000 || m_build > 1000 || m_build_sub > 1000) + return {}; // Invalid at best, a malicious attempt of writing past the buffer at worst. + std::string ret; - ret.reserve(30); + ret.resize(20); - if (*this >= CUOClientVersionConstants::kMinCliver_NewVersioning) + if ( *this >= CUOClientVersionConstants::kMinCliver_ModernVersioning + || *this <= CUOClientVersionConstants::kMinCliver_LetterVersioning) { snprintf(ret.data(), ret.capacity(), "%u.%u.%u.%u", m_major, m_minor, m_revision, m_build); } else { - const int iVer = snprintf(ret.data(), ret.capacity(), "%u.%u.%u", m_major, m_minor, m_revision); - if (m_revision) + // snprintf returns the number of characters that would have been written had n been sufficiently large, + // not counting the terminating null character, or a negative value if an encoding error occurred + int iWrittenChars = snprintf(ret.data(), ret.capacity(), "%u.%u.%u", m_major, m_minor, m_revision); + if (iWrittenChars < 0) + return {}; // ?! + + if (m_build) { - if (m_revision == 0) - ret[iVer] = '0'; - else - ret[iVer] = uchar(m_revision) + uchar('a' - 1); // 'a' = revision 1 - ret[iVer + 1] = '\0'; + ret[iWrittenChars] = uchar(m_build) + uchar('a' - 1); // 'a' = 'patch' 1 + ++iWrittenChars; + } + if (m_build_sub) + { + // Append the number to the string buffer + Str_FromUI(m_build_sub, &ret[iWrittenChars], ret.capacity() - (size_t)iWrittenChars - 1); } + // ret[iVer] = '\0'; // resize already zeroes everything } return ret; @@ -63,15 +82,17 @@ std::string CUOClientVersion::GetVersionString() const noexcept CUOClientVersion::CUOClientVersion(dword uiClientVersionNumber) noexcept : - m_extrachar(0) + m_build_sub(kuiBuildSubCatchAllVal) + // build_sub isn't encoded in the version number, so use a special value to inform + // that we got this CUOClientVersion from a ver number. { // launch some tests for this function - if (uiClientVersionNumber >= CUOClientVersionConstants::kMinCliver_NewVersioning.GetLegacyVersionNumber()) //MINCLIVER_NEWVERSIONING) + if (uiClientVersionNumber >= CUOClientVersionConstants::kMinCliver_ModernVersioning.GetLegacyVersionNumber()) //MINCLIVER_NEWVERSIONING) { // We should really ditch this number, giving the difficulty to parse (imagine that in scripts...). // We should just keep backwards compatibility with CLIVER/CLIVERREPORTED and provide just the string // Maybe CLIVERSTR/CLIVERREPORTEDSTR ? - if (uiClientVersionNumber > 100'00'000'00) + if (uiClientVersionNumber > 10'00'000'00) { // Two extra digits: it's a EC (one more digit used for the bigger major version) with a greater rev number (like 4.00.101.00) m_major = uiClientVersionNumber / 100'000'00; @@ -79,7 +100,7 @@ CUOClientVersion::CUOClientVersion(dword uiClientVersionNumber) noexcept : m_revision = (uiClientVersionNumber / 100 ) % 1000; } - else if (uiClientVersionNumber > 10'00'000'00) + else if (uiClientVersionNumber > 1'00'000'00) { // The extra digit can be used for a bigger major (EC, like 67.01.00.00) or rev number (CC, like 7.101.00.00) const bool fEC = ((uiClientVersionNumber / 1000'00) < kuiECMajorVerOffset); @@ -113,8 +134,8 @@ CUOClientVersion::CUOClientVersion(dword uiClientVersionNumber) noexcept : } } -CUOClientVersion::CUOClientVersion(lpctstr ptcVersion) noexcept : - m_major(0), m_minor(0), m_revision(0), m_build(0), m_extrachar(0) +CUOClientVersion::CUOClientVersion(lpctstr ptcVersion, bool fEnhancedClient) noexcept : + m_major(0), m_minor(0), m_revision(0), m_build(0), m_build_sub(0) { if ((ptcVersion == nullptr) || (*ptcVersion == '\0')) return; @@ -123,13 +144,22 @@ CUOClientVersion::CUOClientVersion(lpctstr ptcVersion) noexcept : Str_CopyLimitNull(ptcVersionBuf, ptcVersion, sizeof(ptcVersionBuf)); // Ranges algorithms not yet supported by Apple Clang... - // const ptrdiff_t count = std::ranges::count(std::string_view(ptcVersion), '.'); + // const size_t count = std::ranges::count(std::string_view(ptcVersion), '.'); const auto svVersion = std::string_view(ptcVersion); const auto count = std::count(svVersion.cbegin(), svVersion.cend(), '.'); if (count == 2) + { + if (fEnhancedClient) + { + g_Log.Event(LOGL_CRIT|LOGM_CLIENTS_LOG|LOGM_NOCONTEXT, "Wrong string passed to CUOClientVersion: Enhanced Client version but with old format?.\n"); + return; + } ApplyVersionFromStringOldFormat(ptcVersionBuf); + } else if (count == 3) - ApplyVersionFromStringNewFormat(ptcVersionBuf); + { + ApplyVersionFromStringNewFormat(ptcVersionBuf, fEnhancedClient); + } else { // Malformed string? @@ -140,18 +170,33 @@ CUOClientVersion::CUOClientVersion(lpctstr ptcVersion) noexcept : bool CUOClientVersion::operator ==(CUOClientVersion const& other) const noexcept { - //return (0 == memcmp(this, &other, sizeof(CUOClientVersion))); const bool fSameVerNum = (m_major == other.m_major) && (m_minor == other.m_minor) && (m_revision == other.m_revision) && (m_build == other.m_build); - return fSameVerNum && (m_extrachar == other.m_extrachar); + + // Ignore this comparison is m_build_sub is kuiBuildSubCatchAllVal + // (so, if one of the two CUOClientVersion was calculated from a version number, which doesn't carry this information) + const bool fSameBuildSub = + ((m_build_sub == kuiBuildSubCatchAllVal) || (other.m_build_sub == kuiBuildSubCatchAllVal)) + ? true : (m_build_sub == other.m_build_sub); + + return fSameVerNum && fSameBuildSub; } bool CUOClientVersion::operator > (CUOClientVersion const& other) const noexcept { if (m_major > other.m_major) return true; + + if (m_major < other.m_major) + return false; if (m_minor > other.m_minor) return true; + + if (m_minor < other.m_minor) + return false; if (m_revision > other.m_revision) return true; + + if (m_revision < other.m_revision) + return false; if (m_build > other.m_build) return true; return false; @@ -173,18 +218,19 @@ void CUOClientVersion::ApplyVersionFromStringOldFormat(lptstr ptcVersion) noexce { // Get version of old clients, which report the client version as ASCII string (eg: '5.0.2b') - byte uiLetter = 0; const size_t uiMax = strnlen(ptcVersion, 20); - for (size_t i = 0; i < uiMax; ++i) + size_t uiLetterPos = 0; + byte uiLetter = 0; + for (; uiLetterPos < uiMax; ++uiLetterPos) { - if (IsAlpha(ptcVersion[i])) + if (IsAlpha(ptcVersion[uiLetterPos])) { - uiLetter = uchar(ptcVersion[i] - 'a') + 1u; + uiLetter = uchar(ptcVersion[uiLetterPos] - 'a') + 1u; break; } } - tchar *piVer[3]; + tchar *piVer[3]{}; lptstr ptcVersionParsed = ptcVersion; Str_ParseCmds(ptcVersionParsed, piVer, ARRAY_COUNT(piVer), "."); @@ -200,9 +246,13 @@ void CUOClientVersion::ApplyVersionFromStringOldFormat(lptstr ptcVersion) noexce m_minor = uint(atoi(piVer[1])); m_revision = uint(atoi(piVer[2])); m_build = uiLetter; + + ++ uiLetterPos; + if (ptcVersion[uiLetterPos] != '\0') + m_build_sub = uint(atoi(ptcVersion + uiLetterPos)); } -void CUOClientVersion::ApplyVersionFromStringNewFormat(lptstr ptcVersion) noexcept +void CUOClientVersion::ApplyVersionFromStringNewFormat(lptstr ptcVersion, bool fEnhancedClient) noexcept { // Get version of newer clients, which use only 4 numbers separated by dots (example: 6.0.1.1) @@ -225,19 +275,19 @@ void CUOClientVersion::ApplyVersionFromStringNewFormat(lptstr ptcVersion) noexce if (dot3 == np) goto ret_err; - const std::string_view sv1(sv.data(), dot1 - 1); - const std::string_view sv2(sv1.data() + dot1, dot2 - 1); - const std::string_view sv3(sv2.data() + dot2, dot3 - 1); - const std::string_view sv4(sv3.data() + dot3); + const std::string_view sv1(sv.data(), dot1); + const std::string_view sv2(sv.data() + dot1 + 1, dot2 - dot1 - 1); + const std::string_view sv3(sv.data() + dot2 + 1, dot3 - dot2 - 1); + const std::string_view sv4(sv.data() + dot3 + 1); bool ok = true; try { std::optional val1, val2, val3, val4; - ok = ok && (val1 = Str_ToU(sv1.data(), 10, true)).has_value(); - ok = ok && (val2 = Str_ToU(sv1.data(), 10, true)).has_value(); - ok = ok && (val3 = Str_ToU(sv1.data(), 10, true)).has_value(); - ok = ok && (val4 = Str_ToU(sv1.data(), 10, true)).has_value(); + ok = ok && (val1 = Str_ToU(sv1.data(), 10, sv1.length(), false)).has_value(); + ok = ok && (val2 = Str_ToU(sv2.data(), 10, sv2.length(), false)).has_value(); + ok = ok && (val3 = Str_ToU(sv3.data(), 10, sv3.length(), false)).has_value(); + ok = ok && (val4 = Str_ToU(sv4.data(), 10, sv4.length(), false)).has_value(); if (!ok) return; @@ -245,6 +295,9 @@ void CUOClientVersion::ApplyVersionFromStringNewFormat(lptstr ptcVersion) noexce m_minor = val2.value(); m_revision = val3.value(); m_build = val4.value(); + + if ((m_major < kuiECMajorVerOffset) && fEnhancedClient) + m_major += kuiECMajorVerOffset; } catch (std::bad_optional_access const&) { diff --git a/src/common/CUOClientVersion.h b/src/common/CUOClientVersion.h index 81ce47f0f..048264447 100644 --- a/src/common/CUOClientVersion.h +++ b/src/common/CUOClientVersion.h @@ -11,11 +11,12 @@ struct CUOClientVersion { - static constexpr uint kuiECMajorVerOffset = 63u; + static constexpr uint kuiECMajorVerOffset = 63u; + static constexpr uint kuiBuildSubCatchAllVal = 127; /* Members. */ uint m_major, m_minor, m_revision, m_build /* build or patch, different names for the same thing */; - char m_extrachar; // like 2.0.0[x] or 4.0.4b[2] + uint m_build_sub; // like 2.0.0[x] or 4.0.4b[2] /* Methods. */ bool Valid() const noexcept; @@ -28,23 +29,24 @@ struct CUOClientVersion // This explicit constructor has to be used for new version formats (without the trailing letter, thus clients >= 5.0.6.5, equivalent to > 5.0.6e), // or old versions formats without a letter as build number (it means that build number is 0, because build 'a' is 1). explicit constexpr - CUOClientVersion(const uint major, const uint minor, const uint revision, const uint build, const char extrachar = 0) noexcept : - m_major(major), m_minor(minor), m_revision(revision), m_build(build), m_extrachar(extrachar) + CUOClientVersion(const uint major, const uint minor, const uint revision, const uint build, const uint build_sub = 0) noexcept : + m_major(major), m_minor(minor), m_revision(revision), m_build(build), m_build_sub(build_sub) {} // Equivalent to ReportedCliVer! Offsets already applied! (like kuiECMajorVerOffset). // This explicit constructor has to be used for old version formats (with the trailing letter, thus clients < 5.0.6.5, equivalent to <= 5.0.6e), // if this version has a letter in place of a build number. explicit constexpr - CUOClientVersion(const uint major, const uint minor, const uint revision, const char build, const char extrachar = 0) noexcept : - m_major(major), m_minor(minor), m_revision(revision), m_build(build - 'a'), m_extrachar(extrachar) + CUOClientVersion(const uint major, const uint minor, const uint revision, const char build, const uint build_sub = 0) noexcept : + m_major(major), m_minor(minor), m_revision(revision), m_build(build - 'a' + 1), m_build_sub(build_sub) {} CUOClientVersion(dword uiClientVersionNumber) noexcept; - CUOClientVersion(lpctstr ptcVersionString) noexcept; + CUOClientVersion(lpctstr ptcVersionString, bool fEnhancedClient = false) noexcept; /* Operators. */ bool operator ==(CUOClientVersion const& other) const noexcept; + //bool operator == (auto const& other) const noexcept = delete; bool operator > (CUOClientVersion const& other) const noexcept; bool operator >=(CUOClientVersion const& other) const noexcept; bool operator < (CUOClientVersion const& other) const noexcept; @@ -53,7 +55,7 @@ struct CUOClientVersion /* Private methods. */ private: void ApplyVersionFromStringOldFormat(lptstr ptcVersion) noexcept; - void ApplyVersionFromStringNewFormat(lptstr ptcVersion) noexcept; + void ApplyVersionFromStringNewFormat(lptstr ptcVersion, bool fEnhancedClient) noexcept; }; @@ -78,6 +80,7 @@ struct CUOClientVersionConstants SCDECL kMinCliver_StatusV6 = CUOClientVersion(7u, 0u, 30u, 0u); // minimum client to receive v6 of 0x11 packet (7.0.30.0), old vernum 7003000 /* Client versions (behaviours) */ + SCDECL kMinCliver_LetterVersioning = CUOClientVersion(1u, 25u, 35u, 0u); // after this client, the versioning style will start to use letters for the last version number (patch) SCDECL kMinCliver_CheckWalkCode = CUOClientVersion(1u, 26u, 0u, 0u); // minimum client to use walk crypt codes for fastwalk prevention (obsolete), old vernum 1260000 SCDECL kMinCliver_PadCharList = CUOClientVersion(3u, 0u, 0u, 'j'); // minimum client to pad character list to at least 5 characters, old vernum 3000010 SCDECL kMinCliver_AutoAsync = CUOClientVersion(4u, 0u, 0u, 0u); // minimum client to auto-enable async networking, old vernum 4000000 @@ -85,7 +88,7 @@ struct CUOClientVersionConstants SCDECL kMinCliver_SkillCaps = CUOClientVersion(4u, 0u, 0u, 0u); // minimum client to send skill caps in 0x3A packet, old vernum 4000000 SCDECL kMinCliver_CloseDialog = CUOClientVersion(4u, 0u, 4u, 0u); // minimum client where close dialog does not trigger a client response, old vernum 4000400 SCDECL kMinCliver_CompressDialog = CUOClientVersion(5u, 0u, 0u, 0u); // minimum client to receive zlib compressed dialogs(5.0.0a), old vernum 5000000 - SCDECL kMinCliver_NewVersioning = CUOClientVersion(5u, 0u, 6u, 5u); // minimum client to use the new versioning format (after 5.0.6e it change to 5.0.6.5), old vernum 5000605 + SCDECL kMinCliver_ModernVersioning = CUOClientVersion(5u, 0u, 7u, 0u); // minimum client to use the new versioning format (after 5.0.6e it change to 5.0.7.0), old vernum 5000605 SCDECL kMinCliver_ItemGrid = CUOClientVersion(6u, 0u, 1u, 7u); // minimum client to use grid index (6.0.1.7), old vernum 6000107 SCDECL kMinCliver_NewChatSystemCC = CUOClientVersion(7u, 0u, 4u, 1u); // minimum client to use the new chat system (7.0.4.1) - classic client, old vernum 7000401 SCDECL kMinCliver_GlobalChat = CUOClientVersion(7u, 0u, 62u, 2u); // minimum client to use global chat system (7.0.62.2), old vernum 7006202 @@ -93,7 +96,7 @@ struct CUOClientVersionConstants SCDECL kMinCliver_MapWaypoint = CUOClientVersion(7u, 0u, 84u, 0u); // minimum client to use map waypoints on classic client (7.0.84.0), old vernum 7008400 // minimum client to use the new chat system (4.0.4.0) - enhanced client, old vernum 4000400 - SCDECL kMinCliverEC_NewChatSystem = CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 0u, 4u, 0u); + SCDECL kMinCliverEC_NewChatSystem = CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 0u, 4u, 0u); /* Client versions (packets) */ SCDECL kMinCliver_ReverseIP = CUOClientVersion(4u, 0u, 0u, 0u); // maximum client to reverse IP in 0xA8 packet, old vernum 4000000 diff --git a/src/common/crypto/CCryptoKeyCalc.cpp b/src/common/crypto/CCryptoKeyCalc.cpp index 3e6e9ea2e..f630b38a0 100644 --- a/src/common/crypto/CCryptoKeyCalc.cpp +++ b/src/common/crypto/CCryptoKeyCalc.cpp @@ -26,7 +26,7 @@ static GetEncryptionTypeForClient(CUOClientVersion ver) noexcept if (ver.m_major == 2) { if (ver.m_minor == 0 && ver.m_revision == 0 && ver.m_build == 0) - return (ver.m_extrachar == 'x') ? ENC_BTFISH : ENC_BFISH; // 2.0.0x or 2.0.0 + return (ver.m_build_sub == 23 /*'x'*/) ? ENC_BTFISH : ENC_BFISH; // 2.0.0x or 2.0.0 if (ver.m_minor == 0 && ver.m_revision <= 3) return ENC_BTFISH; } diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index 9f03d0e0e..fc3e4709e 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -57,6 +57,7 @@ bool cstr_to_num( const char * RESTRICT str, _IntType * const out, uint base = 0, + size_t stop_at_len = 0, bool const ignore_trailing_extra_chars = false ) noexcept { @@ -125,11 +126,13 @@ bool cstr_to_num( _UIntType acc = 0; // accumulator // 4) Parse digits + bool parse_more; ushort ndigits = 0; const char* startDigits = str; - for (; *str; ++str) + do { const char c = *str; + parse_more = (size_t(++str - startDigits) < stop_at_len); _UIntType digit; if (c >= '0' && c <= '9') digit = c - '0'; @@ -143,14 +146,16 @@ bool cstr_to_num( break; if (acc > maxDiv || (acc == maxDiv && digit > maxRem)) return false; // overflow + acc = acc * base_casted + digit; ++ndigits; - } + } while (*str && (!stop_at_len || parse_more)); + if (str == startDigits) return false; // no digits consumed // 5) Trailing‐space or end‐of‐string - if (!ignore_trailing_extra_chars) + if (!ignore_trailing_extra_chars && parse_more) { // Some old code expects string to num conversion to tolerate trailing whitespaces or even extra chars // (like the atoi C function). @@ -203,73 +208,73 @@ bool cstr_to_num( } -std::optional Str_ToI8 (const tchar * ptcStr, uint base, bool fIgnoreExcessChars) noexcept +std::optional Str_ToI8 (const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { char val = 0; - const bool fSuccess = cstr_to_num(ptcStr, &val, base, fIgnoreExcessChars); + const bool fSuccess = cstr_to_num(ptcStr, &val, base, uiStopAtLen, fIgnoreExcessChars); if (!fSuccess) return std::nullopt; return val; } -std::optional Str_ToU8 (const tchar * ptcStr, uint base, bool fIgnoreExcessChars) noexcept +std::optional Str_ToU8 (const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { uchar val = 0; - const bool fSuccess = cstr_to_num(ptcStr, &val, base, fIgnoreExcessChars); + const bool fSuccess = cstr_to_num(ptcStr, &val, base, uiStopAtLen, fIgnoreExcessChars); if (!fSuccess) return std::nullopt; return val; } -std::optional Str_ToI16 (const tchar * ptcStr, uint base, bool fIgnoreExcessChars) noexcept +std::optional Str_ToI16 (const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { short val = 0; - const bool fSuccess = cstr_to_num(ptcStr, &val, base, fIgnoreExcessChars); + const bool fSuccess = cstr_to_num(ptcStr, &val, base, uiStopAtLen, fIgnoreExcessChars); if (!fSuccess) return std::nullopt; return val; } -std::optional Str_ToU16 (const tchar * ptcStr, uint base, bool fIgnoreExcessChars) noexcept +std::optional Str_ToU16 (const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { ushort val = 0; - const bool fSuccess = cstr_to_num(ptcStr, &val, base, fIgnoreExcessChars); + const bool fSuccess = cstr_to_num(ptcStr, &val, base, uiStopAtLen, fIgnoreExcessChars); if (!fSuccess) return std::nullopt; return val; } -std::optional Str_ToI (const tchar * ptcStr, uint base, bool fIgnoreExcessChars) noexcept +std::optional Str_ToI (const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { int val = 0; - const bool fSuccess = cstr_to_num(ptcStr, &val, base, fIgnoreExcessChars); + const bool fSuccess = cstr_to_num(ptcStr, &val, base, uiStopAtLen, fIgnoreExcessChars); if (!fSuccess) return std::nullopt; return val; } -std::optional Str_ToU(const tchar * ptcStr, uint base, bool fIgnoreExcessChars) noexcept +std::optional Str_ToU(const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { uint val = 0; - const bool fSuccess = cstr_to_num(ptcStr, &val, base, fIgnoreExcessChars); + const bool fSuccess = cstr_to_num(ptcStr, &val, base, uiStopAtLen, fIgnoreExcessChars); if (!fSuccess) return std::nullopt; return val; } -std::optional Str_ToLL(const tchar * ptcStr, uint base, bool fIgnoreExcessChars) noexcept +std::optional Str_ToLL(const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { llong val = 0; - const bool fSuccess = cstr_to_num(ptcStr, &val, base, fIgnoreExcessChars); + const bool fSuccess = cstr_to_num(ptcStr, &val, base, uiStopAtLen, fIgnoreExcessChars); if (!fSuccess) return std::nullopt; return val; } -std::optional Str_ToULL(const tchar * ptcStr, uint base, bool fIgnoreExcessChars) noexcept +std::optional Str_ToULL(const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { ullong val = 0; - const bool fSuccess = cstr_to_num(ptcStr, &val, base, fIgnoreExcessChars); + const bool fSuccess = cstr_to_num(ptcStr, &val, base, uiStopAtLen, fIgnoreExcessChars); if (!fSuccess) return std::nullopt; return val; diff --git a/src/common/sphere_library/sstring.h b/src/common/sphere_library/sstring.h index 036fbbb5d..8417dfccd 100644 --- a/src/common/sphere_library/sstring.h +++ b/src/common/sphere_library/sstring.h @@ -65,17 +65,18 @@ struct KeyTableDesc_s /** @name String utilities: Modifiers */ -// If you want to use base = 16 to convert an hexadecimal string, it has to be in the format: 0x*** -[[nodiscard]] std::optional Str_ToI8 (const tchar * ptcStr, uint base = 0, bool fIgnoreExcessChars = true) noexcept; -[[nodiscard]] std::optional Str_ToU8 (const tchar * ptcStr, uint base = 0, bool fIgnoreExcessChars = true) noexcept; -[[nodiscard]] std::optional Str_ToI16(const tchar * ptcStr, uint base = 0, bool fIgnoreExcessChars = true) noexcept; -[[nodiscard]] std::optional Str_ToU16(const tchar * ptcStr, uint base = 0, bool fIgnoreExcessChars = true) noexcept; -[[nodiscard]] std::optional Str_ToI (const tchar * ptcStr, uint base = 0, bool fIgnoreExcessChars = true) noexcept; -[[nodiscard]] std::optional Str_ToU (const tchar * ptcStr, uint base = 0, bool fIgnoreExcessChars = true) noexcept; -[[nodiscard]] std::optional Str_ToLL (const tchar * ptcStr, uint base = 0, bool fIgnoreExcessChars = true) noexcept; -[[nodiscard]] std::optional Str_ToULL(const tchar * ptcStr, uint base = 0, bool fIgnoreExcessChars = true) noexcept; +// If you want to use base = 16 to convert an hexadecimal string, it has to be in the format: 0*** +// uiStopAtLen param: stop parsing at char at the given index. If 0, just parse until the end. +[[nodiscard]] std::optional Str_ToI8 (const tchar * ptcStr, uint base = 0, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; +[[nodiscard]] std::optional Str_ToU8 (const tchar * ptcStr, uint base = 0, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; +[[nodiscard]] std::optional Str_ToI16(const tchar * ptcStr, uint base = 0, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; +[[nodiscard]] std::optional Str_ToU16(const tchar * ptcStr, uint base = 0, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; +[[nodiscard]] std::optional Str_ToI (const tchar * ptcStr, uint base = 0, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; +[[nodiscard]] std::optional Str_ToU (const tchar * ptcStr, uint base = 0, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; +[[nodiscard]] std::optional Str_ToLL (const tchar * ptcStr, uint base = 0, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; +[[nodiscard]] std::optional Str_ToULL(const tchar * ptcStr, uint base = 0, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; [[nodiscard]] inline -std::optional Str_ToST(const tchar * ptcStr, uint base = 10) noexcept; +std::optional Str_ToST(const tchar * ptcStr, uint base = 10, size_t uiStopAtLen = 0, bool fIgnoreExcessChars = true) noexcept; // The _Fast variants write from the end of the given buffer and return a pointer to the new start of the string, which in most // cases is different from the pointer passed as argument! @@ -406,20 +407,20 @@ inline ssize_t sGetLine_StaticBuf(const char *data, const size_t datasize) noexc //--- Inline methods -std::optional Str_ToST(const tchar * ptcStr, uint base) noexcept +std::optional Str_ToST(const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { if constexpr (sizeof(size_t) == 4) - return Str_ToU(ptcStr, base); + return Str_ToU(ptcStr, base, uiStopAtLen, fIgnoreExcessChars); else - return Str_ToULL(ptcStr, base); + return Str_ToULL(ptcStr, base, uiStopAtLen, fIgnoreExcessChars); } bool IsHexNumDigit(int c) noexcept { return - (c >= 'A' && c <= 'F') || - (c >= 'a' && c <= 'f') || - (c >= '0' && c <= '9'); + (c >= 'A' && c <= 'F') || + (c >= 'a' && c <= 'f') || + (c >= '0' && c <= '9'); } diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 706d2db83..dc2f1be82 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -70,10 +70,19 @@ GlobalInitializer::GlobalInitializer() */ PeriodicSyncTimeConstants(); + //--- Sphere threading system DummySphereThread::createInstance(); + { + // Ensure i have this to have context for ADDTOCALLSTACK and other operations. + const AbstractThread* curthread = ThreadHolder::get().current(); + ASSERT(curthread != nullptr); + ASSERT(dynamic_cast(curthread)); + UnreferencedParameter(curthread); + } + //--- Exception handling #ifdef WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS @@ -504,14 +513,6 @@ int Sphere_MainEntryPoint( int argc, char *argv[] ) int main( int argc, char * argv[] ) #endif { - { - // Ensure i have this to have context for ADDTOCALLSTACK and other operations. - const AbstractThread* curthread = ThreadHolder::get().current(); - ASSERT(curthread != nullptr); - ASSERT(dynamic_cast(curthread)); - UnreferencedParameter(curthread); - } - #ifdef UNIT_TESTING return DocTestMain(); #endif diff --git a/src/network/packet.h b/src/network/packet.h index 3e2877e99..c669098f9 100644 --- a/src/network/packet.h +++ b/src/network/packet.h @@ -52,8 +52,7 @@ class Packet Packet(const byte* data, uint size); virtual ~Packet(void); -private: - Packet& operator=(const Packet& other); + Packet& operator=(const Packet& other) = delete; public: bool isValid(void) const; @@ -159,7 +158,7 @@ class PacketSend : public Packet public: explicit PacketSend(byte id, uint len = 0, Priority priority = PRI_NORMAL); PacketSend(const PacketSend* other); - virtual ~PacketSend(); + virtual ~PacketSend() override; private: PacketSend& operator=(const PacketSend& other); diff --git a/src/network/send.h b/src/network/send.h index 2af34a892..0ca013654 100644 --- a/src/network/send.h +++ b/src/network/send.h @@ -62,7 +62,6 @@ class PacketGeneric : public PacketSend class PacketTelnet : public PacketSend { public: - PacketTelnet(const CClient* target, lpctstr message, bool fNullTerminated = false); }; diff --git a/tests/CMakeSources.cmake b/tests/CMakeSources.cmake index 4ff975819..c82e4cfac 100644 --- a/tests/CMakeSources.cmake +++ b/tests/CMakeSources.cmake @@ -1,7 +1,7 @@ set(SPHERE_TESTS_SOURCES tests/src/t_num_parsing.cpp tests/src/t_CPointBase.cpp - #tests/src/t_CUOClientVersion.cpp + tests/src/t_CUOClientVersion.cpp ) source_group(tests FILES ${SPHERE_TESTS_SOURCES}) From 792c0d8f0b7e018afee72d57d791edcf7991581f Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 11 Jun 2025 13:15:26 +0200 Subject: [PATCH 061/112] CMake: added generic FetchGithubDep.cmake, to download and cache remote external dependencies. --- .gitignore | 2 + CMakeLists.txt | 9 +- cmake/FetchGithubDep.cmake | 195 +++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 41 +++----- 4 files changed, 215 insertions(+), 32 deletions(-) create mode 100644 cmake/FetchGithubDep.cmake diff --git a/.gitignore b/.gitignore index ea9394397..4f89e8de3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,9 @@ /.ctagsd /.codelite +/_deps /CMakeFiles +/external /spheresvr.dir /bin diff --git a/CMakeLists.txt b/CMakeLists.txt index 435b7411d..5f8aa791d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,10 +286,6 @@ foreach(tgt ${TARGETS}) target_precompile_headers(${tgt} ${pch_list}) endforeach() -if(UNIT_TESTING) - include("tests/CMakeLists.txt") # quick and dirty - setup_tests() -endif() # -------------------- @@ -306,6 +302,11 @@ add_subdirectory(lib) toolchain_exe_stuff() # stuff to be executed after ADD_EXECUTABLE +if(UNIT_TESTING) + include("tests/CMakeLists.txt") # quick and dirty + setup_tests() +endif() + # Get the Git revision number message(STATUS) message(STATUS "Retrieving git data...") diff --git a/cmake/FetchGithubDep.cmake b/cmake/FetchGithubDep.cmake new file mode 100644 index 000000000..39dbbb336 --- /dev/null +++ b/cmake/FetchGithubDep.cmake @@ -0,0 +1,195 @@ +include(FetchContent) + +# ------------------------------------------------------------------ +# fetch_github_library(): +# - Derives the GitHub API URL for “latest release” +# - Caches the JSON metadata locally +# - Extracts `tag_name` from JSON (cached or freshly downloaded) +# - Invokes FetchContent to clone/update into a local cache dir +# - On offline/download failure, automatically reuses the existing cache +# ------------------------------------------------------------------ + +function(fetch_github_library project_name repo_url local_cache_dir) + # project_name e.g. "doctest" + # repo_url e.g. "https://github.com/doctest/doctest.git" + # local_cache_dir e.g. "${CMAKE_SOURCE_DIR}/external" + + # -------------------------------------------------------------- + # 1) Parse and from the provided Git URL + # -------------------------------------------------------------- + string(REGEX MATCH + "github\\.com[/:]([^/]+)/([^/]+)\\.git$" + _ "${repo_url}" + ) + #[[ + Parsing the GitHub owner/repo from a `.git` URL + - `github\.com` + - `\.` → a literal dot. (In regex, `.` normally means “any character,” so we escape it to match an actual `.`.) + - `[/:]` + - A character class matching exactly one of the characters inside: either a slash `/` or a colon `:`. + - This covers both URL styles: + - HTTPS: `https://github.com/owner/repo.git` + - SSH: `git@github.com:owner/repo.git` + - `([^/]+)` + - `(` `)` → defines a capturing group, so whatever matches inside will be saved as CMAKE_MATCH_1. + - `[^/]` → another character class, but with a `^` at the start: “anything but a slash.” + - `+` → “one or more” of the preceding item. + - Together, `([^/]+)` means “capture one or more characters that are not `/`.” That gives us the owner. + - `/` + - Literal, not a special character. Separates owner from repo name. + - `([^/]+)` + - Same as the previous group, capturing the repo name. + - `\.git` + - Literal. Matches the suffix `.git`. + - `$` + - The “end-of-string” anchor. Ensures there’s nothing after `.git`. + ]] + + if(NOT CMAKE_MATCH_COUNT EQUAL 2) + message(FATAL_ERROR + "Could not parse owner/repo from '${repo_url}'") + endif() + set(owner "${CMAKE_MATCH_1}") + set(repo "${CMAKE_MATCH_2}") + + # -------------------------------------------------------------- + # 2) Build the GitHub API URL for “latest release” + # -------------------------------------------------------------- + set(api_url + "https://api.github.com/repos/${owner}/${repo}/releases/latest" + ) + + # -------------------------------------------------------------- + # 3) Prepare cache paths (JSON + source tree) + # -------------------------------------------------------------- + file(MAKE_DIRECTORY "${local_cache_dir}") + if(NOT EXISTS "${local_cache_dir}") + message(FATAL_ERROR + "Failed to create cache directory: ${local_cache_dir}") + endif() + + set(json_file "${local_cache_dir}/${project_name}_latest.json") + set(source_cache_dir "${local_cache_dir}/${project_name}") + + # -------------------------------------------------------------- + # 4) Try to read a cached JSON → tag_name + # -------------------------------------------------------------- + set(lib_tag "") + set(offline FALSE) + + if(EXISTS "${json_file}") + message(STATUS "Found cached JSON for ${project_name}, validating…") + file(READ "${json_file}" _data) + string(REGEX MATCH "\"tag_name\"[ \t]*:[ \t]*\"([^\"]+)\"" _ "${_data}") + #[[ + Extracting the `"tag_name"` value from the JSON + - `"tag_name"` + - A literal double-quoted string, matching exactly `"tag_name"`. + - `[ \t]*` + - A character class containing a space and `\t` (tab). + - `*` means “zero or more” of that class. + - So this matches any amount of whitespace (spaces or tabs) after `"tag_name"`. + - `:` + - A literal colon, since in JSON the key/value separator is `:`. + - `[ \t]*` + - Again, “any amount of spaces/tabs” after the colon. + - `"` + - A literal opening quote for the tag value. + - `([^"]+)` + - Capturing group #1. + - `[^"]` is “any character except a quote.” + - `+` is “one or more.” + - So this grabs all characters inside the quotes (e.g. `v2.4.1`) until it hits the next `"`. + - `"` + - The closing literal quote. + ]] + if(CMAKE_MATCH_COUNT EQUAL 1) + set(lib_tag "${CMAKE_MATCH_1}") + message(STATUS "✔ Using cached tag: ${lib_tag}") + else() + message(WARNING "Cached JSON is invalid. Will attempt fresh download.") + endif() + endif() + + # -------------------------------------------------------------- + # 5) If no valid cached tag, download the latest JSON + # -------------------------------------------------------------- + if(NOT lib_tag) + message(STATUS "Downloading latest release info: ${api_url}") + file(DOWNLOAD + "${api_url}" # ← remote + "${json_file}" # ← local destination + STATUS dl_status + TIMEOUT 5 + SHOW_PROGRESS + INACTIVITY_TIMEOUT 5 + ) + list(GET dl_status 0 code) + if(code EQUAL 0 AND EXISTS "${json_file}") + file(READ "${json_file}" _data) + string(REGEX MATCH "\"tag_name\"[ \t]*:[ \t]*\"([^\"]+)\"" _ "${_data}") + if(CMAKE_MATCH_COUNT EQUAL 1) + set(lib_tag "${CMAKE_MATCH_1}") + message(STATUS "✔ Fetched tag: ${lib_tag}") + else() + message(WARNING "Failed to parse tag_name from downloaded JSON.") + set(offline TRUE) + endif() + else() + message(WARNING "Download failed (code=${code}). Will fall back to cache if any.") + set(offline TRUE) + endif() + endif() + + # -------------------------------------------------------------- + # 6) Final sanity check: ensure we have a lib_tag + # -------------------------------------------------------------- + if(NOT lib_tag) + message(FATAL_ERROR + "Could not determine a valid tag for ${project_name}. Aborting.") + endif() + + # -------------------------------------------------------------- + # 7) Declare FetchContent variables *before* we call it + # + # NEW: In offline mode we must prevent both “git pull” and the + # very first “git clone”. The trick is to: + # + # a) Point CMake at the already-downloaded directory via + # FETCHCONTENT_SOURCE_DIR_ + # b) Disable updates via FETCHCONTENT_UPDATES_DISCONNECTED_ + # + # These must be set *before* FetchContent_MakeAvailable(). + # -------------------------------------------------------------- + string(TOUPPER "${project_name}" UC_NAME) # doctest → DOCTEST + + if(offline AND EXISTS "${source_cache_dir}/CMakeLists.txt") + # ---------------------------------------------------------- + # We *have* source on disk and cannot reach the network → + # pretend the project is “already populated”. + # ---------------------------------------------------------- + set(FETCHCONTENT_SOURCE_DIR_${UC_NAME} "${source_cache_dir}") + set(FETCHCONTENT_UPDATES_DISCONNECTED_${UC_NAME} ON) + message(STATUS "Offline mode → reusing existing source at ${source_cache_dir}") + else() + message(STATUS "Online mode → CMake may clone or update ${project_name}") + endif() + + # -------------------------------------------------------------- + # 8) Formal declaration (still points at the cache dir so both + # online and offline modes end up in the same place). + # -------------------------------------------------------------- + FetchContent_Declare( + ${project_name} + GIT_REPOSITORY ${repo_url} + GIT_TAG ${lib_tag} + SOURCE_DIR "${source_cache_dir}" + ) + + # -------------------------------------------------------------- + # 9) Bring the dependency into the build + # -------------------------------------------------------------- + FetchContent_MakeAvailable(${project_name}) + +endfunction() + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8ab5f1d29..ad61028da 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,37 +1,15 @@ #set(FETCHCONTENT_BASE_DIR # "${CMAKE_SOURCE_DIR}/third_party" #) -include(FetchContent) message(STATUS "") message(STATUS "Unit Tests requested") -message(STATUS "Fetching DocTest") - -# Download the JSON for the latest release -set(DOCTEST_RELEASE_API - "https://api.github.com/repos/doctest/doctest/releases/latest" -) - -file(DOWNLOAD - ${DOCTEST_RELEASE_API} - ${CMAKE_BINARY_DIR}/doctest_latest.json - SHOW_PROGRESS -) - -# Extract "tag_name" via regex -file(READ "${CMAKE_BINARY_DIR}/doctest_latest.json" _json) -string(REGEX MATCH "\"tag_name\"[ \t]*:[ \t]*\"([^\"]+)\"" _ - "${_json}") -set(DOCTEST_TAG "${CMAKE_MATCH_1}") - -message(STATUS "Using doctest tag: ${DOCTEST_TAG}") - -FetchContent_Declare( - doctest - GIT_REPOSITORY https://github.com/doctest/doctest.git - GIT_TAG ${DOCTEST_TAG} -) -FetchContent_MakeAvailable(doctest) + +include("cmake/FetchGithubDep.cmake") +set(EXTERNAL_CACHE_DIR "${CMAKE_SOURCE_DIR}/external") + +# Fetch Doctest +fetch_github_library(doctest "https://github.com/doctest/doctest.git" "${EXTERNAL_CACHE_DIR}") # Enable testing enable_testing() @@ -40,6 +18,12 @@ function(setup_tests) foreach(tgt ${TARGETS}) target_link_libraries(${tgt} PRIVATE doctest::doctest) target_compile_definitions(${tgt} PRIVATE UNIT_TESTING) + # Doctest for some reason uses NaN, which is disabled by -ffast-math + if("${CXX_COMPILER_ID}" STREQUAL "MSVC") + target_compile_options(${tgt} PRIVATE /fp:precise) + else() + target_compile_options(${tgt} PRIVATE -fno-finite-math-only) + endif() #[[ add_custom_target(run_tests @@ -54,3 +38,4 @@ function(setup_tests) COMMAND ${tgt}) endforeach() endfunction() + From d5a1d115d5bcba62ad7382281e71de2037fc02cd Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 11 Jun 2025 13:16:32 +0200 Subject: [PATCH 062/112] Added CUOClientVersion unit tests. --- src/network/send.h | 2 +- tests/src/t_CPointBase.cpp | 42 +++--- tests/src/t_CUOClientVersion.cpp | 220 +++++++++++++++++++++++++++++++ tests/src/t_num_parsing.cpp | 4 +- 4 files changed, 244 insertions(+), 24 deletions(-) create mode 100644 tests/src/t_CUOClientVersion.cpp diff --git a/src/network/send.h b/src/network/send.h index 0ca013654..ccab69ba7 100644 --- a/src/network/send.h +++ b/src/network/send.h @@ -15,10 +15,10 @@ struct CMenuItem; +struct CRectMap; class CNetState; class CItemMap; class CObjBaseTemplate; -class CRectMap; class CSpellDef; class CItemContainer; class CItemMessage; diff --git a/tests/src/t_CPointBase.cpp b/tests/src/t_CPointBase.cpp index b42fe00b2..eddf6fd95 100644 --- a/tests/src/t_CPointBase.cpp +++ b/tests/src/t_CPointBase.cpp @@ -81,7 +81,7 @@ TEST_CASE("CPointBase::GetDir")\ ptOther.m_x += x; ptOther.m_y += y; - CHECK(pt.GetDir(ptOther) == ref_impl::GetDir(pt, ptOther)); + CHECK_EQ(pt.GetDir(ptOther), ref_impl::GetDir(pt, ptOther)); } } } @@ -95,16 +95,16 @@ TEST_CASE("CPointBase::IsValidPoint")\ SUBCASE("Valid") { pt.Set(0, 0, 0, 0); - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); pt.Set(1, 2, 3, 4); - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); pt.Set(0, 0, -126, 0); - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); pt.Set(0, 0, 126, 0); - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); } @@ -113,19 +113,19 @@ TEST_CASE("CPointBase::IsValidPoint")\ // For now avoid negative values, the unsigned conversion trick will fail because sphere hasn't // yet loaded map sizes and the default is -1. //pt.Set(-11, 2, 3, 4); - //CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + //CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); pt.Set(0, 0, -128, 0); // uint8_t goes from [-128; 127] - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); pt.Set(0, 0, -127, 0); - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); pt.Set(0, 0, 127, 0); - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); pt.Set(7168, 4096, 127, 0); - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); } } @@ -138,39 +138,39 @@ TEST_CASE("CPointBase::IsCharValid")\ SUBCASE("Valid") { pt.Set(1, 2, 3, 4); - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); pt.Set(1, 1, -126, 0); - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); pt.Set(1, 1, 126, 0); - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); } SUBCASE("Invalid") { pt.Set(0, 0, 0, 0); - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); //pt.Set(-11, 2, 3, 4); - //CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + //CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); pt.Set(1, 1, -127, 0); - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); pt.Set(1, 1, 127, 0); - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); pt.Set(1, 1, -128, 0); // uint8_t goes from [-128; 127] - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); pt.Set(7168, 4096, 127, 0); - CHECK(pt.IsValidPoint() == ref_impl::IsValidPoint(pt)); + CHECK_EQ(pt.IsValidPoint(), ref_impl::IsValidPoint(pt)); pt.Set(0, 1, 1, 0); - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); pt.Set(1, 0, 1, 0); - CHECK(pt.IsCharValid() == ref_impl::IsCharValid(pt)); + CHECK_EQ(pt.IsCharValid(), ref_impl::IsCharValid(pt)); } } diff --git a/tests/src/t_CUOClientVersion.cpp b/tests/src/t_CUOClientVersion.cpp new file mode 100644 index 000000000..c84205c5d --- /dev/null +++ b/tests/src/t_CUOClientVersion.cpp @@ -0,0 +1,220 @@ +#include +#include "../../src/common/CUOClientVersion.h" +//#include "../../src/common/sphereproto.h" +#include // atoi +#include + +namespace { namespace ref_impl +{ +#ifdef _WIN32 +# define FMTdword "lu" // Windows uses '%lu' to format dec dword (unsigned long) +# define FMTdwordH "lx" // Windows uses '%lx' to format hex dword (unsigned long) +#else +# define FMTdword "u" // Linux uses '%u' to format dec dword (unsigned long) +# define FMTdwordH "x" // Linux uses '%x' to format hex dword (unsigned int) +#endif + + // From 0.56d + dword CCrypt_GetVerFromString(lpctstr pszVer) + { + // Get version of old clients, which report the client version as ASCII string (eg: '5.0.2b') + if ( (pszVer == nullptr) || (*pszVer == '\0') ) + return 0; + + byte bLetter = 0; + for ( size_t i = 0; i < strlen(pszVer); ++i ) + { + if ( IsAlpha(pszVer[i]) ) + { + bLetter = (pszVer[i] - 'a') + 1; + break; + } + } + + tchar *ppVer[3]; + Str_ParseCmds(const_cast(pszVer), ppVer, ARRAY_COUNT(ppVer), "."); + + // Don't rely on all values reported by client, because it can be easily faked. Injection users can report any + // client version they want, and some custom clients may also report client version as "Custom" instead X.X.Xy + if ( !ppVer[0] || !ppVer[1] || !ppVer[2] || (bLetter > 26) ) + return 0; + + return (atoi(ppVer[0]) * 1000000) + (atoi(ppVer[1]) * 10000) + (atoi(ppVer[2]) * 100) + bLetter; + } + + // From 0.56d + dword CCrypt_GetVerFromNumber(dword dwMajor, dword dwMinor, dword dwRevision, dword dwPatch) + { + // Get version of new clients (5.0.6.5+), which report the client version as numbers (eg: 5,0,6,5) + + return (dwMajor * 1000000) + (dwMinor * 10000) + (dwRevision * 100) + dwPatch; + } + + // From 0.56d + tchar *CCrypt_WriteClientVerString(dword dwVer, tchar *pszOutput) + { + if ( dwVer >= MINCLIVER_NEWVERSIONING ) + { + snprintf(pszOutput, 44, "%" FMTdword ".%" FMTdword ".%" FMTdword ".%" FMTdword, dwVer / 1000000, (dwVer / 10000) % 100, (dwVer % 10000) / 100, dwVer % 100); + } + else + { + int iVer = snprintf(pszOutput, 33, "%" FMTdword ".%" FMTdword ".%" FMTdword, dwVer / 1000000, (dwVer / 10000) % 100, (dwVer % 10000) / 100); + int iPatch = static_cast(dwVer % 100); + if ( iPatch ) + { + pszOutput[iVer++] = static_cast(iPatch + 'a' - 1); + pszOutput[iVer] = '\0'; + } + } + return pszOutput; + } + +}} + + +TEST_CASE("CUOClientVersion parsing")\ +// clazy:exclude=non-pod-global-static +{ + constexpr std::pair data_pair_str_old_format[] + { + {"1.0.1.0", CUOClientVersion(1u, 0u, 1u, 0u)}, // Ancient versioning, no letters. + {"2.0.0a", CUOClientVersion(2u, 0u, 0u, 'a')}, + {"2.0.0x", CUOClientVersion(2u, 0u, 0u, 'x')}, + {"2.0.1j", CUOClientVersion(2u, 0u, 1u, 'j')}, + {"2.0.8z", CUOClientVersion(2u, 0u, 8u, 'z')}, + {"4.0.4b2", CUOClientVersion(4u, 0u, 4u, 'b', 2)}, + {"5.0.2b", CUOClientVersion(5u, 0u, 2u, 'b')} + }; + constexpr std::pair data_pair_str_new_format[] + { + {"6.0.0.0", CUOClientVersion(6u, 0u, 0u, 0u)}, + {"7.1.2.34", CUOClientVersion(7u, 1u, 2u, 34u)}, + {"7.12.3.4", CUOClientVersion(7u, 12u, 3u, 4u)}, + {"7.1.23.4", CUOClientVersion(7u, 1u, 23u, 4u)}, + {"7.1.2.34", CUOClientVersion(7u, 1u, 2u, 34u)}, + {"7.12.34.5", CUOClientVersion(7u, 12u, 34u, 5u)}, + {"7.12.34.56", CUOClientVersion(7u, 12u, 34u, 56u)}, + {"7.100.0.0", CUOClientVersion(7u, 100u, 0u, 0u)}, + {"7.100.0.1", CUOClientVersion(7u, 100u, 0u, 1u)}, + {"7.101.1.5", CUOClientVersion(7u, 101u, 1u, 5u)} + }; + constexpr std::pair data_pair_str_enhanced_client[] + { + {"67.1.0.0", CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 1u, 0u, 0u)}, + {"67.5.0.5", CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 5u, 0u, 5u)}, + {"67.100.0.0", CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 100u, 0u, 0u)}, + {"67.101.1.5", CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 101u, 1u, 5u)} + }; + constexpr std::pair data_pair_num[] + { + {1253500, CUOClientVersion(1u, 25u, 35u, 0u)}, // Ancient versioning, no letters. + {2000000, CUOClientVersion(2u, 0u, 0u, 0u, 'x')}, // build_sub + {3000702, CUOClientVersion(3u, 0u, 7u, 'b')}, // Letter versioning. + {3000810, CUOClientVersion(3u, 0u, 8u, 'j')}, + {5000000, CUOClientVersion(5u, 0u, 0u, 0u)}, + {5000605, CUOClientVersion(5u, 0u, 6u, 'e')}, + {5000700, CUOClientVersion(5u, 0u, 7u, 0u)}, + {7004565, CUOClientVersion(7u, 0u, 45u, 65u)}, + {70010200, CUOClientVersion(7u, 0u, 102u, 0u)} + }; + constexpr std::pair data_pair_num_enhanced_client[] + { + {67000900, CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 0u, 9u, 0u)}, + {67009900, CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 0u, 99u, 0u)}, + {670010000, CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 0u, 100u, 0u)}, + {670110800, CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 1u, 108u, 0u)}, + {670110801, CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 1u, 108u, 1u)}, + {670110810, CUOClientVersion(CUOClientVersion::kuiECMajorVerOffset + 4u, 1u, 108u, 10u)} + }; + + SUBCASE("Read from string, old format") + { + for (auto const& elem : data_pair_str_old_format) + { + INFO("Test: " << std::string_view(elem.first) << " -> " << elem.second.GetVersionString()); + CUOClientVersion cv(elem.first); + CHECK_EQ(cv, elem.second); + //CHECK_EQ(ref_impl::CCrypt_GetVerFromString(elem.first), elem.second); + } + } + + SUBCASE("Read from string, new format") + { + for (auto const& elem : data_pair_str_new_format) + { + CUOClientVersion cv(elem.first); + CHECK_EQ(cv, elem.second); + } + } + + SUBCASE("Read from string, Enhanced Client") + { + for (auto const& elem : data_pair_str_enhanced_client) + { + CUOClientVersion cv(elem.first); + CHECK_EQ(cv, elem.second); + } + } + + SUBCASE("Read from number, all formats") + { + for (auto const& elem : data_pair_num) + { + CUOClientVersion cv(elem.first); + CHECK_EQ(cv, elem.second); + } + } + + SUBCASE("Write as string, old format") + { + std::string strVer; + for (auto const& elem : data_pair_str_old_format) + { + strVer = elem.second.GetVersionString(); + CHECK(!strcmp(strVer.c_str(), elem.first)); + } + } + + SUBCASE("Write as string, new format") + { + std::string strVer; + for (auto const& elem : data_pair_str_new_format) + { + strVer = elem.second.GetVersionString(); + CHECK(!strcmp(strVer.c_str(), elem.first)); + } + } + + SUBCASE("Write as string, Enhanced Client") + { + std::string strVer; + for (auto const& elem : data_pair_str_enhanced_client) + { + strVer = elem.second.GetVersionString(); + CHECK(!strcmp(strVer.c_str(), elem.first)); + } + } + + SUBCASE("Write as number, all formats") + { + for (auto const& elem : data_pair_num) + { + uint ver = elem.second.GetLegacyVersionNumber(); + CHECK_EQ(ver, elem.first); + + // Doesn't work for revision > 100 + //uint ver_ref_impl = ref_impl::CCrypt_GetVerFromNumber(elem.second.m_major, elem.second.m_minor, elem.second.m_revision, elem.second.m_build); + //CHECK_EQ(ver, ver_ref_impl); + } + } + + SUBCASE("Write as number, Enhanced Client") + { + for (auto const& elem : data_pair_num_enhanced_client) + { + uint ver = elem.second.GetLegacyVersionNumber(); + CHECK_EQ(ver, elem.first); + } + } +} diff --git a/tests/src/t_num_parsing.cpp b/tests/src/t_num_parsing.cpp index c26151a4b..31109b7db 100644 --- a/tests/src/t_num_parsing.cpp +++ b/tests/src/t_num_parsing.cpp @@ -11,7 +11,7 @@ namespace { namespace ref_impl { - +/* // From 0.56d, known to be faulty... Copying it here just for historical reasons. int ahextoi(lpctstr pszArgs) // convert hex string to int { @@ -92,7 +92,7 @@ namespace { namespace ref_impl } return iVal; } - +*/ // Part of CExpression::GetSingle that parses numerical strings. int64 GetSingle_num(lpctstr &pszArgs) ATTR_NO_UBSAN From 7efe1722a51635f6694122d79b9284f9520c3b06 Mon Sep 17 00:00:00 2001 From: David Kindl Date: Tue, 5 Aug 2025 10:02:55 +0200 Subject: [PATCH 063/112] Updated packet documentation / unused values. --- src/network/receive.cpp | 12 ++++++++---- src/network/send.cpp | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/network/receive.cpp b/src/network/receive.cpp index 309b0f3c7..e1136f6ab 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -949,8 +949,12 @@ bool PacketCharPlay::onReceive(CNetState* net) ADDTOCALLSTACK("PacketCharPlay::onReceive"); skip(4); // 0xedededed - skip(MAX_NAME_SIZE); // char name - skip(MAX_NAME_SIZE); // char pass + skip(MAX_NAME_SIZE); // Character name. + skip(2); // ? + skip(4); // Client flags (0x3f - Orion, Classic, ClassicUO, 0xff - Enhanced client). + skip(4); // ? (0 - Orion, Classic, ClassicUO, 0xff000000 - Enhanced client). + skip(4); // Login count. + skip(16); // ? uint slot = readInt32(); skip(4); // ip @@ -1452,7 +1456,7 @@ bool PacketServersReq::onReceive(CNetState* net) readStringASCII(acctname, ARRAY_COUNT(acctname)); tchar acctpass[MAX_NAME_SIZE]; readStringASCII(acctpass, ARRAY_COUNT(acctpass)); - skip(1); + skip(1); // "NextLoginKey" value from uo.cfg on client machine. CClient* client = net->getClient(); ASSERT(client); @@ -1682,7 +1686,7 @@ bool PacketCharListReq::onReceive(CNetState* net) { ADDTOCALLSTACK("PacketCharListReq::onReceive"); - skip(4); + skip(4); // Session key sent to the client in packet 0x8c (customerId). tchar acctname[MAX_ACCOUNT_NAME_SIZE]; readStringASCII(acctname, ARRAY_COUNT(acctname)); tchar acctpass[MAX_NAME_SIZE]; diff --git a/src/network/send.cpp b/src/network/send.cpp index cc5486344..7086daaae 100644 --- a/src/network/send.cpp +++ b/src/network/send.cpp @@ -633,7 +633,8 @@ PacketPlayerStart::PacketPlayerStart(const CClient* target) : PacketSend(XCMD_St writeInt16((word)(character->GetDispID())); writeInt16(pt.m_x); writeInt16(pt.m_y); - writeInt16(pt.m_z); + writeByte(0); + writeByte(pt.m_z); writeByte(character->GetDirFlag()); writeByte(0); writeInt32(0xffffffff); From 707f304fbe60b8be6f0bda2928210f7241279f5e Mon Sep 17 00:00:00 2001 From: David Kindl Date: Wed, 17 Sep 2025 08:40:04 +0200 Subject: [PATCH 064/112] Remove empty line with dot after invalid more error message --- src/game/items/CItem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 87c00016d..2069e80bc 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -2374,7 +2374,7 @@ void CItem::r_WriteMore1(CSString & sVal) if (ptcErr) { - g_Log.EventError("Invalid MORE1 for item 0%" PRIx32 ": %s.\n", + g_Log.EventError("Invalid MORE1 for item 0%" PRIx32 ": %s", GetUID().GetObjUID(), ptcErr); } } @@ -2434,7 +2434,7 @@ void CItem::r_WriteMore2( CSString & sVal ) if (ptcErr) { - g_Log.EventError("Invalid MORE2 for item 0%" PRIx32 ": %s.\n", + g_Log.EventError("Invalid MORE2 for item 0%" PRIx32 ": %s", GetUID().GetObjUID(), ptcErr); } } From a64849388fd5e9e8acb32d23cea02044231d542a Mon Sep 17 00:00:00 2001 From: Jhobean <51728381+Jhobean@users.noreply.github.com> Date: Fri, 25 Apr 2025 22:37:01 -0400 Subject: [PATCH 065/112] Update sphereCrypt.ini --- src/sphereCrypt.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sphereCrypt.ini b/src/sphereCrypt.ini index ad3e4f34f..b503fd5a8 100644 --- a/src/sphereCrypt.ini +++ b/src/sphereCrypt.ini @@ -232,12 +232,12 @@ ENC_LOGIN 4 // Rotation cipher used for the Login Server crypt by every client, 1232700 0D47BD45C 034081CD6 ENC_LOGIN // 1.23.27 //Enhanced Clients -679001080 01A68833D 0BA22127F ENC_TFISH // 4.0.108.0 -679001070 01AA2D14D 0BA7C9E7F ENC_TFISH // 4.0.107.0 -679001060 01AF46B5D 0BA46A27F ENC_TFISH // 4.0.106.0 -679001050 01B3D816D 0BAA8BE7F ENC_TFISH // 4.0.105.0 -679001040 01B07237D 0B94B527F ENC_TFISH // 4.0.104 -679001030 01B49418D 0B964FE7F ENC_TFISH // 4.0.103 +6790010800 01A68833D 0BA22127F ENC_TFISH // 4.0.108.00 +6790010700 01AA2D14D 0BA7C9E7F ENC_TFISH // 4.0.107.00 +6790010600 01AF46B5D 0BA46A27F ENC_TFISH // 4.0.106.00 +6790010500 01B3D816D 0BAA8BE7F ENC_TFISH // 4.0.105.00 +6790010400 01B07237D 0B94B527F ENC_TFISH // 4.0.104 +6790010300 01B49418D 0B964FE7F ENC_TFISH // 4.0.103 670010200 01B92EB9D 0B91E227F ENC_TFISH // 4.0.102 670010100 01BC411AD 0B903DE7F ENC_TFISH // 4.0.101 670010000 0183D83BD 0B8F5127F ENC_TFISH // 4.0.100 From 4c688980e4379bb972d3e97b33d2cb0ea3c1c8f9 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 12 Jun 2025 10:53:55 +0200 Subject: [PATCH 066/112] sresetevents: protected from spurious wakeups --- src/common/sphere_library/sresetevents.cpp | 112 ++++++++++++++------- src/common/sphere_library/sresetevents.h | 31 +++--- 2 files changed, 94 insertions(+), 49 deletions(-) diff --git a/src/common/sphere_library/sresetevents.cpp b/src/common/sphere_library/sresetevents.cpp index e7a187b05..647ef46e2 100644 --- a/src/common/sphere_library/sresetevents.cpp +++ b/src/common/sphere_library/sresetevents.cpp @@ -2,23 +2,25 @@ // AutoResetEvent:: Constructors, Destructor, Assign operator. -AutoResetEvent::AutoResetEvent() +AutoResetEvent::AutoResetEvent() noexcept { #ifdef _WIN32 m_handle = CreateEvent(nullptr, FALSE, FALSE, nullptr); #else pthread_mutexattr_init(&m_criticalSectionAttr); -#ifndef __APPLE__ +# ifndef __APPLE__ pthread_mutexattr_settype(&m_criticalSectionAttr, PTHREAD_MUTEX_RECURSIVE_NP); -#endif +# endif pthread_mutex_init(&m_criticalSection, &m_criticalSectionAttr); pthread_condattr_init(&m_conditionAttr); pthread_cond_init(&m_condition, &m_conditionAttr); + + m_signaled = false; #endif } -AutoResetEvent::~AutoResetEvent() +AutoResetEvent::~AutoResetEvent() noexcept { #ifdef _WIN32 CloseHandle(m_handle); @@ -32,11 +34,11 @@ AutoResetEvent::~AutoResetEvent() // AutoResetEvent:: Interaction. -void AutoResetEvent::wait(uint timeout) +void AutoResetEvent::wait(uint timeout) noexcept { if (timeout == 0) { - // if timeout is 0 then the thread's timeslice may not be given up as with normal + // If timeout is 0 then the thread's timeslice may not be given up as with normal // sleep methods - so we will check for this condition ourselves and use SleepEx // instead #ifdef _WIN32 @@ -48,42 +50,84 @@ void AutoResetEvent::wait(uint timeout) } #ifdef _WIN32 - // it's possible for WaitForSingleObjectEx to exit before the timeout and without + // It's possible for WaitForSingleObjectEx to exit before the timeout and without // the signal. due to bAlertable=TRUE other events (e.g. async i/o) can cancel the // waiting period early. - WaitForSingleObjectEx(m_handle, timeout, TRUE); + + DWORD start = GetTickCount(); + DWORD remaining = timeout; + + // Checks below to protect outselves from spurious wakeups + while (true) + { + DWORD result = WaitForSingleObjectEx(m_handle, timeout, TRUE); + + EnterCriticalSection(&m_criticalSection); + if (m_signaled) + { + m_signaled = false; // auto-reset + LeaveCriticalSection(&m_criticalSection); + break; + } + LeaveCriticalSection(&m_criticalSection); + + if (result == WAIT_TIMEOUT) + break; + + DWORD now = GetTickCount(); + DWORD elapsed = now - start; + if (elapsed >= timeout) + break; + + remaining = timeout - elapsed; + } #else pthread_mutex_lock(&m_criticalSection); - // it's possible for pthread_cond_wait/timedwait to exit before the timeout and - // without a signal, but there's little we can actually do to check it since we - // don't usually care about the condition - if the calling thread does care then - // it needs to implement it's own checks - if (timeout == _kiInfinite) - { - pthread_cond_wait(&m_condition, &m_criticalSection); - } - else - { - // pthread_cond_timedwait expects the timeout to be the actual time - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - time.tv_sec += timeout / 1000; - time.tv_nsec += (timeout % 1000) * 1000000L; - - pthread_cond_timedwait(&m_condition, &m_criticalSection, &time); - } + // Check m_signaled to protect outselves from spurious wakeups, like above, but leverage POSIX threads + while (!m_signaled) + { + if (timeout == _kiInfinite) + { + pthread_cond_wait(&m_condition, &m_criticalSection); + continue; + } + + // pthread_cond_timedwait expects the timeout to be the actual time + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + time.tv_sec += timeout / 1000; + time.tv_nsec += (timeout % 1000) * 1000000L; + if (time.tv_nsec >= 1000'000'000) //static_cast(1.0e9)) + { + // Normalize timespec + time.tv_sec += 1; + time.tv_nsec -= 1000'000'000; //static_cast(1.0e9); + } + + int res = pthread_cond_timedwait(&m_condition, &m_criticalSection, &time); + if (res == ETIMEDOUT) + break; + } + + if (m_signaled) + m_signaled = false; // autoreset pthread_mutex_unlock(&m_criticalSection); #endif } -void AutoResetEvent::signal() +void AutoResetEvent::signal() noexcept { #ifdef _WIN32 + EnterCriticalSection(&m_criticalSection); + m_signaled = true; + LeaveCriticalSection(&m_criticalSection); + SetEvent(m_handle); #else pthread_mutex_lock(&m_criticalSection); + m_signaled = true; pthread_cond_signal(&m_condition); pthread_mutex_unlock(&m_criticalSection); #endif @@ -91,16 +135,16 @@ void AutoResetEvent::signal() // ManualResetEvent:: Constructors, Destructor, Assign operator. -ManualResetEvent::ManualResetEvent() +ManualResetEvent::ManualResetEvent() noexcept { #ifdef _WIN32 m_handle = CreateEvent(nullptr, TRUE, FALSE, nullptr); #else m_value = false; pthread_mutexattr_init(&m_criticalSectionAttr); -#ifndef __APPLE__ +# ifndef __APPLE__ pthread_mutexattr_settype(&m_criticalSectionAttr, PTHREAD_MUTEX_RECURSIVE_NP); -#endif +# endif pthread_mutex_init(&m_criticalSection, &m_criticalSectionAttr); pthread_condattr_init(&m_conditionAttr); @@ -108,7 +152,7 @@ ManualResetEvent::ManualResetEvent() #endif } -ManualResetEvent::~ManualResetEvent() +ManualResetEvent::~ManualResetEvent() noexcept { #ifdef _WIN32 CloseHandle(m_handle); @@ -122,7 +166,7 @@ ManualResetEvent::~ManualResetEvent() // ManualResetEvent:: Interaction. -void ManualResetEvent::wait(uint timeout) +void ManualResetEvent::wait(uint timeout) noexcept { #ifdef _WIN32 WaitForSingleObjectEx(m_handle, timeout, FALSE); @@ -154,7 +198,7 @@ void ManualResetEvent::wait(uint timeout) #endif } -void ManualResetEvent::set() +void ManualResetEvent::set() noexcept { #ifdef _WIN32 SetEvent(m_handle); @@ -166,7 +210,7 @@ void ManualResetEvent::set() #endif } -void ManualResetEvent::reset() +void ManualResetEvent::reset() noexcept { #ifdef _WIN32 ResetEvent(m_handle); diff --git a/src/common/sphere_library/sresetevents.h b/src/common/sphere_library/sresetevents.h index f83d856b5..e92dbeedf 100644 --- a/src/common/sphere_library/sresetevents.h +++ b/src/common/sphere_library/sresetevents.h @@ -27,11 +27,11 @@ class AutoResetEvent /** @name Constructors, Destructor, Assign operator: */ ///@{ - AutoResetEvent(); - ~AutoResetEvent(); -private: - AutoResetEvent(const AutoResetEvent& copy); - AutoResetEvent& operator=(const AutoResetEvent& other); + AutoResetEvent() noexcept; + ~AutoResetEvent() noexcept; + + AutoResetEvent(const AutoResetEvent& copy) = delete; + AutoResetEvent& operator=(const AutoResetEvent& other) = delete; ///@} public: @@ -42,11 +42,11 @@ class AutoResetEvent * @brief Wait until the timeout interval elapses or this object is signaled. * @param timeout max elapsed time to wait. */ - void wait(uint timeout = _kiInfinite); + void wait(uint timeout = _kiInfinite) noexcept; /** * @brief Signal this object to stop the wait. */ - void signal(); + void signal() noexcept; ///@} private: @@ -57,6 +57,7 @@ class AutoResetEvent pthread_mutexattr_t m_criticalSectionAttr; // Unix API mutex attr. pthread_condattr_t m_conditionAttr; // Unix API condition attr. pthread_cond_t m_condition; // Unix API condition. + bool m_signaled; #endif }; @@ -76,11 +77,11 @@ class ManualResetEvent /** @name Constructors, Destructor, Assign operator: */ ///@{ - ManualResetEvent(); - ~ManualResetEvent(); -private: - ManualResetEvent(const ManualResetEvent& copy); - ManualResetEvent& operator=(const ManualResetEvent& other); + ManualResetEvent() noexcept; + ~ManualResetEvent() noexcept; + + ManualResetEvent(const ManualResetEvent& copy) = delete; + ManualResetEvent& operator=(const ManualResetEvent& other) = delete; ///@} public: @@ -91,15 +92,15 @@ class ManualResetEvent * @brief Waits until event is set, checking it each time interval. * @param timeout time interval to check. */ - void wait(uint timeout = _kiInfinite); + void wait(uint timeout = _kiInfinite) noexcept; /** * @brief resets the event. */ - void reset(); + void reset() noexcept; /** * @brief sets the event. */ - void set(); + void set() noexcept; ///@} private: From d557d12922278e437b888a138177e881c661d8f0 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 12 Jun 2025 11:06:17 +0200 Subject: [PATCH 067/112] asyncdb: fixed possible data race condition in CDataBaseAsyncHelper::tick CDataBase: moved mysql include files from .h to .cpp. receive.cpp: do not take CScriptTriggerArgs from the standard script parser buffer pool, because networking might happen in a different thread than script parsing. --- src/common/CDataBase.cpp | 46 +++++++++++++++++------------- src/common/CDataBase.h | 14 ++++------ src/game/CContainer.h | 4 +-- src/game/CObjBase.h | 2 +- src/game/chars/CCharAttacker.cpp | 1 - src/network/receive.cpp | 48 ++++++++++++++++++++++++++------ src/sphere/asyncdb.cpp | 38 +++++++++++++------------ src/sphere/asyncdb.h | 14 +++++----- 8 files changed, 102 insertions(+), 65 deletions(-) diff --git a/src/common/CDataBase.cpp b/src/common/CDataBase.cpp index f3f199138..b8b0561db 100644 --- a/src/common/CDataBase.cpp +++ b/src/common/CDataBase.cpp @@ -9,9 +9,17 @@ #include "CScriptTriggerArgs.h" #include "CDataBase.h" +#include // mysql standard include +#include // this needs to be defined AFTER common.h + + +struct MySQLDataWrapper +{ + MYSQL *ptr; +}; CDataBase::CDataBase() : - m_fConnected(false), _myData(nullptr) + _myData(std::make_unique()), m_fConnected(false) { } @@ -37,8 +45,8 @@ bool CDataBase::Connect(const char *user, const char *password, const char *base return false; } - _myData = mysql_init(_myData ? _myData : nullptr); - if ( !_myData ) + _myData->ptr = mysql_init(_myData->ptr ? _myData->ptr : nullptr); + if ( !_myData->ptr ) return false; int portnum = 0; @@ -52,12 +60,12 @@ bool CDataBase::Connect(const char *user, const char *password, const char *base host = pcTemp; } - if ( !mysql_real_connect(_myData, host, user, password, base, portnum, nullptr, CLIENT_MULTI_STATEMENTS ) ) + if ( !mysql_real_connect(_myData->ptr, host, user, password, base, portnum, nullptr, CLIENT_MULTI_STATEMENTS ) ) { - const char *error = mysql_error(_myData); + const char *error = mysql_error(_myData->ptr); g_Log.Event(LOGM_NOCONTEXT|LOGL_ERROR, "MariaDB connect fail with error: %s\n", error); - mysql_close(_myData); - _myData = nullptr; + mysql_close(_myData->ptr); + _myData->ptr = nullptr; return false; } @@ -80,8 +88,8 @@ void CDataBase::Close() { ADDTOCALLSTACK("CDataBase::Close"); SimpleThreadLock lock(m_connectionMutex); - mysql_close(_myData); - _myData = nullptr; + mysql_close(_myData->ptr); + _myData->ptr = nullptr; m_fConnected = false; } @@ -104,17 +112,17 @@ bool CDataBase::query(const char *query, CVarDefMap & mapQueryResult) * finish the query -and- retrieve the results */ SimpleThreadLock lock(m_connectionMutex); - result = mysql_query(_myData, query); + result = mysql_query(_myData->ptr, query); if ( result == 0 ) { - m_res = mysql_store_result(_myData); + m_res = mysql_store_result(_myData->ptr); if ( m_res == nullptr ) return false; } else { - myErr = mysql_error(_myData); + myErr = mysql_error(_myData->ptr); } } @@ -188,12 +196,12 @@ bool CDataBase::exec(const char *query) { // connection can only handle one query at a time, so we need to lock until we finish SimpleThreadLock lock(m_connectionMutex); - result = mysql_query(_myData, query); + result = mysql_query(_myData->ptr, query); if (result == 0) { // even though we don't want (or expect) any result data, we must retrieve // is anyway otherwise we will lose our connection to the server - MYSQL_RES* res = mysql_store_result(_myData); + MYSQL_RES* res = mysql_store_result(_myData->ptr); if (res != nullptr) mysql_free_result(res); @@ -201,7 +209,7 @@ bool CDataBase::exec(const char *query) } else { - const char *myErr = mysql_error(_myData); + const char *myErr = mysql_error(_myData->ptr); g_Log.Event(LOGM_NOCONTEXT|LOGL_ERROR, "MariaDB query \"%s\" failed due to \"%s\"\n", query, ( *myErr ? myErr : "unknown reason")); } @@ -243,7 +251,7 @@ bool CDataBase::addQuery(bool isQuery, lpctstr theFunction, lpctstr theQuery) } } -void CDataBase::addQueryResult(CSString & theFunction, CScriptTriggerArgs * theResult) +void CDataBase::addQueryResult(CSString & theFunction, CScriptTriggerArgsPtr theResult) { SimpleThreadLock stlThelock(m_resultMutex); @@ -267,7 +275,7 @@ bool CDataBase::_OnTick() if ( isConnected() ) // currently connected - just check that the link is alive { SimpleThreadLock lock(m_connectionMutex); - const int iPingRet = mysql_ping(_myData); + const int iPingRet = mysql_ping(_myData->ptr); if ( iPingRet ) { g_Log.EventError("MariaDB server link has been lost (error code: %d). Trying to reattach to it.\n", iPingRet); @@ -286,7 +294,7 @@ bool CDataBase::_OnTick() FunctionArgsPair_t currentPair; { SimpleThreadLock lock(m_resultMutex); - currentPair = m_QueryArgs.front(); + currentPair = m_QueryArgs.front(); m_QueryArgs.pop(); } @@ -434,7 +442,7 @@ bool CDataBase::r_WriteVal(lpctstr ptcKey, CSString &sVal, CTextConsole *pSrc, b tchar * escapedString = Str_GetTemp(); SimpleThreadLock lock(m_connectionMutex); - if ( isConnected() && mysql_real_escape_string(_myData, escapedString, ptcKey, (uint)(strlen(ptcKey))) ) + if ( isConnected() && mysql_real_escape_string(_myData->ptr, escapedString, ptcKey, (uint)(strlen(ptcKey))) ) { sVal = escapedString; } diff --git a/src/common/CDataBase.h b/src/common/CDataBase.h index 1cf7bc6d0..22ff94b82 100644 --- a/src/common/CDataBase.h +++ b/src/common/CDataBase.h @@ -12,12 +12,8 @@ #include "CVarDefMap.h" #include -#include // mysql standard include -#include // this needs to be defined AFTER common.h - - #define MIN_MARIADB_VERSION_ALLOW 30002 - +struct MySQLDataWrapper; class CDataBase : public CScriptObj { @@ -40,7 +36,7 @@ class CDataBase : public CScriptObj bool queryf(CVarDefMap & mapQueryResult, char *fmt, ...) SPHERE_PRINTFARGS(3,4); bool exec(const char *query); // executes query (pretty faster) for ALTER, UPDATE, INSERT, DELETE, ... bool execf(char *fmt, ...) SPHERE_PRINTFARGS(2,3); - void addQueryResult(CSString & theFunction, CScriptTriggerArgs * theResult); + void addQueryResult(CSString & theFunction, CScriptTriggerArgsPtr theResult); // set / get / info methods bool isConnected(); @@ -66,9 +62,9 @@ class CDataBase : public CScriptObj typedef std::queue QueueFunction_t; protected: - bool m_fConnected; // are we online? - MYSQL *_myData; // mySQL link - QueueFunction_t m_QueryArgs; + std::unique_ptr _myData; // mySQL link + bool m_fConnected; // are we online? + QueueFunction_t m_QueryArgs; private: SimpleMutex m_connectionMutex; diff --git a/src/game/CContainer.h b/src/game/CContainer.h index 958ac6bbc..c2ea6b602 100644 --- a/src/game/CContainer.h +++ b/src/game/CContainer.h @@ -136,7 +136,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m CItem * ContentFind( CResourceID const& rid, dword dwArg = 0, int iDescendLevels = 255 ) const; /** - * @fn TRIGRET_TYPE CContainer::OnContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, RESOURCE_ID_BASE rid, dword dwArg = 0, int iDescendLevels = 255 ); + * @fn TRIGRET_TYPE CContainer::OnContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgsPtr pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, RESOURCE_ID_BASE rid, dword dwArg = 0, int iDescendLevels = 255 ); * @brief Executes the container trigger for loop action. * @param [in,out] s The CScript to process. * @param [in,out] pSrc If non-null, source for the. @@ -153,7 +153,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m TRIGRET_TYPE OnContTriggerForLoop(CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, CResourceID const& rid, dword dwArg = 0, int iDescendLevels = 255 ); /** - * @fn TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); + * @fn TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgsPtr pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); * @brief Executes the generic container trigger for loop action. * @param [in,out] s The CScript to process. * @param [in,out] pSrc If non-null, source for the. diff --git a/src/game/CObjBase.h b/src/game/CObjBase.h index 017724708..66bb871a7 100644 --- a/src/game/CObjBase.h +++ b/src/game/CObjBase.h @@ -897,7 +897,7 @@ public: virtual bool IsDeleted() const override; = 0; /** - * @fn TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CChar * pSrc, CScriptTriggerArgs * pArgs ); + * @fn TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CChar * pSrc, CScriptTriggerArgsPtr pArgs ); * * @brief Spell's trigger (@Effect, @Start...). * diff --git a/src/game/chars/CCharAttacker.cpp b/src/game/chars/CCharAttacker.cpp index 0989818b8..7bcfd39a2 100644 --- a/src/game/chars/CCharAttacker.cpp +++ b/src/game/chars/CCharAttacker.cpp @@ -326,7 +326,6 @@ bool CChar::Attacker_Delete(std::vector::iterator &itAttacker, bo if (IsTrigUsed(TRIGGER_COMBATDELETE)) { CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - CScriptTriggerArgs Args; pScriptArgs->m_iN1 = fForced; pScriptArgs->m_iN2 = (int)type; TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatDelete, pScriptArgs, pChar); diff --git a/src/network/receive.cpp b/src/network/receive.cpp index e1136f6ab..a5dcac1fe 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -201,7 +201,10 @@ bool PacketCreate::doCreate(CNetState* net, lpctstr charname, bool fFemale, RACE ASSERT(pChar != nullptr); TRIGRET_TYPE tr = TRIGRET_RET_DEFAULT; - CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + // Networking might be done in a different thread. Do not use the standard object pool, since it's thread unsafe. + //CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + CScriptTriggerArgsPtr pScriptArgs = std::make_shared(); // RW pScriptArgs->m_iN1 = uiFlags; pScriptArgs->m_iN2 = prProf; @@ -2749,7 +2752,10 @@ bool PacketArrowClick::onReceive(CNetState* net) if ( IsTrigUsed(TRIGGER_USERQUESTARROWCLICK) ) { - CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + // Networking might be done in a different thread. Do not use the standard object pool, since it's thread unsafe. + //CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + CScriptTriggerArgsPtr pScriptArgs = std::make_shared(); pScriptArgs->m_iN1 = (fRightClick == true? 1 : 0); #ifdef _ALPHASPHERE pScriptArgs->m_iN2 = character->GetKeyNum("ARROWQUEST_X", true); @@ -3241,7 +3247,11 @@ bool PacketBandageMacro::onReceive(CNetState* net) //Should we simulate the dclick? client->m_Targ_UID = bandage->GetUID(); - CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + // Networking might be done in a different thread. Do not use the standard object pool, since it's thread unsafe. + //CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + CScriptTriggerArgsPtr pScriptArgs = std::make_shared(); pScriptArgs->m_iN1 = 1; // Signal we're from the macro if (bandage->OnTrigger( ITRIG_DCLICK, pScriptArgs, character) == TRIGRET_RET_TRUE) { @@ -3334,7 +3344,11 @@ bool PacketGargoyleFly::onReceive(CNetState* net) if ( IsTrigUsed(TRIGGER_TOGGLEFLYING) ) { - if ( character->OnTrigger(CTRIG_ToggleFlying, CScriptTriggerArgsPtr{}, character) == TRIGRET_RET_TRUE ) + // Networking might be done in a different thread. Do not use the standard object pool, since it's thread unsafe. + //CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + CScriptTriggerArgsPtr pScriptArgs = std::make_shared(); + if ( character->OnTrigger(CTRIG_ToggleFlying, pScriptArgs, character) == TRIGRET_RET_TRUE ) return false; } @@ -4154,7 +4168,13 @@ bool PacketGuildButton::onReceive(CNetState* net) return false; if ( IsTrigUsed(TRIGGER_USERGUILDBUTTON) ) - character->OnTrigger(CTRIG_UserGuildButton, CScriptTriggerArgsPtr{}, character); + { + // Networking might be done in a different thread. Do not use the standard object pool, since it's thread unsafe. + //CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + CScriptTriggerArgsPtr pScriptArgs = std::make_shared(); + character->OnTrigger(CTRIG_UserGuildButton, pScriptArgs, character); + } return true; } @@ -4181,8 +4201,14 @@ bool PacketQuestButton::onReceive(CNetState* net) return false; if ( IsTrigUsed(TRIGGER_USERQUESTBUTTON) ) - character->OnTrigger(CTRIG_UserQuestButton, CScriptTriggerArgsPtr{}, character); - return true; + { + // Networking might be done in a different thread. Do not use the standard object pool, since it's thread unsafe. + //CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + CScriptTriggerArgsPtr pScriptArgs = std::make_shared(); + character->OnTrigger(CTRIG_UserQuestButton, pScriptArgs, character); + } + return true; } @@ -4744,7 +4770,13 @@ bool PacketUltimaStoreButton::onReceive(CNetState *net) return false; if (IsTrigUsed(TRIGGER_USERULTIMASTOREBUTTON)) - character->OnTrigger(CTRIG_UserUltimaStoreButton, CScriptTriggerArgsPtr{}, character); + { + // Networking might be done in a different thread. Do not use the standard object pool, since it's thread unsafe. + //CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + + CScriptTriggerArgsPtr pScriptArgs = std::make_shared(); + character->OnTrigger(CTRIG_UserUltimaStoreButton, pScriptArgs, character); + } return true; } diff --git a/src/sphere/asyncdb.cpp b/src/sphere/asyncdb.cpp index 7eece99e5..f17277a67 100644 --- a/src/sphere/asyncdb.cpp +++ b/src/sphere/asyncdb.cpp @@ -1,5 +1,6 @@ #include "../common/CScriptObj.h" +//#include "../common/CScriptParserBufs.h" #include "../common/CScriptTriggerArgs.h" #include "../game/CServer.h" #include "asyncdb.h" @@ -20,28 +21,29 @@ void CDataBaseAsyncHelper::onStart() void CDataBaseAsyncHelper::tick() { - if ( !m_queriesTodo.empty() ) - { - QueryBlob_t currentPair; - { - SimpleThreadLock lock(m_queryMutex); - currentPair = m_queriesTodo.front(); - m_queriesTodo.pop_front(); - } + SimpleThreadLock lock(m_queryMutex); + if ( m_queriesTodo.empty() ) + return; - FunctionQueryPair_t currentFunctionPair = currentPair.second; + QueryBlob_t currentPair; + currentPair = m_queriesTodo.front(); + m_queriesTodo.pop_front(); + lock.unlock(); - CScriptTriggerArgs * theArgs = new CScriptTriggerArgs(); - theArgs->m_iN1 = currentPair.first; - theArgs->m_s1 = currentFunctionPair.second; + FunctionQueryPair_t currentFunctionPair = currentPair.second; - if ( currentPair.first ) - theArgs->m_iN2 = g_Serv._hDb.query(currentFunctionPair.second, theArgs->m_VarsLocal); - else - theArgs->m_iN2 = g_Serv._hDb.exec(currentFunctionPair.second); + // Don't take it from CScriptParserBufs, since we are using a thread-unsafe object pool (script parsing is done in one thread only) + //CScriptTriggerArgsPtr theArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + auto theArgs = std::make_shared(); + theArgs->m_iN1 = currentPair.first; + theArgs->m_s1 = currentFunctionPair.second; - g_Serv._hDb.addQueryResult(currentFunctionPair.first, theArgs); - } + if ( currentPair.first ) + theArgs->m_iN2 = g_Serv._hDb.query(currentFunctionPair.second, theArgs->m_VarsLocal); + else + theArgs->m_iN2 = g_Serv._hDb.exec(currentFunctionPair.second); + + //g_Serv._hDb.addQueryResult(currentFunctionPair.first, std::move(theArgs)); } void CDataBaseAsyncHelper::waitForClose() diff --git a/src/sphere/asyncdb.h b/src/sphere/asyncdb.h index 2bc3018c0..c7020c77a 100644 --- a/src/sphere/asyncdb.h +++ b/src/sphere/asyncdb.h @@ -25,15 +25,15 @@ class CDataBaseAsyncHelper : public AbstractSphereThread public: CDataBaseAsyncHelper(void); - ~CDataBaseAsyncHelper(void); -private: - CDataBaseAsyncHelper(const CDataBaseAsyncHelper& copy); - CDataBaseAsyncHelper& operator=(const CDataBaseAsyncHelper& other); + virtual ~CDataBaseAsyncHelper(void) override; + + CDataBaseAsyncHelper(const CDataBaseAsyncHelper& copy) = delete; + CDataBaseAsyncHelper& operator=(const CDataBaseAsyncHelper& other) = delete; public: - virtual void onStart(); - virtual void tick(); - virtual void waitForClose(); + virtual void onStart() override; + virtual void tick() override; + virtual void waitForClose() override; public: void addQuery(bool isQuery, lpctstr sFunction, lpctstr sQuery); From e37e9de6a95ac96a6fa41fd86da409c1104cb01b Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 12 Jun 2025 13:39:52 +0200 Subject: [PATCH 068/112] Changed enum SERVMODE_TYPE to ServMode enum class. Split Loading into two phases, scripts/other and savefiles. --- src/common/resource/CResourceScript.cpp | 4 +-- src/game/CObjBase.cpp | 8 ++--- src/game/CServer.cpp | 46 +++++++++++++++++-------- src/game/CServer.h | 39 +++++++++++---------- src/game/CServerConfig.cpp | 8 ++--- src/game/CWorld.cpp | 26 +++++++------- src/game/chars/CChar.cpp | 2 +- src/game/items/CItem.cpp | 4 +-- src/game/spheresvr.cpp | 16 +++++---- src/sphere/ntwindow.cpp | 21 ++++++----- 10 files changed, 96 insertions(+), 78 deletions(-) diff --git a/src/common/resource/CResourceScript.cpp b/src/common/resource/CResourceScript.cpp index f290f0bbb..93833d500 100644 --- a/src/common/resource/CResourceScript.cpp +++ b/src/common/resource/CResourceScript.cpp @@ -74,9 +74,9 @@ bool CResourceScript::Open( lpctstr pszFilename, uint wFlags ) if ( CheckForChange() ) { // what should we do about it ? reload it of course ! - g_Serv.SetServerMode(SERVMODE_ResyncLoad); + g_Serv.SetServerMode(ServMode::ResyncLoad); g_Cfg.LoadResourcesOpen( this, true ); - g_Serv.SetServerMode(SERVMODE_Run); + g_Serv.SetServerMode(ServMode::Run); } } ASSERT(HasCache()); diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index f117b1a4f..935224428 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -207,9 +207,7 @@ void CObjBase::DeletePrepare() CObjBase::_GoSleep(); // virtual, but superclass methods are called in their ::DeletePrepare methods - const SERVMODE_TYPE servMode = g_Serv.GetServerMode(); - const bool fDestroyingWorld = (servMode == SERVMODE_Exiting || servMode == SERVMODE_Loading); - if (!fDestroyingWorld) + if (!g_Serv.IsDestroyingWorld()) { RemoveFromView(); } @@ -249,9 +247,7 @@ bool CObjBase::Delete(bool fForce) EXC_TRY("Cleanup in Delete method"); bool fScheduleDeletion = true; - const SERVMODE_TYPE servMode = g_Serv.GetServerMode(); - const bool fDestroyingWorld = (servMode == SERVMODE_Exiting || servMode == SERVMODE_Loading); - if (fDestroyingWorld) + if (g_Serv.IsDestroyingWorld()) { // Why resort to _uiInternalStateFlags and not simply check if GetParent() is a CSectorObjCont* ? // Because at this point CSObjContRec::RemoveSelf might have been called (it depends on how CObjBase::Delete was called, diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index 38acbde62..0dbe7a5bd 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -362,7 +362,7 @@ CServer::CServer() : CServerDef( SPHERE_TITLE, CSocketAddressIP( SOCKET_LOCAL_AD m_fConsoleTextReadyFlag = false; // we are in start up mode. // IsLoading() - SetServerMode( SERVMODE_PreLoadingINI ); + SetServerMode( ServMode::StartupPreLoadingIni ); memset(m_PacketFilter, 0, sizeof(m_PacketFilter)); memset(m_OutPacketFilter, 0, sizeof(m_OutPacketFilter)); @@ -438,7 +438,12 @@ bool CServer::SetProcessPriority(int iPriorityLevel) return fSuccess; } -void CServer::SetServerMode( SERVMODE_TYPE mode ) +ServMode CServer::GetServerMode() const noexcept +{ + return m_iModeCode.load(std::memory_order_acquire); +} + +void CServer::SetServerMode( ServMode mode ) { ADDTOCALLSTACK("CServer::SetServerMode"); m_iModeCode.store(mode, std::memory_order_release); @@ -453,13 +458,14 @@ bool CServer::IsValidBusy() const // ? switch ( GetServerMode() ) { - case SERVMODE_Saving: + case ServMode::Saving: if ( g_World.IsSaving() ) return true; break; - case SERVMODE_Loading: - case SERVMODE_GarbageCollection: - case SERVMODE_RestockAll: // these may look stuck but are not. + case ServMode::StartupLoadingSaves: + case ServMode::StartupLoadingScripts: + case ServMode::GarbageCollection: + case ServMode::RestockAll: // these may look stuck but are not. return true; default: return false; @@ -482,12 +488,18 @@ void CServer::SetExitFlag(int iFlag) noexcept bool CServer::IsLoading() const noexcept { - return ( m_fResyncPause || (GetServerMode() > SERVMODE_Run) ); + return ( m_fResyncPause || (GetServerMode() > ServMode::Run) ); } bool CServer::IsResyncing() const noexcept { - return m_fResyncPause || (GetServerMode() == SERVMODE_ResyncLoad); + return m_fResyncPause || (GetServerMode() == ServMode::ResyncLoad); +} + +bool CServer::IsDestroyingWorld() const noexcept +{ + const ServMode servMode = GetServerMode(); + return (servMode == ServMode::Exiting || servMode == ServMode::StartupLoadingScripts || servMode == ServMode::StartupLoadingSaves); } void CServer::Shutdown( int64 iMinutes ) // If shutdown is initialized @@ -626,25 +638,29 @@ lpctstr CServer::GetStatusString( byte iIndex ) const // typical (first time) poll response. { std::string cliver = m_ClientVersion.GetClientVer(); - snprintf(pTemp, Str_TempLength(), SPHERE_TITLE ", Name=%s, Port=%d, Ver=" SPHERE_BUILD_INFO_STR ", TZ=%d, EMail=%s, URL=%s, Lang=%s, CliVer=%s\n", + snprintf(pTemp, Str_TempLength(), + SPHERE_TITLE ", Name=%s, Port=%d, Ver=" SPHERE_BUILD_INFO_STR ", TZ=%d, EMail=%s, URL=%s, Lang=%s, CliVer=%s\n", GetName(), m_ip.GetPort(), m_TimeZone, m_sEMail.GetBuffer(), m_sURL.GetBuffer(), m_sLang.GetBuffer(), cliver.c_str()); } break; case 0x22: // '"' { // shown in the INFO page in game. - snprintf(pTemp, Str_TempLength(), SPHERE_TITLE ", Name=%s, Age=%" PRId64 ", Clients=%" PRIuSIZE_T ", Items=%" PRIuSIZE_T ", Chars=%" PRIuSIZE_T ", Mem=%" PRIuSIZE_T "K\n", + snprintf(pTemp, Str_TempLength(), + SPHERE_TITLE ", Name=%s, Age=%" PRId64 ", Clients=%" PRIuSIZE_T ", Items=%" PRIuSIZE_T ", Chars=%" PRIuSIZE_T ", Mem=%" PRIuSIZE_T "K\n", GetName(), iHours, iClients, StatGet(SERV_STAT_ITEMS), StatGet(SERV_STAT_CHARS), StatGet(SERV_STAT_MEM)); } break; case 0x24: // '$' // show at startup. - snprintf(pTemp, Str_TempLength(), "Admin=%s, URL=%s, Lang=%s, TZ=%d\n", + snprintf(pTemp, Str_TempLength(), + "Admin=%s, URL=%s, Lang=%s, TZ=%d\n", m_sEMail.GetBuffer(), m_sURL.GetBuffer(), m_sLang.GetBuffer(), m_TimeZone); break; case 0x25: // '%' // ConnectUO Status string - snprintf(pTemp, Str_TempLength(), SPHERE_TITLE " Items=%" PRIuSIZE_T ", Mobiles=%" PRIuSIZE_T ", Clients=%" PRIuSIZE_T ", Mem=%" PRIuSIZE_T, + snprintf(pTemp, Str_TempLength(), + SPHERE_TITLE " Items=%" PRIuSIZE_T ", Mobiles=%" PRIuSIZE_T ", Clients=%" PRIuSIZE_T ", Mem=%" PRIuSIZE_T, StatGet(SERV_STAT_ITEMS), StatGet(SERV_STAT_CHARS), iClients, StatGet(SERV_STAT_MEM)); break; } @@ -2458,12 +2474,12 @@ void CServer::SetResyncPause(bool fPause, CTextConsole * pSrc, bool fMessage) pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_START)); g_Cfg.Unload(true); - SetServerMode(SERVMODE_ResyncPause); + SetServerMode(ServMode::ResyncPause); } else { g_Log.Event(LOGL_EVENT, "%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_RESTART)); - SetServerMode(SERVMODE_ResyncLoad); + SetServerMode(ServMode::ResyncLoad); if ( !g_Cfg.Load(true) ) { @@ -2486,7 +2502,7 @@ void CServer::SetResyncPause(bool fPause, CTextConsole * pSrc, bool fMessage) g_World.SyncGameTime(); - SetServerMode(SERVMODE_Run); + SetServerMode(ServMode::Run); } } diff --git a/src/game/CServer.h b/src/game/CServer.h index 709920b40..153a91537 100644 --- a/src/game/CServer.h +++ b/src/game/CServer.h @@ -18,18 +18,19 @@ class CItemShip; -enum SERVMODE_TYPE +enum class ServMode { - SERVMODE_RestockAll, // Major event, potentially slow. - SERVMODE_GarbageCollection, // Executing the garbage collection, potentially demanding. - SERVMODE_Saving, // Forced save freezes the system. - SERVMODE_Run, // Game is up and running. - SERVMODE_ResyncPause, // Server paused during resync. - - SERVMODE_PreLoadingINI, // Initial (first) parsing of the sphere.ini - SERVMODE_Loading, // Initial load. - SERVMODE_ResyncLoad, // Loading after resync. - SERVMODE_Exiting // Closing down. + RestockAll, // Major event, potentially slow. + GarbageCollection, // Executing the garbage collection, potentially demanding. + Saving, // Forced save freezes the system. + Run, // Game is up and running. + ResyncPause, // Server paused during resync. + + StartupPreLoadingIni, // Initial (first) parsing of the sphere.ini + StartupLoadingScripts, // Initial load. + StartupLoadingSaves, + ResyncLoad, // Loading after resync. + Exiting // Closing down. }; @@ -39,7 +40,7 @@ extern class CServer : public CServerDef, public CTextConsole public: static const char* m_sClassName; - std::atomic m_iModeCode; // Just some error code to return to system. + std::atomic m_iModeCode; // Query the server state. std::atomic_int m_iExitFlag; // identifies who caused the exit. <0 = error bool m_fResyncPause; // Server is temporarily halted so files can be updated. CTextConsole* m_fResyncRequested; // A resync pause has been requested by this source. @@ -78,17 +79,17 @@ extern class CServer : public CServerDef, public CTextConsole CServer& operator=(const CServer& other) = delete; public: - inline SERVMODE_TYPE GetServerMode() const noexcept { - return m_iModeCode.load(std::memory_order_acquire); - } + void SetServerMode( ServMode mode ); + ServMode GetServerMode() const noexcept; + bool IsLoading() const noexcept; + bool IsResyncing() const noexcept; + bool IsDestroyingWorld() const noexcept; - void SetServerMode( SERVMODE_TYPE mode ); bool IsValidBusy() const; + int GetExitFlag() const noexcept; void SetExitFlag(int iFlag) noexcept; - bool IsLoading() const noexcept; - bool IsResyncing() const noexcept; - void Shutdown( int64 iMinutes ); + void Shutdown( int64 iMinutes ); void SetSignals( bool fMsg = true ); bool SetProcessPriority(int iPriorityLevel); diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index e3687392f..72d0b0c92 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -1060,9 +1060,9 @@ bool CServerConfig::r_LoadVal( CScript &s ) ADDTOCALLSTACK("CServerConfig::r_LoadVal"); EXC_TRY("LoadVal"); -#define DEBUG_MSG_NOINIT(x) if (g_Serv.GetServerMode() != SERVMODE_PreLoadingINI) DEBUG_MSG(x) -#define LOG_WARN_NOINIT(x) if (g_Serv.GetServerMode() != SERVMODE_PreLoadingINI) g_Log.EventWarn(x) -#define LOG_ERR_NOINIT(x) if (g_Serv.GetServerMode() != SERVMODE_PreLoadingINI) g_Log.EventError(x) +#define DEBUG_MSG_NOINIT(x) if (g_Serv.GetServerMode() != ServMode::StartupPreLoadingIni) DEBUG_MSG(x) +#define LOG_WARN_NOINIT(x) if (g_Serv.GetServerMode() != ServMode::StartupPreLoadingIni) g_Log.EventWarn(x) +#define LOG_ERR_NOINIT(x) if (g_Serv.GetServerMode() != ServMode::StartupPreLoadingIni) g_Log.EventError(x) int i = FindCAssocRegTableHeadSorted( s.GetKey(), reinterpret_cast(sm_szLoadKeys), ARRAY_COUNT( sm_szLoadKeys )-1, sizeof(sm_szLoadKeys[0])); if ( i < 0 ) @@ -4376,7 +4376,7 @@ CResourceID CServerConfig::ResourceGetNewID( RES_TYPE restype, lpctstr pszName, return rid; } #ifdef _DEBUG - if ( g_Serv.GetServerMode() != SERVMODE_ResyncLoad ) // this really is ok. + if ( g_Serv.GetServerMode() != ServMode::ResyncLoad ) // this really is ok. { // Warn of duplicates. size_t duplicateIndex = m_ResHash.FindKey( rid ); diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index b74ab9f51..6485b4874 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -413,10 +413,10 @@ void CWorldThread::ScheduleObjDeletion(CSObjContRec* obj) { // If the world is being destroyed, do not schedule the object for deletion but delete it right away. const auto servMode = g_Serv.GetServerMode(); - // I can't destroy it while SERVMODE_Loading, because the script parser can't know (without creating a global state holder, TODO) that this + // I can't destroy it while ServMode::LoadingScripts/Saves, because the script parser can't know (without creating a global state holder, TODO) that this // object was deleted/destroyed. The object pointer will become invalid, and if something uses it, even for calling a method, Sphere will crash. - //const bool fDestroy = (servMode == SERVMODE_Exiting || servMode == SERVMODE_Loading); - const bool fDestroy = (servMode == SERVMODE_Exiting); + //const bool fDestroy = g_Serv.IsDestroyingWorld(); + const bool fDestroy = (servMode == ServMode::Exiting); if (fDestroy) { @@ -947,7 +947,7 @@ bool CWorld::SaveForce() // Save world state if (g_NetworkManager.isOutputThreaded() == false) g_NetworkManager.flushAllClients(); - g_Serv.SetServerMode(SERVMODE_Saving); // Forced save freezes the system. + g_Serv.SetServerMode(ServMode::Saving); // Forced save freezes the system. bool fSave = true; bool fSuccess = true; @@ -1003,7 +1003,7 @@ bool CWorld::SaveForce() // Save world state fSuccess = false; } - g_Serv.SetServerMode(SERVMODE_Run); // Game is up and running + g_Serv.SetServerMode(ServMode::Run); // Game is up and running return fSuccess; } @@ -1448,14 +1448,14 @@ bool CWorld::LoadWorld() // Load world from script bool CWorld::LoadAll() // Load world from script { - // start count. (will grow as needed) + // start count (will grow as needed). _GameClock.Init(); // will be loaded from the world file. // Load all the accounts. if ( !g_Accounts.Account_LoadAll(false) ) return false; - // Try to load the world and chars files . + // Try to load the world and chars files. if ( !LoadWorld() ) return false; @@ -1652,7 +1652,7 @@ void CWorld::RespawnDeadNPCs() { ADDTOCALLSTACK("CWorld::RespawnDeadNPCs"); // Respawn dead story NPC's - g_Serv.SetServerMode(SERVMODE_RestockAll); + g_Serv.SetServerMode(ServMode::RestockAll); for ( int m = 0; m < MAP_SUPPORTED_QTY; ++m ) { if ( !g_MapList.IsMapSupported(m) ) @@ -1669,7 +1669,7 @@ void CWorld::RespawnDeadNPCs() EXC_CATCH; } } - g_Serv.SetServerMode(SERVMODE_Run); + g_Serv.SetServerMode(ServMode::Run); } void CWorld::Restock() @@ -1677,7 +1677,7 @@ void CWorld::Restock() ADDTOCALLSTACK("CWorld::Restock"); // Recalc all the base items as well. g_Log.Event(LOGL_EVENT, "World Restock: started.\n"); - g_Serv.SetServerMode(SERVMODE_RestockAll); + g_Serv.SetServerMode(ServMode::RestockAll); for ( size_t i = 0; i < ARRAY_COUNT(g_Cfg.m_ResHash.m_Array); ++i ) { @@ -1710,7 +1710,7 @@ void CWorld::Restock() } } - g_Serv.SetServerMode(SERVMODE_Run); + g_Serv.SetServerMode(ServMode::Run); g_Log.Event(LOGL_EVENT, "World Restock: done.\n"); } @@ -1760,10 +1760,10 @@ void CWorld::GarbageCollection() { ADDTOCALLSTACK("CWorld::GarbageCollection"); g_Log.Flush(); - g_Serv.SetServerMode(SERVMODE_GarbageCollection); + g_Serv.SetServerMode(ServMode::GarbageCollection); g_Log.Event(LOGL_EVENT|LOGM_NOCONTEXT, "Garbage Collection: started.\n"); GarbageCollection_UIDs(); - g_Serv.SetServerMode(SERVMODE_Run); + g_Serv.SetServerMode(ServMode::Run); g_Log.Flush(); } diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index d9a37f778..9154c09a2 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -596,7 +596,7 @@ void CChar::ClearPlayer() } else { - if (g_Serv.GetServerMode() != SERVMODE_Exiting) + if (g_Serv.GetServerMode() != ServMode::Exiting) { g_Log.EventWarn("Character '%s'(UID 0%x) on account '%s' as been deleted.\n", GetName(), (dword)GetUID(), pAccount->GetName()); } diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 2069e80bc..6dac7ba34 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -3273,8 +3273,8 @@ bool CItem::r_LoadVal( CScript & s ) // Load an item Script bool normcont = LoadSetContainer(CUID(s.GetArgDWVal()), (LAYER_TYPE)GetUnkZ()); if (!normcont) { - SERVMODE_TYPE iModeCode = g_Serv.GetServerMode(); - if ((iModeCode == SERVMODE_Loading) || (iModeCode == SERVMODE_GarbageCollection)) + ServMode iModeCode = g_Serv.GetServerMode(); + if ((iModeCode == ServMode::StartupLoadingSaves) || (iModeCode == ServMode::GarbageCollection)) Delete(); // since the item is no longer in container, it should be deleted } return normcont; diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index dc2f1be82..1488c6303 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -268,12 +268,12 @@ static bool WritePidFile(int iMode = 0) int Sphere_InitServer( int argc, char *argv[] ) { constexpr const char *m_sClassName = "SphereInit"; - EXC_TRY("Init Server"); + EXC_TRY("Init Server"); GlobalInitializer::InitRuntimeDefaultValues(); EXC_SET_BLOCK("loading ini and scripts"); if ( !g_Serv.Load() ) - return -3; + return -3; if ( argc > 1 ) { @@ -287,7 +287,9 @@ int Sphere_InitServer( int argc, char *argv[] ) EXC_SET_BLOCK("sockets init"); if ( !g_Serv.SocketsInit() ) return -9; + EXC_SET_BLOCK("load world"); + g_Serv.SetServerMode(ServMode::StartupLoadingSaves); if ( !g_World.LoadAll() ) return -8; @@ -328,7 +330,7 @@ int Sphere_InitServer( int argc, char *argv[] ) g_Cfg.PrintEFOFFlags(); EXC_SET_BLOCK("finalizing"); - g_Serv.SetServerMode(SERVMODE_Run); // ready to go. + g_Serv.SetServerMode(ServMode::Run); // ready to go. g_Log.Event(LOGM_INIT, "%s", g_Serv.GetStatusString(0x24)); g_Log.Event(LOGM_INIT, "\nStartup complete (items=%" PRIuSIZE_T ", chars=%" PRIuSIZE_T ", Accounts = %" PRIuSIZE_T ")\n", g_Serv.StatGet(SERV_STAT_ITEMS), g_Serv.StatGet(SERV_STAT_CHARS), g_Serv.StatGet(SERV_STAT_ACCOUNTS)); @@ -360,7 +362,7 @@ void Sphere_ExitServer() // Trigger server quit g_Serv.r_Call("f_onserver_exit", CScriptTriggerArgsPtr{}, &g_Serv); - g_Serv.SetServerMode(SERVMODE_Exiting); + g_Serv.SetServerMode(ServMode::Exiting); g_NetworkManager.stop(); g_Main.waitForClose(); @@ -546,7 +548,7 @@ int main( int argc, char * argv[] ) #ifndef _WIN32 // We need to find out the log files folder... look it up in the .ini file (on Windows it's done in WinMain function). - g_Serv.SetServerMode(SERVMODE_PreLoadingINI); + g_Serv.SetServerMode(ServMode::StartupPreLoadingIni); // Parse command line arguments. for (int argn = 1; argn < argc; ++argn) @@ -570,8 +572,8 @@ int main( int argc, char * argv[] ) g_Cfg.LoadIni(false); #endif - g_Serv.SetServerMode(SERVMODE_Loading); - g_Serv.SetExitFlag( Sphere_InitServer( argc, argv )); + g_Serv.SetServerMode(ServMode::StartupLoadingScripts); + g_Serv.SetExitFlag( Sphere_InitServer( argc, argv )); if ( ! g_Serv.GetExitFlag() ) { WritePidFile(); diff --git a/src/sphere/ntwindow.cpp b/src/sphere/ntwindow.cpp index 88ee0cff5..e70946e15 100644 --- a/src/sphere/ntwindow.cpp +++ b/src/sphere/ntwindow.cpp @@ -972,29 +972,32 @@ void CNTWindow::NTWindow_CheckUpdateWindowTitle() LPCTSTR pszMode; switch ( g_Serv.GetServerMode() ) { - case SERVMODE_RestockAll: // Major event. + case ServMode::RestockAll: // Major event. pszMode = "Restocking"; break; - case SERVMODE_GarbageCollection: // Major event. + case ServMode::GarbageCollection: // Major event. pszMode = "Collecting Garbage"; break; - case SERVMODE_Saving: // Forced save freezes the system. + case ServMode::Saving: // Forced save freezes the system. pszMode = "Saving"; break; - case SERVMODE_Run: // Game is up and running + case ServMode::Run: // Game is up and running pszMode = "Running"; break; - case SERVMODE_PreLoadingINI: - case SERVMODE_Loading: // Initial load. + case ServMode::StartupPreLoadingIni: + case ServMode::StartupLoadingScripts: // Initial load. pszMode = "Loading"; break; - case SERVMODE_ResyncPause: + case ServMode::StartupLoadingSaves: // Initial load. + pszMode = "Loading (savefiles)"; + break; + case ServMode::ResyncPause: pszMode = "Resync Pause"; break; - case SERVMODE_ResyncLoad: // Loading after resync + case ServMode::ResyncLoad: // Loading after resync pszMode = "Resync Load"; break; - case SERVMODE_Exiting: // Closing down + case ServMode::Exiting: // Closing down pszMode = "Exiting"; break; default: From fb297e46f4766e28e04e6b5d047263b8e59a2b27 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 12 Jun 2025 13:48:20 +0200 Subject: [PATCH 069/112] Made VarDef overwrite warning be issued only during startup script parsing. --- src/common/CLog.cpp | 2 +- src/common/CVarDefMap.cpp | 9 +++++---- src/game/CObjBase.cpp | 16 ++++++++-------- src/game/CSector.cpp | 2 +- src/game/CServer.cpp | 18 +++++++++++++++--- src/game/CServer.h | 4 +++- src/game/CServerConfig.cpp | 8 ++++---- src/game/CWorld.cpp | 6 +++--- src/game/chars/CChar.cpp | 4 ++-- src/game/chars/CCharAct.cpp | 24 ++++++++++++------------ src/game/chars/CCharNPC.cpp | 2 +- src/game/chars/CCharPlayer.cpp | 4 ++-- src/game/chars/CCharSkill.cpp | 2 +- src/game/chars/CCharSpell.cpp | 2 +- src/game/chars/CCharStat.cpp | 4 ++-- src/game/chars/CCharStatus.cpp | 2 +- src/game/chars/CStoneMember.cpp | 2 +- src/game/clients/CAccount.cpp | 10 +++++----- src/game/clients/CClient.cpp | 2 +- src/game/components/CCChampion.cpp | 10 +++++----- src/game/components/CCItemDamageable.cpp | 4 ++-- src/game/components/CCMultiMovable.cpp | 2 +- src/game/components/CCSpawn.cpp | 4 ++-- src/game/items/CItem.cpp | 4 ++-- src/game/items/CItemContainer.cpp | 6 +++--- src/game/items/CItemMulti.cpp | 18 +++++++++--------- src/game/items/CItemMultiCustom.cpp | 10 +++++----- src/game/items/CItemShip.cpp | 2 +- src/game/spheresvr.cpp | 2 +- src/game/triggers.cpp | 4 ++-- src/network/CNetworkInput.cpp | 2 +- 31 files changed, 103 insertions(+), 88 deletions(-) diff --git a/src/common/CLog.cpp b/src/common/CLog.cpp index 3571ca8d3..9703d0fc3 100644 --- a/src/common/CLog.cpp +++ b/src/common/CLog.cpp @@ -277,7 +277,7 @@ int CLog::EventStr( dword dwMask, lpctstr pszMsg, ConsoleTextColor iLogColor) no // Print to screen. if ( !(dwMask & LOGF_LOGFILE_ONLY) ) { - if ( !(dwMask & LOGM_INIT) && !g_Serv.IsLoading() ) + if ( !(dwMask & LOGM_INIT) && !g_Serv.IsLoadingGeneric() ) { g_Serv.PrintStr(CTCOL_YELLOW, szTime ); } diff --git a/src/common/CVarDefMap.cpp b/src/common/CVarDefMap.cpp index b7a223a96..24e7583e1 100644 --- a/src/common/CVarDefMap.cpp +++ b/src/common/CVarDefMap.cpp @@ -406,15 +406,16 @@ CVarDefContNum* CVarDefMap::SetNum( lpctstr pszName, int64 iVal, bool fDeleteZer return SetNumNew( pszName, iVal ); CVarDefContNum * pVarNum = dynamic_cast ( pVarBase ); + const bool fShouldWarn = fWarnOverwrite && g_Serv.IsStartupLoadingScripts(); if ( pVarNum ) { - if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoading() ) + if ( fShouldWarn ) g_Log.EventWarn( "Replacing existing VarNum '%s' with number: 0x%" PRIx64" \n", pVarBase->GetKey(), iVal ); pVarNum->SetValNum( iVal ); } else { - if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoading() ) + if ( fShouldWarn ) g_Log.EventWarn( "Replacing existing VarStr '%s' with number: 0x%" PRIx64" \n", pVarBase->GetKey(), iVal ); return SetNumOverride( pszName, iVal ); } @@ -489,13 +490,13 @@ CVarDefCont* CVarDefMap::SetStr( lpctstr pszName, bool fQuoted, lpctstr pszVal, CVarDefContStr * pVarStr = dynamic_cast ( pVarBase ); if ( pVarStr ) { - if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoading() ) + if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoadingGeneric() ) g_Log.EventWarn( "Replacing existing VarStr '%s' with string: '%s'\n", pVarBase->GetKey(), pszVal ); pVarStr->SetValStr( pszVal ); } else { - if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoading() ) + if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoadingGeneric() ) g_Log.EventWarn( "Replacing existing VarNum '%s' with string: '%s'\n", pVarBase->GetKey(), pszVal ); return SetStrOverride( pszName, pszVal ); } diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index 935224428..d3fd8bb9b 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -121,7 +121,7 @@ CObjBase::CObjBase( bool fItem ) : m_PropertyHash = 0; m_PropertyRevision = 0; - if ( g_Serv.IsLoading()) + if ( g_Serv.IsLoadingGeneric()) { // Don't do this yet if we are loading. UID will be set later. // Just say if this is an item or not. @@ -324,7 +324,7 @@ void CObjBase::SetHueQuick(HUE_TYPE wHue) void CObjBase::SetHue( HUE_TYPE wHue, bool fAvoidTrigger, CTextConsole *pSrc, CObjBase *pSourceObj, llong iSound) { ADDTOCALLSTACK("CObjBase::SetHue"); - if (g_Serv.IsLoading()) //We do not want tons of @Dye being called during world load, just set the hue then continue... + if (g_Serv.IsLoadingGeneric()) //We do not want tons of @Dye being called during world load, just set the hue then continue... { m_wHue = wHue; return; @@ -376,7 +376,7 @@ int CObjBase::IsWeird() const { return( iResultCode ); } - if ( ! g_Serv.IsLoading()) + if ( ! g_Serv.IsLoadingGeneric()) { if ( GetUID().ObjFind() != this ) // make sure it's linked both ways correctly. { @@ -1920,7 +1920,7 @@ bool CObjBase::r_LoadVal( CScript & s ) } const HUE_TYPE hue = (HUE_TYPE)s.GetArgVal(); SetHue(hue, false, &g_Serv); //@Dye is called from @Create/.xcolor/script command here // since we can not receive pSrc on this r_LoadVal function ARGO/SRC will be null - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) Update(); } break; @@ -1974,7 +1974,7 @@ bool CObjBase::r_LoadVal( CScript & s ) case OC_TIMER: { int64 iTimeout = s.GetArg64Val(); - if (g_Serv.IsLoading()) + if (g_Serv.IsLoadingGeneric()) { const int iPrevBuild = g_World.m_iPrevBuild; /* @@ -2006,7 +2006,7 @@ bool CObjBase::r_LoadVal( CScript & s ) SetTimeStampS(s.GetArgLLVal()); break; case OC_SPAWNITEM: - if ( !g_Serv.IsLoading() ) // SPAWNITEM is read-only + if ( !g_Serv.IsLoadingGeneric() ) // SPAWNITEM is read-only return false; _uidSpawn.SetObjUID(s.GetArgDWVal()); break; @@ -3155,7 +3155,7 @@ dword CObjBase::UpdatePropertyRevision(dword hash) void CObjBase::UpdatePropertyFlag() { ADDTOCALLSTACK("CObjBase::UpdatePropertyFlag"); - if (!(g_Cfg.m_iFeatureAOS & FEATURE_AOS_UPDATE_B) || g_Serv.IsLoading()) + if (!(g_Cfg.m_iFeatureAOS & FEATURE_AOS_UPDATE_B) || g_Serv.IsLoadingGeneric()) return; m_fStatusUpdate |= SU_UPDATE_TOOLTIP; @@ -3315,7 +3315,7 @@ void CObjBase::ResendTooltip(bool fSendFull, bool fUseCache) ADDTOCALLSTACK("CObjBase::ResendTooltip"); // Send tooltip packet to all nearby clients - if (g_Serv.IsLoading()) + if (g_Serv.IsLoadingGeneric()) return; else if ( IsAosFlagEnabled(FEATURE_AOS_UPDATE_B) == false ) return; // tooltips are disabled. diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index 198438060..f0fdc3c3d 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -808,7 +808,7 @@ void CSector::SetLightNow( bool fFlash ) } // don't fire trigger when server is loading or light is flashing - if (( ! g_Serv.IsLoading() && fFlash == false ) && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) + if (( ! g_Serv.IsLoadingGeneric() && fFlash == false ) && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) { pChar->OnTrigger( CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar ); } diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index 0dbe7a5bd..07192ecd5 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -382,7 +382,7 @@ void CServer::SetSignals( bool fMsg ) #ifndef _WIN32 SetUnixSignals(g_Cfg.m_fSecure); LOG_TYPE lt = LOGM_INIT; - if (!IsLoading()) { + if (!IsLoadingGeneric()) { lt = (LOG_TYPE)((uint)lt | (uint)LOGL_EVENT); } if ( g_Cfg.m_fSecure ) @@ -395,7 +395,7 @@ void CServer::SetSignals( bool fMsg ) } #endif - if ( fMsg && !IsLoading() ) + if ( fMsg && !IsLoadingGeneric() ) { CWorldComm::Broadcast( g_Cfg.m_fSecure ? "The world is now running in SECURE MODE." : @@ -486,7 +486,19 @@ void CServer::SetExitFlag(int iFlag) noexcept m_iExitFlag.store(iFlag, std::memory_order_release); } -bool CServer::IsLoading() const noexcept +bool CServer::IsStartupLoadingScripts() const noexcept +{ + return (GetServerMode() > ServMode::StartupLoadingScripts); +} + +/* +bool CServer::IsStartupLoadingSaves() const noexcept +{ + return (GetServerMode() > ServMode::StartupLoadingSaves); +} +*/ + +bool CServer::IsLoadingGeneric() const noexcept { return ( m_fResyncPause || (GetServerMode() > ServMode::Run) ); } diff --git a/src/game/CServer.h b/src/game/CServer.h index 153a91537..9efc49fc7 100644 --- a/src/game/CServer.h +++ b/src/game/CServer.h @@ -81,7 +81,9 @@ extern class CServer : public CServerDef, public CTextConsole public: void SetServerMode( ServMode mode ); ServMode GetServerMode() const noexcept; - bool IsLoading() const noexcept; + bool IsStartupLoadingScripts() const noexcept; + //bool IsStartupLoadingSaves() const noexcept; + bool IsLoadingGeneric() const noexcept; bool IsResyncing() const noexcept; bool IsDestroyingWorld() const noexcept; diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 72d0b0c92..8ae54cdd1 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -1417,7 +1417,7 @@ bool CServerConfig::r_LoadVal( CScript &s ) case RC_SECURE: m_fSecure = (s.GetArgVal() != 0); - if ( !g_Serv.IsLoading() ) + if ( !g_Serv.IsLoadingGeneric() ) g_Serv.SetSignals(); break; @@ -1489,7 +1489,7 @@ bool CServerConfig::r_LoadVal( CScript &s ) break; case RC_NETWORKTHREADS: - if (g_Serv.IsLoading()) + if (g_Serv.IsLoadingGeneric()) { int iNetThreads = s.GetArgVal(); //if (iNetThreads < 0) @@ -4732,7 +4732,7 @@ void CServerConfig::_OnTick( bool fNow ) { ADDTOCALLSTACK("CServerConfig::_OnTick"); // Give a tick to the less critical stuff. - if ( !fNow && ( g_Serv.IsLoading() || ( m_timePeriodic > CWorldGameTime::GetCurrentTime().GetTimeRaw()) ) ) + if ( !fNow && ( g_Serv.IsLoadingGeneric() || ( m_timePeriodic > CWorldGameTime::GetCurrentTime().GetTimeRaw()) ) ) return; if ( this->m_fUseHTTP ) @@ -4766,7 +4766,7 @@ void CServerConfig::_OnTick( bool fNow ) void CServerConfig::PrintEFOFFlags(bool bEF, bool bOF, CTextConsole *pSrc) { ADDTOCALLSTACK("CServerConfig::PrintEFOFFlags"); - if ( g_Serv.IsLoading() ) + if ( g_Serv.IsLoadingGeneric() ) return; #define catresname(a,b) \ diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index 6485b4874..f4cbbd90d 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -1618,7 +1618,7 @@ bool CWorld::r_LoadVal( CScript &s ) m_iSaveCountID = s.GetArgVal(); break; case WC_TIME: // "TIME" - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { g_Log.EventError("Can't set TIME while server is running.\n"); return false; @@ -1626,7 +1626,7 @@ bool CWorld::r_LoadVal( CScript &s ) _GameClock.InitTime( s.GetArgLLVal() * MSECS_PER_SEC); break; case WC_TIMEHIRES: // "TIMEHIRES" - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { g_Log.EventError("Can't set TIMEHIRES while server is running.\n"); return false; @@ -1773,7 +1773,7 @@ void CWorld::_OnTick() // 256 real secs = 1 server hour. 19 light levels. check every 10 minutes or so. // Do not tick while loading (startup, resync, exiting...) or when double ticking in the same msec?. - if (g_Serv.IsLoading() || !_GameClock.Advance()) + if (g_Serv.IsLoadingGeneric() || !_GameClock.Advance()) return; EXC_TRY("CWorld Tick"); diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index 9154c09a2..d5e581f38 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -3833,7 +3833,7 @@ bool CChar::r_LoadVal( CScript & s ) }break; case CHC_CREATE: { - if (g_Serv.IsLoading()) + if (g_Serv.IsLoadingGeneric()) { _iTimeCreate = (CWorldGameTime::GetCurrentTime().GetTimeRaw() - (s.GetArgLLVal() * MSECS_PER_TENTH)); break; @@ -3865,7 +3865,7 @@ bool CChar::r_LoadVal( CScript & s ) } case CHC_FLAGS: { - if (g_Serv.IsLoading()) + if (g_Serv.IsLoadingGeneric()) { // Don't set STATF_SAVEPARITY at server startup, otherwise the first worldsave will not save these chars _uiStatFlag = s.GetArgLLVal() & ~ (uint64)STATF_SAVEPARITY; diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 462871d28..c18eb9ee4 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -263,7 +263,7 @@ void CChar::LayerAdd( CItem * pItem, LAYER_TYPE layer ) // NOTE: CanEquipLayer may bounce an item . If it stacks with this we are in trouble ! } - if ( g_Serv.IsLoading() == false ) + if ( g_Serv.IsLoadingGeneric() == false ) { // This takes care of any conflicting items in the slot ! layer = CanEquipLayer(pItem, layer, nullptr, false); @@ -406,7 +406,7 @@ void CChar::OnRemoveObj( CSObjContRec* pObRec ) // Override this = called when r LAYER_TYPE layer = pItem->GetEquipLayer(); if (( IsTrigUsed(TRIGGER_UNEQUIP) ) || ( IsTrigUsed(TRIGGER_ITEMUNEQUIP) )) { - if ( layer != LAYER_DRAGGING && ! g_Serv.IsLoading()) + if ( layer != LAYER_DRAGGING && ! g_Serv.IsLoadingGeneric()) pItem->OnTrigger( ITRIG_UNEQUIP, CScriptTriggerArgsPtr{}, this ); } @@ -700,7 +700,7 @@ void CChar::SysMessage( lpctstr pMsg ) const // Push a message back to the clien void CChar::UpdateStatsFlag() const { ADDTOCALLSTACK("CChar::UpdateStatsFlag"); - if ( g_Serv.IsLoading() ) + if ( g_Serv.IsLoadingGeneric() ) return; if ( IsClientActive() ) @@ -712,7 +712,7 @@ void CChar::UpdateStatsFlag() const void CChar::UpdateHitsFlag() { ADDTOCALLSTACK("CChar::UpdateHitsFlag"); - if ( g_Serv.IsLoading() ) + if ( g_Serv.IsLoadingGeneric() ) return; m_fStatusUpdate |= SU_UPDATE_HITS; @@ -724,7 +724,7 @@ void CChar::UpdateHitsFlag() void CChar::UpdateModeFlag() { ADDTOCALLSTACK("CChar::UpdateModeFlag"); - if ( g_Serv.IsLoading() ) + if ( g_Serv.IsLoadingGeneric() ) return; m_fStatusUpdate |= SU_UPDATE_MODE; @@ -733,7 +733,7 @@ void CChar::UpdateModeFlag() void CChar::UpdateManaFlag() const { ADDTOCALLSTACK("CChar::UpdateManaFlag"); - if ( g_Serv.IsLoading() ) + if ( g_Serv.IsLoadingGeneric() ) return; if ( IsClientActive() ) @@ -743,7 +743,7 @@ void CChar::UpdateManaFlag() const void CChar::UpdateStamFlag() const { ADDTOCALLSTACK("CChar::UpdateStamFlag"); - if ( g_Serv.IsLoading() ) + if ( g_Serv.IsLoadingGeneric() ) return; if ( IsClientActive() ) @@ -2460,7 +2460,7 @@ void CChar::UpdateMode( bool fFull, CClient * pExcludeClient ) void CChar::UpdateSpeedMode() { ADDTOCALLSTACK("CChar::UpdateSpeedMode"); - if ( g_Serv.IsLoading() || !m_pPlayer ) + if ( g_Serv.IsLoadingGeneric() || !m_pPlayer ) return; if ( IsClientActive() ) @@ -2470,7 +2470,7 @@ void CChar::UpdateSpeedMode() void CChar::UpdateVisualRange() { ADDTOCALLSTACK("CChar::UpdateVisualRange"); - if ( g_Serv.IsLoading() || !m_pPlayer ) + if ( g_Serv.IsLoadingGeneric() || !m_pPlayer ) return; DEBUG_WARN(("CChar::UpdateVisualRange called, m_iVisualRange is %d\n", m_iVisualRange)); @@ -5100,7 +5100,7 @@ bool CChar::MoveToRegion( CRegionWorld * pNewArea, bool fAllowReject ) if ( m_pArea == pNewArea ) return true; - if ( ! g_Serv.IsLoading()) + if ( ! g_Serv.IsLoadingGeneric()) { if ( fAllowReject && IsPriv( PRIV_GM )) { @@ -5208,7 +5208,7 @@ bool CChar::MoveToRoom( CRegion * pNewRoom, bool fAllowReject) if ( m_pRoom == pNewRoom ) return true; - if ( ! g_Serv.IsLoading()) + if ( ! g_Serv.IsLoadingGeneric()) { if ( fAllowReject && IsPriv( PRIV_GM )) { @@ -5322,7 +5322,7 @@ bool CChar::MoveToChar(const CPointMap& pt, bool fStanding, bool fCheckLocationE if ( !m_fClimbUpdated || fForceFix ) FixClimbHeight(); - if ( fSectorChanged && !g_Serv.IsLoading() ) + if ( fSectorChanged && !g_Serv.IsLoadingGeneric() ) { if ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) ) { diff --git a/src/game/chars/CCharNPC.cpp b/src/game/chars/CCharNPC.cpp index da9e8e751..7768aeb4a 100644 --- a/src/game/chars/CCharNPC.cpp +++ b/src/game/chars/CCharNPC.cpp @@ -78,7 +78,7 @@ bool CCharNPC::r_LoadVal( CChar * pChar, CScript &s ) //Set as numbers only case CNC_BONDED: m_bonded = (s.GetArgVal() > 0); - if ( !g_Serv.IsLoading() ) + if ( !g_Serv.IsLoadingGeneric() ) pChar->UpdatePropertyFlag(); break; case CNC_ACTPRI: diff --git a/src/game/chars/CCharPlayer.cpp b/src/game/chars/CCharPlayer.cpp index 2c3f98fda..343c3e33d 100644 --- a/src/game/chars/CCharPlayer.cpp +++ b/src/game/chars/CCharPlayer.cpp @@ -374,7 +374,7 @@ bool CCharPlayer::r_LoadVal( CChar * pChar, CScript &s ) case CPC_ADDHOUSE: { - if (g_Serv.IsLoading()) //Prevent to load it from saves, it may cause a crash when accessing to a non-yet existant multi + if (g_Serv.IsLoadingGeneric()) //Prevent to load it from saves, it may cause a crash when accessing to a non-yet existant multi { break; } @@ -403,7 +403,7 @@ bool CCharPlayer::r_LoadVal( CChar * pChar, CScript &s ) } case CPC_ADDSHIP: { - if (g_Serv.IsLoading()) //Prevent to load it from saves, it may cause a crash when accessing to a non-yet existant multi + if (g_Serv.IsLoadingGeneric()) //Prevent to load it from saves, it may cause a crash when accessing to a non-yet existant multi { break; } diff --git a/src/game/chars/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index e4d20ea7b..83c8278d4 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -4392,7 +4392,7 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) // RETURN: // false = failed outright with no wait. "You have no chance of taming this" - if ( g_Serv.IsLoading() ) + if ( g_Serv.IsLoadingGeneric() ) { if ( skill != SKILL_NONE && !IsSkillBase(skill) && !IsSkillNPC(skill) ) { diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index d7aa5ee7b..7c063ea16 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -590,7 +590,7 @@ void CChar::Spell_Effect_Remove(CItem * pSpell) { if (m_pPlayer) // summoned players ? thats odd. return; - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { Effect(EFFECT_XYZ, ITEMID_FX_TELE_VANISH, this, 8, 20); Sound(0x201); diff --git a/src/game/chars/CCharStat.cpp b/src/game/chars/CCharStat.cpp index 5307601bc..bcc75c720 100644 --- a/src/game/chars/CCharStat.cpp +++ b/src/game/chars/CCharStat.cpp @@ -343,7 +343,7 @@ void CChar::Stat_SetBase( STAT_TYPE i, ushort uiVal ) ASSERT(i >= 0 && i < STAT_QTY); ushort uiStatVal = Stat_GetBase(i); - if (IsTrigUsed(TRIGGER_STATCHANGE) && !g_Serv.IsLoading() && !IsTriggerActive("CREATE")) + if (IsTrigUsed(TRIGGER_STATCHANGE) && !g_Serv.IsLoadingGeneric() && !IsTriggerActive("CREATE")) { // Only Str, Dex, Int, Food fire @StatChange here if (i >= STAT_STR && i <= STAT_FOOD) @@ -684,7 +684,7 @@ void CChar::SetKarma(short iNewKarma, CChar* pNPC) m_iKarma = (short)(maximum(g_Cfg.m_iMinKarma, minimum(g_Cfg.m_iMaxKarma, iNewKarma))); - if ( !g_Serv.IsLoading() ) + if ( !g_Serv.IsLoadingGeneric() ) NotoSave_Update(); } diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index b0bc769ba..1e48cb456 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -157,7 +157,7 @@ CItemContainer *CChar::GetBank( LAYER_TYPE layer ) if ( pBankBox ) return pBankBox; - if ( !g_Serv.IsLoading() ) + if ( !g_Serv.IsLoadingGeneric() ) { if ( pItemTest ) { diff --git a/src/game/chars/CStoneMember.cpp b/src/game/chars/CStoneMember.cpp index 55f5b90de..5a22e4a14 100644 --- a/src/game/chars/CStoneMember.cpp +++ b/src/game/chars/CStoneMember.cpp @@ -381,7 +381,7 @@ CStoneMember::CStoneMember( CItemStone * pStone, CUID uid, STONEPRIV_TYPE iType, m_Member.m_iAccountGold = nAccountGold; - if ( ! g_Serv.IsLoading() && pStone->GetMemoryType()) + if ( ! g_Serv.IsLoadingGeneric() && pStone->GetMemoryType()) { CChar * pChar = uid.CharFind(); if ( pChar != nullptr ) diff --git a/src/game/clients/CAccount.cpp b/src/game/clients/CAccount.cpp index 18aaa58b8..c125c3100 100644 --- a/src/game/clients/CAccount.cpp +++ b/src/game/clients/CAccount.cpp @@ -232,7 +232,7 @@ void CAccounts::Account_Add( CAccount * pAccount ) { ADDTOCALLSTACK("CAccounts::Account_Add"); ASSERT(pAccount != nullptr); - if ( !g_Serv.IsLoading() ) + if ( !g_Serv.IsLoadingGeneric() ) { CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); pScriptArgs->Init(pAccount->GetName()); @@ -394,7 +394,7 @@ bool CAccounts::Cmd_ListUnused(CTextConsole * pSrc, lpctstr pszDays, lpctstr psz void CAccount::SetBlockStatus(bool fNewStatus) { - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (IsPriv(PRIV_BLOCKED) && fNewStatus == false) { @@ -617,7 +617,7 @@ void CAccount::DeleteChars() } // Now track down all my disconnected chars ! - if ( ! g_Serv.IsLoading()) + if ( ! g_Serv.IsLoadingGeneric()) { size_t i = m_Chars.GetCharCount(); while (i > 0) @@ -993,7 +993,7 @@ bool CAccount::SetPassword( lpctstr pszPassword, bool isMD5Hash ) bool useMD5 = g_Cfg.m_fMd5Passwords; //Accounts are 'created' in server startup so we don't fire the function. - if ( !g_Serv.IsLoading() ) + if ( !g_Serv.IsLoadingGeneric() ) { CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); pScriptArgs->Init(GetName()); @@ -1393,7 +1393,7 @@ bool CAccount::r_LoadVal( CScript & s ) break; case AC_CHARUID: // just ignore this ? chars are loaded later ! - if ( ! g_Serv.IsLoading()) + if ( ! g_Serv.IsLoadingGeneric()) { const CUID uid( s.GetArgVal()); CChar * pChar = uid.CharFind(); diff --git a/src/game/clients/CClient.cpp b/src/game/clients/CClient.cpp index 5e0a72999..e29c3d5ae 100644 --- a/src/game/clients/CClient.cpp +++ b/src/game/clients/CClient.cpp @@ -139,7 +139,7 @@ CClient::~CClient() noexcept bool CClient::CanInstantLogOut() const { ADDTOCALLSTACK("CClient::CanInstantLogOut"); - if ( g_Serv.IsLoading()) // or exiting. + if ( g_Serv.IsLoadingGeneric()) // or exiting. return true; if ( ! g_Cfg.m_iClientLingerTime ) return true; diff --git a/src/game/components/CCChampion.cpp b/src/game/components/CCChampion.cpp index 4c3a45feb..5e4cd7dec 100644 --- a/src/game/components/CCChampion.cpp +++ b/src/game/components/CCChampion.cpp @@ -398,7 +398,7 @@ void CCChampion::AddWhiteCandle(const CUID& uid) } pCandle->SetTopPoint(pt); - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (IsTrigUsed(TRIGGER_ADDWHITECANDLE)) { @@ -443,7 +443,7 @@ void CCChampion::AddRedCandle(const CUID& uid) return; _iSpawnsNextWhite = _iSpawnsNextRed / (CANDLESNEXTRED + 1); - if (!g_Serv.IsLoading()) // Do not remove white candles, while server is loading them from save. + if (!g_Serv.IsLoadingGeneric()) // Do not remove white candles, while server is loading them from save. { ClearWhiteCandles(); } @@ -520,7 +520,7 @@ void CCChampion::AddRedCandle(const CUID& uid) default: break; } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (IsTrigUsed(TRIGGER_ADDREDCANDLE)) { @@ -549,7 +549,7 @@ void CCChampion::AddRedCandle(const CUID& uid) void CCChampion::SetLevel(byte iLevel) { ADDTOCALLSTACK("CCChampion::SetLevel"); - if (g_Serv.IsLoading()) + if (g_Serv.IsLoadingGeneric()) return; _iLevel = iLevel; @@ -999,7 +999,7 @@ bool CCChampion::r_LoadVal(CScript& s) { case ICHMPL_ACTIVE: { - if (g_Serv.IsLoading() == true) //Only when the server is loading. + if (g_Serv.IsLoadingGeneric() == true) //Only when the server is loading. { _fActive = (bool)s.GetArgBVal(); } diff --git a/src/game/components/CCItemDamageable.cpp b/src/game/components/CCItemDamageable.cpp index 639a63e24..2017dd557 100644 --- a/src/game/components/CCItemDamageable.cpp +++ b/src/game/components/CCItemDamageable.cpp @@ -30,7 +30,7 @@ bool CCItemDamageable::CanSubscribe(const CItem* pItem) noexcept // static void CCItemDamageable::SetCurHits(word iCurHits) { - if (!g_Serv.IsLoading() && (_iCurHits != iCurHits)) + if (!g_Serv.IsLoadingGeneric() && (_iCurHits != iCurHits)) { _fNeedUpdate = true; } @@ -40,7 +40,7 @@ void CCItemDamageable::SetCurHits(word iCurHits) void CCItemDamageable::SetMaxHits(word iMaxHits) { - if (!g_Serv.IsLoading() && (_iMaxHits != iMaxHits)) + if (!g_Serv.IsLoadingGeneric() && (_iMaxHits != iMaxHits)) { _fNeedUpdate = true; } diff --git a/src/game/components/CCMultiMovable.cpp b/src/game/components/CCMultiMovable.cpp index 4e51d8e39..50c034d13 100644 --- a/src/game/components/CCMultiMovable.cpp +++ b/src/game/components/CCMultiMovable.cpp @@ -431,7 +431,7 @@ bool CCMultiMovable::MoveToRegion(CRegionWorld * pRegionOld, CRegionWorld *pRegi { return false; } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { // Leaving region trigger. (may not be allowed to leave ?) if (pRegionOld) diff --git a/src/game/components/CCSpawn.cpp b/src/game/components/CCSpawn.cpp index fdcf72df2..7ef1b980e 100644 --- a/src/game/components/CCSpawn.cpp +++ b/src/game/components/CCSpawn.cpp @@ -597,7 +597,7 @@ void CCSpawn::AddObj(const CUID& uid) bool fIsSpawnChar = (pSpawnItem->IsType(IT_SPAWN_CHAR) || pSpawnItem->IsType(IT_SPAWN_CHAMPION)); bool fIsSpawnChampion = pSpawnItem->IsType(IT_SPAWN_CHAMPION); - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { // Only checking UIDs when server is running because some of them may not yet exist when loading worldsave. if (!uid.IsValidUID()) @@ -1020,7 +1020,7 @@ bool CCSpawn::r_LoadVal(CScript & s) return false; } } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { FixDef(); SetTrackID(); diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 6dac7ba34..2ac4e1b60 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -1566,7 +1566,7 @@ bool CItem::MoveTo(const CPointMap& pt, bool fForceFix) // Put item on the groun pSector->MoveItemToSector(this); // This also awakes the item // Is this area too complex ? - if ( ! g_Serv.IsLoading()) + if ( ! g_Serv.IsLoadingGeneric()) { if (pSector->CheckItemComplexity()) { @@ -3003,7 +3003,7 @@ void CItem::r_LoadMore1(dword dwVal) case IT_SPAWN_CHAR: case IT_SPAWN_ITEM: case IT_SPAWN_CHAMPION: - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { CCSpawn* pSpawn = GetSpawn(); if (pSpawn) diff --git a/src/game/items/CItemContainer.cpp b/src/game/items/CItemContainer.cpp index 60c13e8bc..2c13b964a 100644 --- a/src/game/items/CItemContainer.cpp +++ b/src/game/items/CItemContainer.cpp @@ -549,7 +549,7 @@ void CItemContainer::ContentAdd( CItem *pItem, CPointMap pt, bool bForceNoStack, if ( pItem == this ) return; // infinite loop. - if ( !g_Serv.IsLoading() ) + if ( !g_Serv.IsLoadingGeneric() ) { switch (GetType()) { @@ -619,7 +619,7 @@ void CItemContainer::ContentAdd( CItem *pItem, CPointMap pt, bool bForceNoStack, if (pt.m_x < 0 && pt.m_y < 0) { // Try to stack it. - if ( !g_Serv.IsLoading() && pItem->Item_GetDef()->IsStackableType() && !bForceNoStack ) + if ( !g_Serv.IsLoadingGeneric() && pItem->Item_GetDef()->IsStackableType() && !bForceNoStack ) { for (CSObjContRec* pObjRec : *this) { @@ -740,7 +740,7 @@ void CItemContainer::ContentAdd( CItem *pItem, bool bForceNoStack ) return; // already here. CPointMap pt; // invalid point. - if ( g_Serv.IsLoading() ) + if ( g_Serv.IsLoadingGeneric() ) pt = pItem->GetUnkPoint(); ContentAdd(pItem, pt, bForceNoStack); diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index a57f35ca0..de110692a 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -737,7 +737,7 @@ void CItemMulti::AddCoowner(const CUID& uidCoowner) { return; } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (GetCoownerIndex(uidCoowner) >= 0) { @@ -815,7 +815,7 @@ void CItemMulti::AddFriend(const CUID& uidFriend) { return; } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (GetFriendIndex(uidFriend) >= 0) { @@ -892,7 +892,7 @@ void CItemMulti::AddBan(const CUID& uidBan) { return; } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (GetBanIndex(uidBan) >= 0) { @@ -967,7 +967,7 @@ void CItemMulti::AddAccess(const CUID& uidAccess) { return; } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (GetAccessIndex(uidAccess) >= 0) { @@ -1538,7 +1538,7 @@ void CItemMulti::AddAddon(const CUID& uidAddon) { return; } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (!pAddon->IsType(IT_MULTI_ADDON)) { @@ -1594,7 +1594,7 @@ void CItemMulti::AddComponent(const CUID& uidComponent) { return; } - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (GetComponentIndex(uidComponent) >= 0) { @@ -1751,7 +1751,7 @@ void CItemMulti::LockItem(const CUID& uidItem) } CItem *pItem = uidItem.ItemFind(); ASSERT(pItem); - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (GetLockedItemIndex(uidItem) >= 0 || GetSecuredContainerIndex(uidItem) >= 0) { @@ -1828,7 +1828,7 @@ void CItemMulti::Secure(const CUID& uidContainer) } CItemContainer *pContainer = static_cast(uidContainer.ItemFind()); ASSERT(pContainer); - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (GetSecuredContainerIndex(uidContainer) >= 0) { @@ -1911,7 +1911,7 @@ void CItemMulti::AddVendor(const CUID& uidVendor) { return; } - if ((g_Serv.IsLoading() == false) && (GetVendorIndex(uidVendor) >= 0)) + if ((g_Serv.IsLoadingGeneric() == false) && (GetVendorIndex(uidVendor) >= 0)) { return; } diff --git a/src/game/items/CItemMultiCustom.cpp b/src/game/items/CItemMultiCustom.cpp index f257e584f..27334a190 100644 --- a/src/game/items/CItemMultiCustom.cpp +++ b/src/game/items/CItemMultiCustom.cpp @@ -33,7 +33,7 @@ CItemMultiCustom::CItemMultiCustom(ITEMID_TYPE id, CItemBase * pItemDef) : m_rectDesignArea.SetRectEmpty(); _iMaxPlane = -1; - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { ResetStructure(); CommitChanges(); @@ -338,7 +338,7 @@ void CItemMultiCustom::CommitChanges(CClient * pClientSrc) CopyDesign(&m_designWorking, &m_designMain); const CPointMap ptMe = GetTopPoint(); - if (g_Serv.IsLoading() || !ptMe.IsValidPoint()) + if (g_Serv.IsLoadingGeneric() || !ptMe.IsValidPoint()) return; // remove all existing dynamic item fixtures @@ -461,7 +461,7 @@ void CItemMultiCustom::AddItem(CClient * pClientSrc, ITEMID_TYPE id, int16 x, in // fixtures are items that should be replaced by dynamic items const bool fFixture = (pItemBase->IsType(IT_DOOR) || pItemBase->IsType(IT_DOOR_LOCKED) || pItemBase->IsID_Door(id) || pItemBase->IsType(IT_TELEPAD)); - if (!g_Serv.IsLoading()) + if (!g_Serv.IsLoadingGeneric()) { if (!IsValidItem(id, pClientSrc, false)) { @@ -552,7 +552,7 @@ void CItemMultiCustom::AddItem(CClient * pClientSrc, ITEMID_TYPE id, int16 x, in m_designWorking.m_vectorComponents.emplace_back(pComponent); ++m_designWorking.m_iRevision; - if (!g_Serv.IsLoading()) // quick fix, change it to execute only on customize mode + if (!g_Serv.IsLoadingGeneric()) // quick fix, change it to execute only on customize mode { CItemContainer *pMovingCrate = static_cast(GetMovingCrate(true).ItemFind()); ASSERT(pMovingCrate); @@ -1841,7 +1841,7 @@ bool CItemMultiCustom::r_LoadVal(CScript & s) ADDTOCALLSTACK("CItemMultiCustom::r_LoadVal"); EXC_TRY("LoadVal"); - if (g_Serv.IsLoading()) + if (g_Serv.IsLoadingGeneric()) { if (s.IsKey("COMP")) { diff --git a/src/game/items/CItemShip.cpp b/src/game/items/CItemShip.cpp index a997e97d0..a4da6c6e2 100644 --- a/src/game/items/CItemShip.cpp +++ b/src/game/items/CItemShip.cpp @@ -201,7 +201,7 @@ bool CItemShip::r_LoadVal(CScript & s) EXC_TRY("LoadVal"); lpctstr ptcKey = s.GetKey(); IMCS_TYPE index = (IMCS_TYPE)FindTableHeadSorted(ptcKey, sm_szLoadKeys, ARRAY_COUNT(sm_szLoadKeys) - 1); - if (index >= 0 && g_Serv.IsLoading()) + if (index >= 0 && g_Serv.IsLoadingGeneric()) { switch (index) { diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 1488c6303..a383f8b36 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -476,7 +476,7 @@ static void Sphere_MainMonitorLoop() EXC_SET_BLOCK("Checks"); // Don't look for freezing when doing certain things. - if ( g_Serv.IsLoading() || ! g_Cfg.m_fSecure || g_Serv.IsValidBusy() ) + if ( g_Serv.IsLoadingGeneric() || ! g_Cfg.m_fSecure || g_Serv.IsValidBusy() ) continue; #ifndef _DEBUG diff --git a/src/game/triggers.cpp b/src/game/triggers.cpp index 8dd92fbcf..74271dd5a 100644 --- a/src/game/triggers.cpp +++ b/src/game/triggers.cpp @@ -26,7 +26,7 @@ static std::vector sm_vTriggersId; bool IsTrigUsed(E_TRIGGERS id) { - if ( g_Serv.IsLoading() == true) + if ( g_Serv.IsLoadingGeneric() == true) return false; return (( (uint)id < sm_vTriggersId.size() ) && sm_vTriggersId[id].m_used ); @@ -34,7 +34,7 @@ bool IsTrigUsed(E_TRIGGERS id) bool IsTrigUsed(const char *name) { - if ( g_Serv.IsLoading() == true) + if ( g_Serv.IsLoadingGeneric() == true) return false; const int index = FindTableSorted(name, kOrderedTrigsNames, ARRAY_COUNT(kOrderedTrigsNames)); diff --git a/src/network/CNetworkInput.cpp b/src/network/CNetworkInput.cpp index 70f050b37..57443969e 100644 --- a/src/network/CNetworkInput.cpp +++ b/src/network/CNetworkInput.cpp @@ -204,7 +204,7 @@ void CNetworkInput::processData() delete packet; } - if (g_Serv.IsLoading() == false) + if (g_Serv.IsLoadingGeneric() == false) { EXC_SET_BLOCK("start client profile"); const ProfileTask clientTask(PROFILE_CLIENTS); From 181b84025610aa2077ae66e665fb11860f474c32 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 12 Jun 2025 14:40:27 +0200 Subject: [PATCH 070/112] Fixed typo in asyncdb.cpp. --- src/sphere/asyncdb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sphere/asyncdb.cpp b/src/sphere/asyncdb.cpp index f17277a67..08dd73012 100644 --- a/src/sphere/asyncdb.cpp +++ b/src/sphere/asyncdb.cpp @@ -43,7 +43,7 @@ void CDataBaseAsyncHelper::tick() else theArgs->m_iN2 = g_Serv._hDb.exec(currentFunctionPair.second); - //g_Serv._hDb.addQueryResult(currentFunctionPair.first, std::move(theArgs)); + g_Serv._hDb.addQueryResult(currentFunctionPair.first, std::move(theArgs)); } void CDataBaseAsyncHelper::waitForClose() From 13978931e30abba0938698e3a12d5e1688dd5e0c Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 12 Jun 2025 14:41:12 +0200 Subject: [PATCH 071/112] Changed functions taking CScriptTriggerArgsPtr to take a const&, in order to avoid useless costly copies of shared_ptr. --- src/common/CDataBase.cpp | 2 +- src/common/CExpression.cpp | 17 +- src/common/CExpression.h | 8 +- src/common/CFloatMath.cpp | 244 +++++++++--------- src/common/CFloatMath.h | 10 +- src/common/CScriptObj.cpp | 32 ++- src/common/CScriptObj.h | 24 +- src/common/resource/sections/CDialogDef.cpp | 4 +- .../resource/sections/CRandGroupDef.cpp | 2 +- .../resource/sections/CRegionResourceDef.cpp | 4 +- .../resource/sections/CRegionResourceDef.h | 2 +- src/common/resource/sections/CWebPageDef.cpp | 19 +- src/game/CContainer.cpp | 16 +- src/game/CContainer.h | 8 +- src/game/CObjBase.cpp | 42 +-- src/game/CObjBase.h | 4 +- src/game/CRegion.cpp | 4 +- src/game/CSector.cpp | 8 +- src/game/CTimedFunctionHandler.cpp | 4 +- src/game/CTimedFunctionHandler.h | 2 +- src/game/CWorldTimedFunctions.cpp | 4 +- src/game/CWorldTimedFunctions.h | 2 +- src/game/chars/CChar.h | 12 +- src/game/chars/CCharAct.cpp | 22 +- src/game/chars/CCharAttacker.cpp | 4 +- src/game/chars/CCharFight.cpp | 4 +- src/game/chars/CCharMemory.cpp | 6 +- src/game/chars/CCharNPC.cpp | 4 +- src/game/chars/CCharNPCAct.cpp | 12 +- src/game/chars/CCharNPCPet.cpp | 4 +- src/game/chars/CCharSkill.cpp | 22 +- src/game/chars/CCharStatus.cpp | 10 +- src/game/chars/CCharUse.cpp | 2 +- src/game/clients/CClient.h | 4 +- src/game/clients/CClientDialog.cpp | 16 +- src/game/clients/CClientEvent.cpp | 16 +- src/game/clients/CClientMsg.cpp | 12 +- src/game/clients/CClientTarg.cpp | 2 +- src/game/clients/CClientUse.cpp | 10 +- src/game/clients/CParty.cpp | 8 +- src/game/components/CCChampion.cpp | 12 +- src/game/components/CCChampion.h | 2 +- src/game/components/CCMultiMovable.cpp | 2 +- src/game/items/CItem.cpp | 48 ++-- src/game/items/CItem.h | 4 +- src/game/items/CItemMulti.cpp | 2 +- src/game/items/CItemPlant.cpp | 2 +- src/game/spheresvr.cpp | 4 +- 48 files changed, 351 insertions(+), 357 deletions(-) diff --git a/src/common/CDataBase.cpp b/src/common/CDataBase.cpp index b8b0561db..cbc694cfa 100644 --- a/src/common/CDataBase.cpp +++ b/src/common/CDataBase.cpp @@ -255,7 +255,7 @@ void CDataBase::addQueryResult(CSString & theFunction, CScriptTriggerArgsPtr the { SimpleThreadLock stlThelock(m_resultMutex); - m_QueryArgs.push(FunctionArgsPair_t(theFunction, theResult)); + m_QueryArgs.push(FunctionArgsPair_t(theFunction, std::move(theResult))); } bool CDataBase::_OnTick() diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index a5b9a3a56..f73698833 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -2118,7 +2118,7 @@ CSString CExpression::GetRangeString(lpctstr & refStrExpr) bool CExpression::EvaluateConditionalSingle( CScriptSubExprState& refSubExprState, CScriptExprContext& refExprContext, - CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) + CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc) { ADDTOCALLSTACK("CExpression::EvaluateConditionalSingle"); @@ -2206,13 +2206,11 @@ bool CExpression::EvaluateConditionalSingle( return fVal; } -bool CExpression::EvaluateConditionalWhole(lptstr ptcExpr, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) +bool CExpression::EvaluateConditionalWhole(lptstr ptcExpr, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc) { ADDTOCALLSTACK("CExpression::EvaluateConditionalWhole"); ASSERT(refExprContext._pScriptObjI != nullptr); - - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + ASSERT(pScriptArgs); lptstr ptcExprDbg = ptcExpr; const auto pSubexprArena = GetConditionalSubexpressions(ptcExprDbg, _pBufs.get()->m_poolCScriptExprSubStatesPool); // number of arguments @@ -2336,7 +2334,7 @@ static void EvaluateConditionalQval_ParseArg(tchar* ptcSrc, tchar** ptcDest, lpc bool CExpression::EvaluateConditionalQval( lpctstr ptcKey, CSString& refStrVal, CScriptExprContext& pContext, - CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) + CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc) { // Do a switch ? type statement ADDTOCALLSTACK("CExpression::EvaluateConditionalQval"); @@ -2378,12 +2376,13 @@ bool CExpression::EvaluateConditionalQval( int CExpression::ParseScriptText( tchar * ptcResponse, CScriptExprContext& pContext, - CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, + CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, int iFlags) { ADDTOCALLSTACK("CScriptObj::ParseScriptText"); //ASSERT(ptcResponse[0] != ' '); // Not needed: i remove whitespaces and invalid characters here. ASSERT(pContext._pScriptObjI != nullptr); + ASSERT(pScriptArgs); // Take in a line of text that may have fields that can be replaced with operators here. // ARGS: @@ -2400,10 +2399,6 @@ int CExpression::ParseScriptText( // _iParseScriptText_Reentrant = 0; // _fParseScriptText_Brackets = false; // Am i evaluating a statement? (Am i inside < > brackets of a statement i am currently evaluating?) - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - - const bool fNoRecurseBrackets = ((iFlags & 2) != 0); // General purpose variables. diff --git a/src/common/CExpression.h b/src/common/CExpression.h index 58f7bad71..6c9908f04 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -250,18 +250,18 @@ class CExpression /* * @brief Do the first-level parsing of a script line and eventually replace requested values got by r_WriteVal. */ - int ParseScriptText( tchar * pszResponse, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, int iFlags = 0 ); + int ParseScriptText( tchar * pszResponse, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, int iFlags = 0 ); [[nodiscard]] - bool EvaluateConditionalWhole(lptstr ptcExpression, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + bool EvaluateConditionalWhole(lptstr ptcExpression, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc); private: // Arguments inside conditional statements: IF, ELIF, ELSEIF [[nodiscard]] - bool EvaluateConditionalSingle(CScriptSubExprState& refSubExprState, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + bool EvaluateConditionalSingle(CScriptSubExprState& refSubExprState, CScriptExprContext& refExprContext, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc); [[nodiscard]] - bool EvaluateConditionalQval(lpctstr ptcKey, CSString& refStrVal, CScriptExprContext& refContext, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + bool EvaluateConditionalQval(lpctstr ptcKey, CSString& refStrVal, CScriptExprContext& refContext, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc); [[nodiscard]] PrvBuffersPool::CSubExprStatesArenaPool_t::UniquePtr_t diff --git a/src/common/CFloatMath.cpp b/src/common/CFloatMath.cpp index 6d2c9de06..74eea4bbe 100644 --- a/src/common/CFloatMath.cpp +++ b/src/common/CFloatMath.cpp @@ -10,70 +10,70 @@ #include "CFloatMath.h" -CSString CFloatMath::FloatMath(lpctstr& Expr) +CSString CFloatMath::FloatMath(lpctstr& ptcRefExpr) { ADDTOCALLSTACK("CFloatMath::FloatMath"); char szReal[VARDEF_FLOAT_MAXBUFFERSIZE]; - snprintf(szReal, VARDEF_FLOAT_MAXBUFFERSIZE, "%f", MakeFloatMath(Expr)); + snprintf(szReal, VARDEF_FLOAT_MAXBUFFERSIZE, "%f", MakeFloatMath(ptcRefExpr)); return CSString(szReal); } static thread_local short int _iReentrant_Count = 0; -realtype CFloatMath::MakeFloatMath( lpctstr & Expr ) +realtype CFloatMath::MakeFloatMath(lpctstr & ptcRefExpr ) { ADDTOCALLSTACK("CFloatMath::MakeFloatMath"); - if ( ! Expr ) + if ( ! ptcRefExpr ) return 0; - GETNONWHITESPACE( Expr ); + GETNONWHITESPACE( ptcRefExpr ); ++_iReentrant_Count; if ( _iReentrant_Count > 128 ) { - DEBUG_WARN(( "Deadlock detected while parsing '%s'. Fix the error in your scripts.\n", Expr )); + DEBUG_WARN(( "Deadlock detected while parsing '%s'. Fix the error in your scripts.\n", ptcRefExpr )); --_iReentrant_Count; return 0; } //DEBUG_ERR(("Expr: '%s' GetSingle(Expr) '%f' GetValMath(GetSingle(Expr), Expr) '%f'\n",Expr,GetSingle(Expr),GetValMath(GetSingle(Expr), Expr))); - realtype dVal = GetValMath(GetSingle(Expr), Expr); + realtype dVal = GetValMath(GetSingle(ptcRefExpr), ptcRefExpr); --_iReentrant_Count; return dVal; } -realtype CFloatMath::GetValMath( realtype dVal, lpctstr & pExpr ) +realtype CFloatMath::GetValMath(realtype dVal, lpctstr & ptcRefExpr ) { ADDTOCALLSTACK("CFloatMath::GetValMath"); //DEBUG_ERR(("GetValMath dVal %f pExpr %s\n",dVal,pExpr)); - GETNONWHITESPACE(pExpr); + GETNONWHITESPACE(ptcRefExpr); // Look for math type operator. - switch ( pExpr[0] ) + switch ( ptcRefExpr[0] ) { case '\0': break; case ')': // expression end markers. case '}': case ']': - ++pExpr; // consume this. + ++ptcRefExpr; // consume this. break; case '+': - ++pExpr; - dVal += MakeFloatMath( pExpr ); + ++ptcRefExpr; + dVal += MakeFloatMath( ptcRefExpr ); break; case '-': - ++pExpr; - dVal -= MakeFloatMath( pExpr ); + ++ptcRefExpr; + dVal -= MakeFloatMath( ptcRefExpr ); break; case '*': - ++pExpr; - dVal *= MakeFloatMath( pExpr ); + ++ptcRefExpr; + dVal *= MakeFloatMath( ptcRefExpr ); break; case '/': - ++pExpr; + ++ptcRefExpr; { - realtype dTempVal = MakeFloatMath( pExpr ); + realtype dTempVal = MakeFloatMath( ptcRefExpr ); if ( ! dTempVal ) { g_Log.EventError("Evaluating float math: Divide by 0\n"); @@ -83,21 +83,21 @@ realtype CFloatMath::GetValMath( realtype dVal, lpctstr & pExpr ) } break; case '!': - ++pExpr; - if ( pExpr[0] != '=' ) + ++ptcRefExpr; + if ( ptcRefExpr[0] != '=' ) break; // boolean ! is handled as a single expresion. - ++pExpr; - dVal = ( dVal != MakeFloatMath( pExpr )); + ++ptcRefExpr; + dVal = ( dVal != MakeFloatMath( ptcRefExpr )); break; case '=': // boolean - while ( pExpr[0] == '=' ) - ++pExpr; - dVal = ( dVal == MakeFloatMath( pExpr )); + while ( ptcRefExpr[0] == '=' ) + ++ptcRefExpr; + dVal = ( dVal == MakeFloatMath( ptcRefExpr )); break; case '@': - ++pExpr; + ++ptcRefExpr; { - realtype dTempVal = MakeFloatMath( pExpr ); + realtype dTempVal = MakeFloatMath( ptcRefExpr ); if ( (dVal == 0) && (dTempVal <= 0) ) { DEBUG_ERR(( "Float_MakeFloatMath: Power of zero with zero or negative exponent is undefined\n" )); @@ -109,81 +109,81 @@ realtype CFloatMath::GetValMath( realtype dVal, lpctstr & pExpr ) break; //Following operations are not allowed with Double case '|': - ++pExpr; - if ( pExpr[0] == '|' ) // boolean ? + ++ptcRefExpr; + if ( ptcRefExpr[0] == '|' ) // boolean ? { - ++pExpr; - dVal = ( MakeFloatMath( pExpr ) || dVal ); + ++ptcRefExpr; + dVal = ( MakeFloatMath( ptcRefExpr ) || dVal ); } else // bitwise DEBUG_ERR(("Operator '%s' is not allowed with floats.\n","|")); break; case '&': - ++pExpr; - if ( pExpr[0] == '&' ) // boolean ? + ++ptcRefExpr; + if ( ptcRefExpr[0] == '&' ) // boolean ? { - ++pExpr; - dVal = ( MakeFloatMath( pExpr ) && dVal ); // tricky stuff here. logical ops must come first or possibly not get processed. + ++ptcRefExpr; + dVal = ( MakeFloatMath( ptcRefExpr ) && dVal ); // tricky stuff here. logical ops must come first or possibly not get processed. } else // bitwise DEBUG_ERR(("Operator '%s' is not allowed with floats.\n","&")); break; case '%': - ++pExpr; + ++ptcRefExpr; DEBUG_ERR(("Operator '%s' is not allowed with floats.\n","%")); break; case '^': - ++pExpr; + ++ptcRefExpr; DEBUG_ERR(("Operator '%s' is not allowed with floats.\n","^")); break; case '>': // boolean - ++pExpr; - if ( pExpr[0] == '=' ) // boolean ? + ++ptcRefExpr; + if ( ptcRefExpr[0] == '=' ) // boolean ? { - ++pExpr; - dVal = ( dVal >= MakeFloatMath( pExpr )); + ++ptcRefExpr; + dVal = ( dVal >= MakeFloatMath( ptcRefExpr )); } - else if ( pExpr[0] == '>' ) // shift + else if ( ptcRefExpr[0] == '>' ) // shift { - ++pExpr; + ++ptcRefExpr; DEBUG_ERR(("Operator '%s' is not allowed with floats.\n",">>")); } else { - dVal = ( dVal > MakeFloatMath( pExpr )); + dVal = ( dVal > MakeFloatMath( ptcRefExpr )); } break; case '<': // boolean - ++pExpr; - if ( pExpr[0] == '=' ) // boolean ? + ++ptcRefExpr; + if ( ptcRefExpr[0] == '=' ) // boolean ? { - ++pExpr; - dVal = ( dVal <= MakeFloatMath( pExpr )); + ++ptcRefExpr; + dVal = ( dVal <= MakeFloatMath( ptcRefExpr )); } - else if ( pExpr[0] == '<' ) // shift + else if ( ptcRefExpr[0] == '<' ) // shift { - ++pExpr; + ++ptcRefExpr; DEBUG_ERR(("Operator '%s' is not allowed with floats.\n","<<")); } else { - dVal = ( dVal < MakeFloatMath( pExpr )); + dVal = ( dVal < MakeFloatMath( ptcRefExpr )); } break; } return dVal; } -realtype CFloatMath::GetSingle( lpctstr & pArgs ) +realtype CFloatMath::GetSingle( lpctstr & ptcRefArgs ) { ADDTOCALLSTACK("CFloatMath::GetSingle"); - //DEBUG_ERR(("GetSingle pArgs %s\n",pArgs)); - GETNONWHITESPACE( pArgs ); - const size_t uiArgsCopySize = strlen(pArgs) + 1; - char * pArgsCopy = new char[uiArgsCopySize]; - Str_CopyLimitNull(pArgsCopy, pArgs, uiArgsCopySize); + //DEBUG_ERR(("GetSingle ptcRefArgs %s\n",ptcRefArgs)); + GETNONWHITESPACE( ptcRefArgs ); + const size_t uiArgsCopySize = strlen(ptcRefArgs) + 1; + char * ptcRefArgsCopy = new char[uiArgsCopySize]; + Str_CopyLimitNull(ptcRefArgsCopy, ptcRefArgs, uiArgsCopySize); /*bool IsNum = true; // Old Ellessar's code without support for negative numbers - for( char ch = tolower(*pArgs); ch; ch = tolower(*(++pArgs)) ) + for( char ch = tolower(*ptcRefArgs); ch; ch = tolower(*(++ptcRefArgs)) ) { if (( IsDigit( ch ) ) || ( ch == '.' ) || ( ch == ',' )) continue; @@ -195,7 +195,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) break; }*/ bool IsNum = false; - for (tchar ch = static_cast(tolower(*pArgs)); ch; ch = static_cast(tolower(*(++pArgs)))) + for (tchar ch = static_cast(tolower(*ptcRefArgs)); ch; ch = static_cast(tolower(*(++ptcRefArgs)))) { if (( IsDigit( ch ) ) || ( ch == '.' ) || ( ch == ',' )) { @@ -213,53 +213,53 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) if ( IsNum ) { char * pEnd; - realtype ret = strtod(pArgsCopy,&pEnd); - //DEBUG_ERR(("IsNum: '%d' pArgsCopy '%s' Ret: '%f'\n",IsNum,pArgsCopy,strtod(pArgsCopy,&pEnd))); - delete[] pArgsCopy; + realtype ret = strtod(ptcRefArgsCopy,&pEnd); + //DEBUG_ERR(("IsNum: '%d' ptcRefArgsCopy '%s' Ret: '%f'\n",IsNum,ptcRefArgsCopy,strtod(ptcRefArgsCopy,&pEnd))); + delete[] ptcRefArgsCopy; return( ret ); } - delete[] pArgsCopy; - switch ( pArgs[0] ) + delete[] ptcRefArgsCopy; + switch ( ptcRefArgs[0] ) { case '{': - // ++pArgs; - // return( GetRangeNumber( pArgs )); + // ++ptcRefArgs; + // return( GetRangeNumber( ptcRefArgs )); case '[': case '(': // Parse out a sub expression. - ++pArgs; - return( MakeFloatMath( pArgs )); + ++ptcRefArgs; + return( MakeFloatMath( ptcRefArgs )); case '+': - ++pArgs; + ++ptcRefArgs; break; case '-': - ++pArgs; - return( -GetSingle( pArgs )); + ++ptcRefArgs; + return( -GetSingle( ptcRefArgs )); case '~': // Bitwise not. - ++pArgs; + ++ptcRefArgs; DEBUG_ERR(("Operator '~' is not allowed with floats.\n")); return 0; case '!': // boolean not. - ++pArgs; - if ( pArgs[0] == '=' ) // odd condition such as (!=x) which is always true of course. + ++ptcRefArgs; + if ( ptcRefArgs[0] == '=' ) // odd condition such as (!=x) which is always true of course. { - ++pArgs; // so just skip it. and compare it to 0 - return( GetSingle( pArgs )); + ++ptcRefArgs; // so just skip it. and compare it to 0 + return( GetSingle( ptcRefArgs )); } - return( !GetSingle( pArgs )); + return( !GetSingle( ptcRefArgs )); case ';': // seperate field. case ',': // seperate field. case '\0': return 0; } - INTRINSIC_TYPE iIntrinsic = (INTRINSIC_TYPE) FindTableHeadSorted( pArgs, sm_IntrinsicFunctions, ARRAY_COUNT(sm_IntrinsicFunctions)-1 ); + INTRINSIC_TYPE iIntrinsic = (INTRINSIC_TYPE) FindTableHeadSorted( ptcRefArgs, sm_IntrinsicFunctions, ARRAY_COUNT(sm_IntrinsicFunctions)-1 ); if ( iIntrinsic >= 0 ) { size_t iLen = strlen(sm_IntrinsicFunctions[iIntrinsic]); - if ( strchr("( ", pArgs[iLen]) ) + if ( strchr("( ", ptcRefArgs[iLen]) ) { - pArgs += (iLen + 1); - tchar * pArgsNext; - Str_Parse( const_cast(pArgs), &(pArgsNext), ")" ); + ptcRefArgs += (iLen + 1); + tchar * ptcRefArgsNext; + Str_Parse( const_cast(ptcRefArgs), &(ptcRefArgsNext), ")" ); tchar * ppCmd[5]; realtype rResult; @@ -271,10 +271,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) { case INTRINSIC_ID: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - rResult = ResGetIndex(int(MakeFloatMath(pArgs))); // ResGetIndex + rResult = ResGetIndex(int(MakeFloatMath(ptcRefArgs))); // ResGetIndex } else { @@ -286,7 +286,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_MAX: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 2, "," ); if ( iCount < 2 ) rResult = 0; else @@ -302,7 +302,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_MIN: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 2, "," ); if ( iCount < 2 ) rResult = 0; else @@ -318,7 +318,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_LOGARITHM: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 3, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 3, "," ); if ( iCount < 1 ) { rResult = 0; @@ -362,10 +362,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_NAPIERPOW: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - rResult = exp(MakeFloatMath(pArgs)); + rResult = exp(MakeFloatMath(ptcRefArgs)); } else { @@ -379,9 +379,9 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) { iCount = 0; - if ( *pArgs ) + if ( *ptcRefArgs ) { - realtype dTosquare = MakeFloatMath(pArgs); + realtype dTosquare = MakeFloatMath(ptcRefArgs); if (dTosquare >= 0) { @@ -405,10 +405,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_SIN: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - realtype dArgument = MakeFloatMath(pArgs); + realtype dArgument = MakeFloatMath(ptcRefArgs); rResult = sin(dArgument * M_PI / 180); } else @@ -421,10 +421,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_ARCSIN: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - realtype dArgument = MakeFloatMath(pArgs); + realtype dArgument = MakeFloatMath(ptcRefArgs); rResult = asin(dArgument) * 180 / M_PI; } else @@ -437,10 +437,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_COS: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - realtype dArgument = MakeFloatMath(pArgs); + realtype dArgument = MakeFloatMath(ptcRefArgs); rResult = cos(dArgument * M_PI / 180); } else @@ -453,10 +453,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_ARCCOS: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - realtype dArgument = MakeFloatMath(pArgs); + realtype dArgument = MakeFloatMath(ptcRefArgs); rResult = acos(dArgument) * 180 / M_PI; } else @@ -469,10 +469,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_TAN: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - realtype dArgument = MakeFloatMath(pArgs); + realtype dArgument = MakeFloatMath(ptcRefArgs); rResult = tan(dArgument * M_PI / 180); } else @@ -485,10 +485,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_ARCTAN: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - realtype dArgument = MakeFloatMath(pArgs); + realtype dArgument = MakeFloatMath(ptcRefArgs); rResult = atan(dArgument) * 180 / M_PI; } else @@ -502,7 +502,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_StrIndexOf: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 3, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 3, "," ); if ( iCount < 2 ) rResult = -1; else @@ -514,7 +514,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_STRMATCH: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 2, "," ); if ( iCount < 2 ) rResult = 0; else @@ -523,7 +523,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_STRREGEX: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 2, "," ); if ( iCount < 2 ) rResult = 0; else @@ -539,7 +539,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_RANDBELL: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 2, "," ); if ( iCount < 2 ) rResult = 0; else @@ -552,10 +552,10 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_STRASCII: { - if ( *pArgs ) + if ( *ptcRefArgs ) { iCount = 1; - rResult = pArgs[0]; + rResult = ptcRefArgs[0]; } else { @@ -566,7 +566,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_RAND: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 2, "," ); if ( iCount <= 0 ) rResult = 0; else @@ -586,7 +586,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_STRCMP: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 2, "," ); if ( iCount < 2 ) rResult = 1; else @@ -595,7 +595,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_STRCMPI: { - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 2, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 2, "," ); if ( iCount < 2 ) rResult = 1; else @@ -605,26 +605,26 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) case INTRINSIC_STRLEN: { iCount = 1; - rResult = (realtype)strlen(pArgs); + rResult = (realtype)strlen(ptcRefArgs); } break; case INTRINSIC_ISOBSCENE: { iCount = 1; - rResult = g_Cfg.IsObscene( pArgs ); + rResult = g_Cfg.IsObscene( ptcRefArgs ); } break; case INTRINSIC_ISNUMBER: { iCount = 1; - SKIP_NONNUM( pArgs ); - rResult = IsStrNumeric( pArgs ); + SKIP_NONNUM( ptcRefArgs ); + rResult = IsStrNumeric( ptcRefArgs ); } break; case INTRINSIC_QVAL: { // Here is handled the intrinsic QVAL form: QVAL(VALUE1,VALUE2,LESSTHAN,EQUAL,GREATERTHAN) - iCount = Str_ParseCmds( const_cast(pArgs), ppCmd, 5, "," ); + iCount = Str_ParseCmds( const_cast(ptcRefArgs), ppCmd, 5, "," ); if (iCount < 3) { rResult = 0; @@ -659,7 +659,7 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) break; } - pArgs = pArgsNext; + ptcRefArgs = ptcRefArgsNext; if ( iCount <= 0 ) { @@ -675,11 +675,11 @@ realtype CFloatMath::GetSingle( lpctstr & pArgs ) auto gReader = g_ExprGlobals.mtEngineLockedReader(); llong llVal; - if ( gReader->m_VarGlobals.GetParseVal( pArgs, &llVal ) ) + if ( gReader->m_VarGlobals.GetParseVal( ptcRefArgs, &llVal ) ) return (int)llVal; - if ( gReader->m_VarResDefs.GetParseVal( pArgs, &llVal ) ) + if ( gReader->m_VarResDefs.GetParseVal( ptcRefArgs, &llVal ) ) return (int)llVal; - if ( gReader->m_VarDefs.GetParseVal( pArgs, &llVal ) ) + if ( gReader->m_VarDefs.GetParseVal( ptcRefArgs, &llVal ) ) return (int)llVal; return 0; } diff --git a/src/common/CFloatMath.h b/src/common/CFloatMath.h index fef632401..13635aabe 100644 --- a/src/common/CFloatMath.h +++ b/src/common/CFloatMath.h @@ -12,18 +12,18 @@ struct CFloatMath { // Expression Parsing - static CSString FloatMath( lpctstr & Expr ); + static CSString FloatMath( lpctstr & ptcRefExpr ); private: - static realtype MakeFloatMath( lpctstr & Expr ); + static realtype MakeFloatMath( lpctstr & ptcRefExpr ); static realtype GetRandVal( realtype dQty ); static realtype GetRandVal2( realtype dMin, realtype dMax ); //Does not work as it should, would be too slow, and nobody needs that /*static realtype GetRangeNumber( lpctstr & pExpr ); static int GetRangeVals( lpctstr & pExpr, realtype * piVals, short int iMaxQty );*/ - static realtype GetValMath( realtype dVal, lpctstr & pExpr ); - static realtype GetSingle( lpctstr & pArgs ); + static realtype GetValMath( realtype dVal, lpctstr & ptcRefExpr ); + static realtype GetSingle(lpctstr & ptcRefArgs ); }; -#endif // _INC_CFLOATMATH_H \ No newline at end of file +#endif // _INC_CFLOATMATH_H diff --git a/src/common/CScriptObj.cpp b/src/common/CScriptObj.cpp index 0b3697587..a56b86602 100644 --- a/src/common/CScriptObj.cpp +++ b/src/common/CScriptObj.cpp @@ -220,7 +220,7 @@ bool CScriptObj::r_CanCall(size_t uiFunctionIndex) // static return true; } -bool CScriptObj::r_Call( lpctstr pszFunction, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * psVal, TRIGRET_TYPE * piRet ) +bool CScriptObj::r_Call( lpctstr pszFunction, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * psVal, TRIGRET_TYPE * piRet ) { ADDTOCALLSTACK("CScriptObj::r_Call (FunctionName)"); @@ -231,7 +231,7 @@ bool CScriptObj::r_Call( lpctstr pszFunction, CScriptTriggerArgsPtr pScriptArgs, return r_Call(index, pScriptArgs, pSrc, psVal, piRet); } -bool CScriptObj::r_Call( size_t uiFunctionIndex, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * psVal, TRIGRET_TYPE * piRet ) +bool CScriptObj::r_Call( size_t uiFunctionIndex, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * psVal, TRIGRET_TYPE * piRet ) { ADDTOCALLSTACK("CScriptObj::r_Call (FunctionIndex)"); EXC_TRY("Call by index"); @@ -1502,7 +1502,7 @@ bool CScriptObj::r_Load( CScript & s ) return true; } -bool CScriptObj::Execute_Call(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) +bool CScriptObj::Execute_Call(CScript& s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc) { ADDTOCALLSTACK("CScriptObj::Execute_Call"); bool fRes = false; @@ -1561,7 +1561,7 @@ bool CScriptObj::Execute_Call(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTe return fRes; } -bool CScriptObj::Execute_FullTrigger(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc) +bool CScriptObj::Execute_FullTrigger(CScript& s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc) { ADDTOCALLSTACK("CScriptObj::Execute_FullTrigger"); bool fRes = false; @@ -1655,7 +1655,7 @@ bool CScriptObj::OnTriggerFind( CScript & s, lpctstr pszTrigName ) return false; } -TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, lpctstr pszTrigName, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc) +TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc) { ADDTOCALLSTACK("CScriptObj::OnTriggerScript"); // look for exact trigger matches @@ -1735,7 +1735,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, lpctstr pszTrigName, CScr return iRet; } -TRIGRET_TYPE CScriptObj::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc) +TRIGRET_TYPE CScriptObj::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc) { UnreferencedParameter(pszTrigName); UnreferencedParameter(pSrc); @@ -1818,14 +1818,13 @@ lpctstr const CScriptObj::sm_szScriptKeys[SK_QTY+1] = nullptr }; -TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult) +TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult) { ADDTOCALLSTACK("CScriptObj::OnTriggerLoopGeneric"); // loop from start here to the ENDFOR // See WebPageScriptList for dealing with Arrays. - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + ASSERT(pScriptArgs); CScriptLineContext StartContext = s.GetContext(); CScriptLineContext EndContext(StartContext); @@ -2171,7 +2170,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopGeneric(CScript& s, int iType, CScriptTrig return TRIGRET_ENDIF; } -TRIGRET_TYPE CScriptObj::OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult) +TRIGRET_TYPE CScriptObj::OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult) { ADDTOCALLSTACK("CScriptObj::OnTriggerLoopForCharSpecial"); TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; @@ -2206,7 +2205,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, C return iRet; } -TRIGRET_TYPE CScriptObj::OnTriggerLoopForCont(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult) +TRIGRET_TYPE CScriptObj::OnTriggerLoopForCont(CScript& s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult) { ADDTOCALLSTACK("CScriptObj::OnTriggerLoopForCont"); TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; @@ -2266,7 +2265,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForCont(CScript& s, CScriptTriggerArgsPtr return iRet; } -TRIGRET_TYPE CScriptObj::OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult) +TRIGRET_TYPE CScriptObj::OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult) { ADDTOCALLSTACK("CScriptObj::OnTriggerLoopForContSpecial"); TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; @@ -2340,7 +2339,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, C return iRet; } -TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * pResult ) +TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult ) { ADDTOCALLSTACK("CScriptObj::OnTriggerRun"); // ARGS: @@ -2355,6 +2354,8 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScript // all scripts should have args for locals to work. + ASSERT(pScriptArgs); + static constexpr uint g_reentrant_OnTriggerRun_limit = 75; static thread_local size_t g_reentrant_OnTriggerRun = 0; auto clean_return = [](const TRIGRET_TYPE ret) noexcept -> TRIGRET_TYPE { @@ -2369,9 +2370,6 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScript return clean_return(TRIGRET_RET_ABORTED); } - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - // Script execution is always not threaded action EXC_TRY("TriggerRun"); @@ -2656,7 +2654,7 @@ TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CScript return clean_return(TRIGRET_RET_DEFAULT); } -TRIGRET_TYPE CScriptObj::OnTriggerRunVal( CScript &s, TRIGRUN_TYPE trigrun, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) +TRIGRET_TYPE CScriptObj::OnTriggerRunVal( CScript &s, TRIGRUN_TYPE trigrun, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ) { // Get the TRIGRET_TYPE that is returned by the script // This should be used instead of OnTriggerRun() when pReturn is not used diff --git a/src/common/CScriptObj.h b/src/common/CScriptObj.h index 1384bf85e..6b6362577 100644 --- a/src/common/CScriptObj.h +++ b/src/common/CScriptObj.h @@ -112,29 +112,29 @@ class CScriptObj // FUNCTION methods static size_t r_GetFunctionIndex(lpctstr pszFunction); static bool r_CanCall(size_t uiFunctionIndex); - bool r_Call( lpctstr ptcFunction, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * psVal = nullptr, TRIGRET_TYPE * piRet = nullptr ); // Try to execute function - bool r_Call( size_t uiFunctionIndex, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * psVal = nullptr, TRIGRET_TYPE * piRet = nullptr ); // Try to execute function + bool r_Call( lpctstr ptcFunction, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * psVal = nullptr, TRIGRET_TYPE * piRet = nullptr ); // Try to execute function + bool r_Call( size_t uiFunctionIndex, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * psVal = nullptr, TRIGRET_TYPE * piRet = nullptr ); // Try to execute function // Generic section parsing - virtual TRIGRET_TYPE OnTrigger(lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + virtual TRIGRET_TYPE OnTrigger(lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc); bool OnTriggerFind(CScript& s, lpctstr pszTrigName); - TRIGRET_TYPE OnTriggerScript(CScript& s, lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); - TRIGRET_TYPE OnTriggerRun(CScript& s, TRIGRUN_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc, CSString* pReturn); - TRIGRET_TYPE OnTriggerRunVal(CScript& s, TRIGRUN_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + TRIGRET_TYPE OnTriggerScript(CScript& s, lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc); + TRIGRET_TYPE OnTriggerRun(CScript& s, TRIGRUN_TYPE trigger, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pReturn); + TRIGRET_TYPE OnTriggerRunVal(CScript& s, TRIGRUN_TYPE trigger, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc); // Special statements private: // While, standard for loop and some special for loops - TRIGRET_TYPE OnTriggerLoopGeneric(CScript& s, int iType, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); - TRIGRET_TYPE OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); - TRIGRET_TYPE OnTriggerLoopForCont(CScript& s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); - TRIGRET_TYPE OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr pScriptArgs, CTextConsole* pSrc, CSString* pResult); + TRIGRET_TYPE OnTriggerLoopGeneric(CScript& s, int iType, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult); + TRIGRET_TYPE OnTriggerLoopForCharSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult); + TRIGRET_TYPE OnTriggerLoopForCont(CScript& s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult); + TRIGRET_TYPE OnTriggerLoopForContSpecial(CScript& s, SK_TYPE iCmd, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult); // Special statements - bool Execute_Call(CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); - bool Execute_FullTrigger(CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc); + bool Execute_Call(CScript& s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc); + bool Execute_FullTrigger(CScript& s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc); // Utilities diff --git a/src/common/resource/sections/CDialogDef.cpp b/src/common/resource/sections/CDialogDef.cpp index f978663a4..19d96d43b 100644 --- a/src/common/resource/sections/CDialogDef.cpp +++ b/src/common/resource/sections/CDialogDef.cpp @@ -568,7 +568,7 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp while ( s.ReadKey()) { CScriptExprContext scpContext{._pScriptObjI = m_pObj}; - expr_parser.ParseScriptText( s.GetKeyBuffer(), scpContext, CScriptTriggerArgsPtr{}, pClient->GetChar() ); + expr_parser.ParseScriptText( s.GetKeyBuffer(), scpContext, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pClient->GetChar() ); m_sText.emplace_back(false) = s.GetKey(); } } @@ -587,7 +587,7 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp int64 iSizes[2]; tchar * pszBuf = s.GetKeyBuffer(); CScriptExprContext scpContext{._pScriptObjI = m_pObj}; - expr_parser.ParseScriptText( pszBuf, scpContext, CScriptTriggerArgsPtr{}, pClient->GetChar() ); + expr_parser.ParseScriptText( pszBuf, scpContext, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pClient->GetChar() ); Str_ParseCmds( pszBuf, iSizes, ARRAY_COUNT(iSizes) ); m_x = (int)(iSizes[0]); diff --git a/src/common/resource/sections/CRandGroupDef.cpp b/src/common/resource/sections/CRandGroupDef.cpp index 7a1bae0ec..8f0a78e11 100644 --- a/src/common/resource/sections/CRandGroupDef.cpp +++ b/src/common/resource/sections/CRandGroupDef.cpp @@ -258,7 +258,7 @@ size_t CRandGroupDef::GetRandMemberIndex( CChar * pCharSrc, bool fTrigger ) cons if (IsTrigUsed(TRIGGER_RESOURCETEST)) { - if (fTrigger && pOreDef->OnTrigger("@ResourceTest", CScriptTriggerArgsPtr{}, pCharSrc) == TRIGRET_RET_TRUE) + if (fTrigger && pOreDef->OnTrigger("@ResourceTest", CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharSrc) == TRIGRET_RET_TRUE) continue; } } diff --git a/src/common/resource/sections/CRegionResourceDef.cpp b/src/common/resource/sections/CRegionResourceDef.cpp index 7b592a50d..03ec6845a 100644 --- a/src/common/resource/sections/CRegionResourceDef.cpp +++ b/src/common/resource/sections/CRegionResourceDef.cpp @@ -37,7 +37,7 @@ lpctstr const CRegionResourceDef::sm_szTrigName[RRTRIG_QTY+1] = // static }; -TRIGRET_TYPE CRegionResourceDef::OnTrigger(lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc) +TRIGRET_TYPE CRegionResourceDef::OnTrigger(lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc) { ADDTOCALLSTACK("CRegionResourceDef::OnTrigger"); // Attach some trigger to the cchar. (PC or NPC) @@ -46,7 +46,7 @@ TRIGRET_TYPE CRegionResourceDef::OnTrigger(lpctstr pszTrigName, CScriptTriggerAr CResourceLock s; if ( ResourceLock( s )) { - TRIGRET_TYPE iRet = CScriptObj::OnTriggerScript( s, pszTrigName, std::move(pArgs), pSrc); + TRIGRET_TYPE iRet = CScriptObj::OnTriggerScript( s, pszTrigName, pScriptArgs, pSrc); return iRet; } return TRIGRET_RET_DEFAULT; diff --git a/src/common/resource/sections/CRegionResourceDef.h b/src/common/resource/sections/CRegionResourceDef.h index a806cf794..002a55315 100644 --- a/src/common/resource/sections/CRegionResourceDef.h +++ b/src/common/resource/sections/CRegionResourceDef.h @@ -58,7 +58,7 @@ class CRegionResourceDef : public CResourceLink public: virtual bool r_LoadVal( CScript & s ) override; virtual bool r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc = nullptr, bool fNoCallParent = false, bool fNoCallChildren = false ) override; - virtual TRIGRET_TYPE OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) override; + virtual TRIGRET_TYPE OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ) override; }; #endif // _INC_CREGIONRESOURCEDEF_H diff --git a/src/common/resource/sections/CWebPageDef.cpp b/src/common/resource/sections/CWebPageDef.cpp index 1e6e78c95..12ee1e8a1 100644 --- a/src/common/resource/sections/CWebPageDef.cpp +++ b/src/common/resource/sections/CWebPageDef.cpp @@ -219,9 +219,10 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on Str_CopyLimitNull( pszTmp2, pszArgs, Str_TempLength() ); CScriptExprContext scpContext{._pScriptObjI = pChar}; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); CExpression::GetExprParser().ParseScriptText( Str_MakeFiltered(pszTmp2), - scpContext, CScriptTriggerArgsPtr{}, + scpContext, pScriptArgs, &g_Serv, 1 ); pSrc->SysMessage( pszTmp2 ); @@ -247,9 +248,10 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on Str_CopyLimitNull(pszTmp2, s.GetArgStr(), Str_TempLength()); CScriptExprContext scpContext{._pScriptObjI = pStone}; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); CExpression::GetExprParser().ParseScriptText( Str_MakeFiltered(pszTmp2), - scpContext, CScriptTriggerArgsPtr{}, + scpContext, pScriptArgs, &g_Serv, 1); pSrc->SysMessage(pszTmp2); @@ -268,9 +270,10 @@ bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on Str_CopyLimitNull( pszTmp2, s.GetArgStr(), Str_TempLength()); CScriptExprContext scpContext{._pScriptObjI = pPage}; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); CExpression::GetExprParser().ParseScriptText( Str_MakeFiltered(pszTmp2), - scpContext, CScriptTriggerArgsPtr{}, + scpContext, pScriptArgs, &g_Serv, 1 ); pSrc->SysMessage( pszTmp2 ); @@ -345,8 +348,9 @@ bool CWebPageDef::WebPageUpdate( bool fNow, lpctstr pszDstName, CTextConsole * p pszHead += 26; CScriptExprContext scpContext{._pScriptObjI = this}; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); expr_parser.ParseScriptText( - pszTmp, scpContext, CScriptTriggerArgsPtr{}, + pszTmp, scpContext, pScriptArgs, pSrc, 1 ); FileOut.SysMessage( pszTmp ); @@ -388,8 +392,9 @@ bool CWebPageDef::WebPageUpdate( bool fNow, lpctstr pszDstName, CTextConsole * p // Look for stuff we can displace here. %STUFF% CScriptExprContext scpContext{._pScriptObjI = this}; + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); expr_parser.ParseScriptText( pszHead, - scpContext, CScriptTriggerArgsPtr{}, + scpContext, pScriptArgs, pSrc, 1 ); FileOut.SysMessage( pszHead ); } @@ -572,7 +577,9 @@ int CWebPageDef::ServPageRequest( CClient * pClient, lpctstr pszURLArgs, CSTime CResourceLock s; if ( ResourceLock(s)) { - if (CScriptObj::OnTriggerScript( s, sm_szTrigName[WTRIG_Load], CScriptTriggerArgsPtr{}, pClient ) == TRIGRET_RET_TRUE) + // Taking the script args by this way is allowed only because the web page is parsed by the main thread. + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + if (CScriptObj::OnTriggerScript( s, sm_szTrigName[WTRIG_Load], pScriptArgs, pClient ) == TRIGRET_RET_TRUE) return 0; // Block further action. } } diff --git a/src/game/CContainer.cpp b/src/game/CContainer.cpp index b8b46e048..1e45f898e 100644 --- a/src/game/CContainer.cpp +++ b/src/game/CContainer.cpp @@ -244,7 +244,7 @@ CItem *CContainer::ContentFind(CResourceID const& rid, dword dwArg, int iDescend } TRIGRET_TYPE CContainer::OnContTriggerForLoop( - CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole *pSrc, + CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole *pSrc, CSString *pResult, CScriptLineContext &StartContext, CScriptLineContext &EndContext, const CResourceID &rid, dword dwArg, int iDescendLevels ) { @@ -257,7 +257,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop( if ( pItem->IsResourceMatch(rid, dwArg) ) { s.SeekContext(StartContext); - TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pArgs, pSrc, pResult); + TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK || iRet == TRIGRET_RET_ABORTED) { EndContext = StartContext; @@ -279,7 +279,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop( if ( pCont->IsSearchable() ) { CContainer *pContBase = dynamic_cast(pCont); - TRIGRET_TYPE iRet = pContBase->OnContTriggerForLoop(s, pArgs, pSrc, pResult, StartContext, EndContext, rid, dwArg, iDescendLevels - 1); + TRIGRET_TYPE iRet = pContBase->OnContTriggerForLoop(s, pScriptArgs, pSrc, pResult, StartContext, EndContext, rid, dwArg, iDescendLevels - 1); if ( iRet != TRIGRET_ENDIF ) return iRet; @@ -293,7 +293,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop( if ( EndContext.m_iOffset <= StartContext.m_iOffset ) { CScriptObj *pScript = dynamic_cast(this); - TRIGRET_TYPE iRet = pScript->OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pArgs, pSrc, pResult); + TRIGRET_TYPE iRet = pScript->OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); if ( iRet != TRIGRET_ENDIF ) return iRet; } @@ -305,7 +305,7 @@ TRIGRET_TYPE CContainer::OnContTriggerForLoop( } TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( - CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole *pSrc, + CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole *pSrc, CSString *pResult, CScriptLineContext &StartContext, CScriptLineContext &EndContext, int iDecendLevels ) { ADDTOCALLSTACK("CContainer::OnGenericContTriggerForLoop"); @@ -313,7 +313,7 @@ TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( { CItem* pItem = static_cast(pObjRec); s.SeekContext(StartContext); - TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pArgs, pSrc, pResult); + TRIGRET_TYPE iRet = pItem->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK || iRet == TRIGRET_RET_ABORTED) { EndContext = StartContext; @@ -332,7 +332,7 @@ TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( if ( pCont && pCont->IsSearchable() ) { CContainer *pContBase = dynamic_cast(pCont); - iRet = pContBase->OnGenericContTriggerForLoop(s, pArgs, pSrc, pResult, StartContext, EndContext, iDecendLevels - 1); + iRet = pContBase->OnGenericContTriggerForLoop(s, pScriptArgs, pSrc, pResult, StartContext, EndContext, iDecendLevels - 1); if ( iRet != TRIGRET_ENDIF ) return iRet; @@ -344,7 +344,7 @@ TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( if ( EndContext.m_iOffset <= StartContext.m_iOffset ) { CScriptObj *pScript = dynamic_cast(this); - TRIGRET_TYPE iRet = pScript->OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pArgs, pSrc, pResult); + TRIGRET_TYPE iRet = pScript->OnTriggerRun(s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult); if ( iRet != TRIGRET_ENDIF ) return iRet; } diff --git a/src/game/CContainer.h b/src/game/CContainer.h index c2ea6b602..8a88500ed 100644 --- a/src/game/CContainer.h +++ b/src/game/CContainer.h @@ -136,7 +136,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m CItem * ContentFind( CResourceID const& rid, dword dwArg = 0, int iDescendLevels = 255 ) const; /** - * @fn TRIGRET_TYPE CContainer::OnContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgsPtr pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, RESOURCE_ID_BASE rid, dword dwArg = 0, int iDescendLevels = 255 ); + * @fn TRIGRET_TYPE CContainer::OnContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgsPtr const& pScriptArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, RESOURCE_ID_BASE rid, dword dwArg = 0, int iDescendLevels = 255 ); * @brief Executes the container trigger for loop action. * @param [in,out] s The CScript to process. * @param [in,out] pSrc If non-null, source for the. @@ -150,10 +150,10 @@ class CContainer : public CSObjCont // This class contains a list of items but m * * @return A TRIGRET_TYPE. */ - TRIGRET_TYPE OnContTriggerForLoop(CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, CResourceID const& rid, dword dwArg = 0, int iDescendLevels = 255 ); + TRIGRET_TYPE OnContTriggerForLoop(CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, CResourceID const& rid, dword dwArg = 0, int iDescendLevels = 255 ); /** - * @fn TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgsPtr pArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); + * @fn TRIGRET_TYPE CContainer::OnGenericContTriggerForLoop( CScript &s, CTextConsole * pSrc, CScriptTriggerArgsPtr const& pScriptArgs, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); * @brief Executes the generic container trigger for loop action. * @param [in,out] s The CScript to process. * @param [in,out] pSrc If non-null, source for the. @@ -165,7 +165,7 @@ class CContainer : public CSObjCont // This class contains a list of items but m * * @return A TRIGRET_TYPE. */ - TRIGRET_TYPE OnGenericContTriggerForLoop(CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); + TRIGRET_TYPE OnGenericContTriggerForLoop(CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult, CScriptLineContext & StartContext, CScriptLineContext & EndContext, int iDecendLevels = 255 ); /** * @fn int CContainer::ContentCount( RESOURCE_ID_BASE rid, dword dwArg = 0 ); diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index d3fd8bb9b..4252b94ce 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -337,19 +337,19 @@ void CObjBase::SetHue( HUE_TYPE wHue, bool fAvoidTrigger, CTextConsole *pSrc, CO lpctstr ptcTrig = (IsChar() ? CChar::sm_szTrigName[CTRIG_DYE] : CItem::sm_szTrigName[ITRIG_DYE]); if (IsTrigUsed(ptcTrig)) { - CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pArgs->Init(wHue, iSound, 0, pSourceObj); - TRIGRET_TYPE iRet = OnTrigger(ptcTrig, pArgs, pSrc); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(wHue, iSound, 0, pSourceObj); + TRIGRET_TYPE iRet = OnTrigger(ptcTrig, pScriptArgs, pSrc); if (iRet == TRIGRET_RET_TRUE) return; - if (pArgs->m_iN2 > 0) // No sound? No checks for who can hear, packets... + if (pScriptArgs->m_iN2 > 0) // No sound? No checks for who can hear, packets... { - Sound((SOUND_TYPE)(pArgs->m_iN2)); + Sound((SOUND_TYPE)(pScriptArgs->m_iN2)); } - m_wHue = (HUE_TYPE)(pArgs->m_iN1); + m_wHue = (HUE_TYPE)(pScriptArgs->m_iN1); return; } } @@ -852,7 +852,7 @@ TRIGRET_TYPE CObjBase::OnHearTrigger( CResourceLock & s, lpctstr pszCmd, CChar * // TRIGRET_ENDIF = no match. // TRIGRET_DEFAULT = found match but it had no RETURN bool fMatch = false; - CScriptTriggerArgsPtr pArgs; + CScriptTriggerArgsPtr pScriptArgs; while ( s.ReadKeyParse()) { @@ -869,18 +869,18 @@ TRIGRET_TYPE CObjBase::OnHearTrigger( CResourceLock & s, lpctstr pszCmd, CChar * if ( ! fMatch ) continue; // look for the next "ON" section. - pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pArgs->m_iN1 = iModeRef; - pArgs->m_iN2 = wHue; - TRIGRET_TYPE iRet = CObjBase::OnTriggerRunVal( s, TRIGRUN_SECTION_EXEC, pArgs, pSrc ); + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = iModeRef; + pScriptArgs->m_iN2 = wHue; + TRIGRET_TYPE iRet = CObjBase::OnTriggerRunVal( s, TRIGRUN_SECTION_EXEC, pScriptArgs, pSrc ); if ( iRet != TRIGRET_RET_FALSE ) return iRet; fMatch = false; } - if (pArgs) - iModeRef = TALKMODE_TYPE(pArgs->m_iN1); + if (pScriptArgs) + iModeRef = TALKMODE_TYPE(pScriptArgs->m_iN1); return TRIGRET_ENDIF; // continue looking. } @@ -981,9 +981,9 @@ bool CObjBase::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc, SKIP_SEPARATORS(pszArgs); } - CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pArgs->Init(pszArgs != nullptr ? pszArgs : ""); - if (r_Call(uiFunctionIndex, pArgs, pSrc, &sVal)) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pszArgs != nullptr ? pszArgs : ""); + if (r_Call(uiFunctionIndex, pScriptArgs, pSrc, &sVal)) { return true; } @@ -2101,9 +2101,9 @@ bool CObjBase::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command fro { // RES_FUNCTION call CSString sVal; - CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pArgs->Init( s.GetArgRaw() ); - if ( r_Call( uiFunctionIndex, pArgs, pSrc, &sVal ) ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init( s.GetArgRaw() ); + if ( r_Call( uiFunctionIndex, pScriptArgs, pSrc, &sVal ) ) return true; } @@ -3641,7 +3641,7 @@ void CObjBase::DupeCopy( const CObjBase * pObj ) CEntityProps::Copy(pObj); } -TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CScriptTriggerArgsPtr pArgs, CChar * pSrc ) +TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CScriptTriggerArgsPtr const& pScriptArgs, CChar * pSrc ) { ADDTOCALLSTACK("CObjBase::Spell_OnTrigger"); CSpellDef * pSpellDef = g_Cfg.GetSpellDef( spell ); @@ -3654,7 +3654,7 @@ TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CSc CResourceLock s; if ( pSpellDef->ResourceLock( s )) { - return CScriptObj::OnTriggerScript( s, CSpellDef::sm_szTrigName[stage], std::move(pArgs), pSrc ); + return CScriptObj::OnTriggerScript( s, CSpellDef::sm_szTrigName[stage], pScriptArgs, pSrc ); } } return TRIGRET_RET_DEFAULT; diff --git a/src/game/CObjBase.h b/src/game/CObjBase.h index 66bb871a7..b11546656 100644 --- a/src/game/CObjBase.h +++ b/src/game/CObjBase.h @@ -897,7 +897,7 @@ public: virtual bool IsDeleted() const override; = 0; /** - * @fn TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CChar * pSrc, CScriptTriggerArgsPtr pArgs ); + * @fn TRIGRET_TYPE CObjBase::Spell_OnTrigger( SPELL_TYPE spell, SPTRIG_TYPE stage, CChar * pSrc, CScriptTriggerArgsPtr const& pScriptArgs ); * * @brief Spell's trigger (@Effect, @Start...). * @@ -908,7 +908,7 @@ public: virtual bool IsDeleted() const override; * * @return A TRIGRET_TYPE. */ - TRIGRET_TYPE Spell_OnTrigger(SPELL_TYPE spell, SPTRIG_TYPE stage, CScriptTriggerArgsPtr pArgs, CChar * pSrc); + TRIGRET_TYPE Spell_OnTrigger(SPELL_TYPE spell, SPTRIG_TYPE stage, CScriptTriggerArgsPtr const& pScriptArgs, CChar * pSrc); protected: virtual void _GoAwake() override; diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 8179dd220..471c6acf4 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -875,7 +875,7 @@ TRIGRET_TYPE CRegion::OnRegionTrigger( CTextConsole * pSrc, RTRIG_TYPE iAction ) CResourceLock s; if ( pLink->ResourceLock(s) ) { - iRet = CScriptObj::OnTriggerScript(s, sm_szTrigName[iAction], CScriptTriggerArgsPtr{}, pSrc); + iRet = CScriptObj::OnTriggerScript(s, sm_szTrigName[iAction], CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc); if ( iRet == TRIGRET_RET_TRUE ) return iRet; } @@ -892,7 +892,7 @@ TRIGRET_TYPE CRegion::OnRegionTrigger( CTextConsole * pSrc, RTRIG_TYPE iAction ) if ( !pLink->ResourceLock(s) ) continue; - iRet = CScriptObj::OnTriggerScript(s, sm_szTrigName[iAction], CScriptTriggerArgsPtr{}, pSrc); + iRet = CScriptObj::OnTriggerScript(s, sm_szTrigName[iAction], CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc); if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) return iRet; } diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index f0fdc3c3d..71e391ac9 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -810,7 +810,7 @@ void CSector::SetLightNow( bool fFlash ) // don't fire trigger when server is loading or light is flashing if (( ! g_Serv.IsLoadingGeneric() && fFlash == false ) && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) { - pChar->OnTrigger( CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar ); + pChar->OnTrigger( CTRIG_EnvironChange, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar ); } } } @@ -893,7 +893,7 @@ void CSector::SetWeather( WEATHER_TYPE w ) pChar->GetClientActive()->addWeather( w ); if ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) ) - pChar->OnTrigger( CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar ); + pChar->OnTrigger( CTRIG_EnvironChange, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar ); } } @@ -914,7 +914,7 @@ void CSector::SetSeason( SEASON_TYPE season ) pChar->GetClientActive()->addSeason(season); if ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) ) - pChar->OnTrigger(CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar); + pChar->OnTrigger(CTRIG_EnvironChange, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar); } } @@ -1307,7 +1307,7 @@ bool CSector::_OnTick() ASSERT(pChar); if (fEnvironChange && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) - pChar->OnTrigger(CTRIG_EnvironChange, CScriptTriggerArgsPtr{}, pChar); + pChar->OnTrigger(CTRIG_EnvironChange, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar); if ( pChar->IsClientActive()) { diff --git a/src/game/CTimedFunctionHandler.cpp b/src/game/CTimedFunctionHandler.cpp index 7bde9dfe4..5175dcb4e 100644 --- a/src/game/CTimedFunctionHandler.cpp +++ b/src/game/CTimedFunctionHandler.cpp @@ -64,7 +64,7 @@ void CTimedFunctionHandler::Clear() } TRIGRET_TYPE CTimedFunctionHandler::Loop(lpctstr ptcCommand, int iLoopsMade, CScriptLineContext StartContext, - CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult) + CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult) { ADDTOCALLSTACK("CTimedFunctionHandler::Loop"); for (CSObjContRec* obj : _timedFunctions.GetIterationSafeCont()) @@ -86,7 +86,7 @@ TRIGRET_TYPE CTimedFunctionHandler::Loop(lpctstr ptcCommand, int iLoopsMade, CSc break; } - TRIGRET_TYPE iRet = pObj->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pArgs, pSrc, pResult); + TRIGRET_TYPE iRet = pObj->OnTriggerRun(s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult); if (iRet == TRIGRET_BREAK) { diff --git a/src/game/CTimedFunctionHandler.h b/src/game/CTimedFunctionHandler.h index 54a61eb53..b2085c5c4 100644 --- a/src/game/CTimedFunctionHandler.h +++ b/src/game/CTimedFunctionHandler.h @@ -40,7 +40,7 @@ class CTimedFunctionHandler void Stop(const CUID& uid, lpctstr ptcCommand); void Clear(); TRIGRET_TYPE Loop(lpctstr ptcCommand, int iLoopsMade, CScriptLineContext StartContext, - CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult); + CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult); int64 IsTimer(const CUID& uid, lpctstr ptcCommand) const; }; #endif // _INC_CTIMEDFUNCTIONHANDLER_H diff --git a/src/game/CWorldTimedFunctions.cpp b/src/game/CWorldTimedFunctions.cpp index 6c923fbd0..f51fac4f5 100644 --- a/src/game/CWorldTimedFunctions.cpp +++ b/src/game/CWorldTimedFunctions.cpp @@ -24,9 +24,9 @@ void CWorldTimedFunctions::Clear() // static } TRIGRET_TYPE CWorldTimedFunctions::Loop(lpctstr ptcCommand, int LoopsMade, CScriptLineContext StartContext, - CScript& s, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc, CSString* pResult) // static + CScript& s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc, CSString* pResult) // static { - return g_World._Ticker._TimedFunctions.Loop(ptcCommand, LoopsMade, StartContext, s, std::move(pArgs), pSrc, pResult); + return g_World._Ticker._TimedFunctions.Loop(ptcCommand, LoopsMade, StartContext, s, pScriptArgs, pSrc, pResult); } void CWorldTimedFunctions::Add(const CUID& uid, int64 iTimeout, lpctstr ptcCommand) // static diff --git a/src/game/CWorldTimedFunctions.h b/src/game/CWorldTimedFunctions.h index 8119d6cce..efaa01afc 100644 --- a/src/game/CWorldTimedFunctions.h +++ b/src/game/CWorldTimedFunctions.h @@ -23,7 +23,7 @@ class CWorldTimedFunctions static void Stop(const CUID& uid, lpctstr ptcCommand); static void Clear(); static TRIGRET_TYPE Loop(lpctstr ptcCommand, int LoopsMade, CScriptLineContext StartContext, - CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult); + CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult); static int64 IsTimer(const CUID& uid, lpctstr funcname); }; #endif // _INC_CWORLDTIMEDFUNCTIONS_H diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index 6e48a255d..d87e95d5e 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -591,8 +591,8 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; CItem * LayerFind( LAYER_TYPE layer ) const; void LayerAdd( CItem * pItem, LAYER_TYPE layer = LAYER_QTY ); - TRIGRET_TYPE OnCharTrigForLayerLoop(CScript &s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * pResult, LAYER_TYPE layer ); - TRIGRET_TYPE OnCharTrigForMemTypeLoop( CScript &s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc, CSString * pResult, word wMemType ); + TRIGRET_TYPE OnCharTrigForLayerLoop(CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult, LAYER_TYPE layer ); + TRIGRET_TYPE OnCharTrigForMemTypeLoop( CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult, word wMemType ); virtual void OnWeightChange( int iChange ) override; virtual int GetWeight(word amount = 0) const override; @@ -620,8 +620,8 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; */ void SetTriggerActive(lpctstr trig = nullptr); - virtual TRIGRET_TYPE OnTrigger( lpctstr pTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) override; - TRIGRET_TYPE OnTrigger( CTRIG_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc); + virtual TRIGRET_TYPE OnTrigger( lpctstr pTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ) override; + TRIGRET_TYPE OnTrigger( CTRIG_TYPE trigger, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc); public: // Load/Save---------------------------------- @@ -931,8 +931,8 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; SOUND_TYPE Skill_GetSound( SKILL_TYPE skill); int Skill_Stage( SKTRIG_TYPE stage ); - TRIGRET_TYPE Skill_OnTrigger(SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr pScriptArgs); //pScriptArgs.m_iN1 will be rewritten with skill - TRIGRET_TYPE Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr pScriptArgs); //pArgs.m_iN1 will be rewritten with skill + TRIGRET_TYPE Skill_OnTrigger(SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr const& pScriptArgs); //pScriptArgs.m_iN1 will be rewritten with skill + TRIGRET_TYPE Skill_OnCharTrigger( SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr const& pScriptArgs); //pArgs.m_iN1 will be rewritten with skill bool Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ); bool Skill_Tracking( CUID uidTarg, DIR_TYPE & dirPrv, int iDistMax = INT16_MAX ); diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index c18eb9ee4..84b522d53 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -407,7 +407,7 @@ void CChar::OnRemoveObj( CSObjContRec* pObRec ) // Override this = called when r if (( IsTrigUsed(TRIGGER_UNEQUIP) ) || ( IsTrigUsed(TRIGGER_ITEMUNEQUIP) )) { if ( layer != LAYER_DRAGGING && ! g_Serv.IsLoadingGeneric()) - pItem->OnTrigger( ITRIG_UNEQUIP, CScriptTriggerArgsPtr{}, this ); + pItem->OnTrigger( ITRIG_UNEQUIP, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this ); } CContainer::OnRemoveObj( pObRec ); @@ -3310,7 +3310,7 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) // Swap the layer for real one, because if we don't use dclick for equip, real layer gets rewritten with LAYER_DRAGGING. pItem->SetContainedLayer(layer); - if (pItem->OnTrigger(ITRIG_EQUIPTEST, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE) + if (pItem->OnTrigger(ITRIG_EQUIPTEST, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this) == TRIGRET_RET_TRUE) { // Reset layer to the original value, that item doesn't get misplaced. pItem->SetContainedLayer(requestedLayer); @@ -3345,7 +3345,7 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) if (( IsTrigUsed(TRIGGER_EQUIP) ) || ( IsTrigUsed(TRIGGER_ITEMEQUIP) )) { - if ( pItem->OnTrigger(ITRIG_EQUIP, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE ) + if ( pItem->OnTrigger(ITRIG_EQUIP, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this) == TRIGRET_RET_TRUE ) return false; } @@ -3499,7 +3499,7 @@ bool CChar::Reveal( uint64 iFlags ) if (IsTrigUsed(TRIGGER_REVEAL)) { - if (OnTrigger(CTRIG_Reveal, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE) + if (OnTrigger(CTRIG_Reveal, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this) == TRIGRET_RET_TRUE) return false; } @@ -4318,7 +4318,7 @@ CChar::DeathRequestResult CChar::Death() if ( IsTrigUsed(TRIGGER_DEATH) ) { - if ( OnTrigger(CTRIG_Death, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE ) + if ( OnTrigger(CTRIG_Death, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this) == TRIGRET_RET_TRUE ) return DeathRequestResult::Aborted; } //Dismount now. Later is may be too late and cause problems @@ -4840,7 +4840,7 @@ void CChar::CheckRevealOnMove() return; if ( IsTrigUsed(TRIGGER_STEPSTEALTH) ) - OnTrigger(CTRIG_StepStealth, CScriptTriggerArgsPtr{}, this); + OnTrigger(CTRIG_StepStealth, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this); if (g_Cfg.m_iRevealFlags & REVEALF_ONHORSE && IsStatFlag(STATF_ONHORSE)) Reveal(); @@ -5523,16 +5523,14 @@ void CChar::SetTriggerActive(lpctstr trig) // 4) CHARDEF // 5) EVENTSPET/EVENTSPLAYER set on .ini file // RETURNS = TRIGRET_TYPE (in cscriptobj.h) -TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) +TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ) { ADDTOCALLSTACK("CChar::OnTrigger"); if ( IsTriggerActive( pszTrigName ) ) //This should protect any char trigger from infinite loop return TRIGRET_RET_DEFAULT; - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - + ASSERT(pScriptArgs); if ( !pSrc ) pSrc = &g_Serv; @@ -5701,10 +5699,10 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip return iRet; } -TRIGRET_TYPE CChar::OnTrigger( CTRIG_TYPE trigger, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) +TRIGRET_TYPE CChar::OnTrigger( CTRIG_TYPE trigger, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ) { ASSERT( (trigger > CTRIG_AAAUNUSED) && (trigger < CTRIG_QTY) ); - return OnTrigger( CChar::sm_szTrigName[trigger], std::move(pScriptArgs), pSrc); + return OnTrigger( CChar::sm_szTrigName[trigger], pScriptArgs, pSrc); } // process m_fStatusUpdate flags diff --git a/src/game/chars/CCharAttacker.cpp b/src/game/chars/CCharAttacker.cpp index 7bcfd39a2..eb3b81489 100644 --- a/src/game/chars/CCharAttacker.cpp +++ b/src/game/chars/CCharAttacker.cpp @@ -23,7 +23,7 @@ bool CChar::Attacker_Add(CChar * pChar, int iThreat) } else if (IsTrigUsed(TRIGGER_COMBATSTART)) { - TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatStart, CScriptTriggerArgsPtr{}, pChar); + TRIGRET_TYPE tRet = OnTrigger(CTRIG_CombatStart, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar); if (tRet == TRIGRET_RET_TRUE) return false; else @@ -258,7 +258,7 @@ void CChar::Attacker_Clear() { if (m_lastAttackers.empty() || !Fight_IsActive() || !m_Fight_Targ_UID.IsValidUID() || !m_Fight_Targ_UID.CharFind()) { - OnTrigger(CTRIG_CombatEnd, CScriptTriggerArgsPtr{}, this); + OnTrigger(CTRIG_CombatEnd, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this); } } diff --git a/src/game/chars/CCharFight.cpp b/src/game/chars/CCharFight.cpp index 88548d6fd..10e6451f8 100644 --- a/src/game/chars/CCharFight.cpp +++ b/src/game/chars/CCharFight.cpp @@ -2160,7 +2160,7 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) if ( IsTrigUsed(TRIGGER_SKILLSUCCESS) ) { - if ( Skill_OnCharTrigger(skill, CTRIG_SkillSuccess, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger(skill, CTRIG_SkillSuccess, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return WAR_SWING_EQUIPPING; // ok, so no hit - skill failed. Pah! @@ -2168,7 +2168,7 @@ WAR_SWING_TYPE CChar::Fight_Hit( CChar * pCharTarg ) } if ( IsTrigUsed(TRIGGER_SUCCESS) ) { - if ( Skill_OnTrigger(skill, SKTRIG_SUCCESS, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_SUCCESS, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return WAR_SWING_EQUIPPING; // ok, so no hit - skill failed. Pah! diff --git a/src/game/chars/CCharMemory.cpp b/src/game/chars/CCharMemory.cpp index f18b8dc8e..327b2631c 100644 --- a/src/game/chars/CCharMemory.cpp +++ b/src/game/chars/CCharMemory.cpp @@ -300,7 +300,7 @@ CItemMemory * CChar::Memory_AddObj( const CObjBase * pObj, word MemTypes ) } // Looping through all memories ( ForCharMemoryType ). -TRIGRET_TYPE CChar::OnCharTrigForMemTypeLoop( CScript &s, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc, CSString * pResult, word wMemType ) +TRIGRET_TYPE CChar::OnCharTrigForMemTypeLoop( CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc, CSString * pResult, word wMemType ) { ADDTOCALLSTACK("CChar::OnCharTrigForMemTypeLoop"); const CScriptLineContext StartContext = s.GetContext(); @@ -313,7 +313,7 @@ TRIGRET_TYPE CChar::OnCharTrigForMemTypeLoop( CScript &s, CScriptTriggerArgsPtr CItem* pItem = static_cast(pObjRec); if ( !pItem->IsMemoryTypes(wMemType) ) continue; - TRIGRET_TYPE iRet = pItem->OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pArgs, pSrc, pResult ); + TRIGRET_TYPE iRet = pItem->OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pScriptArgs, pSrc, pResult ); if ( iRet == TRIGRET_BREAK ) { EndContext = StartContext; @@ -331,7 +331,7 @@ TRIGRET_TYPE CChar::OnCharTrigForMemTypeLoop( CScript &s, CScriptTriggerArgsPtr if ( EndContext.m_iOffset <= StartContext.m_iOffset ) { // just skip to the end. - TRIGRET_TYPE iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pArgs, pSrc, pResult ); + TRIGRET_TYPE iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pScriptArgs, pSrc, pResult ); if ( iRet != TRIGRET_ENDIF ) return iRet; } diff --git a/src/game/chars/CCharNPC.cpp b/src/game/chars/CCharNPC.cpp index 7768aeb4a..5ce9efb1a 100644 --- a/src/game/chars/CCharNPC.cpp +++ b/src/game/chars/CCharNPC.cpp @@ -318,7 +318,7 @@ void CChar::NPC_CreateTrigger() continue; executedEvents.emplace_back(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, CScriptTriggerArgsPtr{}, this); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) return; } @@ -336,7 +336,7 @@ void CChar::NPC_CreateTrigger() continue; executedEvents.emplace_back(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, CScriptTriggerArgsPtr{}, this); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this); if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) return; } diff --git a/src/game/chars/CCharNPCAct.cpp b/src/game/chars/CCharNPCAct.cpp index 4732de971..e133177ea 100644 --- a/src/game/chars/CCharNPCAct.cpp +++ b/src/game/chars/CCharNPCAct.cpp @@ -304,7 +304,7 @@ void CChar::NPC_OnHear( lpctstr pszCmd, CChar * pSrc, bool fAllPets ) // This or CTRIG_SeeNewPlayer will be our first contact with people. if ( IsTrigUsed(TRIGGER_NPCHEARGREETING) ) { - if ( OnTrigger( CTRIG_NPCHearGreeting, CScriptTriggerArgsPtr{}, pSrc ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCHearGreeting, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc ) == TRIGRET_RET_TRUE ) return; } @@ -368,7 +368,7 @@ void CChar::NPC_OnHear( lpctstr pszCmd, CChar * pSrc, bool fAllPets ) // can't figure you out. if ( IsTrigUsed(TRIGGER_NPCHEARUNKNOWN) ) { - if ( OnTrigger( CTRIG_NPCHearUnknown, CScriptTriggerArgsPtr{}, pSrc ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCHearUnknown, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc ) == TRIGRET_RET_TRUE ) return; } @@ -1018,7 +1018,7 @@ bool CChar::NPC_LookAtChar( CChar * pChar, int iDist ) if ( IsTrigUsed(TRIGGER_NPCLOOKATCHAR) ) { - switch ( OnTrigger(CTRIG_NPCLookAtChar, CScriptTriggerArgsPtr{}, pChar) ) + switch ( OnTrigger(CTRIG_NPCLookAtChar, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar) ) { case TRIGRET_RET_TRUE: return true; case TRIGRET_RET_FALSE: return false; @@ -1046,7 +1046,7 @@ bool CChar::NPC_LookAtChar( CChar * pChar, int iDist ) { if ( IsTrigUsed(TRIGGER_NPCSEENEWPLAYER) ) { - if ( OnTrigger( CTRIG_NPCSeeNewPlayer, CScriptTriggerArgsPtr{}, pChar ) != TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCSeeNewPlayer, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar ) != TRIGRET_RET_TRUE ) { // record that we attempted to speak to them. CItemMemory * pMemory = Memory_AddObjTypes( pChar, MEMORY_SPEAK ); @@ -1988,7 +1988,7 @@ void CChar::NPC_Act_Idle() { if ( IsTrigUsed(TRIGGER_NPCSPECIALACTION) ) { - if ( OnTrigger( CTRIG_NPCSpecialAction, CScriptTriggerArgsPtr{}, this ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCSpecialAction, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this ) == TRIGRET_RET_TRUE ) return; } @@ -2681,7 +2681,7 @@ void CChar::NPC_ExtraAI() EXC_SET_BLOCK("init"); if ( IsTrigUsed(TRIGGER_NPCACTION) ) { - if ( OnTrigger( CTRIG_NPCAction, CScriptTriggerArgsPtr{}, this ) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_NPCAction, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this ) == TRIGRET_RET_TRUE ) return; } diff --git a/src/game/chars/CCharNPCPet.cpp b/src/game/chars/CCharNPCPet.cpp index 371659d30..b7a8a626b 100644 --- a/src/game/chars/CCharNPCPet.cpp +++ b/src/game/chars/CCharNPCPet.cpp @@ -869,7 +869,7 @@ void CChar::NPC_PetRelease() if (IsTrigUsed(TRIGGER_PETRELEASE)) { - if (OnTrigger(CTRIG_PetRelease, CScriptTriggerArgsPtr{}, pCharOwn) == TRIGRET_RET_TRUE) + if (OnTrigger(CTRIG_PetRelease, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharOwn) == TRIGRET_RET_TRUE) return; } @@ -911,7 +911,7 @@ void CChar::NPC_PetDesert() if ( IsTrigUsed(TRIGGER_PETDESERT) ) { - if ( OnTrigger( CTRIG_PetDesert, CScriptTriggerArgsPtr{}, pCharOwn) == TRIGRET_RET_TRUE ) + if ( OnTrigger( CTRIG_PetDesert, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharOwn) == TRIGRET_RET_TRUE ) return; } diff --git a/src/game/chars/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index 83c8278d4..c0d195a74 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -3808,12 +3808,12 @@ void CChar::Skill_Fail( bool fCancel ) { if ( IsTrigUsed(TRIGGER_SKILLFAIL) ) { - if ( Skill_OnCharTrigger(skill, CTRIG_SkillFail, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger(skill, CTRIG_SkillFail, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) fCancel = true; } if ( IsTrigUsed(TRIGGER_FAIL) && !fCancel ) { - if ( Skill_OnTrigger(skill, SKTRIG_FAIL, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_FAIL, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) fCancel = true; } } @@ -3821,7 +3821,7 @@ void CChar::Skill_Fail( bool fCancel ) { if ( IsTrigUsed(TRIGGER_SKILLABORT) ) { - if ( Skill_OnCharTrigger(skill, CTRIG_SkillAbort, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger(skill, CTRIG_SkillAbort, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return; @@ -3829,7 +3829,7 @@ void CChar::Skill_Fail( bool fCancel ) } if ( IsTrigUsed(TRIGGER_ABORT) ) { - if ( Skill_OnTrigger(skill, SKTRIG_ABORT, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_ABORT, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return; @@ -3848,14 +3848,13 @@ void CChar::Skill_Fail( bool fCancel ) } -TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr pScriptArgs ) +TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScriptTriggerArgsPtr const& pScriptArgs ) { ADDTOCALLSTACK("CChar::Skill_OnTrigger"); if ( !IsSkillBase(skill) ) return TRIGRET_RET_DEFAULT; - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + ASSERT(pScriptArgs); if ( !(stage == SKTRIG_SELECT || stage == SKTRIG_GAIN || stage == SKTRIG_USEQUICK || stage == SKTRIG_WAIT || stage == SKTRIG_TARGETCANCEL) ) m_Act_SkillCurrent = skill; @@ -3878,14 +3877,13 @@ TRIGRET_TYPE CChar::Skill_OnTrigger( SKILL_TYPE skill, SKTRIG_TYPE stage, CScrip return iRet; } -TRIGRET_TYPE CChar::Skill_OnCharTrigger(SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr pScriptArgs ) +TRIGRET_TYPE CChar::Skill_OnCharTrigger(SKILL_TYPE skill, CTRIG_TYPE ctrig, CScriptTriggerArgsPtr const& pScriptArgs ) { ADDTOCALLSTACK("CChar::Skill_OnCharTrigger"); if ( !IsSkillBase(skill) ) return TRIGRET_RET_DEFAULT; - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + ASSERT(pScriptArgs); if ( !(ctrig == CTRIG_SkillSelect || ctrig == CTRIG_SkillGain || ctrig == CTRIG_SkillUseQuick || ctrig == CTRIG_SkillWait || ctrig == CTRIG_SkillTargetCancel) ) m_Act_SkillCurrent = skill; @@ -4431,7 +4429,7 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) // 0-100 scale of Difficulty if ( IsTrigUsed(TRIGGER_SKILLPRESTART) ) { - if ( Skill_OnCharTrigger(skill, CTRIG_SkillPreStart, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( Skill_OnCharTrigger(skill, CTRIG_SkillPreStart, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return false; @@ -4439,7 +4437,7 @@ bool CChar::Skill_Start( SKILL_TYPE skill, int iDifficultyIncrease ) } if ( IsTrigUsed(TRIGGER_PRESTART) ) { - if ( Skill_OnTrigger(skill, SKTRIG_PRESTART, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( Skill_OnTrigger(skill, SKTRIG_PRESTART, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) { Skill_Cleanup(); return false; diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index 1e48cb456..8e672f18d 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -209,7 +209,7 @@ CItem *CChar::LayerFind( LAYER_TYPE layer ) const return nullptr; } -TRIGRET_TYPE CChar::OnCharTrigForLayerLoop( CScript &s, CScriptTriggerArgsPtr pScriptArgs, CTextConsole *pSrc, CSString *pResult, LAYER_TYPE layer ) +TRIGRET_TYPE CChar::OnCharTrigForLayerLoop( CScript &s, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole *pSrc, CSString *pResult, LAYER_TYPE layer ) { ADDTOCALLSTACK("CChar::OnCharTrigForLayerLoop"); const CScriptLineContext StartContext = s.GetContext(); @@ -1191,12 +1191,12 @@ bool CChar::CanSee( const CObjBaseTemplate *pObj ) const { if ( IsTrigUsed( TRIGGER_SEEHIDDEN ) ) { - CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pArgs->m_iN1 = (plevelMe <= plevelChar); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = (plevelMe <= plevelChar); CChar *pChar2 = const_cast< CChar* >( pChar ); CChar *this2 = const_cast< CChar* >( this ); - this2->OnTrigger( CTRIG_SeeHidden, pArgs, pChar2 ); - return ( pArgs->m_iN1 != 1 ); + this2->OnTrigger( CTRIG_SeeHidden, pScriptArgs, pChar2 ); + return ( pScriptArgs->m_iN1 != 1 ); } } //Here we analyse how GM can see other player/GM when they are hide. diff --git a/src/game/chars/CCharUse.cpp b/src/game/chars/CCharUse.cpp index 3dbe909ac..e9a1861b9 100644 --- a/src/game/chars/CCharUse.cpp +++ b/src/game/chars/CCharUse.cpp @@ -1580,7 +1580,7 @@ int CChar::Do_Use_Item(CItem *pItem, bool fLink) if (m_pNPC && (IsTrigUsed(TRIGGER_DCLICK) || IsTrigUsed(TRIGGER_ITEMDCLICK))) // for players, DClick was called before this function { - if (pItem->OnTrigger(ITRIG_DCLICK, CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE) + if (pItem->OnTrigger(ITRIG_DCLICK, CScriptParserBufs::GetCScriptTriggerArgsPtr(), this) == TRIGRET_RET_TRUE) return false; } diff --git a/src/game/clients/CClient.h b/src/game/clients/CClient.h index 8a201b231..6900fd8e5 100644 --- a/src/game/clients/CClient.h +++ b/src/game/clients/CClient.h @@ -360,7 +360,7 @@ class CClient : public CSObjListRec, public CScriptObj, public CChatChanMember, bool Event_ExceededNetworkQuota(uchar uiType, int64 iBytes, int64 iQuota); TRIGRET_TYPE Menu_OnSelect( const CResourceID& rid, int iSelect, CObjBase * pObj ); - TRIGRET_TYPE Dialog_OnButton( const CResourceID& rid, dword dwButtonID, CObjBase * pObj, std::shared_ptr pArgs ); + TRIGRET_TYPE Dialog_OnButton(const CResourceID& rid, dword dwButtonID, CObjBase * pObj, std::shared_ptr pScriptArgs ); bool Login_Relay( uint iServer ); // Relay player to a certain IP byte Login_ServerList( const char * pszAccount, const char * pszPassword ); // Initial login (Login on "loginserver", new format) @@ -454,7 +454,7 @@ class CClient : public CSObjListRec, public CScriptObj, public CChatChanMember, void addItem_InContainer( const CItem * pItem ); void addItem( CItem * pItem ); - void addBuff( const BUFF_ICONS IconId, const dword ClilocOne, const dword ClilocTwo, const word durationSeconds = 0, lpctstr* pArgs = nullptr, uint uiArgCount = 0) const; + void addBuff(const BUFF_ICONS IconId, const dword ClilocOne, const dword ClilocTwo, const word durationSeconds = 0, lpctstr* pptcArgs = nullptr, uint uiArgCount = 0) const; void removeBuff(const BUFF_ICONS IconId) const; void resendBuffs() const; diff --git a/src/game/clients/CClientDialog.cpp b/src/game/clients/CClientDialog.cpp index 613ef9260..43b6fc7db 100644 --- a/src/game/clients/CClientDialog.cpp +++ b/src/game/clients/CClientDialog.cpp @@ -138,7 +138,7 @@ bool CClient::addGumpDialogProps( const CUID& uid ) return true; } -TRIGRET_TYPE CClient::Dialog_OnButton(const CResourceID& rid, dword dwButtonID, CObjBase * pObj, std::shared_ptr pArgs ) +TRIGRET_TYPE CClient::Dialog_OnButton(const CResourceID& rid, dword dwButtonID, CObjBase * pObj, std::shared_ptr pScriptArgs ) { ADDTOCALLSTACK("CClient::Dialog_OnButton"); // one of the gump dialog buttons was pressed. @@ -178,16 +178,16 @@ TRIGRET_TYPE CClient::Dialog_OnButton(const CResourceID& rid, dword dwButtonID, continue; } - pArgs->m_iN1 = dwButtonID; + pScriptArgs->m_iN1 = dwButtonID; auto stopPrebutton = TRIGRET_RET_FALSE; CResourceLock prebutton; if (g_Cfg.ResourceLock(prebutton, CResourceID(RES_DIALOG, rid.GetResIndex(), RES_DIALOG_PREBUTTON))) - stopPrebutton = pObj->OnTriggerRun(prebutton, TRIGRUN_SECTION_TRUE, pArgs, m_pChar, nullptr); + stopPrebutton = pObj->OnTriggerRun(prebutton, TRIGRUN_SECTION_TRUE, pScriptArgs, m_pChar, nullptr); if (stopPrebutton != TRIGRET_RET_TRUE) - return pObj->OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, pArgs, m_pChar); + return pObj->OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, pScriptArgs, m_pChar); } return TRIGRET_ENDIF; @@ -255,7 +255,7 @@ TRIGRET_TYPE CClient::Menu_OnSelect( const CResourceID& rid, int iSelect, CObjBa if (strnicmp(ptcStr, "@CANCEL", 7 ) ) continue; - return pObj->OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, CScriptTriggerArgsPtr{}, m_pChar); + return pObj->OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar); } } else @@ -272,7 +272,7 @@ TRIGRET_TYPE CClient::Menu_OnSelect( const CResourceID& rid, int iSelect, CObjBa if ( i > iSelect ) break; - return pObj->OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, CScriptTriggerArgsPtr{}, m_pChar); + return pObj->OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar); } } @@ -325,7 +325,7 @@ bool CMenuItem::ParseLine( tchar * pszArgs, CScriptObj * pObjBase, CTextConsole CScriptExprContext scpContext{ ._pScriptObjI = pObjBase ? pObjBase : &g_Serv }; - CExpression::GetExprParser().ParseScriptText( pszArgs, scpContext, CScriptTriggerArgsPtr{}, pSrc ); + CExpression::GetExprParser().ParseScriptText( pszArgs, scpContext, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc ); // Parsing @color if ( *pszArgs == '@' ) @@ -381,7 +381,7 @@ void CClient::Menu_Setup( CResourceID rid, CObjBase * pObj ) return; } CScriptExprContext scpContext{._pScriptObjI = pObj}; - CExpression::GetExprParser().ParseScriptText( s.GetKeyBuffer(), scpContext, CScriptTriggerArgsPtr{}, m_pChar ); + CExpression::GetExprParser().ParseScriptText( s.GetKeyBuffer(), scpContext, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar ); CMenuItem item[MAX_MENU_ITEMS]; item[0].m_sText = s.GetKey(); diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index 2e404baca..c323aca24 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -54,7 +54,7 @@ void CClient::Event_ChatButton(const nachar* pszName) // Client's chat button wa if ( IsTrigUsed(TRIGGER_USERCHATBUTTON) ) { - if (m_pChar && m_pChar->OnTrigger(CTRIG_UserChatButton, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE) + if (m_pChar && m_pChar->OnTrigger(CTRIG_UserChatButton, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar) == TRIGRET_RET_TRUE) return; } GetChar()->SetTriggerActive("UserChatButton"); // dirty fix for SA Classic clients with injection moving a lot when using chat button, we set 'active trigger' to this, so we check it back on the packet to limit the amount of steps to do. @@ -614,7 +614,7 @@ void CClient::Event_Skill_Use( SKILL_TYPE skill ) // Skill is clicked on the ski if ( IsTrigUsed(TRIGGER_SKILLSELECT) ) { - if ( m_pChar->Skill_OnCharTrigger( skill, CTRIG_SkillSelect, CScriptTriggerArgsPtr{} ) == TRIGRET_RET_TRUE ) + if ( m_pChar->Skill_OnCharTrigger( skill, CTRIG_SkillSelect, CScriptParserBufs::GetCScriptTriggerArgsPtr() ) == TRIGRET_RET_TRUE ) { m_pChar->Skill_Fail( true ); // clean up current skill. return; @@ -623,7 +623,7 @@ void CClient::Event_Skill_Use( SKILL_TYPE skill ) // Skill is clicked on the ski if ( IsTrigUsed(TRIGGER_SELECT) ) { - if ( m_pChar->Skill_OnTrigger( skill, SKTRIG_SELECT, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if ( m_pChar->Skill_OnTrigger( skill, SKTRIG_SELECT, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) { m_pChar->Skill_Fail( true ); // clean up current skill. return; @@ -808,7 +808,7 @@ bool CClient::Event_CheckWalkBuffer(byte rawdir) DEBUG_WARN(("%s (%x): Fast Walk ?\n", GetName(), GetSocketID())); if ( IsTrigUsed(TRIGGER_USEREXWALKLIMIT) ) { - if ( m_pChar->OnTrigger(CTRIG_UserExWalkLimit, CScriptTriggerArgsPtr{}, m_pChar) != TRIGRET_RET_TRUE ) + if ( m_pChar->OnTrigger(CTRIG_UserExWalkLimit, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar) != TRIGRET_RET_TRUE ) return false; } } @@ -1586,7 +1586,7 @@ void CClient::Event_Profile( byte fWriteMode, CUID uid, lpctstr pszProfile, int if ( IsTrigUsed(TRIGGER_PROFILE) ) { - if ( pChar->OnTrigger(CTRIG_Profile, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) + if ( pChar->OnTrigger(CTRIG_Profile, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar) == TRIGRET_RET_TRUE ) return; } @@ -1629,7 +1629,7 @@ void CClient::Event_MailMsg( CUID uid1, CUID uid2 ) if ( IsTrigUsed(TRIGGER_USERMAILBAG) ) { - if (pChar->OnTrigger(CTRIG_UserMailBag, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE) + if (pChar->OnTrigger(CTRIG_UserMailBag, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar) == TRIGRET_RET_TRUE) return; } @@ -1653,7 +1653,7 @@ void CClient::Event_ToolTip( CUID uid ) if (( IsTrigUsed(TRIGGER_TOOLTIP) ) || (( IsTrigUsed(TRIGGER_ITEMTOOLTIP) )&&(pObj->IsItem()))) { - if ( pObj->OnTrigger("@ToolTip", CScriptTriggerArgsPtr{}, this) == TRIGRET_RET_TRUE ) // CTRIG_ToolTip, ITRIG_ToolTip + if ( pObj->OnTrigger("@ToolTip", CScriptParserBufs::GetCScriptTriggerArgsPtr(), this) == TRIGRET_RET_TRUE ) // CTRIG_ToolTip, ITRIG_ToolTip return; } @@ -2353,7 +2353,7 @@ bool CClient::Event_DoubleClick( CUID uid, bool fMacro, bool fTestTouch, bool fS CChar * pChar = static_cast(pObj); if ( IsTrigUsed(TRIGGER_DCLICK) || IsTrigUsed(TRIGGER_CHARDCLICK) ) { - if ( pChar->OnTrigger(CTRIG_DClick, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) + if ( pChar->OnTrigger(CTRIG_DClick, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar) == TRIGRET_RET_TRUE ) return true; } diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index fe3edde97..05cefd4f5 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -219,7 +219,7 @@ void CClient::resendBuffs() const } } -void CClient::addBuff( const BUFF_ICONS IconId, const dword ClilocOne, const dword ClilocTwo, const word durationSeconds, lpctstr* pArgs, uint uiArgCount) const +void CClient::addBuff( const BUFF_ICONS IconId, const dword ClilocOne, const dword ClilocTwo, const word durationSeconds, lpctstr* pptcArgs, uint uiArgCount) const { ADDTOCALLSTACK("CClient::addBuff"); if ( !IsSetOF(OF_Buffs) ) @@ -227,7 +227,7 @@ void CClient::addBuff( const BUFF_ICONS IconId, const dword ClilocOne, const dwo if ( PacketBuff::CanSendTo(GetNetState()) == false ) return; - new PacketBuff(this, IconId, ClilocOne, ClilocTwo, durationSeconds, pArgs, uiArgCount); + new PacketBuff(this, IconId, ClilocOne, ClilocTwo, durationSeconds, pptcArgs, uiArgCount); } void CClient::removeBuff(const BUFF_ICONS IconId) const @@ -1670,7 +1670,7 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou CItem * pItemUse = m_Targ_UID.ItemFind(); if (pItemUse && (IsTrigUsed(TRIGGER_TARGON_CANCEL) || IsTrigUsed(TRIGGER_ITEMTARGON_CANCEL))) { - if ( pItemUse->OnTrigger( ITRIG_TARGON_CANCEL, CScriptTriggerArgsPtr{}, pCharThis) == TRIGRET_RET_TRUE ) + if ( pItemUse->OnTrigger( ITRIG_TARGON_CANCEL, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharThis) == TRIGRET_RET_TRUE ) fSuppressCancelMessage = true; } } break; @@ -1728,7 +1728,7 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou { if ( IsTrigUsed(TRIGGER_SKILLTARGETCANCEL) ) { - if (pCharThis->Skill_OnCharTrigger(action, CTRIG_SkillTargetCancel, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if (pCharThis->Skill_OnCharTrigger(action, CTRIG_SkillTargetCancel, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) { fSuppressCancelMessage = true; break; @@ -1736,7 +1736,7 @@ void CClient::SetTargMode( CLIMODE_TYPE targmode, lpctstr pPrompt, int64 iTimeou } if ( IsTrigUsed(TRIGGER_TARGETCANCEL) ) { - if (pCharThis->Skill_OnTrigger(action, SKTRIG_TARGETCANCEL, CScriptTriggerArgsPtr{}) == TRIGRET_RET_TRUE ) + if (pCharThis->Skill_OnTrigger(action, SKTRIG_TARGETCANCEL, CScriptParserBufs::GetCScriptTriggerArgsPtr()) == TRIGRET_RET_TRUE ) fSuppressCancelMessage = true; } } @@ -2690,7 +2690,7 @@ void CClient::addCharPaperdoll( CChar * pChar ) if (IsTrigUsed(TRIGGER_SENDPAPERDOLL)) { - pChar->OnTrigger(CTRIG_SendPaperdoll, CScriptTriggerArgsPtr{}, m_pChar); + pChar->OnTrigger(CTRIG_SendPaperdoll, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar); } new PacketPaperdoll(this, pChar); diff --git a/src/game/clients/CClientTarg.cpp b/src/game/clients/CClientTarg.cpp index c864f2ed9..54547dc51 100644 --- a/src/game/clients/CClientTarg.cpp +++ b/src/game/clients/CClientTarg.cpp @@ -2467,7 +2467,7 @@ bool CClient::OnTarg_Party_Add( CChar * pChar ) if ( IsTrigUsed(TRIGGER_PARTYINVITE) ) { - if ( pChar->OnTrigger(CTRIG_PartyInvite, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) + if ( pChar->OnTrigger(CTRIG_PartyInvite, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar) == TRIGRET_RET_TRUE ) return false; } diff --git a/src/game/clients/CClientUse.cpp b/src/game/clients/CClientUse.cpp index 8ea3d58c8..4a8ce4a2d 100644 --- a/src/game/clients/CClientUse.cpp +++ b/src/game/clients/CClientUse.cpp @@ -104,7 +104,7 @@ bool CClient::Cmd_Use_Item( CItem *pItem, bool fTestTouch, bool fScript ) if ( IsTrigUsed(TRIGGER_DCLICK) || IsTrigUsed(TRIGGER_ITEMDCLICK) ) { - if ( pItem->OnTrigger(ITRIG_DCLICK, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) + if ( pItem->OnTrigger(ITRIG_DCLICK, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar) == TRIGRET_RET_TRUE ) return true; } @@ -794,7 +794,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte if ( strcmpi(s.GetArgStr(), "@Cancel") ) continue; - if ( m_pChar->OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, CScriptTriggerArgsPtr{}, m_pChar) == TRIGRET_RET_TRUE ) + if ( m_pChar->OnTriggerRunVal(s, TRIGRUN_SECTION_TRUE, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar) == TRIGRET_RET_TRUE ) return 0; break; @@ -890,7 +890,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte if ( s.IsKey("TEST") ) { CScriptExprContext scpContext{._pScriptObjI = m_pChar}; - CExpression::GetExprParser().ParseScriptText(s.GetArgRaw(), scpContext, CScriptTriggerArgsPtr{}, m_pChar); + CExpression::GetExprParser().ParseScriptText(s.GetArgRaw(), scpContext, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar); CResourceQtyArray skills(s.GetArgStr()); if ( !skills.IsResourceMatchAll(m_pChar) ) { @@ -902,7 +902,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte if ( s.IsKey("TESTIF") ) { CScriptExprContext scpContext{._pScriptObjI = m_pChar}; - CExpression::GetExprParser().ParseScriptText(s.GetArgRaw(), scpContext, CScriptTriggerArgsPtr{}, m_pChar); + CExpression::GetExprParser().ParseScriptText(s.GetArgRaw(), scpContext, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar); if ( !s.GetArgVal() ) { fSkipNeedCleanup = true; @@ -914,7 +914,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte if ( iOnCount == iSelect ) { // Execute command from script - TRIGRET_TYPE tRet = m_pChar->OnTriggerRunVal(s, TRIGRUN_SINGLE_EXEC, CScriptTriggerArgsPtr{}, m_pChar); + TRIGRET_TYPE tRet = m_pChar->OnTriggerRunVal(s, TRIGRUN_SINGLE_EXEC, CScriptParserBufs::GetCScriptTriggerArgsPtr(), m_pChar); if ( tRet != TRIGRET_RET_DEFAULT ) return (tRet == TRIGRET_RET_TRUE) ? 0 : 1; diff --git a/src/game/clients/CParty.cpp b/src/game/clients/CParty.cpp index 60c1b7780..76620ea92 100644 --- a/src/game/clients/CParty.cpp +++ b/src/game/clients/CParty.cpp @@ -320,12 +320,12 @@ bool CPartyDef::RemoveMember( CUID uidRemove, CUID uidCommand ) CChar *pSrc = uidCommand.CharFind(); if ( pSrc && IsTrigUsed(TRIGGER_PARTYREMOVE) ) { - if ( pCharRemove->OnTrigger(CTRIG_PartyRemove, CScriptTriggerArgsPtr{}, pSrc) == TRIGRET_RET_TRUE ) + if ( pCharRemove->OnTrigger(CTRIG_PartyRemove, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc) == TRIGRET_RET_TRUE ) return false; } if ( IsTrigUsed(TRIGGER_PARTYLEAVE) ) { - if ( pCharRemove->OnTrigger(CTRIG_PartyLeave, CScriptTriggerArgsPtr{}, pCharRemove) == TRIGRET_RET_TRUE ) + if ( pCharRemove->OnTrigger(CTRIG_PartyLeave, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharRemove) == TRIGRET_RET_TRUE ) return false; } @@ -360,7 +360,7 @@ bool CPartyDef::Disband( CUID uidMaster ) CChar *pMaster = GetMaster().CharFind(); if ( pMaster && IsTrigUsed(TRIGGER_PARTYDISBAND) ) { - if ( pMaster->OnTrigger(CTRIG_PartyDisband, CScriptTriggerArgsPtr{}, pMaster) == TRIGRET_RET_TRUE ) + if ( pMaster->OnTrigger(CTRIG_PartyDisband, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pMaster) == TRIGRET_RET_TRUE ) return false; } @@ -456,7 +456,7 @@ bool CPartyDef::AcceptEvent( CChar *pCharAccept, CUID uidInviter, bool bForced, if (IsTrigUsed(TRIGGER_PARTYADD)) { - if ( pCharAccept->OnTrigger(CTRIG_PartyAdd, CScriptTriggerArgsPtr{}, pCharInviter) == TRIGRET_RET_TRUE ) + if ( pCharAccept->OnTrigger(CTRIG_PartyAdd, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharInviter) == TRIGRET_RET_TRUE ) return false; } diff --git a/src/game/components/CCChampion.cpp b/src/game/components/CCChampion.cpp index 5e4cd7dec..cb107f991 100644 --- a/src/game/components/CCChampion.cpp +++ b/src/game/components/CCChampion.cpp @@ -174,7 +174,7 @@ void CCChampion::Start(CChar *pChar) if (pChar && IsTrigUsed(TRIGGER_START)) { // TODO: add source? - if (OnTrigger(ITRIG_Start, CScriptTriggerArgsPtr{}, pChar) == TRIGRET_RET_TRUE) + if (OnTrigger(ITRIG_Start, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar) == TRIGRET_RET_TRUE) return; } @@ -190,7 +190,7 @@ void CCChampion::Stop(CChar* pChar) { if (IsTrigUsed(TRIGGER_STOP)) { - if (OnTrigger(ITRIG_STOP, CScriptTriggerArgsPtr{}, pChar) == TRIGRET_RET_TRUE) + if (OnTrigger(ITRIG_STOP, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar) == TRIGRET_RET_TRUE) return; } } @@ -224,7 +224,7 @@ void CCChampion::Complete() // TODO: Add attacker list in trigger. if (IsTrigUsed(TRIGGER_COMPLETE)) { - OnTrigger(ITRIG_COMPLETE, CScriptTriggerArgsPtr{}, &g_Serv); + OnTrigger(ITRIG_COMPLETE, CScriptParserBufs::GetCScriptTriggerArgsPtr(), &g_Serv); } // TODO: add rewards, titles, etc? } @@ -1197,7 +1197,7 @@ bool CCChampion::r_Verb(CScript & s, CTextConsole * pSrc) } -TRIGRET_TYPE CCChampion::OnTrigger(ITRIG_TYPE trig, CScriptTriggerArgsPtr pArgs, CTextConsole* pSrc) +TRIGRET_TYPE CCChampion::OnTrigger(ITRIG_TYPE trig, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole* pSrc) { lpctstr pszTrigName = CItem::sm_szTrigName[trig]; @@ -1211,12 +1211,12 @@ TRIGRET_TYPE CCChampion::OnTrigger(ITRIG_TYPE trig, CScriptTriggerArgsPtr pArgs, CResourceLock s; if (pResourceLink->ResourceLock(s)) { - iRet = GetLink()->OnTriggerScript(s, pszTrigName, pArgs, pSrc); + iRet = GetLink()->OnTriggerScript(s, pszTrigName, pScriptArgs, pSrc); } } if (iRet == TRIGRET_RET_DEFAULT) { - iRet = GetLink()->OnTrigger(trig, std::move(pArgs), pSrc); + iRet = GetLink()->OnTrigger(trig, pScriptArgs, pSrc); } return iRet; } diff --git a/src/game/components/CCChampion.h b/src/game/components/CCChampion.h index 2a6940c09..61aad2360 100644 --- a/src/game/components/CCChampion.h +++ b/src/game/components/CCChampion.h @@ -302,7 +302,7 @@ class CCChampion : public CComponent virtual bool r_LoadVal(CScript & s) override; virtual bool r_Verb(CScript & s, CTextConsole * pSrc) override; // Execute command from script virtual void Copy(const CComponent *target) override; - TRIGRET_TYPE OnTrigger(ITRIG_TYPE trig, CScriptTriggerArgsPtr args, CTextConsole *pSrc); + TRIGRET_TYPE OnTrigger(ITRIG_TYPE trig, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole *pSrc); /************************************************************************ * CItem related section. diff --git a/src/game/components/CCMultiMovable.cpp b/src/game/components/CCMultiMovable.cpp index 50c034d13..2255677f7 100644 --- a/src/game/components/CCMultiMovable.cpp +++ b/src/game/components/CCMultiMovable.cpp @@ -1268,7 +1268,7 @@ bool CCMultiMovable::r_Verb(CScript & s, CTextConsole * pSrc) // Execute command Str_CopyLimitNull(szText, pszSpeak, MAX_TALK_BUFFER); CScriptExprContext scpContext{._pScriptObjI = pChar}; - CExpression::GetExprParser().ParseScriptText(szText, scpContext, CScriptTriggerArgsPtr{}, &g_Serv); + CExpression::GetExprParser().ParseScriptText(szText, scpContext, CScriptParserBufs::GetCScriptTriggerArgsPtr(), &g_Serv); pTiller->Speak(szText, HUE_TEXT_DEF, TALKMODE_SAY, FONT_NORMAL); } diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 2ac4e1b60..1b464e7f1 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -244,7 +244,7 @@ bool CItem::NotifyDelete() ADDTOCALLSTACK("CItem::NotifyDelete"); if ((IsTrigUsed(TRIGGER_DESTROY)) || (IsTrigUsed(TRIGGER_ITEMDESTROY))) { - if (CItem::OnTrigger(ITRIG_DESTROY, CScriptTriggerArgsPtr{}, &g_Serv) == TRIGRET_RET_TRUE) + if (CItem::OnTrigger(ITRIG_DESTROY, CScriptParserBufs::GetCScriptTriggerArgsPtr(), &g_Serv) == TRIGRET_RET_TRUE) return false; } @@ -453,7 +453,7 @@ CItem * CItem::GenerateScript( CChar * pSrc) CResourceLock s; if ( pItemDef->ResourceLock(s)) { - OnTrigger(ITRIG_Create, CScriptTriggerArgsPtr{}, pSrc ? static_cast(pSrc) : static_cast(&g_Serv)); + OnTrigger(ITRIG_Create, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc ? static_cast(pSrc) : static_cast(&g_Serv)); } return this; } @@ -1625,19 +1625,19 @@ bool CItem::MoveToCheck( const CPointMap & pt, CChar * pCharMover ) TRIGRET_TYPE ttResult = TRIGRET_RET_DEFAULT; if (IsTrigUsed(TRIGGER_DROPON_GROUND) || IsTrigUsed(TRIGGER_ITEMDROPON_GROUND)) { - CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pArgs->m_iN1 = iDecayTime / MSECS_PER_TENTH; // ARGN1 = Decay time for the dropped item (in tenths of second) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->m_iN1 = iDecayTime / MSECS_PER_TENTH; // ARGN1 = Decay time for the dropped item (in tenths of second) //args->m_iN2 = 0; - pArgs->m_s1 = ptNewPlace.WriteUsed(); - ttResult = OnTrigger(ITRIG_DROPON_GROUND, pArgs, pCharMover); + pScriptArgs->m_s1 = ptNewPlace.WriteUsed(); + ttResult = OnTrigger(ITRIG_DROPON_GROUND, pScriptArgs, pCharMover); if (IsDeleted()) return false; - iDecayTime = pArgs->m_iN1 * MSECS_PER_TENTH; + iDecayTime = pScriptArgs->m_iN1 * MSECS_PER_TENTH; // Warning: here we ignore the read-onlyness of CSString's buffer only because we know that CPointMap constructor won't write past the end, but only replace some characters with '\0'. It's not worth it to build another string just for that. - tchar* ptcArgs = const_cast(pArgs->m_s1.GetBuffer()); + tchar* ptcArgs = const_cast(pScriptArgs->m_s1.GetBuffer()); const CPointMap ptChanged(ptcArgs); if (!ptChanged.IsValidPoint()) g_Log.EventError("Trying to override item drop P with an invalid P. Using the original one.\n"); @@ -3722,16 +3722,14 @@ void CItem::SetTriggerActive(lpctstr trig) _iRunningTriggerId = -1; } -TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScriptArgs, CTextConsole * pSrc ) +TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ) { ADDTOCALLSTACK("CItem::OnTrigger"); if (IsTriggerActive(pszTrigName)) //This should protect any item trigger from infinite loop return TRIGRET_RET_ABORTED; - if (!pScriptArgs) - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - + ASSERT(pScriptArgs); if ( !pSrc ) pSrc = &g_Serv; @@ -3895,10 +3893,10 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pScrip return iRet; } -TRIGRET_TYPE CItem::OnTrigger( ITRIG_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) +TRIGRET_TYPE CItem::OnTrigger( ITRIG_TYPE trigger, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ) { ASSERT((trigger >= 0) && (trigger < ITRIG_QTY)); - return OnTrigger( CItem::sm_szTrigName[trigger], std::move(pArgs), pSrc ); + return OnTrigger( CItem::sm_szTrigName[trigger], pScriptArgs, pSrc ); } // Item type specific stuff. @@ -5595,12 +5593,12 @@ bool CItem::OnSpellEffect(SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, C // iSkillLevel = 0-1000 = difficulty. may be slightly larger . how advanced is this spell (might be from a wand) const CSpellDef * pSpellDef = g_Cfg.GetSpellDef(spell); - CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pArgs->Init(spell, iSkillLevel, 0, pSourceItem); + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(spell, iSkillLevel, 0, pSourceItem); if ( IsTrigUsed(TRIGGER_SPELLEFFECT) || IsTrigUsed(TRIGGER_ITEMSPELL) ) { - switch ( OnTrigger(ITRIG_SPELLEFFECT, pArgs, pCharSrc) ) + switch ( OnTrigger(ITRIG_SPELLEFFECT, pScriptArgs, pCharSrc) ) { case TRIGRET_RET_TRUE: return false; @@ -5615,7 +5613,7 @@ bool CItem::OnSpellEffect(SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, C if ( IsTrigUsed(TRIGGER_EFFECT) ) { - switch (Spell_OnTrigger(spell, SPTRIG_EFFECT, pArgs, pCharSrc)) + switch (Spell_OnTrigger(spell, SPTRIG_EFFECT, pScriptArgs, pCharSrc)) { case TRIGRET_RET_TRUE: return false; @@ -5628,8 +5626,8 @@ bool CItem::OnSpellEffect(SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, C } } - spell = (SPELL_TYPE)(pArgs->m_iN1); - iSkillLevel = (int)(pArgs->m_iN2); + spell = (SPELL_TYPE)(pScriptArgs->m_iN1); + iSkillLevel = (int)(pScriptArgs->m_iN2); pSpellDef = g_Cfg.GetSpellDef( spell ); ASSERT(pSpellDef); @@ -5749,8 +5747,8 @@ bool CItem::OnSpellEffect(SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, C if ( (iEffectID > ITEMID_NOTHING) && (iEffectID < ITEMID_QTY) ) { bool fExplode = (pSpellDef->IsSpellType(SPELLFLAG_FX_BOLT) && !pSpellDef->IsSpellType(SPELLFLAG_GOOD)); // bolt (chasing) spells have explode = 1 by default (if not good spell) - dword dwColor = (dword)(pArgs->m_VarsLocal.GetKeyNum("EffectColor")); - dword dwRender = (dword)(pArgs->m_VarsLocal.GetKeyNum("EffectRender")); + dword dwColor = (dword)(pScriptArgs->m_VarsLocal.GetKeyNum("EffectColor")); + dword dwRender = (dword)(pScriptArgs->m_VarsLocal.GetKeyNum("EffectRender")); if ( pSpellDef->IsSpellType(SPELLFLAG_FX_BOLT) ) Effect(EFFECT_BOLT, iEffectID, pCharSrc, 5, 1, fExplode, dwColor, dwRender); @@ -5823,9 +5821,9 @@ int CItem::OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uType ) if ( IsTrigUsed(TRIGGER_DAMAGE) || IsTrigUsed(TRIGGER_ITEMDAMAGE) ) { - CScriptTriggerArgsPtr pArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pArgs->Init(iDmg, (int)uType, 0, nullptr); - if ( OnTrigger( ITRIG_DAMAGE, pArgs, pSrc ) == TRIGRET_RET_TRUE ) + CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(iDmg, (int)uType, 0, nullptr); + if ( OnTrigger( ITRIG_DAMAGE, pScriptArgs, pSrc ) == TRIGRET_RET_TRUE ) return 0; } diff --git a/src/game/items/CItem.h b/src/game/items/CItem.h index 20bf07403..34ebe0448 100644 --- a/src/game/items/CItem.h +++ b/src/game/items/CItem.h @@ -823,8 +823,8 @@ protected: virtual void _SetTimeout(int64 iMsecs) override final; */ void SetTriggerActive(lpctstr trig = nullptr); - virtual TRIGRET_TYPE OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ) override; - TRIGRET_TYPE OnTrigger( ITRIG_TYPE trigger, CScriptTriggerArgsPtr pArgs, CTextConsole * pSrc ); + virtual TRIGRET_TYPE OnTrigger( lpctstr pszTrigName, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ) override; + TRIGRET_TYPE OnTrigger( ITRIG_TYPE trigger, CScriptTriggerArgsPtr const& pScriptArgs, CTextConsole * pSrc ); // Item type specific stuff. inline bool IsType(IT_TYPE type) const noexcept { diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index de110692a..3c9760ab8 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -407,7 +407,7 @@ void CItemMulti::Multi_Setup(CChar *pChar, dword dwKeyCode) } } } - pChar->r_Call("f_multi_setup", CScriptTriggerArgsPtr{}, pChar, nullptr, nullptr); + pChar->r_Call("f_multi_setup", CScriptParserBufs::GetCScriptTriggerArgsPtr(), pChar, nullptr, nullptr); } bool CItemMulti::Multi_IsPartOf(const CItem * pItem) const diff --git a/src/game/items/CItemPlant.cpp b/src/game/items/CItemPlant.cpp index ebe997ebe..21b5321bc 100644 --- a/src/game/items/CItemPlant.cpp +++ b/src/game/items/CItemPlant.cpp @@ -168,7 +168,7 @@ bool CItem::Plant_OnTick() bool CItem::Plant_SetID(ITEMID_TYPE id) { bool iRet = SetID(id); - OnTrigger(ITRIG_Create, CScriptTriggerArgsPtr{}, &g_Serv); + OnTrigger(ITRIG_Create, CScriptParserBufs::GetCScriptTriggerArgsPtr(), &g_Serv); return iRet; } diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index a383f8b36..5fabb4ece 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -346,7 +346,7 @@ int Sphere_InitServer( int argc, char *argv[] ) // Trigger server start - g_Serv.r_Call("f_onserver_start", CScriptTriggerArgsPtr{}, &g_Serv); + g_Serv.r_Call("f_onserver_start", CScriptParserBufs::GetCScriptTriggerArgsPtr(), &g_Serv); return g_Serv.GetExitFlag(); EXC_CATCH; @@ -360,7 +360,7 @@ int Sphere_InitServer( int argc, char *argv[] ) void Sphere_ExitServer() { // Trigger server quit - g_Serv.r_Call("f_onserver_exit", CScriptTriggerArgsPtr{}, &g_Serv); + g_Serv.r_Call("f_onserver_exit", CScriptParserBufs::GetCScriptTriggerArgsPtr(), &g_Serv); g_Serv.SetServerMode(ServMode::Exiting); From 4e21706494ea02a3a1f946e561c2030f07cbe3a1 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 16 Jun 2025 07:43:26 +0200 Subject: [PATCH 072/112] Added a check to ignore spurious thread wakeups from Auto/ManualResetEvent --- src/common/sphere_library/sresetevents.cpp | 276 ++++++++++----------- src/common/sphere_library/sresetevents.h | 20 +- 2 files changed, 139 insertions(+), 157 deletions(-) diff --git a/src/common/sphere_library/sresetevents.cpp b/src/common/sphere_library/sresetevents.cpp index 647ef46e2..069b3c350 100644 --- a/src/common/sphere_library/sresetevents.cpp +++ b/src/common/sphere_library/sresetevents.cpp @@ -1,21 +1,31 @@ #include "sresetevents.h" +#include + +#ifdef _WIN32 +#include +#else +// Helper: convert “now + ms” to an absolute timespec +static void make_timespec(struct timespec& ts, uint32_t ms) noexcept +{ + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += ms / 1000; + ts.tv_nsec += (ms % 1000) * 1000000L; + if (ts.tv_nsec >= 1000000000L) //static_cast(1.0e9); + { + ts.tv_sec += 1; + ts.tv_nsec -= 1000000000L; + } +} +#endif -// AutoResetEvent:: Constructors, Destructor, Assign operator. AutoResetEvent::AutoResetEvent() noexcept { #ifdef _WIN32 - m_handle = CreateEvent(nullptr, FALSE, FALSE, nullptr); + m_handle = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); // auto-reset #else - pthread_mutexattr_init(&m_criticalSectionAttr); -# ifndef __APPLE__ - pthread_mutexattr_settype(&m_criticalSectionAttr, PTHREAD_MUTEX_RECURSIVE_NP); -# endif - pthread_mutex_init(&m_criticalSection, &m_criticalSectionAttr); - - pthread_condattr_init(&m_conditionAttr); - pthread_cond_init(&m_condition, &m_conditionAttr); - + pthread_mutex_init(&m_criticalSection, nullptr); + pthread_cond_init (&m_condition , nullptr); m_signaled = false; #endif } @@ -23,201 +33,177 @@ AutoResetEvent::AutoResetEvent() noexcept AutoResetEvent::~AutoResetEvent() noexcept { #ifdef _WIN32 - CloseHandle(m_handle); + ::CloseHandle(m_handle); #else - pthread_condattr_destroy(&m_conditionAttr); - pthread_cond_destroy(&m_condition); - pthread_mutexattr_destroy(&m_criticalSectionAttr); - pthread_mutex_destroy(&m_criticalSection); + pthread_cond_destroy (&m_condition); + pthread_mutex_destroy(&m_criticalSection); #endif } -// AutoResetEvent:: Interaction. - -void AutoResetEvent::wait(uint timeout) noexcept +void AutoResetEvent::wait(uint32 timeout) noexcept { - if (timeout == 0) - { - // If timeout is 0 then the thread's timeslice may not be given up as with normal - // sleep methods - so we will check for this condition ourselves and use SleepEx - // instead #ifdef _WIN32 - SleepEx(0, TRUE); -#else - SLEEP(0); -#endif - return; - } + if (timeout == 0) + { + // If timeout is 0 then the thread's timeslice may not be given up as with normal + // sleep methods - so we will check for this condition ourselves and use SleepEx + // instead + ::SleepEx(0, TRUE); -#ifdef _WIN32 - // It's possible for WaitForSingleObjectEx to exit before the timeout and without - // the signal. due to bAlertable=TRUE other events (e.g. async i/o) can cancel the - // waiting period early. + // or, if we want a non-blocking probe + //::WaitForSingleObjectEx(m_handle, 0, FALSE); + return; + } - DWORD start = GetTickCount(); - DWORD remaining = timeout; + const DWORD start = ::GetTickCount(); + DWORD remaining = timeout; - // Checks below to protect outselves from spurious wakeups while (true) { - DWORD result = WaitForSingleObjectEx(m_handle, timeout, TRUE); - - EnterCriticalSection(&m_criticalSection); - if (m_signaled) - { - m_signaled = false; // auto-reset - LeaveCriticalSection(&m_criticalSection); - break; - } - LeaveCriticalSection(&m_criticalSection); - - if (result == WAIT_TIMEOUT) - break; - - DWORD now = GetTickCount(); - DWORD elapsed = now - start; - if (elapsed >= timeout) - break; - - remaining = timeout - elapsed; + DWORD rc = ::WaitForSingleObjectEx(m_handle, remaining, TRUE); + if (rc == WAIT_OBJECT_0 || rc == WAIT_TIMEOUT) + return; // signalled or timed out + + // WAIT_IO_COMPLETION – recalc remaining time + DWORD now = ::GetTickCount(); + if (now - start >= timeout) + return; + remaining = timeout - (now - start); + } +#else // POSIX + pthread_mutex_lock(&m_criticalSection); + + if (timeout == 0 && !m_signaled) + { // immediate return + pthread_mutex_unlock(&m_criticalSection); + return; } -#else - pthread_mutex_lock(&m_criticalSection); + + struct timespec ts; + if (timeout != _kiInfinite) + make_timespec(ts, timeout); // Check m_signaled to protect outselves from spurious wakeups, like above, but leverage POSIX threads - while (!m_signaled) + int res = 0; + while (!m_signaled && res == 0) { if (timeout == _kiInfinite) - { - pthread_cond_wait(&m_condition, &m_criticalSection); - continue; - } - - // pthread_cond_timedwait expects the timeout to be the actual time - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - time.tv_sec += timeout / 1000; - time.tv_nsec += (timeout % 1000) * 1000000L; - if (time.tv_nsec >= 1000'000'000) //static_cast(1.0e9)) - { - // Normalize timespec - time.tv_sec += 1; - time.tv_nsec -= 1000'000'000; //static_cast(1.0e9); - } - - int res = pthread_cond_timedwait(&m_condition, &m_criticalSection, &time); - if (res == ETIMEDOUT) - break; + res = pthread_cond_wait(&m_condition, &m_criticalSection); + else + res = pthread_cond_timedwait(&m_condition, &m_criticalSection, &ts); } if (m_signaled) - m_signaled = false; // autoreset + m_signaled = false; // auto-reset - pthread_mutex_unlock(&m_criticalSection); + pthread_mutex_unlock(&m_criticalSection); #endif } void AutoResetEvent::signal() noexcept { #ifdef _WIN32 - EnterCriticalSection(&m_criticalSection); - m_signaled = true; - LeaveCriticalSection(&m_criticalSection); - - SetEvent(m_handle); + ::SetEvent(m_handle); // kernel handles auto-reset #else - pthread_mutex_lock(&m_criticalSection); + pthread_mutex_lock(&m_criticalSection); m_signaled = true; - pthread_cond_signal(&m_condition); - pthread_mutex_unlock(&m_criticalSection); + pthread_cond_signal(&m_condition); // wake exactly one waiter + pthread_mutex_unlock(&m_criticalSection); #endif } -// ManualResetEvent:: Constructors, Destructor, Assign operator. +/*──────────────────────────── Manual-reset event ─────────────────────────*/ ManualResetEvent::ManualResetEvent() noexcept { #ifdef _WIN32 - m_handle = CreateEvent(nullptr, TRUE, FALSE, nullptr); + m_handle = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); // manual-reset #else - m_value = false; - pthread_mutexattr_init(&m_criticalSectionAttr); -# ifndef __APPLE__ - pthread_mutexattr_settype(&m_criticalSectionAttr, PTHREAD_MUTEX_RECURSIVE_NP); -# endif - pthread_mutex_init(&m_criticalSection, &m_criticalSectionAttr); - - pthread_condattr_init(&m_conditionAttr); - pthread_cond_init(&m_condition, &m_conditionAttr); + pthread_mutex_init(&m_criticalSection, nullptr); + pthread_cond_init (&m_condition , nullptr); + m_signaled = false; #endif } ManualResetEvent::~ManualResetEvent() noexcept { #ifdef _WIN32 - CloseHandle(m_handle); + ::CloseHandle(m_handle); #else - pthread_condattr_destroy(&m_conditionAttr); - pthread_cond_destroy(&m_condition); - pthread_mutexattr_destroy(&m_criticalSectionAttr); - pthread_mutex_destroy(&m_criticalSection); + pthread_cond_destroy(&m_condition); + pthread_mutex_destroy(&m_criticalSection); #endif } -// ManualResetEvent:: Interaction. - -void ManualResetEvent::wait(uint timeout) noexcept +void ManualResetEvent::wait(uint32 timeout) noexcept { #ifdef _WIN32 - WaitForSingleObjectEx(m_handle, timeout, FALSE); -#else - pthread_mutex_lock(&m_criticalSection); - - // pthread_cond_timedwait expects the timeout to be the actual time, calculate once here - // rather every time inside the loop - timespec time; - if (timeout != _kiInfinite) - { - clock_gettime(CLOCK_REALTIME, &time); - time.tv_sec += timeout / 1000; - time.tv_nsec += (timeout % 1000) * 1000000L; - } - - // it's possible for pthread_cond_wait/timedwait to exit before the timeout and - // without a signal - int result = 0; - while (result == 0 && m_value == false) - { - if (timeout == _kiInfinite) - result = pthread_cond_wait(&m_condition, &m_criticalSection); - else - result = pthread_cond_timedwait(&m_condition, &m_criticalSection, &time); - } - - pthread_mutex_unlock(&m_criticalSection); + if (timeout == 0) + { + ::WaitForSingleObjectEx(m_handle, 0, FALSE); + return; + } + + const DWORD start = ::GetTickCount(); + DWORD remaining = timeout; + + while (true) + { + DWORD rc = ::WaitForSingleObjectEx(m_handle, remaining, TRUE); + if (rc == WAIT_OBJECT_0 || rc == WAIT_TIMEOUT) + return; + + DWORD now = ::GetTickCount(); + if (now - start >= timeout) + return; + remaining = timeout - (now - start); + } + +#else // POSIX + pthread_mutex_lock(&m_criticalSection); + + if (timeout == 0 && !m_signaled) + { + pthread_mutex_unlock(&m_criticalSection); + return; + } + + struct timespec ts; + if (timeout != _kiInfinite) + make_timespec(ts, timeout); + + int res = 0; + while (!m_signaled && res == 0) + { + if (timeout == _kiInfinite) + res = pthread_cond_wait(&m_condition, &m_criticalSection); + else + res = pthread_cond_timedwait(&m_condition, &m_criticalSection, &ts); + } + + pthread_mutex_unlock(&m_criticalSection); #endif } void ManualResetEvent::set() noexcept { #ifdef _WIN32 - SetEvent(m_handle); + ::SetEvent(m_handle); #else - pthread_mutex_lock(&m_criticalSection); - m_value = true; - pthread_cond_signal(&m_condition); - pthread_mutex_unlock(&m_criticalSection); + pthread_mutex_lock(&m_criticalSection); + m_signaled = true; + pthread_cond_broadcast(&m_condition); // wake *all* waiters + pthread_mutex_unlock(&m_criticalSection); #endif } void ManualResetEvent::reset() noexcept { #ifdef _WIN32 - ResetEvent(m_handle); + ::ResetEvent(m_handle); #else - pthread_mutex_lock(&m_criticalSection); - m_value = false; - pthread_cond_signal(&m_condition); - pthread_mutex_unlock(&m_criticalSection); + pthread_mutex_lock(&m_criticalSection); + m_signaled = false; + pthread_mutex_unlock(&m_criticalSection); #endif } diff --git a/src/common/sphere_library/sresetevents.h b/src/common/sphere_library/sresetevents.h index e92dbeedf..d85cf473a 100644 --- a/src/common/sphere_library/sresetevents.h +++ b/src/common/sphere_library/sresetevents.h @@ -51,12 +51,10 @@ class AutoResetEvent private: #ifdef _WIN32 - HANDLE m_handle; // Windows API Handler to handle the event. + HANDLE m_handle; // Windows API Handler to handle the event. #else - pthread_mutex_t m_criticalSection; // Unix API mutex. - pthread_mutexattr_t m_criticalSectionAttr; // Unix API mutex attr. - pthread_condattr_t m_conditionAttr; // Unix API condition attr. - pthread_cond_t m_condition; // Unix API condition. + pthread_mutex_t m_criticalSection; // Unix API mutex. + pthread_cond_t m_condition; // Unix API condition. bool m_signaled; #endif }; @@ -85,7 +83,7 @@ class ManualResetEvent ///@} public: - /** @name Interaction: + /** @name Interaction: */ ///@{ /** @@ -105,13 +103,11 @@ class ManualResetEvent private: #ifdef _WIN32 - HANDLE m_handle; // Windows API Handler to handle the event. + HANDLE m_handle; // Windows API Handler to handle the event. #else - bool m_value; - pthread_mutex_t m_criticalSection; // Unix API mutex. - pthread_mutexattr_t m_criticalSectionAttr; // Unix API mutex attr. - pthread_condattr_t m_conditionAttr; // Unix API condition attr. - pthread_cond_t m_condition; // Unix API condition. + pthread_mutex_t m_criticalSection; // Unix API mutex. + pthread_cond_t m_condition; // Unix API condition. + bool m_signaled; #endif }; From 1374bd45e35ca90563c1dac27c77d6d7e7b34ab5 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 5 Sep 2025 07:36:59 +0200 Subject: [PATCH 073/112] Fixed boat navigation in map with id > 0 (issue #1444, fix by Canerksk) --- src/game/components/CCMultiMovable.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/game/components/CCMultiMovable.cpp b/src/game/components/CCMultiMovable.cpp index 2255677f7..859edd6c9 100644 --- a/src/game/components/CCMultiMovable.cpp +++ b/src/game/components/CCMultiMovable.cpp @@ -670,6 +670,7 @@ bool CCMultiMovable::Move(DIR_TYPE dir, int distance) CPointMap ptDelta; ptDelta.ZeroPoint(); + ptDelta.m_map = pItemThis->GetTopMap(); CPointMap ptFore(pMultiRegion->GetRegionCorner(dir)); CPointMap ptBack(pMultiRegion->GetRegionCorner(GetDirTurn(dir, 4))); From 97b58143175c16679b939fd33d16456c3377ba0b Mon Sep 17 00:00:00 2001 From: mtwango Date: Tue, 23 Sep 2025 20:13:48 +0200 Subject: [PATCH 074/112] Swap distance calc methods switched in 8b5bfc091b111d622086b5fecfb85dfb8718bc07 (#1461) --- src/game/CWorldSearch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/CWorldSearch.cpp b/src/game/CWorldSearch.cpp index 3e3cccea9..e09e953f2 100644 --- a/src/game/CWorldSearch.cpp +++ b/src/game/CWorldSearch.cpp @@ -192,12 +192,12 @@ void CWorldSearch::SetDistanceFunction() if (_fSearchSquare) _distanceFunction = &CPointMap::GetDistSightBase; else - _distanceFunction = &CPointMap::GetDistSight; + _distanceFunction = &CPointMap::GetDistBase; } else { if (_fSearchSquare) - _distanceFunction = &CPointMap::GetDistBase; + _distanceFunction = &CPointMap::GetDistSight; else _distanceFunction = &CPointMap::GetDist; } From 7df95a95fee34a0b37106abbc24817a941fe242a Mon Sep 17 00:00:00 2001 From: mtwango Date: Tue, 23 Sep 2025 20:14:41 +0200 Subject: [PATCH 075/112] Update Ubuntu LTS to fix mariadb/mysql conflict in build job (#1468) --- .github/workflows/build_aux_files.yml | 2 +- .github/workflows/build_linux_x86.yml | 8 ++++---- .github/workflows/build_linux_x86_64.yml | 8 ++++---- .github/workflows/build_osx_arm.yml | 6 +++--- .github/workflows/build_osx_x86_64.yml | 6 +++--- .github/workflows/build_win_x86.yml | 6 +++--- .github/workflows/build_win_x86_64.yml | 6 +++--- .github/workflows/codeql.yml | 6 +++--- .github/workflows/coverity-scan.yml | 4 ++-- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build_aux_files.yml b/.github/workflows/build_aux_files.yml index cf33df985..52fbc1e22 100644 --- a/.github/workflows/build_aux_files.yml +++ b/.github/workflows/build_aux_files.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 diff --git a/.github/workflows/build_linux_x86.yml b/.github/workflows/build_linux_x86.yml index 45486b7bf..062e5b2cf 100644 --- a/.github/workflows/build_linux_x86.yml +++ b/.github/workflows/build_linux_x86.yml @@ -23,14 +23,14 @@ on: jobs: linux-x86: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: contents: read actions: read steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Install prerequisites @@ -140,7 +140,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-linux-x86 merge-multiple: true @@ -166,7 +166,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-linux-x86 merge-multiple: true diff --git a/.github/workflows/build_linux_x86_64.yml b/.github/workflows/build_linux_x86_64.yml index 86b95a075..ef5221b83 100644 --- a/.github/workflows/build_linux_x86_64.yml +++ b/.github/workflows/build_linux_x86_64.yml @@ -20,14 +20,14 @@ on: jobs: linux-x86_64: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: contents: read actions: read steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Install prerequisites @@ -95,7 +95,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-linux-x86_64 merge-multiple: true @@ -120,7 +120,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-linux-x86_64 merge-multiple: true diff --git a/.github/workflows/build_osx_arm.yml b/.github/workflows/build_osx_arm.yml index 0ddcf1983..d399723be 100644 --- a/.github/workflows/build_osx_arm.yml +++ b/.github/workflows/build_osx_arm.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Install prerequisites @@ -85,7 +85,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-osx-arm64 merge-multiple: true @@ -111,7 +111,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-osx-arm64 merge-multiple: true diff --git a/.github/workflows/build_osx_x86_64.yml b/.github/workflows/build_osx_x86_64.yml index f3eb58f7d..0ecb9f221 100644 --- a/.github/workflows/build_osx_x86_64.yml +++ b/.github/workflows/build_osx_x86_64.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Install prerequisites @@ -84,7 +84,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-osx-x86_64 merge-multiple: true @@ -107,7 +107,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-osx-x86_64 merge-multiple: true diff --git a/.github/workflows/build_win_x86.yml b/.github/workflows/build_win_x86.yml index 165ef56bc..730233c5a 100644 --- a/.github/workflows/build_win_x86.yml +++ b/.github/workflows/build_win_x86.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Add msbuild to PATH @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-win-x86 merge-multiple: true @@ -91,7 +91,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-win-x86 merge-multiple: true diff --git a/.github/workflows/build_win_x86_64.yml b/.github/workflows/build_win_x86_64.yml index fd817079b..2beaf49ac 100644 --- a/.github/workflows/build_win_x86_64.yml +++ b/.github/workflows/build_win_x86_64.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Add msbuild to PATH @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-win-x86_64 merge-multiple: true @@ -90,7 +90,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: Build-win-x86_64 merge-multiple: true diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e65f7fc2a..a6db06206 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -22,7 +22,7 @@ on: jobs: analyze: name: Analyze - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: actions: read contents: read @@ -37,7 +37,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Install prerequisites run: | @@ -67,7 +67,7 @@ jobs: # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. - + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # c++ queries: https://codeql.github.com/codeql-query-help/cpp-cwe/ diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index e1667aa6d..3ba0f5bc5 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -13,14 +13,14 @@ on: jobs: latest: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: contents: read actions: read steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 From 08b69ea9e60829b9a3c4384f2b73098b46ca9ecf Mon Sep 17 00:00:00 2001 From: mtwango Date: Tue, 23 Sep 2025 20:17:09 +0200 Subject: [PATCH 076/112] Fix speech arguments initialization (#1467) --- src/game/CObjBase.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index 4252b94ce..6c928509a 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -859,7 +859,7 @@ TRIGRET_TYPE CObjBase::OnHearTrigger( CResourceLock & s, lpctstr pszCmd, CChar * if ( s.IsKeyHead("ON",2)) { // Look for some key word. - tchar* ptcOn = s.GetArgStr(); + tchar* ptcOn = s.GetArgStr(); //_strupr(ptcOn); // Str_Match is already case insensitive if ( Str_Match( ptcOn, pszCmd ) == MATCH_VALID ) fMatch = true; @@ -869,10 +869,12 @@ TRIGRET_TYPE CObjBase::OnHearTrigger( CResourceLock & s, lpctstr pszCmd, CChar * if ( ! fMatch ) continue; // look for the next "ON" section. - pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); - pScriptArgs->m_iN1 = iModeRef; - pScriptArgs->m_iN2 = wHue; - TRIGRET_TYPE iRet = CObjBase::OnTriggerRunVal( s, TRIGRUN_SECTION_EXEC, pScriptArgs, pSrc ); + pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); + pScriptArgs->Init(pszCmd); + pScriptArgs->m_iN1 = iModeRef; + pScriptArgs->m_iN2 = wHue; + TRIGRET_TYPE iRet = CObjBase::OnTriggerRunVal( s, TRIGRUN_SECTION_EXEC, pScriptArgs, pSrc ); + if ( iRet != TRIGRET_RET_FALSE ) return iRet; From 78a8636b75554c54e13f48a9fea09454b2dab5db Mon Sep 17 00:00:00 2001 From: mtwango Date: Thu, 25 Sep 2025 09:46:27 +0200 Subject: [PATCH 077/112] Increased max dmg for dragon breath (#1466) --- Changelog.txt | 3 +++ src/game/chars/CCharSkill.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index e6836e13b..5c9a933cb 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -4050,3 +4050,6 @@ When setting a property like MORE to the a spell or skill defname, trying to rea 16-05-2025, Mulambo - Changed: When equipping item by dragging or using Dress macro, the item layer is now showing actual layer of the object in EquipTest and ItemEquipTest trigger (instead of 31 = LAYER_DRAGGING). This is the same behaviour, as equipping by double clicking. + +22-09-2025, Mulambo +- Changed: NPC Breath Damage (brain_dragon) is no longer capped at 200, but can go up to approx 65535. diff --git a/src/game/chars/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index c0d195a74..d02d6d81f 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -3309,8 +3309,8 @@ int CChar::Skill_Act_Breath( SKTRIG_TYPE stage ) iDamage = (Stat_GetVal(STAT_STR) * 5) / 100; if ( iDamage < 1 ) iDamage = 1; - else if ( iDamage > 200 ) - iDamage = 200; + else if ( iDamage > UINT16_MAX ) + iDamage = UINT16_MAX; } HUE_TYPE hue = (HUE_TYPE)(GetDefNum("BREATH.HUE", true)); From 3788eb9ec2c10351c38c06d9dea6821560a56e33 Mon Sep 17 00:00:00 2001 From: mtwango Date: Thu, 25 Sep 2025 09:48:47 +0200 Subject: [PATCH 078/112] Show real character name on login screen (#1452) --- src/game/clients/CClientMsg.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index 05cefd4f5..80e81ad30 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -1594,7 +1594,7 @@ uint CClient::Setup_FillCharList(Packet* pPacket, const CChar * pCharFirst) { m_tmSetupCharList[0] = pCharFirst->GetUID(); - pPacket->writeStringFixedASCII(pCharFirst->GetName(), MAX_NAME_SIZE); + pPacket->writeStringFixedASCII(pCharFirst->GetNameWithoutIncognito(), MAX_NAME_SIZE); pPacket->writeStringFixedASCII("", MAX_NAME_SIZE); ++count; @@ -1620,7 +1620,7 @@ uint CClient::Setup_FillCharList(Packet* pPacket, const CChar * pCharFirst) m_tmSetupCharList[count] = uid; - pPacket->writeStringFixedASCII(pChar->GetName(), MAX_NAME_SIZE); + pPacket->writeStringFixedASCII(pChar->GetNameWithoutIncognito(), MAX_NAME_SIZE); pPacket->writeStringFixedASCII("", MAX_NAME_SIZE); ++count; From bbfc587bb2c270c53ecb54cb5e1ea2f54cd9031f Mon Sep 17 00:00:00 2001 From: DavideRei <118212274+DavideRei@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:46:14 +0200 Subject: [PATCH 079/112] Restore out of bounds checks in SetAdjacentSectors (#1474) --- src/game/CSectorTemplate.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index 9e7eddc81..f5ce9a004 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -198,7 +198,13 @@ void CSectorBase::SetAdjacentSectors() { // out of bounds checks const int iAdjX = m_BasePoint.m_x + _xyDir[i].x; + // These checks are needed or the negative iAdjX/iAdjY can lead to a wrong index if the sector is near the map borders. + // For instance m_index = 0, iAdjY > 0 and iAdjX < 0, SW check, the index start from the first column of the second row and it goes back to the first row because there is no SW sector + if ((iAdjX < 0) || (iAdjX >= iMaxX)) + continue; const int iAdjY = m_BasePoint.m_y + _xyDir[i].y; + if ((iAdjY < 0) || (iAdjY >= iMaxY)) + continue; int index = m_index; index += ((iAdjY * iMaxX) + iAdjX); From 2785f49684600c825a18df5b1121d9f8c8b79b1c Mon Sep 17 00:00:00 2001 From: mtwango Date: Tue, 30 Sep 2025 07:30:27 +0200 Subject: [PATCH 080/112] Notoriety (#1481) --- src/game/chars/CChar.h | 30 ++++- src/game/chars/CCharNotoriety.cpp | 199 ++++++++++++++++-------------- src/sphere.ini | 2 +- 3 files changed, 131 insertions(+), 100 deletions(-) diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index d87e95d5e..3af9ab0e6 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -696,16 +696,16 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; NOTO_TYPE Noto_GetFlag( const CChar * pChar, bool fIncog = true, bool fInvul = false, bool fGetColor = false ) const; /** - * @brief Notoriety calculations + * Notoriety calculations. What is this char to the viewer? This allows the noto attack check in the client. * * TAG.OVERRIDE.NOTO will override everything and use the value in the tag for everyone, regardless of what I really are for them. * If this char is a pet, check if notoriety must be inherited from its master or do regular checks for it. - * @param pChar is the CChar that needs to know what I am (good, evil, criminal, neutral...) to him. - * @param fIncog if set to true (usually because of Incognito spell), this character will be gray for the viewer (pChar). - * @param fInvul if set to true invulnerable characters will return NOTO_INVUL (yellow bar, etc). + * @param pCharViewer is the CChar that needs to know what I am (good, evil, criminal, neutral...) to him. + * @param fAllowIncog if set to true (usually because of Incognito spell), this character will be gray for the viewer (pChar). + * @param fAllowInvul if set to true invulnerable characters will return NOTO_INVUL (yellow bar, etc). * @return NOTO_TYPE notoriety level. */ - NOTO_TYPE Noto_CalcFlag( const CChar * pChar, bool fIncog = false, bool fInvul = false ) const; + NOTO_TYPE Noto_CalcFlag( const CChar * pCharViewer, bool fAllowIncog = false, bool fAllowInvul = false ) const; /** * @brief What color should the viewer see from me? @@ -860,6 +860,25 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; */ void NotoSave_CheckTimeout(); + /** + * Helper for checking guild / town war status. + */ + enum NOTO_WAR_STATUS: byte + { + NOTO_WAR_NONE = 0, + NOTO_WAR_ALLY, + NOTO_WAR_ENEMY, + }; + + /** + * Checks ally / war status between two guild / town stones. + * + * @param pMyStone My Guild/Town stone. + * @param pViewerStone The character's looking at me Guild/Town stone. + * @return War status (none, ally, enemy) + */ + NOTO_WAR_STATUS Noto_GetWarStatus(const CItemStone* pMyStone, const CItemStone* pViewerStone) const; + /** * @brief We are snooping or stealing, is taking this item a crime ? * @@ -869,7 +888,6 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; */ bool IsTakeCrime( const CItem * pItem, CChar ** ppCharMark = nullptr ) const; - /** * @brief We killed a character, starting exp calcs * diff --git a/src/game/chars/CCharNotoriety.cpp b/src/game/chars/CCharNotoriety.cpp index 5b844011e..64926d1cc 100644 --- a/src/game/chars/CCharNotoriety.cpp +++ b/src/game/chars/CCharNotoriety.cpp @@ -141,14 +141,11 @@ NOTO_TYPE CChar::Noto_GetFlag(const CChar * pCharViewer, bool fAllowIncog, bool // NOTO_GUILD_WAR 5 // NOTO_EVIL 6 // NOTO_INVUL 7 -NOTO_TYPE CChar::Noto_CalcFlag(const CChar * pCharViewer, bool fAllowIncog, bool fAllowInvul) const +NOTO_TYPE CChar::Noto_CalcFlag(const CChar * pCharViewer, const bool fAllowIncog, const bool fAllowInvul) const { ADDTOCALLSTACK("CChar::Noto_GetFlag"); - // What is this char to the viewer ? - // This allows the noto attack check in the client. - // NOTO_GOOD = it is criminal to attack me. - NOTO_TYPE iNotoFlag = (NOTO_TYPE)(m_TagDefs.GetKeyNum("OVERRIDE.NOTO")); + const auto iNotoFlag = static_cast(m_TagDefs.GetKeyNum("OVERRIDE.NOTO")); if (iNotoFlag != NOTO_INVALID) return iNotoFlag; @@ -158,114 +155,116 @@ NOTO_TYPE CChar::Noto_CalcFlag(const CChar * pCharViewer, bool fAllowIncog, bool if (fAllowInvul && IsStatFlag(STATF_INVUL)) return NOTO_INVUL; - if (m_pArea && m_pArea->IsFlag(REGION_FLAG_ARENA)) // everyone is neutral here. + // Everyone is neutral here. + if (m_pArea && m_pArea->IsFlag(REGION_FLAG_ARENA)) return NOTO_NEUTRAL; - // TODO: refactor this nesting mess and return early - if (this != pCharViewer) // Am I checking myself? - { - if (m_pNPC) - { - if (g_Cfg.m_iPetsInheritNotoriety != 0) - { - // Do we have a master to inherit notoriety from? - CChar* pMaster = NPC_PetGetOwnerRecursive(); - if (pMaster && (pMaster != pCharViewer)) // master doesn't want to see their own status - { - // return master's notoriety - NOTO_TYPE notoMaster = pMaster->Noto_GetFlag(pCharViewer, fAllowIncog, fAllowInvul); - - // check if notoriety is inheritable based on bitmask setting: - // NOTO_GOOD = 0x01 - // NOTO_GUILD_SAME = 0x02 - // NOTO_NEUTRAL = 0x04 - // NOTO_CRIMINAL = 0x08 - // NOTO_GUILD_WAR = 0x10 - // NOTO_EVIL = 0x20 - int iPetNotoFlag = 1 << (notoMaster - 1); - if ((g_Cfg.m_iPetsInheritNotoriety & iPetNotoFlag) == iPetNotoFlag) - return notoMaster; - } - } + const bool fSelfCheck = (this == pCharViewer); - if (!IsSetOF(OF_PetBehaviorOwnerNeutral) && NPC_IsOwnedBy(pCharViewer, false)) // All pets are neutral to their owners. - return NOTO_NEUTRAL; - } + // Player is looking at himself. + if (fSelfCheck) + goto skip_guilds; - // Are we in the same party ? - if (m_pParty && (m_pParty == pCharViewer->m_pParty) ) - { - //if (m_pParty->GetLootFlag(this)) - return NOTO_GUILD_SAME; - } + // We are in the same party. + if (m_pParty && m_pParty == pCharViewer->m_pParty) + return NOTO_GUILD_SAME; - if (m_pPlayer) + // We are looking at NPC. + if (m_pNPC) + { + // Flag to display true notoriety is disabled. + if (!IsSetOF(OF_PetBehaviorOwnerNeutral) && NPC_IsOwnedBy(pCharViewer, false)) + return NOTO_NEUTRAL; + + // Pets inheriting notoriety from master. + if (g_Cfg.m_iPetsInheritNotoriety != 0) { - // Check the guild/town stuff - const CItemStone * pMyTown = Guild_Find(MEMORY_TOWN); - const CItemStone * pMyGuild = Guild_Find(MEMORY_GUILD); - if (pMyGuild || pMyTown) + // Get master and his notoriety. + const CChar * pMaster = NPC_PetGetOwnerRecursive(); + if (pMaster && pMaster != pCharViewer) { - const CItemStone * pViewerGuild = pCharViewer->Guild_Find(MEMORY_GUILD); - const CItemStone * pViewerTown = pCharViewer->Guild_Find(MEMORY_TOWN); - // Are we both in a guild? - if (pViewerGuild || pViewerTown) - { - if (pMyGuild && pMyGuild->IsPrivMember(this)) - { - if (pViewerGuild && pViewerGuild->IsPrivMember(pCharViewer)) - { - if (IsSetOF(OF_EnableGuildAlignNotoriety)) - { - if (pViewerGuild->GetAlignType() != STONEALIGN_STANDARD && pMyGuild->GetAlignType() != STONEALIGN_STANDARD) //We have to check if my guild is also not STONEALIGN_STANDART. - { - if (pViewerGuild->GetAlignType() == pMyGuild->GetAlignType()) - { - return NOTO_GUILD_SAME; - } - return NOTO_GUILD_WAR; - } - } - - if (pViewerGuild == pMyGuild) // Same guild? - return NOTO_GUILD_SAME; // return green - if (pMyGuild->IsAlliedWith(pViewerGuild)) - return NOTO_GUILD_SAME; - // Are we in different guilds but at war? (not actually a crime right?) - if (pMyGuild->IsAtWarWith(pViewerGuild)) - return NOTO_GUILD_WAR; // return orange - } - if (pMyGuild->IsAtWarWith(pViewerTown)) - return NOTO_GUILD_WAR; // return orange - } - if (pMyTown && pMyTown->IsPrivMember(this)) - { - if (pViewerGuild && pViewerGuild->IsPrivMember(pCharViewer)) - { - if (pMyTown->IsAtWarWith(pViewerGuild)) - return NOTO_GUILD_WAR; // return orange - } - if (pMyTown->IsAtWarWith(pViewerTown)) - return NOTO_GUILD_WAR; // return orange - } - } + const NOTO_TYPE notoMaster = pMaster->Noto_GetFlag(pCharViewer, fAllowIncog, fAllowInvul); + + // Get notoriety based on bit flag defined in sphere.ini's `PetsInheritNotoriety`. + const int iPetNotoFlag = 1 << (notoMaster - 1); + if ((g_Cfg.m_iPetsInheritNotoriety & iPetNotoFlag) == iPetNotoFlag) + return notoMaster; } } - } + // Guild/Town relations. + { + const CItemStone * pMyTown = Guild_Find(MEMORY_TOWN); + const CItemStone * pMyGuild = Guild_Find(MEMORY_GUILD); + + // I don't have any town or guild. + if (!pMyTown && !pMyGuild) + goto skip_guilds; + + const CItemStone * pViewerTown = pCharViewer->Guild_Find(MEMORY_TOWN); + const CItemStone * pViewerGuild = pCharViewer->Guild_Find(MEMORY_GUILD); + + // Player looking at me doesn't have town or guild either. + if (!pViewerTown && !pViewerGuild) + goto skip_guilds; + + // We are both in a guild. + if (pMyGuild && pMyGuild->IsPrivMember(this) && pViewerGuild && pViewerGuild->IsPrivMember(pCharViewer)) + { + // Same guild. + if (pViewerGuild == pMyGuild) + return NOTO_GUILD_SAME; + + // Check standard relationships first (war/ally). + const NOTO_WAR_STATUS warStatus = Noto_GetWarStatus(pMyGuild, pViewerGuild); + if (warStatus == NOTO_WAR_ENEMY) + return NOTO_GUILD_WAR; + if (warStatus == NOTO_WAR_ALLY) + return NOTO_GUILD_SAME; + + // Guild alignment. + const STONEALIGN_TYPE myAlign = pMyGuild->GetAlignType(); + const STONEALIGN_TYPE viewerAlign = pViewerGuild->GetAlignType(); + + // We both aren't in a neutral guild. + if (myAlign != STONEALIGN_STANDARD && viewerAlign != STONEALIGN_STANDARD) + { + // Same guilds share notoriety and we are in the same fraction. + if (IsSetOF(OF_EnableGuildAlignNotoriety) && myAlign == viewerAlign) + return NOTO_GUILD_SAME; + + // Or not. + if (myAlign != viewerAlign) + return NOTO_GUILD_WAR; + } + } + + // Town wars / town vs guild wars. + if (pMyGuild && pMyGuild->IsPrivMember(this) && Noto_GetWarStatus(pMyGuild, pViewerTown) == NOTO_WAR_ENEMY) + return NOTO_GUILD_WAR; + if (pMyTown && pMyTown->IsPrivMember(this)) + { + if (pViewerGuild && pViewerGuild->IsPrivMember(pCharViewer) && Noto_GetWarStatus(pMyTown, pViewerGuild) == NOTO_WAR_ENEMY) + return NOTO_GUILD_WAR; + if (Noto_GetWarStatus(pMyTown, pViewerTown) == NOTO_WAR_ENEMY) + return NOTO_GUILD_WAR; + } + } + +skip_guilds: if (Noto_IsEvil()) return NOTO_EVIL; - if (this != pCharViewer) // Am I checking myself? + if (!fSelfCheck) { - // If they saw me commit a crime or I am their aggressor then criminal to just them. + // If viewer saw me commit a crime, or I am his aggressor, then criminal to just them. const CItemMemory * pMemory = pCharViewer->Memory_FindObjTypes(this, MEMORY_SAWCRIME | MEMORY_AGGREIVED); if (pMemory != nullptr) return NOTO_CRIMINAL; } - if (IsStatFlag(STATF_CRIMINAL)) // criminal to everyone. + if (IsStatFlag(STATF_CRIMINAL)) return NOTO_CRIMINAL; if (Noto_IsNeutral() || m_TagDefs.GetKeyNum("NOTO.PERMAGREY")) @@ -757,6 +756,20 @@ void CChar::NotoSave_CheckTimeout() } } +CChar::NOTO_WAR_STATUS CChar::Noto_GetWarStatus(const CItemStone* pMyStone, const CItemStone* pViewerStone) const +{ + if (!pMyStone || !pViewerStone) + return NOTO_WAR_NONE; + + if (pMyStone->IsAtWarWith(pViewerStone)) + return NOTO_WAR_ENEMY; + + if (pMyStone->IsAlliedWith(pViewerStone)) + return NOTO_WAR_ALLY; + + return NOTO_WAR_NONE; +} + void CChar::NotoSave_Resend(CChar * pChar) { ADDTOCALLSTACK("CChar::NotoSave_Resend"); diff --git a/src/sphere.ini b/src/sphere.ini index eefe7300d..6efee0d72 100644 --- a/src/sphere.ini +++ b/src/sphere.ini @@ -873,7 +873,7 @@ Experimental=0 // OF_OWNoDropCarriedItem 00400000 // When overweighted, don't drop items on ground when moving them (or using BOUNCE) and checking if you can carry them. // OF_AllowContainerInsideContainer 00800000 // Allow containers inside other containers even if they are heavier than the container being inserted into. // OF_VendorStockLimit 01000000 // Limits how much of an item a vendor can buy using the value set in the TEMPLATE. Format: BUY=ID,AMOUNT -// OF_EnableGuildAlignNotoriety 02000000 // If enabled, guilds with the same alignment will see each other as enemy or ally. +// OF_EnableGuildAlignNotoriety 02000000 // If enabled, guilds with the same alignment (order / chaos) will see each other as allies (green). // OF_NoDclickEquip 04000000 // If enabled, double-click does not equip items. // OF_PetBehaviorOwnerNeutral 08000000 // Should my pets always appear natural to me? // OF_NPCMovementOldStyle 010000000 // Required setting to make NPCs run like in the old Sphere versions (0.56b). From b87d72228b3ff8abbf2ff0311b0884842c521781 Mon Sep 17 00:00:00 2001 From: mtwango Date: Tue, 30 Sep 2025 07:32:58 +0200 Subject: [PATCH 081/112] ARGN3 for @PersonalSpace trigger to bypass max stamina requirement over players (#1250 #1455) (#1473) --- Changelog.txt | 3 +++ src/game/chars/CCharAct.cpp | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Changelog.txt b/Changelog.txt index 5c9a933cb..7663831db 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -4053,3 +4053,6 @@ When setting a property like MORE to the a spell or skill defname, trying to rea 22-09-2025, Mulambo - Changed: NPC Breath Damage (brain_dragon) is no longer capped at 200, but can go up to approx 65535. + +24-09-2025, Mulambo +- Added: ARGN3 for trigger @PersonalSpace [R/W], allowing to bypass maximum stamina requirement (default 1 = require maximum stamina) when stepping over players. diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 84b522d53..85070eed8 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -4608,6 +4608,7 @@ bool CChar::ShoveCharAtPosition(CPointMap const& ptDst, ushort *uiStaminaRequire // If i'm not pathfinding, ensure that i pass a valid uiStaminaRequirement, since i'll need it for the walk checks. ASSERT(fPathFinding || (nullptr != uiStaminaRequirement)); ushort uiLocalStamReq = 0; + bool fRequireFullStamina = true; CItem *pPoly = LayerFind(LAYER_SPELL_Polymorph); auto AreaChars = CWorldSearchHolder::GetInstance(ptDst); @@ -4629,6 +4630,8 @@ bool CChar::ShoveCharAtPosition(CPointMap const& ptDst, ushort *uiStaminaRequire else if ((pPoly && pPoly->m_itSpell.m_spell == SPELL_Wraith_Form) && (GetTopMap() == 0)) // chars under Wraith Form effect can always walk through chars in Felucca uiLocalStamReq = 0; + fRequireFullStamina = true; + TRIGRET_TYPE iRet = TRIGRET_RET_DEFAULT; if (!fPathFinding) //You want to avoid to trig the triggers if it's only a pathfinding evaluation { @@ -4636,10 +4639,12 @@ bool CChar::ShoveCharAtPosition(CPointMap const& ptDst, ushort *uiStaminaRequire { CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr(); pScriptArgs->m_iN1 = uiLocalStamReq; + pScriptArgs->m_iN3 = fRequireFullStamina; iRet = pChar->OnTrigger(CTRIG_PersonalSpace, pScriptArgs, this); if (iRet == TRIGRET_RET_TRUE) goto set_and_return_false; uiLocalStamReq = (ushort)(pScriptArgs->m_iN1); + fRequireFullStamina = static_cast(pScriptArgs->m_iN3); } if (IsTrigUsed(TRIGGER_CHARSHOVE)) { @@ -4652,7 +4657,7 @@ bool CChar::ShoveCharAtPosition(CPointMap const& ptDst, ushort *uiStaminaRequire } } - if ((uiLocalStamReq > 0) && (Stat_GetVal(STAT_DEX) < Stat_GetMaxAdjusted(STAT_DEX))) + if ((uiLocalStamReq > 0) && fRequireFullStamina && (Stat_GetVal(STAT_DEX) < Stat_GetMaxAdjusted(STAT_DEX))) goto set_and_return_false; if (Stat_GetVal(STAT_DEX) < uiLocalStamReq) // check if we have enough stamina to push the char From 5a052dad8f932592a35f8c442c614828516733c2 Mon Sep 17 00:00:00 2001 From: Mulambo Date: Tue, 30 Sep 2025 19:09:21 +0200 Subject: [PATCH 082/112] Remove Party Member update (#1476) * Remove party member from list (#1475) * Do not disband party when party leader disconnects (possible fix for 1358) - new party message about changing leader - CPartyDef::RemoveMember refactoring There is no solution, when party leader removes himself from the party, since the packet for disbanding and removing leader is the same. --- Changelog.txt | 3 ++ src/game/chars/CChar.cpp | 11 ++----- src/game/clients/CParty.cpp | 62 +++++++++++++++++++++++++------------ src/game/clients/CParty.h | 15 ++++++++- src/network/receive.cpp | 1 + src/tables/defmessages.tbl | 1 + 6 files changed, 64 insertions(+), 29 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 7663831db..54b3fde0d 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -4056,3 +4056,6 @@ When setting a property like MORE to the a spell or skill defname, trying to rea 24-09-2025, Mulambo - Added: ARGN3 for trigger @PersonalSpace [R/W], allowing to bypass maximum stamina requirement (default 1 = require maximum stamina) when stepping over players. + +26-09-2025, Mulambo +- Changed: When the party leader gets disconnected (or deleted), new party leader is appointed, instead of disbanding the party. diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index d5e581f38..bf247ce6e 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -354,7 +354,7 @@ CChar::~CChar() if (m_pParty) { - m_pParty->RemoveMember( GetUID(), GetUID() ); + m_pParty->RemoveMember(GetUID(), GetUID(), false); m_pParty = nullptr; } //Guild_Resign(MEMORY_GUILD); Moved to the ClearPlayer method otherwise it will cause a server crash because the deleted player will still be found in the guild list. @@ -493,13 +493,6 @@ void CChar::ClientDetach() if ( !IsClientActive() ) return; - if ( m_pParty && m_pParty->IsPartyMaster( this )) - { - // Party must disband if the master is logged out. - m_pParty->Disband(GetUID()); - m_pParty = nullptr; - } - // If this char is on a IT_SHIP then we need to stop the ship ! if ( m_pArea && m_pArea->IsFlag( REGION_FLAG_SHIP )) { @@ -548,7 +541,7 @@ void CChar::SetDisconnected(CSector* pNewSector) if (m_pParty) { - m_pParty->RemoveMember( GetUID(), GetUID() ); + m_pParty->RemoveMember(GetUID(), GetUID(), false); m_pParty = nullptr; } diff --git a/src/game/clients/CParty.cpp b/src/game/clients/CParty.cpp index 76620ea92..c912c1cbf 100644 --- a/src/game/clients/CParty.cpp +++ b/src/game/clients/CParty.cpp @@ -293,43 +293,60 @@ void CPartyDef::AcceptMember( CChar *pChar ) SendAddList(nullptr); } -bool CPartyDef::RemoveMember( CUID uidRemove, CUID uidCommand ) +bool CPartyDef::RemoveMember(const CUID& uidRemove, const CUID &uidCommand, const bool fDisband) { ADDTOCALLSTACK("CPartyDef::RemoveMember"); - // ARGS: - // uidRemove = Who is being removed. - // uidCommand = who removed this person (only the master or self can remove) - // - // NOTE: remove of the master will cause the party to disband. - if ( m_Chars.GetCharCount() <= 0 ) + if (m_Chars.GetCharCount() <= 0) return false; - CUID uidMaster(GetMaster()); - if ( (uidRemove != uidCommand) && (uidCommand != uidMaster) ) + const CUID uidMaster(GetMaster()); + if (uidRemove != uidCommand && uidCommand != uidMaster) return false; CChar *pCharRemove(uidRemove.CharFind()); - if ( !pCharRemove ) + if (!pCharRemove) return false; - if ( !IsInParty(pCharRemove) ) + if (!IsInParty(pCharRemove)) return false; - if ( uidRemove == uidMaster ) + if (fDisband && uidRemove == uidMaster) return Disband(uidMaster); CChar *pSrc = uidCommand.CharFind(); - if ( pSrc && IsTrigUsed(TRIGGER_PARTYREMOVE) ) + if (pSrc && IsTrigUsed(TRIGGER_PARTYREMOVE)) { - if ( pCharRemove->OnTrigger(CTRIG_PartyRemove, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc) == TRIGRET_RET_TRUE ) + if (pCharRemove->OnTrigger(CTRIG_PartyRemove, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc) == TRIGRET_RET_TRUE) return false; } - if ( IsTrigUsed(TRIGGER_PARTYLEAVE) ) + if (IsTrigUsed(TRIGGER_PARTYLEAVE)) { - if ( pCharRemove->OnTrigger(CTRIG_PartyLeave, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharRemove) == TRIGRET_RET_TRUE ) + if (pCharRemove->OnTrigger(CTRIG_PartyLeave, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharRemove) == TRIGRET_RET_TRUE) return false; } - // Remove it from the party + // If the party leader left, try to promote new character to be the leader. + tchar *pszChangeLeader = Str_GetTemp(); + if (uidRemove == uidMaster) + { + // No valid character on second position in the party, disband it. + if (!m_Chars.IsValidIndex(1)) + return Disband(uidMaster); + + const CUID newMaster(m_Chars.GetChar(1)); + CChar *pCharNewMaster(newMaster.CharFind()); + + // Cannot find new leader's character, disband it. + if (!pCharNewMaster) + return Disband(uidMaster); + + // If new master wasn't appointed, disband the party. + if (!SetMaster(pCharNewMaster)) + return Disband(uidMaster); + + snprintf(pszChangeLeader, Str_TempLength(), g_Cfg.GetDefaultMsg(DEFMSG_PARTY_CHANGE_LEADER), pCharNewMaster->GetName()); + } + + // Remove the character from the party. SendRemoveList(pCharRemove, true); DetachChar(pCharRemove); pCharRemove->SysMessageDefault(DEFMSG_PARTY_LEAVE_2); @@ -338,13 +355,20 @@ bool CPartyDef::RemoveMember( CUID uidRemove, CUID uidCommand ) snprintf(pszMsg, Str_TempLength(), g_Cfg.GetDefaultMsg(DEFMSG_PARTY_LEAVE_1), pCharRemove->GetName()); SysMessageAll(pszMsg); - if ( m_Chars.GetCharCount() <= 1 ) + // Disband the party if I'm alone. + if (m_Chars.GetCharCount() <= 1) { - // Disband the party SysMessageAll(g_Cfg.GetDefaultMsg(DEFMSG_PARTY_LEAVE_LAST_PERSON)); return Disband(uidMaster); } + // Notify about change in party leadership (we need to do it here, so it doesn't get sent to the previous leader). + if (uidRemove == uidMaster) + SysMessageAll(pszChangeLeader); + + // We still have a party, notify other members about removal. + SendRemoveList(pCharRemove, false); + return true; } diff --git a/src/game/clients/CParty.h b/src/game/clients/CParty.h index 5182f0a4a..18aab6805 100644 --- a/src/game/clients/CParty.h +++ b/src/game/clients/CParty.h @@ -78,7 +78,20 @@ class CPartyDef : public CSObjListRec, public CScriptObj // Commands bool Disband( CUID uidMaster ); - bool RemoveMember( CUID uidRemove, CUID uidCommand ); + + /** + * Removes a member from party. If leader is removed, it tries to pass leader to another character. + * + * @note We need to switch characters at leader position before we remove him, because original client doesn't like removing leader and thinks, the party + * was disbanded (Classic UO doesn't care though). + * + * @param uidRemove Character being removed. + * @param uidCommand Character, who issued the removal (leader or self). + * @param fDisband If set to false, we try to change leader instead of disbanding the party. + * + * @return True if successfully removed, false otherwise. + */ + bool RemoveMember(const CUID& uidRemove, const CUID &uidCommand, bool fDisband = true); void AcceptMember( CChar * pChar ); void SetLootFlag( CChar * pChar, bool fSet ); bool GetLootFlag( const CChar * pChar ); diff --git a/src/network/receive.cpp b/src/network/receive.cpp index a5dcac1fe..1106e32a0 100644 --- a/src/network/receive.cpp +++ b/src/network/receive.cpp @@ -2655,6 +2655,7 @@ bool PacketPartyMessage::onReceive(CNetState* net) client->addTarget( CLIMODE_TARG_PARTY_ADD, g_Cfg.GetDefaultMsg(DEFMSG_PARTY_TARG_WHO), false, false); break; + // This doesn't happen, since disbanding the party is handled by client sending a packet to remove the party master. case PARTYMSG_Disband: if (character->m_pParty == nullptr) return false; diff --git a/src/tables/defmessages.tbl b/src/tables/defmessages.tbl index 4f80adebf..9838b04cc 100644 --- a/src/tables/defmessages.tbl +++ b/src/tables/defmessages.tbl @@ -732,6 +732,7 @@ MSG(PARTY_ADDED, "You have been added to the party.") MSG(PARTY_DECLINE_1, "%s: Does not wish to join the party.") MSG(PARTY_DECLINE_2, "You notify %s that you do not wish to join the party.") MSG(PARTY_DISBANDED, "Your party has disbanded.") +MSG(PARTY_CHANGE_LEADER, "%s was appointed the new party leader.") MSG(PARTY_INVITE, "You have invited %s to join the party.") MSG(PARTY_INVITE_TARG, "%s: You are invited to join the party. Type /accept to join or /decline to decline the offer.") MSG(PARTY_JOINED, "%s: joined the party.") From 750db1ad2ef1d1d711720d251404085b68b4f74f Mon Sep 17 00:00:00 2001 From: Mulambo Date: Tue, 30 Sep 2025 19:28:00 +0200 Subject: [PATCH 083/112] Send login rejection to all client requests (#1479) --- Changelog.txt | 3 +++ src/game/clients/CClientLog.cpp | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 54b3fde0d..796b515e0 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -4059,3 +4059,6 @@ When setting a property like MORE to the a spell or skill defname, trying to rea 26-09-2025, Mulambo - Changed: When the party leader gets disconnected (or deleted), new party leader is appointed, instead of disbanding the party. + +30-09-2025, Mulambo +- Changed: Login rejection (packet `0x82`) is now sent to all clients trying to log in (used to be only to clients, that Sphere could identify). diff --git a/src/game/clients/CClientLog.cpp b/src/game/clients/CClientLog.cpp index aeb38df4d..192deb78a 100644 --- a/src/game/clients/CClientLog.cpp +++ b/src/game/clients/CClientLog.cpp @@ -178,8 +178,7 @@ bool CClient::addLoginErr(byte code) break; } - if ( GetNetState()->m_clientVersionNumber || GetNetState()->m_reportedVersionNumber ) // only reply the packet to valid clients - new PacketLoginError(this, static_cast(code)); + new PacketLoginError(this, static_cast(code)); GetNetState()->markReadClosed(); return false; } From 2a18521d4f4a83ee2d0665c50d1b825d87dca134 Mon Sep 17 00:00:00 2001 From: Gladie Date: Wed, 1 Oct 2025 11:47:10 +0200 Subject: [PATCH 084/112] Fix of custom multi copying first level into other higher ones (#1484) --- src/game/items/CItemMultiCustom.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/game/items/CItemMultiCustom.cpp b/src/game/items/CItemMultiCustom.cpp index 27334a190..636f171b1 100644 --- a/src/game/items/CItemMultiCustom.cpp +++ b/src/game/items/CItemMultiCustom.cpp @@ -963,6 +963,7 @@ void CItemMultiCustom::SendStructureTo(CClient * pClientSrc) int iItemCount = 0; int iMaxIndex = 0; + memset(wPlaneBuffer, 0, sizeof(wPlaneBuffer)); for (const CMultiComponent* pComp : pDesign->m_vectorComponents) { const uchar uiCompPlane = GetPlane(pComp); From 14161a4d6ffe25f41e8448093ed351b79fd35623 Mon Sep 17 00:00:00 2001 From: David Kindl Date: Tue, 30 Sep 2025 12:25:12 +0200 Subject: [PATCH 085/112] Check, whether CCItemDamageable has been placed in the world before calling tick (#1422) --- src/game/components/CCItemDamageable.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/game/components/CCItemDamageable.cpp b/src/game/components/CCItemDamageable.cpp index 2017dd557..f8e1ef1d3 100644 --- a/src/game/components/CCItemDamageable.cpp +++ b/src/game/components/CCItemDamageable.cpp @@ -69,28 +69,28 @@ void CCItemDamageable::OnTickStatsUpdate() if (_iTimeLastUpdate + g_Cfg._iItemHitpointsUpdate < iCurtime) { + CItem *pItem = GetLink(); + const CPointMap pt = pItem->GetTopPoint(); + + // Check, whether item has already been placed in the world, because settings hits/maxhits in @create trigger calls this method. + if (_iTimeLastUpdate == 0 && !pt.IsValidXY()) + return; + _iTimeLastUpdate = iCurtime; - CItem *pItem = static_cast(GetLink()); - auto AreaChars = CWorldSearchHolder::GetInstance(pItem->GetTopPoint(), g_Cfg.m_iMapViewSize); + auto AreaChars = CWorldSearchHolder::GetInstance(pt, g_Cfg.m_iMapViewSize); AreaChars->SetSearchSquare(true); CChar *pChar = nullptr; for (;;) { pChar = AreaChars->GetChar(); if (!pChar) - { break; - } + CClient *pClient = pChar->GetClientActive(); - if (!pClient) - { + if (!pClient || !pClient->CanSee(pItem)) continue; - } - if (!pClient->CanSee(pItem)) - { - continue; - } + pClient->addStatusWindow(pItem); } } From 5e9b84e671931c47145cd5824ec11ee39494193c Mon Sep 17 00:00:00 2001 From: David Kindl Date: Wed, 1 Oct 2025 16:00:33 +0200 Subject: [PATCH 086/112] Assertions for _pLink --- src/game/components/CCChampion.cpp | 1 + src/game/components/CCItemDamageable.cpp | 1 + src/game/components/CCSpawn.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/src/game/components/CCChampion.cpp b/src/game/components/CCChampion.cpp index cb107f991..3c8286ea6 100644 --- a/src/game/components/CCChampion.cpp +++ b/src/game/components/CCChampion.cpp @@ -98,6 +98,7 @@ CCChampion::~CCChampion() CItem* CCChampion::GetLink() const { + ASSERT(_pLink != nullptr); return _pLink; } diff --git a/src/game/components/CCItemDamageable.cpp b/src/game/components/CCItemDamageable.cpp index f8e1ef1d3..d941417b7 100644 --- a/src/game/components/CCItemDamageable.cpp +++ b/src/game/components/CCItemDamageable.cpp @@ -20,6 +20,7 @@ CCItemDamageable::CCItemDamageable(CItem * pLink) : CComponent(COMP_ITEMDAMAGEAB CItem * CCItemDamageable::GetLink() const noexcept { + ASSERT(_pLink != nullptr); return _pLink; } diff --git a/src/game/components/CCSpawn.cpp b/src/game/components/CCSpawn.cpp index 7ef1b980e..317a8636c 100644 --- a/src/game/components/CCSpawn.cpp +++ b/src/game/components/CCSpawn.cpp @@ -81,6 +81,7 @@ CCSpawn::~CCSpawn() CItem * CCSpawn::GetLink() const { + ASSERT(_pLink != nullptr); return _pLink; } From 35d8f140f0d78a53c52a0d8e56ca5a5df6fa8cd2 Mon Sep 17 00:00:00 2001 From: David Kindl Date: Fri, 3 Oct 2025 22:48:40 +0200 Subject: [PATCH 087/112] Fixed issue with building on RISC-V and ARMv5 architectures --- src/common/target_info.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/common/target_info.h b/src/common/target_info.h index dfd9f643f..ea12a1bc6 100644 --- a/src/common/target_info.h +++ b/src/common/target_info.h @@ -1,7 +1,6 @@ #ifndef _INC_TARGET_INFO_H #define _INC_TARGET_INFO_H - [[maybe_unused, nodiscard]] constexpr const char* get_target_os_str() { @@ -39,7 +38,7 @@ constexpr const char* get_target_arch_str() #elif defined(__ARM_ARCH_4T__) || defined(__TARGET_ARM_4T) return "ARMv4T"; #elif defined(__ARM_ARCH_5_) || defined(__ARM_ARCH_5E_) - return "ARMv5" + return "ARMv5"; #elif defined(__ARM_ARCH_6T2_) || defined(__ARM_ARCH_6T2_) return "ARMv6T2"; #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) @@ -57,7 +56,7 @@ constexpr const char* get_target_arch_str() #elif defined(__aarch64__) || defined(_M_ARM64) return "ARMv64"; #elif defined(__riscv) - return "RISC-V" + return "RISC-V"; #elif defined(mips) || defined(__mips__) || defined(__mips) return "MIPS"; #elif defined(__sh__) @@ -75,5 +74,4 @@ constexpr const char* get_target_arch_str() #endif } - #endif // _INC_TARGET_INFO_H From ecd546ab0557d78d5512c791261370734473294b Mon Sep 17 00:00:00 2001 From: Mulambo Date: Sat, 4 Oct 2025 07:47:43 +0200 Subject: [PATCH 088/112] MacOS container image update (13 will be removed in december) (#1489) - fix reference to non-pointer member --- .github/workflows/build_osx_arm.yml | 2 +- .github/workflows/build_osx_x86_64.yml | 2 +- src/common/basic_threading.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_osx_arm.yml b/.github/workflows/build_osx_arm.yml index d399723be..23b108ba5 100644 --- a/.github/workflows/build_osx_arm.yml +++ b/.github/workflows/build_osx_arm.yml @@ -20,7 +20,7 @@ on: jobs: macos-arm64: - runs-on: macos-14 # apple silicon + runs-on: macos-15 # apple silicon permissions: contents: read actions: read diff --git a/.github/workflows/build_osx_x86_64.yml b/.github/workflows/build_osx_x86_64.yml index 0ecb9f221..050d703f1 100644 --- a/.github/workflows/build_osx_x86_64.yml +++ b/.github/workflows/build_osx_x86_64.yml @@ -20,7 +20,7 @@ on: jobs: macos-x86_64: - runs-on: macos-13 # intel + runs-on: macos-15-intel permissions: contents: read actions: read diff --git a/src/common/basic_threading.h b/src/common/basic_threading.h index 71bdcf91a..0b5f5ef18 100644 --- a/src/common/basic_threading.h +++ b/src/common/basic_threading.h @@ -151,7 +151,7 @@ class GuardedAccess public: [[nodiscard]] LockedReader(const GuardedAccess& owner) - : lock_(owner->pmutex_), ptr_(&owner.data_) + : lock_(owner.mutex_), ptr_(&owner.data_) {} RETURNS_NOTNULL const T* operator->() const { return ptr_; } @@ -195,7 +195,7 @@ class GuardedAccess public: [[nodiscard]] LockedWriter(GuardedAccess& owner) - : lock_(owner->pmutex_), ptr_(&owner.data_) + : lock_(owner.mutex_), ptr_(&owner.data_) {} RETURNS_NOTNULL T* operator->() { return ptr_; } From b0c5d25e8d074db6e6125cdf499f926a3c827d0b Mon Sep 17 00:00:00 2001 From: David Kindl Date: Tue, 7 Oct 2025 12:40:30 +0200 Subject: [PATCH 089/112] Fix CClient::~CClient issue in builds without _EXCEPTIONS_DEBUG --- src/game/clients/CClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/clients/CClient.cpp b/src/game/clients/CClient.cpp index e29c3d5ae..45637dc8e 100644 --- a/src/game/clients/CClient.cpp +++ b/src/game/clients/CClient.cpp @@ -94,7 +94,7 @@ CClient::~CClient() noexcept HistoryIP& history = g_NetworkManager.getIPHistoryManager().getHistoryForIP(GetPeer()); if ( GetConnectType() != CONNECT_GAME ) { - EXC_TRYSUB("m_iPendingConnectionRequests") + EXC_TRYSUB("m_iPendingConnectionRequests"); ASSERT(history.m_iPendingConnectionRequests > 0); -- history.m_iPendingConnectionRequests; From 48dae6df01ca9c8ec901c4fe20bd88abed9df07f Mon Sep 17 00:00:00 2001 From: Gladie Date: Wed, 8 Oct 2025 16:23:19 +0200 Subject: [PATCH 090/112] Update CCharStatus.cpp fix equipping in paperdoll --- src/game/chars/CCharStatus.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index 8e672f18d..382bda43b 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -920,6 +920,9 @@ CChar * CChar::GetOwner() const bool CChar::CanDress(const CChar* pChar) const { + // Self dressing always allowed + if (pChar == this) + return true; if (IsPriv(PRIV_GM) && (GetPrivLevel() > pChar->GetPrivLevel() || GetPrivLevel() == PLEVEL_Owner)) return true; else if (g_Cfg.m_fCanUndressPets && pChar->IsOwnedBy(this)) From 1a543076e58375d1db4bd57ca576f3c881850114 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 18 Oct 2025 15:16:00 +0200 Subject: [PATCH 091/112] Changed number parsing algorithms. --- src/common/CExpression.cpp | 267 +++++++++++++++----------- src/common/sphere_library/sstring.cpp | 225 ++++++++++++++-------- tests/src/t_num_parsing.cpp | 204 +++++++++----------- 3 files changed, 395 insertions(+), 301 deletions(-) diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index f73698833..01a4ebd4c 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -3,6 +3,7 @@ #include "sphere_library/CSRand.h" ///include "CException.h" // included in the precompiled header #include "CLog.h" +#include "../sphere/threads.h" #include #include #include @@ -642,117 +643,151 @@ CExpression& CExpression::GetExprParser() // static */ } -llong CExpression::GetSingle(lpctstr & refStrExpr ) +llong CExpression::GetSingle(lpctstr & refStrExpr) { - ADDTOCALLSTACK("CExpression::GetSingle"); - // Parse just a single expression without any operators or ranges. + ADDTOCALLSTACK("CExpression::GetSingle"); ASSERT(refStrExpr); - GETNONWHITESPACE( refStrExpr ); + GETNONWHITESPACE(refStrExpr); + + lpctstr const ptcStart = refStrExpr; - lpctstr ptcStartingString = refStrExpr; - if (refStrExpr[0]=='.') + // Legacy: allow/skip a leading '.' before parsing numbers + if (refStrExpr[0] == '.') ++refStrExpr; - if ( refStrExpr[0] == '0' ) // leading '0' = hex value. - { - // A hex value. - if ( refStrExpr[1] == '.' ) // leading 0. means it really is decimal. - { + // Hex path: a leading '0' indicates hex unless it is "0." (decimal disambiguation) + if (refStrExpr[0] == '0') + { + if (refStrExpr[1] == '.') + { + // "0." means decimal refStrExpr += 2; - goto try_dec; - } + goto try_decimal; + } - // Skip the leading '0' - refStrExpr += 1; + // Consume the '0' hex marker + ++refStrExpr; + // Parse hex digits; significant digits start at first non-zero nibble uint64 uiVal = 0; - uint uiDigits = 0; - while (true) - { - tchar ch = *refStrExpr; - if ( IsDigit(ch) ) + uint uiSig = 0; // count of significant hex digits (max 16) + bool fSeenNonZero = false; + bool fOverflow = false; + + lpctstr p = refStrExpr; + for (; ; ++p) + { + tchar ch = *p; + uint v; + if (ch >= '0' && ch <= '9') v = (uint)(ch - '0'); + else if (ch >= 'A' && ch <= 'F') v = (uint)(ch - 'A' + 10); + else if (ch >= 'a' && ch <= 'f') v = (uint)(ch - 'a' + 10); + else break; // end of hex token + + if (!fSeenNonZero) { - ch -= '0'; - ++ uiDigits; + if (v != 0) + { + fSeenNonZero = true; + uiSig = 1; + uiVal = v; // first non-zero nibble + } + // else: ignore additional zeros before first non-zero; they don't affect width or value } else - { - ch = static_cast(tolower(ch)); - if ( ch > 'f' || ch < 'a' ) - { - if ( ch == '.' && ptcStartingString[0] != '0' ) // ok i'm confused. it must be decimal. - { - refStrExpr = ptcStartingString; - goto try_dec; - } - break; - } - ch -= 'a' - 10; - ++ uiDigits; - } - - if (uiDigits > 16) { - g_Log.EventWarn("Hexadecimal value parsing will overflow: %s.\n", ptcStartingString); - return -1; + if (uiSig == 16) + { + fOverflow = true; // keep consuming to end + } + else + { + uiVal = (uiVal << 4) | v; + ++uiSig; + } } + } + + // Consume the entire hex token (including after overflow) + refStrExpr = p;// + ((*p == '\0') ? 0 : 1); + + // No significant hex nibble after the marker → value is zero (e.g., "0", "-00", "0000") + if (!fSeenNonZero) + return 0; - uiVal = (uiVal << 4ull) | ch; // Equivalent to 'val *= 0x10; val += ch;' - //val *= 0x10; - //val += ch; + if (fOverflow) + { + g_Log.EventWarn("Hexadecimal value parsing will overflow 64 bits: %s.\n", ptcStart); + return -1; + } - ++ refStrExpr; + // Width by significant hex digits: <=8 → 32-bit signed; 9..16 → 64-bit signed + if (uiSig <= 8) + { + // Two's complement 32-bit interpretation + const uint32 u32 = (uint32)uiVal; + const int32 s32 = (int32)u32; + return (llong)s32; } - if (uiDigits <= 8) - return (int64)(int32)uiVal; - return (int64)uiVal; - } - /* - // We could just use this, but it doesn't "eat" the string pointer. - if ( pszArgs[0] == '.' || IsDigit(pszArgs[0]) ) - { - std::optional iVal = Str_ToLL(pszArgs, 0, true); - if (!iVal) + else { - g_Log.EventDebug("Invalid conversion to number for the string '%s'?\n", pszArgs); - return 0; + // Two's complement 64-bit interpretation + if (uiVal & (1ull << 63)) + { + const uint64 mag = (~uiVal) + 1; + const int64 neg = -(int64)mag; + return (llong)neg; + } + else + { + return (llong)(int64)uiVal; + } } - return iVal.value(); } - */ - else if ( refStrExpr[0] == '.' || IsDigit(refStrExpr[0]) ) - { - // A decimal number -try_dec: - constexpr int64 kiMaxBeforeMul = (INT64_MAX - 9) / 10; + else if (refStrExpr[0] == '.' || (refStrExpr[0] >= '0' && refStrExpr[0] <= '9')) + { + // Decimal (with '.' separators allowed) + try_decimal: + // Overflow guard for INT64_MAX (9223372036854775807) + constexpr int64 LIM10 = INT64_MAX / 10; // 922337203685477580 + constexpr int LIMDG = (int)(INT64_MAX % 10); // 7 - int64 iVal = 0; - for ( ; ; ++refStrExpr ) - { - int c = *refStrExpr; // character - if ( c == '.' ) - continue; // just skip this. - if ( ! IsDigit(c) ) + int64 val = 0; + bool overflow = false; + + lpctstr p = refStrExpr; + for (; ; ++p) + { + tchar ch = *p; + if (ch == '.') + continue; // skip separators + if (ch < '0' || ch > '9') break; - c = c - '0'; // digit - if (iVal > kiMaxBeforeMul) + int d = ch - '0'; + if (!overflow && (val > LIM10 || (val == LIM10 && d > LIMDG))) { - if ((iVal > (INT64_MAX - c) / 10)) - { - g_Log.EventWarn("Decimal value parsing will overflow: %s.\n", ptcStartingString); - return -1; - } + overflow = true; // but keep consuming the whole token + } + else if (!overflow) + { + val = val * 10 + d; } + } - iVal *= 10; - iVal += (llong)(*refStrExpr) - '0'; - } - return iVal; - } -else if ( ! _ISCSYMF(refStrExpr[0]) ) + // Consume the entire decimal token + refStrExpr = p;// + ((*p == '\0') ? 0 : 1); + + if (overflow) + { + g_Log.EventWarn("Decimal value parsing will overflow 64 bits: %s.\n", ptcStart); + return -1; + } + return (llong)val; + } + else if ( ! _ISCSYMF(refStrExpr[0]) ) { - //#pragma region maths // MSVC specific + //#pragma region maths // MSVC specific // some sort of math op ? switch ( refStrExpr[0] ) @@ -1206,16 +1241,16 @@ else if ( ! _ISCSYMF(refStrExpr[0]) ) //#pragma endregion intrinsics // MSVC specific // hard end ! Error of some sort. - if (ptcStartingString[0] != '\0') + if (ptcStart[0] != '\0') { tchar szTag[EXPRESSION_MAX_KEY_LEN]; - GetIdentifierString(szTag, ptcStartingString); - const lpctstr ptcLast = (ptcStartingString[0] == '<') ? ">'" : "'"; - DEBUG_ERR(("Undefined symbol '%s' [Evaluated expression: '%s%s].\n", szTag, ptcStartingString, ptcLast)); + GetIdentifierString(szTag, ptcStart); + const lpctstr ptcLast = (ptcStart[0] == '<') ? ">'" : "'"; + g_Log.EventError("Undefined symbol '%s' [Evaluated expression: '%s%s].\n", szTag, ptcStart, ptcLast); } else { - DEBUG_ERR(("Undefined symbol (empty parameter?).\n")); + g_Log.EventError("Undefined symbol (empty parameter?).\n"); } return 0; } @@ -1563,10 +1598,10 @@ CExpression::GetConditionalSubexpressions( } // Helper lambda functions for the next section. - auto findLastClosingBracket = [](lptstr pExpr_) -> lptstr + auto findLastservClosingBracket = [](lptstr pExpr_) -> lptstr { - // Returns a pointer to the last closing bracket in the string. - // If the last character in the string (ignoring comments) is not ')', it means that, if we find a closing bracket, + // Returns a pointer to the last servClosing bracket in the string. + // If the last character in the string (ignoring comments) is not ')', it means that, if we find a servClosing bracket, // it's past other characters, so there's other valid text after the ')'. // Eg: IF (1+2) > 10. The ')' is not at the end of the line, because there's the remaining part of the script. ASSERT(*pExpr_ != '\0'); @@ -1624,7 +1659,7 @@ CExpression::GetConditionalSubexpressions( lptstr ptcTopBracket = (ch == '(') ? refStrExpr : nullptr; // -- Done with preliminar expression analysis. Now look for subexpressions. - lptstr ptcLastClosingBracket = nullptr; // Needs to be preserved in the subexpression parsing. + lptstr ptcLastservClosingBracket = nullptr; // Needs to be preserved in the subexpression parsing. while (true) { // This loop parses a single subexpression. Remember that we checked for a negation prefix like !( ) in the block before. @@ -1637,29 +1672,29 @@ CExpression::GetConditionalSubexpressions( if (sCurSubexpr.ptcEnd == nullptr) { sCurSubexpr.ptcEnd = refStrExpr; - if (ptcTopBracket && ptcLastClosingBracket) + if (ptcTopBracket && ptcLastservClosingBracket) { - lptstr ptcLineLastClosingBracket = findLastClosingBracket(ptcCurSubexprStart); - // ptcLastClosingBracket: the last closing bracket found while parsing the subexpression (might not be at the end of the line). - // ptcExprLastClosingBracket: the last closing bracket ')', if any, of the string. The function used does NOT check if that's a valid closing bracket - // (eg. if in the string for every opening bracket there is a closing bracket). + lptstr ptcLineLastservClosingBracket = findLastservClosingBracket(ptcCurSubexprStart); + // ptcLastservClosingBracket: the last servClosing bracket found while parsing the subexpression (might not be at the end of the line). + // ptcExprLastservClosingBracket: the last servClosing bracket ')', if any, of the string. The function used does NOT check if that's a valid servClosing bracket + // (eg. if in the string for every opening bracket there is a servClosing bracket). if (uiSubexprQty == 1) { - if (nullptr == ptcLineLastClosingBracket) + if (nullptr == ptcLineLastservClosingBracket) { - // There are other valid characters after the closing curly bracket, so leave ptcEnd unchanged, to the end of the string. + // There are other valid characters after the servClosing curly bracket, so leave ptcEnd unchanged, to the end of the string. ; } - else if (ptcLastClosingBracket == ptcLineLastClosingBracket) + else if (ptcLastservClosingBracket == ptcLineLastservClosingBracket) { // I'm here because the whole expression is enclosed by parentheses // + 1 because i want to point to the character after the ')', even if it's the string terminator. - sCurSubexpr.ptcEnd = ptcLastClosingBracket + 1; + sCurSubexpr.ptcEnd = ptcLastservClosingBracket + 1; sCurSubexpr.uiType |= SubexprState_t::TopParenthesizedExpr; } else { - sCurSubexpr.ptcEnd = ptcLastClosingBracket; + sCurSubexpr.ptcEnd = ptcLastservClosingBracket; } } // else: // The starting bracket encloses only a part of the expression @@ -1673,7 +1708,7 @@ CExpression::GetConditionalSubexpressions( if (ptcCurSubexprStart == refStrExpr) { // Start of a subexpression delimited by brackets (it can be preceded by an operator like '!', handled before). - // Now i want only to see where's the matching closing bracket. + // Now i want only to see where's the matching servClosing bracket. sCurSubexpr.ptcStart = refStrExpr; } else @@ -1698,13 +1733,13 @@ CExpression::GetConditionalSubexpressions( } // Just skip what's enclosed in the subexpression. - ptcLastClosingBracket = skipBracketedSubexpression(refStrExpr); - if (ptcLastClosingBracket != nullptr) - refStrExpr = ptcLastClosingBracket; + ptcLastservClosingBracket = skipBracketedSubexpression(refStrExpr); + if (ptcLastservClosingBracket != nullptr) + refStrExpr = ptcLastservClosingBracket; else { g_Log.EventError("Expression started with '(' but isn't closed by a ')' character.\n"); - sCurSubexpr.ptcEnd = refStrExpr - 1; // Position of the char just before the last ')' of the bracketed subexpression -> this eats away the last closing bracket + sCurSubexpr.ptcEnd = refStrExpr - 1; // Position of the char just before the last ')' of the bracketed subexpression -> this eats away the last servClosing bracket return pSubexprsArena; } @@ -1763,7 +1798,7 @@ CExpression::GetConditionalSubexpressions( if (refStrExpr != pExprSkipped) { // I actually have something enclosed in angular brackets. - // The function above moves the pointer after the last closing bracket '>', but we want to point here to it, not the character after. + // The function above moves the pointer after the last servClosing bracket '>', but we want to point here to it, not the character after. refStrExpr = pExprSkipped; ch = *refStrExpr; continue; // This allows us to skip the "ch = *(++pExpr);" below, we don't want to advance further the pointer. @@ -2153,7 +2188,7 @@ bool CExpression::EvaluateConditionalSingle( lptstr ptcParsingStart = refSubExprState.ptcStart; if (fFullyEnclosed) { - -- len; // Exclude the closing bracket ')'. + -- len; // Exclude the servClosing bracket ')'. ASSERT(len > 0); // In this case, we need to start parsing after the opening parenthesis '('; if we start before it and the subexpr is marked with MaybeNestedSubexpr, @@ -2390,7 +2425,7 @@ int CExpression::ParseScriptText( // iFlags & 2: Don't allow recusive bracket count. // iFlags & 4: Just parsing a nested QVAL. // NOTE: - // html will have opening + // html will have opening // RETURN: // iFlags & 4: Position of the ending bracket/delimiter of a QVAL statement. // Otherwise: New length of the string. @@ -2469,7 +2504,7 @@ int CExpression::ParseScriptText( // Are we inside a QVAL and are we searching where its condition end? if ((ch == '?') && (eQval == QvalStatus::Condition)) { - // Now we keep the bracket count to find the closing bracket for the QVAL statement. + // Now we keep the bracket count to find the servClosing bracket for the QVAL statement. eQval = QvalStatus::Returns; continue; } @@ -2484,7 +2519,7 @@ int CExpression::ParseScriptText( lptstr ptcTestNested = ptcResponse + i; lpctstr ptcTestOrig = ptcTestNested; Str_SkipEnclosedAngularBrackets(ptcTestNested); - // If i have matching closing brackets, so it must be nested angular brackets. + // If i have matching servClosing brackets, so it must be nested angular brackets. if (ptcTestNested == ptcTestOrig) { // Otherwise, it might be the << operator. @@ -2567,16 +2602,16 @@ int CExpression::ParseScriptText( continue; } - // At this point i'm sure that ahead we won't find other open angular brackets, we may find their closing one or just plain text. + // At this point i'm sure that ahead we won't find other open angular brackets, we may find their servClosing one or just plain text. if ( ch == chEnd ) { - // Closing bracket found: should we evaluate what's inside the brackets? + // servClosing bracket found: should we evaluate what's inside the brackets? if (eQval != QvalStatus::None) { // Special handling for QVAL if (eQval == QvalStatus::Returns) { - // I'm after the '?' symbol in QVAL. We are searching for the closing bracket. + // I'm after the '?' symbol in QVAL. We are searching for the servClosing bracket. --iQvalOpenBrackets; if (iQvalOpenBrackets == 0) diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index fc3e4709e..dbd9081e4 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -284,100 +284,189 @@ std::optional Str_ToULL(const tchar * ptcStr, uint base, size_t uiStopAt // -- // Number to String -static constexpr tchar DIGITS[] = "0123456789abcdef"; +/* +Hex width and two’s-complement: +After the hex marker '0', leading zeros are ignored for width selection. +Count significant hex digits starting at the first non-zero nibble: + ≤ 8 significant digits → interpret the bit pattern as signed 32-bit two’s-complement (then widen to 64-bit to return). + 9..16 significant digits → interpret as signed 64-bit two’s-complement. + 16 significant digits → warn about 64-bit overflow, return −1, and consume the entire hex token. +*/ + +static constexpr tchar DIGITS_UPPER[] = "0123456789ABCDEF"; +// Return how many hex digits should be emitted for value 'u' within a fixed width (8 or 16 nibbles), +// after trimming leading zero nibbles within that width. +static inline int _hex_num_digits_from_width(uint64 u, int width_nibbles) noexcept +{ + int shift = (width_nibbles - 1) * 4; + while (shift > 0 && ((u >> shift) & 0xFull) == 0) + shift -= 4; + return (shift / 4) + 1; +} + +// Template entry point: returns out_buf on success, nullptr on failure. template -tchar* Str_FromInt_Fast(_IntType val, lptstr_restrict out_buf, size_t buf_length, uint base) noexcept +tchar* Str_FromInt_Fast(_IntType val, tchar* out_buf, size_t buf_length, uint base) noexcept { - if (!out_buf || buf_length == 0 || base == 0 || base > 16) [[unlikely]] + static_assert(std::is_integral_v<_IntType>, "Str_FromInt_Fast requires an integral type"); + + if (!out_buf || buf_length == 0) + { + g_Log.EventWarn("Str_FromInt_Fast: null buffer or zero length.\n"); + return nullptr; + } + if (base < 2 || base > 16) + { + g_Log.EventWarn("Str_FromInt_Fast: invalid base %u (supported 2..16).\n", base); return nullptr; + } const bool fHex = (base == 16); - // Handle zero or base==0 case + + // Zero fast path if (val == 0) { if (fHex) { + // Hex zero is "00" if (buf_length < 3) + { + g_Log.EventWarn("Str_FromInt_Fast[hex]: insufficient buffer (need 3 for \"00\\0\").\n"); return nullptr; + } out_buf[0] = '0'; out_buf[1] = '0'; out_buf[2] = '\0'; + return out_buf; } else { if (buf_length < 2) + { + g_Log.EventWarn("Str_FromInt_Fast[base=%u]: insufficient buffer (need 2 for \"0\\0\").\n", base); return nullptr; + } out_buf[0] = '0'; out_buf[1] = '\0'; + return out_buf; } - return out_buf; } - using _UIntType = std::make_unsigned_t<_IntType>; - _UIntType uval; - bool fIsNegative = false; - - if constexpr (std::is_signed_v<_IntType>) + if (fHex) { - fIsNegative = (val < 0); - // two's-complement bit-pattern reinterpret - const auto utmp = static_cast<_UIntType>(val); - if (fHex) + // Sphere hex formatting: + // - Uppercase, prefix '0' + // - Variable width after trimming within chosen width: + // Signed types: if value fits in int32 → 32-bit t.c.; else 64-bit t.c. + // Unsigned types: <= 0xFFFFFFFF → 32-bit; else 64-bit + uint64 u = 0; + int width_nibbles = 0; + + if constexpr (std::is_signed_v<_IntType>) { - uval = utmp; + if (sizeof(_IntType) <= 4 || (val >= (llong)INT32_MIN && val <= (llong)INT32_MAX)) + { + uint32 u32 = (uint32)(int32)val; // two's complement 32-bit view + u = (uint64)u32; + width_nibbles = 8; + } + else + { + u = (uint64)(int64)val; // two's complement 64-bit view + width_nibbles = 16; + } } else { - // Unsigned subtraction is always defined behaviour, it wraps around. - uval = fIsNegative ? (_UIntType(0) - utmp) : utmp; + ullong uv = (ullong)val; + if (sizeof(_IntType) <= 4 || uv <= 0xFFFFFFFFull) + { + u = (uint64)(uint32)uv; + width_nibbles = 8; + } + else + { + u = (uint64)uv; + width_nibbles = 16; + } } + + const int digits = _hex_num_digits_from_width(u, width_nibbles); + const size_t need = (size_t)digits + 2; // '0' + digits + NUL + if (buf_length < need) + { + g_Log.EventWarn("Str_FromInt_Fast[hex]: insufficient buffer (need %" PRIuSIZE_T ", have %" PRIuSIZE_T ").\n", need, buf_length); + return nullptr; + } + + out_buf[0] = '0'; + int shift = (width_nibbles - digits) * 4; + size_t pos = 1; + for (; shift >= 0; shift -= 4) + out_buf[pos++] = DIGITS_UPPER[(u >> shift) & 0xFull]; + out_buf[pos] = '\0'; + return out_buf; } else { - uval = static_cast<_UIntType>(val); - } + // General base 2..16. + using _UInt = std::make_unsigned_t<_IntType>; + _UInt uiMagnitude; + bool fNeg = false; - // Write digits from back of buffer -#define WRITE_OUT(ch) \ - { \ - if (idx == 0) \ - return nullptr; \ - out_buf[--idx] = (ch); \ - } - - size_t idx = buf_length; - out_buf[--idx] = '\0'; + if constexpr (std::is_signed_v<_IntType>) + { + // two's complement magnitude (works for INT_MIN) + _UInt utwos = (_UInt)val; + fNeg = (val < 0); + uiMagnitude = fNeg ? (_UInt(0) - utwos) : utwos; + } + else + { + uiMagnitude = (_UInt)val; + } - // TODO: gracefully handle integer overflows, maybe printing a warning message. + // Accumulate digits in reverse in a small stack buffer; then write forward. + tchar tmp[64]; + size_t n = 0; + const _UInt B = (_UInt)base; - if (fHex) - { - // Hex path: mask & shift by 4 bits (it's the same as dividing by 16). - do + if (base == 10) { - WRITE_OUT(DIGITS[uval & 0xFu]); - uval >>= 4; - } while (uval); - WRITE_OUT('0'); // leading 0 hex prefix - } - else - { - // General path: division + multiply to get remainder - do + do + { + const _UInt r = uiMagnitude % 10u; + uiMagnitude /= 10u; + tmp[n++] = DIGITS_UPPER[(size_t)r]; + } while (uiMagnitude); + } + else { - const _UIntType q = uval / base; - const _UIntType d = uval - q * base; - WRITE_OUT(DIGITS[d]); - uval = q; - } while (uval); - - if (fIsNegative) { - WRITE_OUT('-'); + do + { + const _UInt r = uiMagnitude % B; + uiMagnitude /= B; + tmp[n++] = DIGITS_UPPER[(size_t)r]; + } while (uiMagnitude); } - } -#undef WRITE_OUT - return &out_buf[idx]; + const size_t uiNeed = (fNeg ? 1 : 0) + n + 1; + if (buf_length < uiNeed) + { + g_Log.EventWarn("Str_FromInt_Fast[base=%u]: insufficient buffer (need %" PRIuSIZE_T ", have %" PRIuSIZE_T ").\n", base, uiNeed, buf_length); + return nullptr; + } + + size_t uiPos = 0; + if (fNeg) + out_buf[uiPos++] = '-'; + while (n) + out_buf[uiPos++] = tmp[--n]; + out_buf[uiPos] = '\0'; + return out_buf; + } } +// Typed front-writing wrappers: they return buf on success, nullptr on failure. +// For now keep _Fast and standard variants. They were there for historical purposes (previous implementation was back-writing). tchar* Str_FromI_Fast(int val, tchar* buf, size_t buf_length, uint base) noexcept { return Str_FromInt_Fast(val, buf, buf_length, base); @@ -400,38 +489,22 @@ tchar* Str_FromULL_Fast (ullong val, tchar* buf, size_t buf_length, uint base) n void Str_FromI(int val, tchar* buf, size_t buf_length, uint base) noexcept { - tchar* modified_buf = Str_FromI_Fast(val, buf, buf_length, base); - const size_t offset = size_t(modified_buf - buf); - if (offset > 0) { - memmove(buf, modified_buf, buf_length - offset); - } + Str_FromI_Fast(val, buf, buf_length, base); } void Str_FromUI(uint val, tchar* buf, size_t buf_length, uint base) noexcept { - tchar* modified_buf = Str_FromUI_Fast(val, buf, buf_length, base); - const size_t offset = size_t(modified_buf - buf); - if (offset > 0) { - memmove(buf, modified_buf, buf_length - offset); - } + Str_FromUI_Fast(val, buf, buf_length, base); } void Str_FromLL(llong val, tchar* buf, size_t buf_length, uint base) noexcept { - tchar* modified_buf = Str_FromLL_Fast(val, buf, buf_length, base); - const size_t offset = size_t(modified_buf - buf); - if (offset > 0) { - memmove(buf, modified_buf, buf_length - offset); - } + Str_FromLL_Fast(val, buf, buf_length, base); } void Str_FromULL(ullong val, tchar* buf, size_t buf_length, uint base) noexcept { - tchar* modified_buf = Str_FromULL_Fast(val, buf, buf_length, base); - const size_t offset = size_t(modified_buf - buf); - if (offset > 0) { - memmove(buf, modified_buf, buf_length - offset); - } + Str_FromULL_Fast(val, buf, buf_length, base); } @@ -1031,7 +1104,7 @@ void Str_MakeUnQuoted(tchar* pStr) noexcept tchar* endPtr = src + std::strlen(src); - // If quoted, locate closing quote and adjust endPtr + // If quoted, locate servClosing quote and adjust endPtr if (fQuoted) { tchar* p = endPtr; @@ -1468,7 +1541,7 @@ MATCH_TYPE Str_Match(const tchar * pPattern, const tchar * pText) noexcept fInvert = true; ++pPattern; } - // if closing bracket here or at range start then we have a + // if servClosing bracket here or at range start then we have a // malformed pattern if (*pPattern == ']') return MATCH_PATTERN; diff --git a/tests/src/t_num_parsing.cpp b/tests/src/t_num_parsing.cpp index 31109b7db..143d1a4a3 100644 --- a/tests/src/t_num_parsing.cpp +++ b/tests/src/t_num_parsing.cpp @@ -1,5 +1,7 @@ #include #include "../../src/common/CExpression.h" +#include +#include #include // toupper #ifndef MSVC_COMPILER @@ -94,114 +96,26 @@ namespace { namespace ref_impl } */ - // Part of CExpression::GetSingle that parses numerical strings. - int64 GetSingle_num(lpctstr &pszArgs) ATTR_NO_UBSAN - { - // Parse just a single expression without any operators or ranges. - GETNONWHITESPACE(pszArgs); - - if ( pszArgs[0] == '.' ) - ++pszArgs; - - if ( pszArgs[0] == '0' ) // leading '0' = hex value - { - // Hex value - if ( pszArgs[1] == '.' ) // leading '0.' = decimal value - { - pszArgs += 2; - goto try_dec; - } - - lpctstr pStart = pszArgs; - int64 iVal = 0; - for (;;) - { - tchar ch = *pszArgs; - if ( IsDigit(ch) ) - ch -= '0'; - else - { - ch = static_cast(tolower(ch)); - if ( (ch > 'f') || (ch < 'a') ) - { - if ( (ch == '.') && (pStart[0] != '0') ) // ok I'm confused, it must be decimal - { - pszArgs = pStart; - goto try_dec; - } - break; - } - ch -= 'a' - 10; - } - iVal *= 0x10; - iVal += ch; - ++pszArgs; - } - return iVal; - } - else if ( (pszArgs[0] == '.') || IsDigit(pszArgs[0]) ) - { - // Decimal value - try_dec: - int64 iVal = 0; - for ( ; ; ++pszArgs ) - { - if ( *pszArgs == '.' ) - continue; // just skip this - if ( !IsDigit(*pszArgs) ) - break; - iVal *= 10; - iVal += static_cast(*pszArgs) - '0'; - } - return iVal; - } - else if ( ! _ISCSYMF(pszArgs[0]) ) - { - //#pragma region maths // MSVC specific - // some sort of math op ? - - switch ( pszArgs[0] ) - { - /* - case '{': - ++pszArgs; - return GetRangeNumber( pszArgs ); - case '[': - case '(': // Parse out a sub expression. - ++pszArgs; - return GetVal( pszArgs ); - */ - case '+': - ++pszArgs; - break; - case '-': - ++pszArgs; - return -GetSingle_num( pszArgs ); - case '~': // Bitwise not. - ++pszArgs; - return ~GetSingle_num( pszArgs ); - case '!': // boolean not. - ++pszArgs; - if ( pszArgs[0] == '=' ) // odd condition such as (!=x) which is always true of course. - { - ++pszArgs; // so just skip it. and compare it to 0 - return GetSingle_num( pszArgs ); - } - return !GetSingle_num( pszArgs ); - case ';': // seperate field. - case ',': // seperate field. - case '\0': - return 0; - } - } - throw std::runtime_error("reached an unexpected point?"); - } }} +// Small helper to check value and how far the input pointer advanced. +// Expects expr.GetSingle to consume the token in-place and leave 'in' pointing +// at the first non-numeric char following the parsed number. +#define CHECK_PARSE_AND_REMAINDER(input, expected_val, expected_remainder) { \ + lpctstr parsed_input_str = input; \ + const int64 actual_val = expr.GetSingle(parsed_input_str); \ + DOCTEST_CHECK_MESSAGE(actual_val == (int64)expected_val, \ + "Value mismatch!\nInput: '" << input << "'\nExpected: '" << expected_val << "'\nGot: '" << actual_val << "'"); \ + const std::string_view sv_post_parse(parsed_input_str); \ + const std::string_view sv_expected(expected_remainder); \ + DOCTEST_CHECK_MESSAGE(sv_expected == sv_post_parse, \ + "Remainder mismatch!\nInput: '" << input << "'\nExpected remainder: '" << sv_expected << "'\nGot: '" << sv_post_parse << "'"); \ +} TEST_CASE("Numerical string parsing")\ // clazy:exclude=non-pod-global-static { + CExpression& expr = CExpression::GetExprParser(); lpctstr in_str_consumable; llong out_cur_impl, out_ref_impl; @@ -223,10 +137,10 @@ TEST_CASE("Numerical string parsing")\ for (auto const& p : test_data_ref_ok) { - in_str_consumable = p.first, out_cur_impl = Exp_GetLLVal(in_str_consumable); + in_str_consumable = p.first, out_cur_impl = expr.GetSingle(in_str_consumable); CHECK_EQ(out_cur_impl, p.second); - in_str_consumable = p.first, out_ref_impl = ref_impl::GetSingle_num(in_str_consumable); + in_str_consumable = p.first, out_ref_impl = expr.GetSingle(in_str_consumable); CHECK_EQ(out_cur_impl, out_ref_impl); } @@ -238,7 +152,7 @@ TEST_CASE("Numerical string parsing")\ for (auto const& p : test_data_ref_fail) { - in_str_consumable = p.first, out_cur_impl = Exp_GetLLVal(in_str_consumable); + in_str_consumable = p.first, out_cur_impl = expr.GetSingle(in_str_consumable); CHECK_EQ(out_cur_impl, p.second); } @@ -258,24 +172,96 @@ TEST_CASE("Numerical string parsing")\ for (auto const& p : test_data_ref_ok) { - in_str_consumable = p.first, out_cur_impl = Exp_GetLLVal(in_str_consumable); + in_str_consumable = p.first, out_cur_impl = expr.GetSingle(in_str_consumable); CHECK_EQ(out_cur_impl, p.second); - in_str_consumable = p.first, out_ref_impl = ref_impl::GetSingle_num(in_str_consumable); + in_str_consumable = p.first, out_ref_impl = expr.GetSingle(in_str_consumable); CHECK_EQ(out_cur_impl, out_ref_impl); } // With this data the old reference implementation failed constexpr std::pair test_data_ref_fail[] { - {"0FFFFFFFF", -1}, // This is a SphereServer quirk for historical reasons. + {"0FFFFFFFF", -1}, // This is a SphereServer quirk for historical reasons {"0FFFFFFFFFFFFFFFF1", -1} // Overflow }; for (auto const& p : test_data_ref_fail) { - in_str_consumable = p.first, out_cur_impl = Exp_GetLLVal(in_str_consumable); + in_str_consumable = p.first, out_cur_impl = expr.GetSingle(in_str_consumable); CHECK_EQ(out_cur_impl, p.second); } } + + SUBCASE("Whitespace and trailing junk consumption") + { + CHECK_PARSE_AND_REMAINDER(" 42", 42, ""); // consume entire string + CHECK_PARSE_AND_REMAINDER(" 42 ", 42, " "); // stop at trailing space + CHECK_PARSE_AND_REMAINDER(" 3]", 3, "]"); // stop right before ']' + CHECK_PARSE_AND_REMAINDER(" 0F]", 15, "]"); // hex with trailing junk + CHECK_PARSE_AND_REMAINDER("123abc", 123, "abc"); // decimal then junk + CHECK_PARSE_AND_REMAINDER("0F xyz", 15, " xyz"); // hex then spaces+text + } + + SUBCASE("Leading dot quirk and 0. decimal disambiguation") + { + // Leading '.' is skipped; ".07" becomes hex marker "0" followed by '7' -> 7 + CHECK_PARSE_AND_REMAINDER(".07]", 7, "]"); + + // "0." is decimal; dots inside decimal are ignored as separators + CHECK_PARSE_AND_REMAINDER("0.", 0, ""); // bare 0. -> 0 + CHECK_PARSE_AND_REMAINDER("0.123", 123, ""); // 0.123 -> 123 + CHECK_PARSE_AND_REMAINDER(".0.1]", 1, "]"); // leading '.' skipped -> "0.1]" -> decimal 1 + CHECK_PARSE_AND_REMAINDER("1.234.567end", 1234567, "end"); + } + + SUBCASE("Hex width by significant digits (leading zeros ignored after marker)") + { + CHECK_PARSE_AND_REMAINDER("00000F", 15, ""); // marker '0' then digits "0000F" -> 15 + CHECK_PARSE_AND_REMAINDER("07FFFFFFF", INT32_MAX, ""); // 8 significant digits -> 32-bit + CHECK_PARSE_AND_REMAINDER("080000000", (llong)INT32_MIN, ""); // 8 significant digits -> 32-bit negative + CHECK_PARSE_AND_REMAINDER("0FFFFFFFF", -1, ""); // 8 significant -> 32-bit -1 + CHECK_PARSE_AND_REMAINDER("0FFFFFFFFFFFFFFFF", -1, "");// 16 significant -> 64-bit -1 + CHECK_PARSE_AND_REMAINDER("0FFFFFFFF00", 0xFFFFFFFF00LL, ""); // 64-bit positive + } + + /* + SUBCASE("Negative decimal vs negative hex") + { + // Negative decimals are supported + EXPECT_PARSE_AND_REMAINDER("-1", -1, ""); + EXPECT_PARSE_AND_REMAINDER("-0", 0, ""); + EXPECT_PARSE_AND_REMAINDER("-1.234", -1234, ""); + + // "-0." is decimal (supported), should parse as 0 with decimal semantics + EXPECT_PARSE_AND_REMAINDER("-0.", 0, ""); + + // Negative hex is unsupported, but parser should: + // - log a warning, + // - consume the '-', ignore the sign, + // - parse and consume the entire hex token. + EXPECT_PARSE_AND_REMAINDER("-00", 0, ""); // hex zero after sign + EXPECT_PARSE_AND_REMAINDER("-0F", 15, ""); // hex positive (sign ignored) + EXPECT_PARSE_AND_REMAINDER("-0FFFFFFFF", -1, ""); // hex, 32-bit width -> -1 (sign ignored) + EXPECT_PARSE_AND_REMAINDER(" -0F]", 15, "]"); // with whitespace and trailing junk + } + */ + + SUBCASE("Overflow behaviors") + { + // Decimal overflow: > INT64_MAX + CHECK_PARSE_AND_REMAINDER("9223372036854775808X", -1, "X"); + + // Hex overflow: >16 significant hex digits (leading zeros ignored before first non-zero) + CHECK_PARSE_AND_REMAINDER("0FFFFFFFFFFFFFFFFFFFFZ", -1, "Z"); // 20 significant digits + CHECK_PARSE_AND_REMAINDER("00000000000000000FFFFFFFFFFFFFFFFFFFF!", -1, "!"); // still overflow + } + + SUBCASE("Unsupported 0x prefix") + { + // Legacy: "0x" is not a hex prefix; hex marker is just '0'. + // Behavior: marker '0' is consumed, 'x' is non-hex -> stop; resulting value is 0; remainder begins with 'x'. + CHECK_PARSE_AND_REMAINDER("0xFF", 0, "xFF"); + CHECK_PARSE_AND_REMAINDER(" 0x7B", 0, "x7B"); + } } From 1a71270a3a301014c64358a930c28572fcccf2df Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 18 Oct 2025 15:17:12 +0200 Subject: [PATCH 092/112] Rewritten STDERR_LOG to be a function, stderrLog --- src/common/CException.cpp | 4 ++-- src/common/CLog.cpp | 2 +- src/common/basic_threading.h | 2 +- src/common/common.cpp | 10 ++++++++++ src/common/common.h | 2 +- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/common/CException.cpp b/src/common/CException.cpp index 35bf12db2..2e8d8dd29 100644 --- a/src/common/CException.cpp +++ b/src/common/CException.cpp @@ -98,7 +98,7 @@ void RaiseImmediateAbort(int iErrCode) UnreferencedParameter(iErrCode); #ifdef _DEBUG - STDERR_LOG("RaiseImmediateAbort with code %d.\n", iErrCode); + stderrLog("RaiseImmediateAbort with code %d.\n", iErrCode); #endif EXC_NOTIFY_DEBUGGER; @@ -326,7 +326,7 @@ static void Signal_Terminate(int sig = 0) noexcept // If shutdown is initialized if ((sig == SIGABRT) && IsAbortImmediate()) { // No clean ending. Abort right now. - STDERR_LOG("FATAL: Immediate abort requested."); + stderrLog("FATAL: Immediate abort requested."); #if defined(__GNUC__) || defined(__clang__) __builtin_trap(); diff --git a/src/common/CLog.cpp b/src/common/CLog.cpp index 9703d0fc3..846ac4739 100644 --- a/src/common/CLog.cpp +++ b/src/common/CLog.cpp @@ -349,7 +349,7 @@ int CLog::EventStr( dword dwMask, lpctstr pszMsg, ConsoleTextColor iLogColor) no // Not much we can do about this iRet = 0; GetCurrentProfileData().Count(PROFILE_STAT_FAULTS, 1); - STDERR_LOG("CLog::EventStr failed to print: '%s'.\n", pszMsg); + stderrLog("CLog::EventStr failed to print: '%s'.\n", pszMsg); } return iRet; diff --git a/src/common/basic_threading.h b/src/common/basic_threading.h index 0b5f5ef18..d7743e8d3 100644 --- a/src/common/basic_threading.h +++ b/src/common/basic_threading.h @@ -94,7 +94,7 @@ success_acquire_ = true; \ break; \ } catch (const std::system_error& e_) { \ - STDERR_LOG("[File '%s', line %d, function '%s']. Failed to acquire lock on attempt %u. Exc msg: '%s'.\n", \ + stderrLog("[File '%s', line %d, function '%s']. Failed to acquire lock on attempt %u. Exc msg: '%s'.\n", \ __FILE__, __LINE__, __func__, spin_attempt_ + 1, e_.what()); \ } \ } \ diff --git a/src/common/common.cpp b/src/common/common.cpp index e41b313aa..d1f148223 100644 --- a/src/common/common.cpp +++ b/src/common/common.cpp @@ -44,3 +44,13 @@ extern "C" return &g_osInfo; } #endif // !_WIN32 + +void stderrLog(lpctstr ptcFormat, ...) noexcept // SPHERE_PRINTFARGS(1,2); +{ + va_list vargs; + va_start(vargs, ptcFormat); + fprintf(stderr, ptcFormat, vargs); + va_end(vargs); + + fflush(stderr); +} diff --git a/src/common/common.h b/src/common/common.h index 0dad50d12..ff0252b64 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -320,7 +320,7 @@ constexpr void UnreferencedParameter([[maybe_unused]] T const& ) noexcept { static_assert(!std::is_nothrow_invocable_v, #_func " function should be noexcept!") // For unrecoverable/unloggable errors. Should be used almost *never*. -#define STDERR_LOG(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) +void stderrLog(lpctstr pszFormat, ...) noexcept SPHERE_PRINTFARGS(1,2); /* Sanitizers utilities */ From 920e22aa6b10ca4b628da0fc0c7b424378e3505c Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 18 Oct 2025 15:18:26 +0200 Subject: [PATCH 093/112] Debug version: added old VarDef value log message when overriding it at startup. --- src/common/CVarDefMap.cpp | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/common/CVarDefMap.cpp b/src/common/CVarDefMap.cpp index 24e7583e1..5a3d7da2e 100644 --- a/src/common/CVarDefMap.cpp +++ b/src/common/CVarDefMap.cpp @@ -410,14 +410,25 @@ CVarDefContNum* CVarDefMap::SetNum( lpctstr pszName, int64 iVal, bool fDeleteZer if ( pVarNum ) { if ( fShouldWarn ) - g_Log.EventWarn( "Replacing existing VarNum '%s' with number: 0x%" PRIx64" \n", pVarBase->GetKey(), iVal ); - pVarNum->SetValNum( iVal ); + { + g_Log.EventWarn( "Replacing existing VarNum '%s' with number: 0%" PRIx64 " (%" PRId64 ")\n", pVarBase->GetKey(), iVal, iVal ); +#ifdef _DEBUG + const int64 iOldVal = pVarNum->GetValNum(); + g_Log.EventDebug("Previous value: %0" PRIx64 "(%" PRId64 ")\n", iOldVal, iOldVal); +#endif + } + pVarNum->SetValNum( iVal ); } else { if ( fShouldWarn ) - g_Log.EventWarn( "Replacing existing VarStr '%s' with number: 0x%" PRIx64" \n", pVarBase->GetKey(), iVal ); - return SetNumOverride( pszName, iVal ); + { + g_Log.EventWarn( "Replacing existing VarStr '%s' with number: 0%" PRIx64" (%" PRId64 ")\n", pVarBase->GetKey(), iVal, iVal ); +#ifdef _DEBUG + g_Log.EventDebug("Previous value: '%s'\n", pVarNum->GetValStr()); +#endif + } + return SetNumOverride( pszName, iVal ); } return pVarNum; @@ -491,14 +502,26 @@ CVarDefCont* CVarDefMap::SetStr( lpctstr pszName, bool fQuoted, lpctstr pszVal, if ( pVarStr ) { if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoadingGeneric() ) + { g_Log.EventWarn( "Replacing existing VarStr '%s' with string: '%s'\n", pVarBase->GetKey(), pszVal ); - pVarStr->SetValStr( pszVal ); +#ifdef _DEBUG + g_Log.EventDebug("Previous value: '%s'\n", pVarStr->GetValStr()); +#endif + } + pVarStr->SetValStr( pszVal ); } else { if ( fWarnOverwrite && !g_Serv.IsResyncing() && g_Serv.IsLoadingGeneric() ) + { g_Log.EventWarn( "Replacing existing VarNum '%s' with string: '%s'\n", pVarBase->GetKey(), pszVal ); - return SetStrOverride( pszName, pszVal ); +#ifdef _DEBUG + const int64 iOldVal = pVarStr->GetValNum(); + g_Log.EventDebug("Previous value: 0%" PRIx64 " (%" PRId64 ")\n", iOldVal, iOldVal); + +#endif + } + return SetStrOverride( pszName, pszVal ); } return pVarStr; } From 1c2968622277e9bae8723f8f057f96371e77d95c Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 18 Oct 2025 15:19:18 +0200 Subject: [PATCH 094/112] Changed: made both Windows and other platforms load scripts from folders in alphabetical order. --- src/game/CServerConfig.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 8ae54cdd1..eb55c3d87 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -3159,11 +3159,26 @@ void CServerConfig::AddResourceDir( lpctstr pszDirName ) if ( iRet <= 0 ) // no files here. return; + // Load the files, but preordering them by file name. + // TODO (low priority): just rework CSStringList to CSStringCont/Vec and add a method to sort it + std::vector vecFileNames; + + // Collect them from the list CSStringListRec * psFile = filelist.GetHead(), *psFileNext = nullptr; for ( ; psFile; psFile = psFileNext ) { psFileNext = psFile->GetNext(); - sFilePath = CSFile::GetMergedFileName( pszDirName, *psFile ); + vecFileNames.push_back(*psFile); + } + + // Order them by name (not including path, it is added later). + std::sort(vecFileNames.begin(), vecFileNames.end(), + [](lpctstr ptcFirst, lpctstr ptcSecond) noexcept {return strcmp(ptcFirst, ptcSecond) < 0;} + ); + + for (lpctstr elem : vecFileNames) + { + sFilePath = CSFile::GetMergedFileName(pszDirName, elem); AddResourceFile( sFilePath ); } } @@ -3593,11 +3608,12 @@ bool CServerConfig::LoadResourceSection( CScript * pScript, bool fInsertSorted ) } else { - if ( rid.GetResIndex() >= (uint)(m_iMaxSkill) ) - m_iMaxSkill = rid.GetResIndex() + 1; + const uint uiResIdx = rid.GetResIndex(); + if ( uiResIdx >= (uint)(m_iMaxSkill) ) + m_iMaxSkill = uiResIdx + 1; // Just replace any previous CSkillDef - pSkill = new CSkillDef((SKILL_TYPE)(rid.GetResIndex())); + pSkill = new CSkillDef((SKILL_TYPE)uiResIdx); } ASSERT(pSkill); From e9f7441e76d5f013d49e40065136ca3f92940691 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 18 Oct 2025 15:21:12 +0200 Subject: [PATCH 095/112] Started threading system rework (race condition/wrong handling of function call stacks, which held data from different threads instead of only one). Split spheresvr.cpp classes in different files. --- src/CMakeSources.cmake | 6 + src/game/spheresvr.cpp | 197 +--- src/game/spheresvr.h | 44 +- src/sphere/GlobalInitializer.cpp | 98 ++ src/sphere/GlobalInitializer.h | 7 + src/sphere/MainThread.cpp | 78 ++ src/sphere/MainThread.h | 21 + src/sphere/ProfileTask.cpp | 8 +- src/sphere/StartupMonitorThread.cpp | 84 ++ src/sphere/StartupMonitorThread.h | 44 + src/sphere/UnixTerminal.cpp | 4 +- src/sphere/threads.cpp | 1358 ++++++++++++--------------- src/sphere/threads.h | 445 ++++----- 13 files changed, 1154 insertions(+), 1240 deletions(-) create mode 100644 src/sphere/GlobalInitializer.cpp create mode 100644 src/sphere/GlobalInitializer.h create mode 100644 src/sphere/MainThread.cpp create mode 100644 src/sphere/MainThread.h create mode 100644 src/sphere/StartupMonitorThread.cpp create mode 100644 src/sphere/StartupMonitorThread.h diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index 3f517b8de..ac6d2d961 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -36,8 +36,11 @@ set(pch_list set(sphere_S src/sphere/asyncdb.cpp src/sphere/ConsoleInterface.cpp + src/sphere/GlobalInitializer.cpp + src/sphere/MainThread.cpp src/sphere/ProfileData.cpp src/sphere/ProfileTask.cpp + src/sphere/StartupMonitorThread.cpp src/sphere/threads.cpp src/sphere/ntservice.cpp src/sphere/ntwindow.cpp @@ -47,8 +50,11 @@ set(sphere_H src/sphere/asyncdb.h src/sphere/containers.h src/sphere/ConsoleInterface.h + src/sphere/GlobalInitializer.h + src/sphere/MainThread.h src/sphere/ProfileData.h src/sphere/ProfileTask.h + src/sphere/StartupMonitorThread.h src/sphere/threads.h src/sphere/ntservice.h src/sphere/ntwindow.h diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 5fabb4ece..f48774a8c 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -1,5 +1,4 @@ #ifdef _WIN32 - #include "../sphere/ntservice.h" // g_Service #include "../sphere/ntwindow.h" #include // getpid() #else @@ -20,17 +19,17 @@ //#include "../common/CException.h" // included in the precompiled header //#include "../common/CExpression.h" // included in the precompiled header #include "../common/CUOInstall.h" -#include "../common/sphereversion.h" #include "../network/CNetworkManager.h" #include "../network/PingServer.h" #include "../sphere/asyncdb.h" +#include "../sphere/GlobalInitializer.h" +#include "../sphere/StartupMonitorThread.h" +#include "../sphere/MainThread.h" #include "clients/CAccount.h" -#include "CObjBase.h" #include "CScriptProfiler.h" #include "CServer.h" #include "CWorld.h" #include "spheresvr.h" -#include #include #ifdef UNIT_TESTING @@ -38,101 +37,11 @@ # include #endif +/* Start global declarations */ -// Dynamic allocation of some global stuff +DummySphereThread* DummySphereThread::_instance = nullptr; std::string g_sServerDescription; -// Dynamic initialization of some static members of other classes, which are used very soon after the server starts -dword CObjBase::sm_iCount = 0; // UID table. -#ifdef _WIN32 -llong CSTime::_kllTimeProfileFrequency = 1; // Default value. -#endif - - -// This method MUST be the first code running when the application starts! -GlobalInitializer::GlobalInitializer() -{ - // The order of the instructions is important! - - std::stringstream ssServerDescription; - ssServerDescription << SPHERE_TITLE << " Version " << SPHERE_BUILD_INFO_STR; - ssServerDescription << " [" << get_target_os_str() << '-' << get_target_arch_str() << "]"; - ssServerDescription << " by www.spherecommunity.net"; - g_sServerDescription = ssServerDescription.str(); - -//-- Time - -/* -#ifdef _WIN32 - // Ensure it's ACTUALLY necessary, before resorting to this. - timeBeginPeriod(1); // from timeapi.h, we need also to link against Winmm.lib... -#endif -*/ - PeriodicSyncTimeConstants(); - - -//--- Sphere threading system - - DummySphereThread::createInstance(); - - { - // Ensure i have this to have context for ADDTOCALLSTACK and other operations. - const AbstractThread* curthread = ThreadHolder::get().current(); - ASSERT(curthread != nullptr); - ASSERT(dynamic_cast(curthread)); - UnreferencedParameter(curthread); - } - -//--- Exception handling - -#ifdef WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS - SetWindowsStructuredExceptionTranslator(); -#endif - - // Set function to handle the invalid case where a pure virtual function is called. - SetPurecallHandler(); - -//--- Pre-startup sanity checks - - constexpr const char* m_sClassName = "GlobalInitializer"; - EXC_TRY("Pre-startup Init"); - - static_assert(MAX_BUFFER >= sizeof(CCommand)); - static_assert(MAX_BUFFER >= sizeof(CEvent)); - static_assert(sizeof(int) == sizeof(dword)); // make this assumption often. - static_assert(sizeof(ITEMID_TYPE) == sizeof(dword)); - static_assert(sizeof(word) == 2); - static_assert(sizeof(dword) == 4); - static_assert(sizeof(nword) == 2); - static_assert(sizeof(ndword) == 4); - static_assert(sizeof(wchar) == 2); // 16 bits - static_assert(sizeof(CUOItemTypeRec) == 37); // is byte packing working ? - - EXC_CATCH; -} - -void GlobalInitializer::InitRuntimeDefaultValues() // static -{ - CPointBase::InitRuntimeDefaultValues(); -} - -void GlobalInitializer::PeriodicSyncTimeConstants() // static -{ - // TODO: actually call it periodically! - -#ifdef _WIN32 - // Needed to get precise system time. - LARGE_INTEGER liProfFreq; - if (QueryPerformanceFrequency(&liProfFreq)) - { - CSTime::_kllTimeProfileFrequency = liProfFreq.QuadPart; - } -#endif // _WIN32 -} - - -/* Start global declarations */ - // NOLINTNEXTLINE(clazy-non-pod-global-static) static GlobalInitializer g_GlobalInitializer; @@ -163,7 +72,7 @@ CAccounts g_Accounts; // All the player accounts. name sorted CAccount CWorld g_World; // the world. (we save this stuff) // Networking stuff. They are declared here (in the same file of the other global declarations) to control the order of construction -// and destruction of these classes. If this order is altered, you'll get segmentation faults (access violations) when the server is closing! +// and destruction of these classes. If this order is altered, you'll get segmentation faults (access violations) when the server is servClosing! #ifdef _LIBEV extern LinuxEv g_NetworkEvent; #endif @@ -181,6 +90,10 @@ CUOMapList g_MapList; // global maps information //-- Threads. +// Startup/Monitor Sphere context bound to the bootstrap thread. +// NOLINTNEXTLINE(clazy-non-pod-global-static) +static StartupMonitorThread g_StartupMonitor; + // NOLINTNEXTLINE(clazy-non-pod-global-static) static MainThread g_Main; @@ -190,44 +103,6 @@ static PingServer g_PingServer; CDataBaseAsyncHelper g_asyncHdb; -//******************************************************************* -// Main server loop - -MainThread::MainThread() - : AbstractSphereThread("T_Main", ThreadPriority::RealTime) -{ - m_profile.EnableProfile(PROFILE_NETWORK_RX); - m_profile.EnableProfile(PROFILE_CLIENTS); - //m_profile.EnableProfile(PROFILE_NETWORK_TX); - m_profile.EnableProfile(PROFILE_CHARS); - m_profile.EnableProfile(PROFILE_ITEMS); - m_profile.EnableProfile(PROFILE_MAP); - m_profile.EnableProfile(PROFILE_MULTIS); - m_profile.EnableProfile(PROFILE_NPC_AI); - m_profile.EnableProfile(PROFILE_SCRIPTS); - m_profile.EnableProfile(PROFILE_SHIPS); - m_profile.EnableProfile(PROFILE_TIMEDFUNCTIONS); - m_profile.EnableProfile(PROFILE_TIMERS); -} - -void MainThread::onStart() -{ - AbstractSphereThread::onStart(); -} - -void MainThread::tick() -{ - Sphere_OnTick(); -} - -bool MainThread::shouldExit() noexcept -{ - if (g_Serv.GetExitFlag() != 0) - return true; - return AbstractSphereThread::shouldExit(); -} - - //******************************************************************* static bool WritePidFile(int iMode = 0) @@ -264,7 +139,6 @@ static bool WritePidFile(int iMode = 0) } } - int Sphere_InitServer( int argc, char *argv[] ) { constexpr const char *m_sClassName = "SphereInit"; @@ -293,7 +167,7 @@ int Sphere_InitServer( int argc, char *argv[] ) if ( !g_World.LoadAll() ) return -8; - // load auto-complete dictionary + // load auto-complete dictionary // TODO: might as well be removed...? EXC_SET_BLOCK("auto-complete"); { CSFileText dict; @@ -331,6 +205,8 @@ int Sphere_InitServer( int argc, char *argv[] ) EXC_SET_BLOCK("finalizing"); g_Serv.SetServerMode(ServMode::Run); // ready to go. + // Enter Run mode: inform ThreadHolder to disable startup fallback for current() + ThreadHolder::markServEnteredRunMode(); g_Log.Event(LOGM_INIT, "%s", g_Serv.GetStatusString(0x24)); g_Log.Event(LOGM_INIT, "\nStartup complete (items=%" PRIuSIZE_T ", chars=%" PRIuSIZE_T ", Accounts = %" PRIuSIZE_T ")\n", g_Serv.StatGet(SERV_STAT_ITEMS), g_Serv.StatGet(SERV_STAT_CHARS), g_Serv.StatGet(SERV_STAT_ACCOUNTS)); @@ -413,41 +289,14 @@ void Sphere_ExitServer() } -int Sphere_OnTick() -{ - // Give the world (CMainTask) a single tick. RETURN: 0 = everything is fine. - constexpr const char *m_sClassName = "SphereTick"; - EXC_TRY("Tick"); -#ifdef _WIN32 - EXC_SET_BLOCK("service"); - g_NTService._OnTick(); -#endif - - EXC_SET_BLOCK("world"); - g_World._OnTick(); - - // process incoming data - EXC_SET_BLOCK("network-in"); - g_NetworkManager.processAllInput(); - - EXC_SET_BLOCK("server"); - g_Serv._OnTick(); - - // push outgoing data - EXC_SET_BLOCK("network-out"); - g_NetworkManager.processAllOutput(); - - // don't put the network-tick between in.tick and out.tick, otherwise it will clean the out queue! - EXC_SET_BLOCK("network-tick"); - g_NetworkManager.tick(); // then this thread has to call the network tick +//***************************************************** - EXC_CATCH; - return g_Serv.GetExitFlag(); +// Provide the monitor stuck-check wrapper referenced by StartupMonitorThread +bool Sphere_CheckMainStuckAndRestart() +{ + return g_Main.checkStuck(); } - -//***************************************************** - static void Sphere_MainMonitorLoop() { constexpr const char *m_sClassName = "SphereMonitor"; @@ -464,7 +313,7 @@ static void Sphere_MainMonitorLoop() } EXC_SET_BLOCK("Sleep"); - // only sleep 1 second at a time, to avoid getting stuck here when closing + // only sleep 1 second at a time, to avoid getting stuck here when servClosing // down with large m_iFreezeRestartTime values set for (int i = 0; i < g_Cfg.m_iFreezeRestartTime; ++i) { @@ -489,8 +338,7 @@ static void Sphere_MainMonitorLoop() } - -void atexit_handler() +static void atexit_handler() { ThreadHolder::get().markThreadsClosing(); } @@ -530,7 +378,10 @@ int main( int argc, char * argv[] ) } #ifndef _WIN32 - AbstractThread::setThreadName("T_SphereStartup"); + // Bind the bootstrap OS thread as a proper Sphere thread during startup. + g_StartupMonitor.attachToCurrentThread("T_SphereStartup"); + + // Start UnixTerminal in its own thread. g_UnixTerminal.start(); #endif diff --git a/src/game/spheresvr.h b/src/game/spheresvr.h index c71ce3c20..2243f0dea 100644 --- a/src/game/spheresvr.h +++ b/src/game/spheresvr.h @@ -7,49 +7,13 @@ #define _INC_SPHERESVR_H #include "../common/sphere_library/CSAssoc.h" -#include "../sphere/threads.h" - - -class GlobalInitializer -{ -public: - GlobalInitializer(); - static void InitRuntimeDefaultValues(); - static void PeriodicSyncTimeConstants(); -}; - -//////////////////////////////////////////////////////////////////////////////////// - -class MainThread : public AbstractSphereThread -{ -public: - MainThread(); - virtual ~MainThread() { }; - -private: - MainThread(const MainThread& copy); - MainThread& operator=(const MainThread& other); - -public: - // we increase the access level from protected to public in order to allow manual execution when - // configuration disables using threads - // TODO: in the future, such simulated functionality should lie in AbstractThread inself instead of hacks - virtual void tick() override; - -protected: - virtual void onStart() override; - virtual bool shouldExit() noexcept override; -}; - -////////////////////////////////////////////////////////////// extern std::string g_sServerDescription; extern CSStringList g_AutoComplete; -extern int Sphere_InitServer( int argc, char *argv[] ); -extern int Sphere_OnTick(); -extern void Sphere_ExitServer(); -extern int Sphere_MainEntryPoint( int argc, char *argv[] ); - +int Sphere_InitServer( int argc, char *argv[] ); +void Sphere_ExitServer(); +int Sphere_MainEntryPoint( int argc, char *argv[] ); +bool Sphere_CheckMainStuckAndRestart(); #endif // _INC_SPHERESVR_H diff --git a/src/sphere/GlobalInitializer.cpp b/src/sphere/GlobalInitializer.cpp new file mode 100644 index 000000000..80f0b9332 --- /dev/null +++ b/src/sphere/GlobalInitializer.cpp @@ -0,0 +1,98 @@ +#include "../common/sphereversion.h" +#include "../game/CObjBase.h" +#include "../game/uo_files/CUOItemTypeRec.h" +#include "GlobalInitializer.h" +#include + +extern std::string g_sServerDescription; + +// Dynamic initialization of some static members of other classes, which are used very soon after the server starts +dword CObjBase::sm_iCount = 0; // UID table. +#ifdef _WIN32 +llong CSTime::_kllTimeProfileFrequency = 1; // Default value. +#endif + + +// This method MUST be the first code running when the application starts! +GlobalInitializer::GlobalInitializer() +{ + // The order of the instructions is important! + + // Overwriting for safety, but it is created/allocated in spheresvr.cpp! + DummySphereThread::_instance = nullptr; + + std::stringstream ssServerDescription; + ssServerDescription << SPHERE_TITLE << " Version " << SPHERE_BUILD_INFO_STR; + ssServerDescription << " [" << get_target_os_str() << '-' << get_target_arch_str() << "]"; + ssServerDescription << " by www.spherecommunity.net"; + g_sServerDescription = ssServerDescription.str(); + + //-- Time + + /* + # *ifdef _WIN32 + // Ensure it's ACTUALLY necessary, before resorting to this. + timeBeginPeriod(1); // from timeapi.h, we need also to link against Winmm.lib... + #endif + */ + PeriodicSyncTimeConstants(); + + + //--- Sphere threading system + + DummySphereThread::createInstance(); + + { + // Ensure i have this to have context for ADDTOCALLSTACK and other operations. + const AbstractThread* curthread = ThreadHolder::get().current(); + ASSERT(curthread != nullptr); + ASSERT(dynamic_cast(curthread)); + UnreferencedParameter(curthread); + } + + //--- Exception handling + + #ifdef WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS + SetWindowsStructuredExceptionTranslator(); + #endif + + // Set function to handle the invalid case where a pure virtual function is called. + SetPurecallHandler(); + + //--- Pre-startup sanity checks + + constexpr const char* m_sClassName = "GlobalInitializer"; + EXC_TRY("Pre-startup Init"); + + static_assert(MAX_BUFFER >= sizeof(CCommand)); + static_assert(MAX_BUFFER >= sizeof(CEvent)); + static_assert(sizeof(int) == sizeof(dword)); // make this assumption often. + static_assert(sizeof(ITEMID_TYPE) == sizeof(dword)); + static_assert(sizeof(word) == 2); + static_assert(sizeof(dword) == 4); + static_assert(sizeof(nword) == 2); + static_assert(sizeof(ndword) == 4); + static_assert(sizeof(wchar) == 2); // 16 bits + static_assert(sizeof(CUOItemTypeRec) == 37); // is byte packing working ? + + EXC_CATCH; +} + +void GlobalInitializer::InitRuntimeDefaultValues() // static +{ + CPointBase::InitRuntimeDefaultValues(); +} + +void GlobalInitializer::PeriodicSyncTimeConstants() // static +{ + // TODO: actually call it periodically! + + #ifdef _WIN32 + // Needed to get precise system time. + LARGE_INTEGER liProfFreq; + if (QueryPerformanceFrequency(&liProfFreq)) + { + CSTime::_kllTimeProfileFrequency = liProfFreq.QuadPart; + } + #endif // _WIN32 +} diff --git a/src/sphere/GlobalInitializer.h b/src/sphere/GlobalInitializer.h new file mode 100644 index 000000000..e845d6b83 --- /dev/null +++ b/src/sphere/GlobalInitializer.h @@ -0,0 +1,7 @@ +struct GlobalInitializer +{ + GlobalInitializer(); + ~GlobalInitializer() = default; + static void InitRuntimeDefaultValues(); + static void PeriodicSyncTimeConstants(); +}; diff --git a/src/sphere/MainThread.cpp b/src/sphere/MainThread.cpp new file mode 100644 index 000000000..74ee5cbf7 --- /dev/null +++ b/src/sphere/MainThread.cpp @@ -0,0 +1,78 @@ +#include "MainThread.h" +#include "../game/CWorld.h" +#include "../game/CServer.h" +#include "../network/CNetworkManager.h" + +#ifdef _WIN32 +#include "../sphere/ntservice.h" // g_Service +#endif + +//******************************************************************* +// Main server loop + +static int Sphere_OnTick() +{ + // Give the world (CMainTask) a single tick. RETURN: 0 = everything is fine. + constexpr const char *m_sClassName = "SphereTick"; + EXC_TRY("Tick"); +#ifdef _WIN32 + EXC_SET_BLOCK("service"); + g_NTService._OnTick(); +#endif + + EXC_SET_BLOCK("world"); + g_World._OnTick(); + + // process incoming data + EXC_SET_BLOCK("network-in"); + g_NetworkManager.processAllInput(); + + EXC_SET_BLOCK("server"); + g_Serv._OnTick(); + + // push outgoing data + EXC_SET_BLOCK("network-out"); + g_NetworkManager.processAllOutput(); + + // don't put the network-tick between in.tick and out.tick, otherwise it will clean the out queue! + EXC_SET_BLOCK("network-tick"); + g_NetworkManager.tick(); // then this thread has to call the network tick + + EXC_CATCH; + return g_Serv.GetExitFlag(); +} + +MainThread::MainThread() +: AbstractSphereThread("T_Main", ThreadPriority::RealTime) +{ + m_profile.EnableProfile(PROFILE_NETWORK_RX); + m_profile.EnableProfile(PROFILE_CLIENTS); + //m_profile.EnableProfile(PROFILE_NETWORK_TX); + m_profile.EnableProfile(PROFILE_CHARS); + m_profile.EnableProfile(PROFILE_ITEMS); + m_profile.EnableProfile(PROFILE_MAP); + m_profile.EnableProfile(PROFILE_MULTIS); + m_profile.EnableProfile(PROFILE_NPC_AI); + m_profile.EnableProfile(PROFILE_SCRIPTS); + m_profile.EnableProfile(PROFILE_SHIPS); + m_profile.EnableProfile(PROFILE_TIMEDFUNCTIONS); + m_profile.EnableProfile(PROFILE_TIMERS); +} + +void MainThread::onStart() +{ + AbstractSphereThread::onStart(); +} + +void MainThread::tick() +{ + Sphere_OnTick(); +} + +bool MainThread::shouldExit() noexcept +{ + if (g_Serv.GetExitFlag() != 0) + return true; + return AbstractSphereThread::shouldExit(); +} + diff --git a/src/sphere/MainThread.h b/src/sphere/MainThread.h new file mode 100644 index 000000000..7b18e4bae --- /dev/null +++ b/src/sphere/MainThread.h @@ -0,0 +1,21 @@ +#include "../sphere/threads.h" + +class MainThread : public AbstractSphereThread +{ +public: + MainThread(); + virtual ~MainThread() { }; + + MainThread(const MainThread& copy) = delete; + MainThread& operator=(const MainThread& other) = delete; + +public: + // we increase the access level from protected to public in order to allow manual execution when + // configuration disables using threads + // TODO: in the future, such simulated functionality should lie in AbstractThread inself instead of hacks + virtual void tick() override; + +protected: + virtual void onStart() override; + virtual bool shouldExit() noexcept override; +}; diff --git a/src/sphere/ProfileTask.cpp b/src/sphere/ProfileTask.cpp index 2aff886b9..4752bc87b 100644 --- a/src/sphere/ProfileTask.cpp +++ b/src/sphere/ProfileTask.cpp @@ -18,18 +18,18 @@ ProfileTask::ProfileTask(PROFILE_TYPE id) : return; auto& th = ThreadHolder::get(); - if (th.closing()) + if (th.isServClosing()) return; AbstractThread* icontext = th.current(); if (icontext == nullptr) { - // Thread was deleted, manually or by app closing signal. + // Thread was deleted, manually or by app servClosing signal. return; } m_context = static_cast(icontext); - if (m_context != nullptr && !m_context->closing()) + if (m_context != nullptr && !m_context->isClosing()) { ProfileData& pdata = m_context->m_profile; const PROFILE_TYPE task = pdata.GetCurrentTask(); @@ -56,7 +56,7 @@ ProfileTask::~ProfileTask(void) noexcept { EXC_TRY("destroy profiletask"); - if (m_context != nullptr && !m_context->closing()) + if (m_context != nullptr && !m_context->isClosing()) m_context->m_profile.Start(m_previousTask); EXC_CATCH; diff --git a/src/sphere/StartupMonitorThread.cpp b/src/sphere/StartupMonitorThread.cpp new file mode 100644 index 000000000..e26b51741 --- /dev/null +++ b/src/sphere/StartupMonitorThread.cpp @@ -0,0 +1,84 @@ +/** + * @file startup_monitor_thread.cpp + * + * Implementation of StartupMonitorThread: a Sphere thread context bound to the + * bootstrap OS thread for startup work, then re-used as the monitor thread. + */ + +#include "../common/CLog.h" +#include "../game/CServer.h" +#include "../game/CServerConfig.h" +#include "StartupMonitorThread.h" + +// External globals used by the monitor loop +extern CServer g_Serv; +extern CServerConfig g_Cfg; +extern CLog g_Log; + +StartupMonitorThread::StartupMonitorThread() +: AbstractSphereThread("T_SphereStartup", ThreadPriority::Highest) +{ + // Enable any profiles useful during startup/monitoring. + m_profile.EnableProfile(PROFILE_OVERHEAD); + m_profile.EnableProfile(PROFILE_STAT_FAULTS); + m_profile.EnableProfile(PROFILE_IDLE); +} + +void StartupMonitorThread::tick() +{ + // Not used: we don’t spawn a separate OS thread for this object. + // The monitor loop is run manually via runMonitorLoop() on the same OS thread. +} + +void StartupMonitorThread::renameAsMonitor() +{ + // Rename the current OS thread (bootstrap thread) to “T_Monitor” + // for better visibility in debuggers and logs. + AbstractThread::setThreadName("T_Monitor"); + // Keep our internal name in sync for logs using getName(). + overwriteInternalThreadName("T_Monitor"); +} + +void StartupMonitorThread::runMonitorLoop() +{ + constexpr const char *m_sClassName = "SphereMonitor"; + + while (!g_Serv.GetExitFlag()) + { + EXC_TRY("MainMonitorLoop"); + + if (g_Cfg.m_iFreezeRestartTime <= 0) + { + DEBUG_ERR(("Freeze Restart Time cannot be cleared at run time\n")); + g_Cfg.m_iFreezeRestartTime = 10; + } + + EXC_SET_BLOCK("Sleep"); + for (int i = 0; i < g_Cfg.m_iFreezeRestartTime; ++i) + { + if (g_Serv.GetExitFlag()) + break; + SLEEP(1000); + } + + EXC_SET_BLOCK("Checks"); + + // Don’t look for freezing when doing certain things. + if (g_Serv.IsLoadingGeneric() || !g_Cfg.m_fSecure || g_Serv.IsValidBusy()) + continue; + + #ifndef _DEBUG + EXC_SET_BLOCK("Check Stuck"); + // NOTE: We do not hold a pointer to g_Main here to avoid coupling; + // the original code called g_Main.checkStuck(). Keep that in the caller + // if needed (or expose a function to do it). + extern bool Sphere_CheckMainStuckAndRestart(); // declare a tiny wrapper in spheresvr.cpp + if (Sphere_CheckMainStuckAndRestart()) + { + g_Log.Event(LOGL_CRIT, "'T_Main' thread hang, restarting...\n"); + } + #endif + + EXC_CATCH; + } +} diff --git a/src/sphere/StartupMonitorThread.h b/src/sphere/StartupMonitorThread.h new file mode 100644 index 000000000..d9deef217 --- /dev/null +++ b/src/sphere/StartupMonitorThread.h @@ -0,0 +1,44 @@ +/** + * @file startup_monitor_thread.h + * + * StartupMonitorThread + * - Provides a proper Sphere thread context on the bootstrap OS thread during startup. + * - Later, it can act as the monitor thread on that same OS thread. + * + * Usage pattern: + * - Very early in main(): g_StartupMonitor.attachToCurrentThread("T_SphereStartup"); + * - Run all startup/bootstrap work (LoadIni, scripts, world loading). + * - If running core on separate OS thread: + * g_Main.start(); + * g_StartupMonitor.renameAsMonitor(); + * g_StartupMonitor.runMonitorLoop(); // blocking, replaces Sphere_MainMonitorLoop() + * Else (inline core on bootstrap thread): + * g_StartupMonitor.detachFromCurrentThread(); + * AbstractThread::ThreadBindingScope bind(g_Main, "T_Main"); + * while (!g_Serv.GetExitFlag()) g_Main.tick(); + */ + +#ifndef _INC_STARTUP_MONITOR_THREAD_H +#define _INC_STARTUP_MONITOR_THREAD_H + +#include "threads.h" + +class StartupMonitorThread final : public AbstractSphereThread +{ +public: + StartupMonitorThread(); + ~StartupMonitorThread() override = default; + + // Not used in attached/inline mode (we don’t spawn an OS thread for this class), + // but we must still define it. + void tick() override; + + // Rename the current OS thread for visibility once we switch from startup duties to monitor duties. + void renameAsMonitor(); + + // Blocking monitor loop (runs on the bootstrap OS thread after startup). + // Mirrors the previous Sphere_MainMonitorLoop() logic. + void runMonitorLoop(); +}; + +#endif // _INC_STARTUP_MONITOR_THREAD_H diff --git a/src/sphere/UnixTerminal.cpp b/src/sphere/UnixTerminal.cpp index 1a650fd5a..6619267b2 100644 --- a/src/sphere/UnixTerminal.cpp +++ b/src/sphere/UnixTerminal.cpp @@ -307,7 +307,7 @@ void UnixTerminal::tick() { this->ConsoleInterface::_ciQueueCV.wait(lock, [this]() { - return (this->m_terminateRequested || !this->ConsoleInterface::_qOutput.empty()); + return (this->m_fTerminateRequested || !this->ConsoleInterface::_qOutput.empty()); }); } @@ -328,7 +328,7 @@ void UnixTerminal::tick() void UnixTerminal::waitForClose() { - this->m_terminateRequested = true; + this->m_fTerminateRequested = true; this->ConsoleInterface::_ciQueueCV.notify_one(); //AbstractSphereThread::waitForClose(); //AbstractThread::terminate(true); diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index 9793b9306..1841bf400 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -1,65 +1,81 @@ +/** + * @file threads.cpp + * + * Implementation details for Sphere threading: + * - O(1) current() via TLS fast path (zero locking). + * - Startup and shutdown flags are atomics (no subsystem calls in fast path). + * - No logging while holding ThreadHolder locks (avoid re-entrancy). + * - Windows thread naming fix (dwThreadID = -1). + * - Correct pthread equality on POSIX. + * - Safe temporary string pools. + */ + #ifdef _WIN32 - // this thing is somehow required to be able to initialise OLE - #define _WIN32_DCOM +#define _WIN32_DCOM +#include +#include +#else +#include +#if !defined(_BSD) && !defined(__APPLE__) +#include +#endif +#include #endif #include "../common/sphere_library/sresetevents.h" #include "../common/sphere_library/sstringobjs.h" -#include "../common/basic_threading.h" -//#include "../common/CException.h" // included in the precompiled header -//#include "../common/CExpression.h" // included in the precompiled header +//#include "../common/basic_threading.h" #include "../common/CLog.h" #include "../game/CServer.h" + #include "ProfileTask.h" #include "threads.h" -#if defined(_WIN32) - #include - #include -#elif !defined(_BSD) && !defined(__APPLE__) - #include // to set thread name -#endif - #include -#include +#include +#include +#include +//#include +// External globals +extern CServer g_Serv; +extern CLog g_Log; -// number of exceptions after which we restart thread and think that the thread have gone in exceptioning loops -#define EXCEPTIONS_ALLOWED 10 +// Thread-local pointer for fast “current thread” lookup. +thread_local AbstractSphereThread* g_tlsCurrentSphereThread = nullptr; -// number of milliseconds to wait for a thread to close -#define THREADJOIN_TIMEOUT 60000 +// Constants +#define THREAD_EXCEPTIONS_ALLOWED 10 +#define THREADJOIN_TIMEOUT 60000 -// temporary string storage #define THREAD_TEMPSTRING_C_STORAGE 2048 #define THREAD_TEMPSTRING_OBJ_STORAGE 1024 - -// This implementation doesn't reserve preallocated strings for each thread or attached to a specific thread, -// but it creates a pool of preallocated strings which access is guarded by a Mutex. +/* + * Shared pools for temporary strings used during logging/formatting. + */ struct TemporaryStringsThreadSafeStateHolder { - // C-style string Buffer (char array) - SimpleMutex g_tmpCStringMutex; - std::atomic g_tmpCStringIndex; + // C-style char buffers + SimpleMutex g_tmpCStringMutex; + std::atomic g_tmpCStringIndex; std::unique_ptr g_tmpCStrings; - // TemporaryString Buffer - SimpleMutex g_tmpTemporaryStringMutex; - std::atomic g_tmpTemporaryStringIndex; - struct TemporaryStringStorage - { - char m_buffer[THREAD_STRING_LENGTH]; - char m_state; - }; - std::unique_ptr g_tmpTemporaryStringStorage; + // TemporaryString buffers with “in-use” flags + SimpleMutex g_tmpTemporaryStringMutex; + std::atomic g_tmpTemporaryStringIndex; + struct TemporaryStringStorage { char m_buffer[THREAD_STRING_LENGTH]; char m_state; }; + std::unique_ptr g_tmpTemporaryStringStorage; private: - TemporaryStringsThreadSafeStateHolder() : - g_tmpCStringIndex(0), g_tmpTemporaryStringIndex(0) + TemporaryStringsThreadSafeStateHolder() + : g_tmpCStringIndex(0) + , g_tmpTemporaryStringIndex(0) { g_tmpCStrings = std::make_unique(THREAD_TEMPSTRING_C_STORAGE * THREAD_STRING_LENGTH); g_tmpTemporaryStringStorage = std::make_unique(THREAD_TEMPSTRING_OBJ_STORAGE); + for (uint i = 0; i < THREAD_TEMPSTRING_OBJ_STORAGE; ++i) + g_tmpTemporaryStringStorage[i].m_state = 0; } public: @@ -70,562 +86,485 @@ struct TemporaryStringsThreadSafeStateHolder } }; +// ----- ThreadHolder (slow-path ops) ----- -/** - * ThreadHolder -**/ - -ThreadHolder::ThreadHolder() noexcept : - m_threadCount(0), m_closingThreads(false) +ThreadHolder::ThreadHolder() noexcept + : m_threadCount(0) { - // While we keep this as a global state and do not decide to get a set of string buffers attached to each AbstractSphereThread, - // we have to ensure that we construct the global string buffer holder as soon as possible. - TemporaryStringsThreadSafeStateHolder::get(); + // Ensure pools constructed early to avoid first-use cost in weird places. + (void)TemporaryStringsThreadSafeStateHolder::get(); } ThreadHolder& ThreadHolder::get() noexcept { - static ThreadHolder instance; - return instance; -} - -bool ThreadHolder::closing() noexcept -{ - std::shared_lock lock(m_mutex); - volatile auto ret = m_closingThreads; - return ret; + static ThreadHolder instance; + return instance; } -AbstractThread* ThreadHolder::current() noexcept +void ThreadHolder::push(AbstractThread *thread) noexcept { - // Do not use ASSERTs here, would cause recursion. - - // RETRY_SHARED_LOCK_FOR_TASK is used to try to not make mutex lock fail and, if needed, - // handle failure while allowing this function to be noexcept. - - AbstractThread* retval = nullptr; - RETRY_SHARED_LOCK_FOR_TASK(m_mutex, lock, retval, - ([this, &lock]() -> AbstractThread* - { - const threadid_t tid = AbstractThread::getCurrentThreadSystemId(); - - if (m_spherethreadpairs_systemid_ptr.empty()) - [[unlikely]] - { - if (m_closingThreads) [[unlikely]] - { - //STDERR_LOG("Closing?\n"); - return nullptr; - } - - auto thread = static_cast(DummySphereThread::getInstance()); - if (!thread) - [[unlikely]] - { - // Should never happen. - RaiseImmediateAbort(11); - } - - thread->m_threadSystemId = tid; - lock.unlock(); - push(thread); - return thread; - } - - spherethreadpair_t *found = nullptr; - for (auto &elem : m_spherethreadpairs_systemid_ptr) - { - if (elem.first == tid) - { - found = &elem; - break; - } - } - - if (!found) - [[unlikely]] - { - //throw CSError(LOGL_FATAL, 0, "Thread handle not found in vector?"); - //STDERR_LOG("Thread handle not found in vector?"); - - // Should never happen. - RaiseImmediateAbort(12); - } - - auto thread = static_cast(found->second); - - ASSERT(thread->m_threadHolderId != ThreadHolder::m_kiInvalidThreadID); - SphereThreadData *tdata = &(m_threads[thread->m_threadHolderId]); - if (tdata->m_closed) - [[unlikely]] - { - //STDERR_LOG("Closed? Idx %u, Name %s.\n", thread->m_threadHolderId, thread->getName()); - return nullptr; - } - - if (m_closingThreads) [[unlikely]] - { - auto spherethread = dynamic_cast(thread); - if (!spherethread) - { - // Should never happen. - RaiseImmediateAbort(13); - } - - if (!spherethread->_fKeepAliveAtShutdown) - { - //STDERR_LOG("Closing?\n"); - return nullptr; - } - } - - // Uncomment it only for testing purposes, since this method is called very often and we don't need the additional overhead - //DEBUG_ASSERT( thread->isSameThread(thread->getId()) ); + if (!thread) + { + stderrLog("ThreadHolder::push: trying to push nullptr AbstractThread* ?.\n"); + return; + } - return thread; - })); + // Stash debug info for post-unlock logging. + const char* pcThreadNameForLog = thread->getName(); + int iThreadIdForLog = -1; + bool fShouldLog = false; - return retval; -} - -void ThreadHolder::push(AbstractThread *thread) noexcept -{ - bool fExceptionThrown = false; try { - auto sphere_thread = dynamic_cast(thread); - if (!sphere_thread) + auto* pSphereThread = dynamic_cast(thread); + if (!pSphereThread) { - //throw CSError(LOGL_FATAL, 0, "AbstractThread not being an AbstractSphereThread?"); - STDERR_LOG("AbstractThread not being an AbstractSphereThread?"); - //fExceptionThrown = true; - goto soft_throw; + stderrLog("ThreadHolder::push: AbstractThread is not an AbstractSphereThread.\n"); + return; } - std::unique_lock lock(m_mutex); - - //ASSERT(thread->m_threadSystemId != 0); - ASSERT(thread->m_threadHolderId == ThreadHolder::m_kiInvalidThreadID); - - m_threads.emplace_back( SphereThreadData{ thread, false }); - thread->m_threadHolderId = m_threadCount; + { + std::unique_lock lock(m_mutex); #ifdef _DEBUG - auto it_thread = std::find_if( - m_spherethreadpairs_systemid_ptr.begin(), - m_spherethreadpairs_systemid_ptr.end(), - [sphere_thread](spherethreadpair_t const &elem) noexcept -> bool { return elem.second == sphere_thread; }); - - // I don't want duplicates. - DEBUG_ASSERT(it_thread == m_spherethreadpairs_systemid_ptr.end()); + auto itp = std::find_if( + m_spherethreadpairs_systemid_ptr.begin(), m_spherethreadpairs_systemid_ptr.end(), + [pSphereThread](spherethreadpair_t const &elem) noexcept -> bool { + return (elem.second == pSphereThread); + }); + DEBUG_ASSERT(itp == m_spherethreadpairs_systemid_ptr.end()); #endif - m_spherethreadpairs_systemid_ptr.emplace_back(sphere_thread->m_threadSystemId, sphere_thread); - ++ m_threadCount; + m_threads.emplace_back(SphereThreadData{.m_ptr = thread, .m_closed = false }); + thread->m_threadHolderId = m_threadCount; + m_spherethreadpairs_systemid_ptr.emplace_back(pSphereThread->m_threadSystemId, pSphereThread); + ++m_threadCount; + + iThreadIdForLog = thread->m_threadHolderId; + fShouldLog = true; + } + +#ifdef _DEBUG + if (fShouldLog) + { + // Logging AFTER releasing the lock avoids re-entrancy into ThreadHolder. + g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, + "ThreadHolder: registered '%s' (ThreadHolder-ID %d).\n", pcThreadNameForLog, iThreadIdForLog); + } +#endif } - catch (CAssert const& e) - { - fExceptionThrown = true; - lptstr ptcBuf = Str_GetTemp(); - e.GetErrorMessage(ptcBuf, usize_narrow_u32(Str_TempLength())); - STDERR_LOG("ASSERT failed.\n%s\n", ptcBuf); - } - catch (std::system_error const& e) + catch (const std::exception& e) { - fExceptionThrown = true; - STDERR_LOG("Mutex cannot be acquired. Err: '%s'.\n", e.what()); + stderrLog("ThreadHolder::push: exception: '%s'.\n", e.what()); } catch (...) { - fExceptionThrown = true; - STDERR_LOG("Unknown exception thrown.\n"); + stderrLog("ThreadHolder::push: unknown exception.\n"); } +} + +void ThreadHolder::remove(AbstractThread *pAbstractThread) CANTHROW +{ + if (!pAbstractThread) + throw CSError(LOGL_FATAL, 0, "ThreadHolder::remove: thread == nullptr"); + + const char* pcThreadNameForLog = pAbstractThread->getName(); + int iThreadIdForLog = pAbstractThread->m_threadHolderId; + bool fShouldLog = false; - if (fExceptionThrown) { -soft_throw: - // Should never happen. - RaiseImmediateAbort(14); + std::unique_lock lock(m_mutex); + + auto* pSphereThread = dynamic_cast(pAbstractThread); + auto itp = std::find_if( + m_spherethreadpairs_systemid_ptr.begin(), m_spherethreadpairs_systemid_ptr.end(), + [pSphereThread](spherethreadpair_t const &elem) noexcept -> bool { + return (elem.second == pSphereThread); + }); + if (itp != m_spherethreadpairs_systemid_ptr.end()) + m_spherethreadpairs_systemid_ptr.erase(itp); + + auto itd = std::find_if(m_threads.begin(), m_threads.end(), + [pAbstractThread](SphereThreadData const& elem) { return elem.m_ptr == pAbstractThread; }); + if (itd != m_threads.end()) + m_threads.erase(itd); + + if (m_threadCount > 0) + --m_threadCount; + + pAbstractThread->m_threadHolderId = m_kiInvalidThreadID; + fShouldLog = true; } #ifdef _DEBUG - if (dynamic_cast(thread)) - { - // Too early in the init process to use the console... - printf("Registered thread '%s' with ThreadHolder ID %d.\n", - thread->getName(), thread->m_threadHolderId); - } - else + if (fShouldLog) { g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, - "Registered thread '%s' with ThreadHolder ID %d.\n", - thread->getName(), thread->m_threadHolderId); + "ThreadHolder: removed '%s' (former ThreadHolder-ID %d).\n", pcThreadNameForLog, iThreadIdForLog); } #endif } -void ThreadHolder::remove(AbstractThread *thread) CANTHROW +void ThreadHolder::markThreadsClosing() CANTHROW { - if (!thread) - throw CSError(LOGL_FATAL, 0, "thread == nullptr"); + // Flip fast-path flag first so concurrent readers immediately see “servClosing”. + ThreadHolder::markServservClosing(); std::unique_lock lock(m_mutex); - ASSERT(m_threadCount > 0); // Trying to de-queue thread while no threads are active? - - auto sphere_thread = static_cast(thread); - auto it_thread = std::find_if( - m_spherethreadpairs_systemid_ptr.begin(), - m_spherethreadpairs_systemid_ptr.end(), - [sphere_thread](spherethreadpair_t const &elem) noexcept -> bool { return elem.second == sphere_thread; }); - ASSERT(it_thread != m_spherethreadpairs_systemid_ptr.end()); + for (auto& thread_data : m_threads) + { + auto sphere_thread = static_cast(thread_data.m_ptr); + if (!sphere_thread) + continue; - auto it_data = std::find_if(m_threads.begin(), m_threads.end(), - [thread](SphereThreadData const& elem) { - return elem.m_ptr == thread; - }); - ASSERT(it_data != m_threads.end()); // Ensure that the thread to de-queue is registered + if (sphere_thread->m_fKeepAliveAtShutdown) + continue; - --m_threadCount; + sphere_thread->m_uiState = AbstractThread::eRunningState::Closing; + thread_data.m_closed = true; + } +} - m_threads.erase(it_data); - m_spherethreadpairs_systemid_ptr.erase(it_thread); +AbstractThread * ThreadHolder::getThreadAt(size_t at) noexcept +{ + std::shared_lock lock(m_mutex); + if (at >= getActiveThreads()) + return nullptr; + return m_threads[at].m_ptr; } -void ThreadHolder::markThreadsClosing() CANTHROW +AbstractThread * ThreadHolder::current() noexcept // static { - g_Log.Event(LOGM_INIT|LOGM_NOCONTEXT|LOGL_EVENT, "Marking threads as closing.\n"); + // TLS fast path: the absolutely hottest path in the system. + AbstractSphereThread* tls = g_tlsCurrentSphereThread; + if (tls != nullptr) [[likely]] + return tls; - std::unique_lock lock(m_mutex); + /* + In two cases, the thread-local pointer (g_tlsCurrentSphereThread) may not be set: + - We are on a thread that is not a registered Sphere thread: + Signal handler threads, or late after detach. + - Some context exists but the thread object was never bound: + Example: If we forget to attachToCurrentThread/bind a Sphere thread object (like StartupMonitorThread or g_Main in inline mode), the TLS pointer remains null. + */ - m_closingThreads = true; - for (auto& thread_data : m_threads) - { - auto sphere_thread = static_cast(thread_data.m_ptr); - if (sphere_thread->_fKeepAliveAtShutdown) - continue; + // No TLS => not a Sphere thread or not yet attached. + if (sm_servClosing.load(std::memory_order_relaxed)) [[unlikely]] + return nullptr; - sphere_thread->_fIsClosing = true; - thread_data.m_closed = true; - } + if (sm_inStartup.load(std::memory_order_relaxed)) [[unlikely]] + return DummySphereThread::getInstance(); // may be null extremely early + + // Runtime (Run mode) and not Closing: missing context => nullptr (by policy). + return nullptr; } -AbstractThread * ThreadHolder::getThreadAt(size_t at) noexcept +// Record that thread has started. +void ThreadHolder::markThreadStarted(AbstractThread* pThr) CANTHROW { - AbstractThread* retval = nullptr; - RETRY_SHARED_LOCK_FOR_TASK(m_mutex, lock, retval, - ([this, at]() -> AbstractThread* - { -// MSVC: warning C5101: use of preprocessor directive in function-like macro argument list is undefined behavior. -//#ifdef _DEBUG - if (getActiveThreads() != m_threads.size()) - [[unlikely]] - { - STDERR_LOG("Active threads %" PRIuSIZE_T ", threads container size %" PRIuSIZE_T ".\n", getActiveThreads(), m_threads.size()); - RaiseImmediateAbort(15); - } -//#endif - - if ( at > getActiveThreads() ) - [[unlikely]] - { - return nullptr; - } - + const int id = pThr->m_threadHolderId; + SphereThreadData& threadData = m_threads.at(id); + ASSERT(threadData.m_closed == false); + threadData.m_closed = false; +} - /* - for ( spherethreadlist_t::const_iterator it = m_threads.begin(), end = m_threads.end(); it != end; ++it ) - { - if ( at == 0 ) - return it->m_ptr; +// Record that the server entered Run mode (disable startup fallback in fast path). +void ThreadHolder::markServEnteredRunMode() noexcept // static +{ + sm_inStartup.store(false, std::memory_order_relaxed); +} - --at; - } - return nullptr; - */ - return m_threads[at].m_ptr; - })); +// Record that threads are servClosing (fast-path observable). +void ThreadHolder::markServservClosing() noexcept // static +{ + sm_servClosing.store(true, std::memory_order_relaxed); +} - return retval; +bool ThreadHolder::isServClosing() noexcept { // static + return sm_servClosing.load(std::memory_order_relaxed); } +// ----- AbstractThread ----- -/* - * AbstractThread -*/ int AbstractThread::m_threadsAvailable = 0; -AbstractThread::AbstractThread(const char *name, ThreadPriority priority) : - m_threadSystemId(0), m_threadHolderId(ThreadHolder::m_kiInvalidThreadID), - _fKeepAliveAtShutdown(false), _fIsClosing(false) +AbstractThread::AbstractThread(const char *name, ThreadPriority priority) { - if( AbstractThread::m_threadsAvailable == 0 ) - { - // no threads were started before - initialise thread subsystem + if (AbstractThread::m_threadsAvailable == 0) + { #ifdef _WIN32 - if( CoInitializeEx(nullptr, COINIT_MULTITHREADED) != S_OK ) - { - throw CSError(LOGL_FATAL, 0, "OLE is not available, threading model unimplementable"); - } + if (CoInitializeEx(nullptr, COINIT_MULTITHREADED) != S_OK) + throw CSError(LOGL_FATAL, 0, "OLE init failed, threading unavailable"); #endif - ++AbstractThread::m_threadsAvailable; - } - m_threadSystemId = 0; + } + ++AbstractThread::m_threadsAvailable; + Str_CopyLimitNull(m_name, name, sizeof(m_name)); - m_hangCheck = 0; - _thread_selfTerminateAfterThisTick = true; - m_terminateRequested = true; - setPriority(priority); - m_sleepEvent = std::make_unique(); + m_fThread_selfTerminateAfterThisTick = true; + m_fTerminateRequested = true; + + setPriority(priority); + + m_sleepEvent = std::make_unique(); m_terminateEvent = std::make_unique(); } AbstractThread::~AbstractThread() { #ifdef _DEBUG - fprintf(stdout, "DEBUG: Destroying thread '%s' with ThreadHolder ID %d%s and system ID %" PRIu64 ".\n", + lpctstr ptcState; + if (m_threadHolderId == ThreadHolder::m_kiInvalidThreadID) { + if (m_uiState == eRunningState::Closing) + ptcState = "[previously registered]"; + else if (m_uiState == eRunningState::NeverStarted) + ptcState = "[unregistered, never started]"; + else + stderrLog("~AbstractThread: Thread never registered in ThreadHolder and still running at this stage?\n"); + } + else + { + if (m_uiState == eRunningState::NeverStarted) + stderrLog("~AbstractThread: Thread registered in ThreadHolder and never started?\n"); + else if (m_uiState == eRunningState::Running) + stderrLog("~AbstractThread: Thread registered in ThreadHolder and still running at this stage?\n"); + } + fprintf(stdout, "DEBUG: Destroying thread '%s' (ThreadHolder-ID %d)%s, sys-id %" PRIu64 ".\n", getName(), m_threadHolderId, - (m_threadHolderId == ThreadHolder::m_kiInvalidThreadID) ? " (never started)" : "", - (uint64)m_threadSystemId); + ptcState, + (uint64)getId()); fflush(stdout); #endif terminate(true); + --AbstractThread::m_threadsAvailable; - if( AbstractThread::m_threadsAvailable == 0 ) - { - // all running threads have gone, the thread subsystem is no longer needed + if (AbstractThread::m_threadsAvailable == 0) + { #ifdef _WIN32 - CoUninitialize(); -#else - // No pthread equivalent + CoUninitialize(); #endif - } + } } void AbstractThread::overwriteInternalThreadName(const char* name) noexcept { - // Use it only if you know what you are doing! - // This doesn't actually do the change of the thread name! Str_CopyLimitNull(m_name, name, sizeof(m_name)); } void AbstractThread::start() { - //ThreadHolder::get().push(this); - //printf("Starting thread '%s' with ThreadHolder ID %d.\n", - // getName(), m_threadHolderId); - //fflush(stdout); + g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, + "Spawning new thread '%s' (0x% " PRIxSIZE_T ")...\n", + getName(), reinterpret_cast(this)); #ifdef _WIN32 - m_handle = reinterpret_cast(_beginthreadex(nullptr, 0, &runner, this, 0, nullptr)); + m_handle = reinterpret_cast(_beginthreadex(nullptr, 0, &runner, this, 0, nullptr)); #else - pthread_attr_t threadAttr; - pthread_attr_init(&threadAttr); - pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); + pthread_attr_t threadAttr; + pthread_attr_init(&threadAttr); + pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); spherethread_t threadHandle{}; - int result = pthread_create( &threadHandle, &threadAttr, &runner, this ); - pthread_attr_destroy(&threadAttr); - - if (result != 0) - { + const int result = pthread_create(&threadHandle, &threadAttr, &runner, this); + pthread_attr_destroy(&threadAttr); + if (result != 0) + { m_handle = std::nullopt; - throw CSError(LOGL_FATAL, 0, "Unable to spawn a new thread"); - } + throw CSError(LOGL_FATAL, 0, "Unable to spawn a new thread"); + } m_handle = threadHandle; #endif - m_terminateEvent->reset(); } void AbstractThread::terminate(bool ended) { - if( isActive() ) - { - bool wasCurrentThread = isCurrentThread(); - if (ended == false) - { - g_Log.Event(LOGL_WARN, "Forcing thread '%s' to terminate...\n", getName()); - - // if the thread is current then terminating here will prevent cleanup from occurring - if (wasCurrentThread == false) - { - if (!m_handle.has_value()) - { - STDERR_LOG("AbstractThread::terminate: no handle?.\n"); - } - else - { + if (!isActive()) + return; + + const bool wasCurrentThread = isCurrentThread(); + + if (ended == false) + { + if (!wasCurrentThread) + { + if (!m_handle.has_value()) + { + stderrLog("AbstractThread::terminate: no handle available.\n"); + } + else + { #ifdef _WIN32 - TerminateThread(m_handle.value(), 0); - CloseHandle(m_handle.value()); + TerminateThread(m_handle.value(), 0); + CloseHandle(m_handle.value()); #else - pthread_cancel(m_handle.value()); // IBM say it so + pthread_cancel(m_handle.value()); #endif - } - } - } + } + } + } - // Common things - ThreadHolder::get().remove(this); - m_threadSystemId = 0; - m_handle = std::nullopt; + ThreadHolder::get().remove(this); + m_threadSystemId = 0; + m_handle = std::nullopt; - // let everyone know we have been terminated - m_terminateEvent->set(); + m_terminateEvent->set(); - // current thread can be terminated now - if (ended == false && wasCurrentThread) - { + if (ended == false && wasCurrentThread) + { #ifdef _WIN32 - _endthreadex(EXIT_SUCCESS); + _endthreadex(EXIT_SUCCESS); #else - //exit(EXIT_SUCCESS) - pthread_exit(nullptr); + pthread_exit(nullptr); #endif - } - } + } } void AbstractThread::run() { - // is the very first since there is a possibility of something being altered there - onStart(); + onStart(); + + int exceptions = 0; + bool lastWasException = false; + m_fThread_selfTerminateAfterThisTick = false; + m_fTerminateRequested = false; - int exceptions = 0; - bool lastWasException = false; - _thread_selfTerminateAfterThisTick = false; - m_terminateRequested = false; + setThreadName(getName()); + m_uiState = eRunningState::Running; - for (;;) - { + for (;;) + { if (shouldExit()) break; - bool gotException = false; + bool gotException = false; - // report me being alive if I am being checked for status - if (m_hangCheck != 0) - { - m_hangCheck = 0; - } - - try - { - tick(); + if (m_uiHangCheck != 0) + m_uiHangCheck = 0; + try + { + tick(); if (shouldExit()) break; - // ensure this is recorded as 'idle' time for this thread (ideally this should - // be in tick() but we cannot guarantee it to be called there GetCurrentProfileData().Start(PROFILE_IDLE); - } - /* - catch( const CAssert& e ) + } + catch (const CSError& e) + { + gotException = true; + g_Log.CatchEvent(&e, "[TR] CSError in %s::tick", getName()); + GetCurrentProfileData().Count(PROFILE_STAT_FAULTS, 1); + } + catch (const std::exception& e) { gotException = true; - g_Log.CatchEvent(&e, "[TR] ExcType=CAssert in %s::tick", getName()); + g_Log.CatchStdException(&e, "[TR] std::exception in %s::tick", getName()); GetCurrentProfileData().Count(PROFILE_STAT_FAULTS, 1); } - */ - catch( const CSError& e ) - { - gotException = true; - g_Log.CatchEvent(&e, "[TR] ExcType=CSError in %s::tick", getName()); - GetCurrentProfileData().Count(PROFILE_STAT_FAULTS, 1); - } - catch( const std::exception& e ) + catch (...) { gotException = true; - g_Log.CatchStdException(&e, "[TR] ExcType=std::exception in %s::tick", getName()); + g_Log.CatchEvent(nullptr, "[TR] Unknown exception in %s::tick", getName()); GetCurrentProfileData().Count(PROFILE_STAT_FAULTS, 1); } - catch( ... ) - { - gotException = true; - g_Log.CatchEvent(nullptr, "[TR] ExcType=pure in %s::tick", getName()); - GetCurrentProfileData().Count(PROFILE_STAT_FAULTS, 1); - } - - if( gotException ) - { - if( lastWasException ) - { - ++exceptions; - } - else - { - lastWasException = true; - exceptions = 0; - } - if( exceptions >= EXCEPTIONS_ALLOWED ) - { - // a bad thing really happened. ALL previous EXCEPTIONS_ALLOWED ticks resulted in exception - // almost for sure we have looped somewhere and have no way to get out from this situation - // probably a thread restart can fix the problems - // but there is no real need to restart a thread, we will just simulate a thread restart, - // notifying a subclass like we have been just restarted, so it will restart it's operations - g_Log.Event(LOGL_CRIT, "'%s' thread raised too many exceptions, restarting...\n", getName()); - onStart(); - lastWasException = false; - } - } - else - { - lastWasException = false; - } - - if( shouldExit() ) - break; - - m_sleepEvent->wait(m_tickPeriod); - } + + if (gotException) + { + if (lastWasException) + ++exceptions; + else + { + lastWasException = true; + exceptions = 1; + } + + if (exceptions >= THREAD_EXCEPTIONS_ALLOWED) + { + g_Log.Event(LOGL_CRIT, "'%s' raised too many exceptions, soft-restarting...\n", getName()); + onStart(); + lastWasException = false; + } + } + else + { + lastWasException = false; + exceptions = 0; + } + + if (shouldExit()) + break; + + m_sleepEvent->wait(m_uiTickPeriod); + } + + m_uiState = eRunningState::Closing; } -SPHERE_THREADENTRY_RETNTYPE AbstractThread::runner(void *callerThread) +SPHERE_THREADENTRY_RETNTYPE SPHERE_THREADENTRY_CALLTYPE AbstractThread::runner(void *callerThread) { - // If the caller thread is an AbstractThread, call run, which starts it and make it enter in its main loop - AbstractThread * caller = reinterpret_cast(callerThread); - if (caller != nullptr) - { - caller->run(); - caller->terminate(true); - } - + auto* caller = reinterpret_cast(callerThread); + if (caller) + { + caller->run(); + caller->terminate(true); + } #ifdef _WIN32 return 0; #else - return nullptr; + return nullptr; #endif } + bool AbstractThread::isActive() const { - return m_handle.has_value(); + const bool fRet = m_handle.has_value(); + return fRet; +} + +bool AbstractThread::isClosing() const +{ + return (m_uiState == eRunningState::Closing); +} + +bool AbstractThread::checkStuck() +{ + if (!isActive()) + return false; + + if (m_uiHangCheck == 0) + m_uiHangCheck = 0xDEAD; + else if (m_uiHangCheck == 0xDEAD) + m_uiHangCheck = 0xDEADDEAD; + else + { + g_Log.Event(LOGL_CRIT, "'%s' hang detected, restarting thread...\n", m_name); + + m_fTerminateRequested = true; + awaken(); + waitForClose(); + + start(); + return true; + } + return false; } void AbstractThread::waitForClose() { - // Another thread has requested us to close and it's waiting for us to complete the current tick, - // or to forcefully be forcefully terminated after a THREADJOIN_TIMEOUT, which of the two happens first. - - if (isActive()) - { - if (isCurrentThread() == false) - { - // flag that we want the thread to terminate - m_terminateRequested = true; - awaken(); - - // give the thread a chance to close on its own, and then - // terminate anyway - m_terminateEvent->wait(THREADJOIN_TIMEOUT); - } - - terminate(false); - } + if (!isActive()) + return; + + if (!isCurrentThread()) + { + m_fTerminateRequested = true; + awaken(); + + m_terminateEvent->wait(THREADJOIN_TIMEOUT); + terminate(false); + } } void AbstractThread::awaken() @@ -635,476 +574,373 @@ void AbstractThread::awaken() bool AbstractThread::isCurrentThread() const noexcept { -#ifdef _WIN32 - return (getId() == ::GetCurrentThreadId()); -#else - return m_handle.has_value() && pthread_equal(m_handle.value(), pthread_self()); -#endif -} - -bool AbstractThread::checkStuck() -{ - if( isActive() ) - { - if( m_hangCheck == 0 ) - { - // initiate hang check - m_hangCheck = 0xDEAD; - } - else if( m_hangCheck == 0xDEAD ) - { - // one time period was not answered, wait a bit more - m_hangCheck = 0xDEADDEADl; - // TODO: - //g_Log.Event(LOGL_CRIT, "'%s' thread seems being hang (frozen) at '%s'?\n", m_name, m_action); - } - else if( m_hangCheck == 0xDEADDEADl ) - { - // TODO: really ugly static_cast... - g_Log.Event(LOGL_CRIT, "'%s' thread hang, restarting...\n", m_name); - #ifdef THREAD_TRACK_CALLSTACK - static_cast(this)->printStackTrace(); - #endif - terminate(false); - run(); - start(); - return true; - } - } - - return false; + return isSameThreadId(getId(), getCurrentThreadSystemId()); } void AbstractThread::onStart() { - // start-up actions for each thread - // when implemented in derived classes this method must always be called too, preferably before - // the custom implementation + m_threadSystemId = getCurrentThreadSystemId(); - // we set the id here to ensure it is available before the first tick, otherwise there's - // a small delay when setting it from AbstractThread::start and it's possible for the id - // to not be set fast enough (particular when using pthreads) - m_threadSystemId = getCurrentThreadSystemId(); + ThreadHolder& th = ThreadHolder::get(); + th.push(this); + th.markThreadStarted(this); - ThreadHolder::get().push(this); - - if (isActive()) // This thread has actually been spawned and the code is executing on a different thread - setThreadName(getName()); +#ifdef THREAD_TRACK_CALLSTACK + g_tlsCurrentSphereThread = dynamic_cast(this); +#else + g_tlsCurrentSphereThread = dynamic_cast(this); +#endif #ifdef _DEBUG g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, - "Started thread '%s' with ThreadHolder ID %d and system ID %" PRIu64 ".\n", - getName(), m_threadHolderId, (uint64)m_threadSystemId); + "Started thread loop for '%s' (ThreadHolder-ID %d), sys-id %" PRIu64 ".\n", + getName(), m_threadHolderId, (uint64)m_threadSystemId); #endif } void AbstractThread::setPriority(ThreadPriority pri) { ASSERT(((pri >= ThreadPriority::Idle) && (pri <= ThreadPriority::RealTime)) || (pri == ThreadPriority::Disabled)); - m_priority = pri; - - // detect a sleep period for thread depending on priority - switch( m_priority ) - { - case ThreadPriority::Idle: - m_tickPeriod = 1000; - break; - case ThreadPriority::Low: - m_tickPeriod = 200; - break; - case ThreadPriority::Normal: - m_tickPeriod = 100; - break; - case ThreadPriority::High: - m_tickPeriod = 50; - break; - case ThreadPriority::Highest: - m_tickPeriod = 5; - break; - case ThreadPriority::RealTime: - m_tickPeriod = 0; - break; - case ThreadPriority::Disabled: - m_tickPeriod = AutoResetEvent::_kiInfinite; - break; + m_priority = pri; + + switch (m_priority) + { + case ThreadPriority::Idle: m_uiTickPeriod = 1000; break; + case ThreadPriority::Low: m_uiTickPeriod = 200; break; + case ThreadPriority::Normal: m_uiTickPeriod = 100; break; + case ThreadPriority::High: m_uiTickPeriod = 50; break; + case ThreadPriority::Highest: m_uiTickPeriod = 5; break; + case ThreadPriority::RealTime: m_uiTickPeriod = 0; break; + case ThreadPriority::Disabled: m_uiTickPeriod = AutoResetEvent::_kiInfinite; break; } } bool AbstractThread::shouldExit() noexcept { - return closing() || m_terminateRequested || _thread_selfTerminateAfterThisTick; + return (m_uiState == eRunningState::Closing) + || m_fTerminateRequested.load(std::memory_order_relaxed) + || m_fThread_selfTerminateAfterThisTick; } void AbstractThread::setThreadName(const char* name) { - // register the thread name - - // Unix uses prctl to set thread name - // thread name must be 16 bytes, zero-padded if shorter - char name_trimmed[m_nameMaxLength] = { '\0' }; // m_nameMaxLength = 16 + char name_trimmed[m_nameMaxLength] = {'\0'}; Str_CopyLimitNull(name_trimmed, name, m_nameMaxLength); #if defined(_WIN32) -#if defined(MSVC_COMPILER) - // TODO: support thread naming when compiling with compilers other than Microsoft's - - // Windows uses THREADNAME_INFO structure to set thread name +#if defined(_MSC_VER) #pragma pack(push, 8) - typedef struct tagTHREADNAME_INFO - { - DWORD dwType; + typedef struct tagTHREADNAME_INFO { + DWORD dwType; // 0x1000 LPCSTR szName; - DWORD dwThreadID; - DWORD dwFlags; + DWORD dwThreadID; // -1 => current thread + DWORD dwFlags; } THREADNAME_INFO; #pragma pack(pop) - static constexpr DWORD MS_VC_EXCEPTION = 0x406D1388; - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = name_trimmed; - info.dwThreadID = (DWORD)(-1); - info.dwFlags = 0; + static constexpr DWORD MS_VC_EXCEPTION = 0x406D1388; + THREADNAME_INFO info{}; + info.dwType = 0x1000; + info.szName = name_trimmed; + info.dwThreadID = static_cast(-1); // CRITICAL: current thread + info.dwFlags = 0; - __try - { + __try { RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } __except(EXCEPTION_EXECUTE_HANDLER) { } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } -#endif // MSVC_COMPILER -#elif defined(__APPLE__) // Mac +#endif +#elif defined(__APPLE__) pthread_setname_np(name_trimmed); -#elif !defined(_BSD) // Linux +#elif !defined(_BSD) prctl(PR_SET_NAME, name_trimmed, 0, 0, 0); #elif defined(__FreeBSD__) || defined(__OpenBSD__) - pthread_set_name_np(getCurrentThreadId(), name_trimmed); + pthread_set_name_np(pthread_self(), name_trimmed); #elif defined(__NetBSD__) - pthread_setname_np(getCurrentThreadId(), "%s", name_trimmed); + pthread_setname_np(pthread_self(), "%s", name_trimmed); #endif - auto athr = static_cast(ThreadHolder::get().current()); - ASSERT(athr); - -#ifdef _DEBUG - g_Log.Event(LOGF_CONSOLE_ONLY|LOGM_DEBUG|LOGL_EVENT, - "Setting thread (ThreadHolder ID %d, internal name '%s') system name: '%s'.\n", - athr->m_threadHolderId, athr->getName(), name_trimmed); -#endif - athr->overwriteInternalThreadName(name_trimmed); + // Update internal name only if a context exists + if (auto* cur = ThreadHolder::current()) + if (auto* athr = dynamic_cast(cur)) + athr->overwriteInternalThreadName(name_trimmed); } - -/* - * AbstractSphereThread -*/ -AbstractSphereThread::AbstractSphereThread(const char *name, ThreadPriority priority) - : AbstractThread(name, priority) -#ifdef THREAD_TRACK_CALLSTACK - , m_stackInfo{}, m_stackInfoCopy{}, m_iStackPos(-1), - m_iStackUnwindingStackPos(-1), m_iCaughtExceptionStackPos(-1), - m_fFreezeCallStack(false) -#endif - , m_pExpr{std::make_unique()} +void AbstractThread::attachToCurrentThread(const char* osThreadName) noexcept { - // profiles that apply to every thread - m_profile.EnableProfile(PROFILE_IDLE); - m_profile.EnableProfile(PROFILE_OVERHEAD); - m_profile.EnableProfile(PROFILE_STAT_FAULTS); -} - -AbstractSphereThread::~AbstractSphereThread() -{ - AbstractThread::_fIsClosing = true; + lpctstr ptcThreadName = (osThreadName && osThreadName[0]) ? osThreadName : getName(); + g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, + "Binding current context to thread '%s'...\n", ptcThreadName); + onStart(); + setThreadName(ptcThreadName); } - -static auto getThreadRawStringBuffer() -> TemporaryStringsThreadSafeStateHolder::TemporaryStringStorage * +void AbstractThread::detachFromCurrentThread() noexcept { - auto& tsholder = TemporaryStringsThreadSafeStateHolder::get(); - SimpleThreadLock stlBuffer(tsholder.g_tmpCStringMutex); - - int initialPosition = tsholder.g_tmpTemporaryStringIndex; - int index; - for (;;) - { - index = tsholder.g_tmpTemporaryStringIndex += 1; - if(tsholder.g_tmpTemporaryStringIndex >= THREAD_TEMPSTRING_OBJ_STORAGE ) - { - const int inc = tsholder.g_tmpTemporaryStringIndex % THREAD_TEMPSTRING_OBJ_STORAGE; - tsholder.g_tmpTemporaryStringIndex = inc; - index = inc; - } - - if(tsholder.g_tmpTemporaryStringStorage[index].m_state == 0 ) - { - auto* store = &(tsholder.g_tmpTemporaryStringStorage[index]); - *store->m_buffer = '\0'; - return store; - } + if (auto* s = dynamic_cast(this)) + if (g_tlsCurrentSphereThread == s) + g_tlsCurrentSphereThread = nullptr; - // a protection against deadlock. All string buffers are marked as being used somewhere, so we - // have few possibilities (the case shows that we have a bug and temporary strings used not such): - // a) return nullptr and wait for exceptions in the program - // b) allocate a string from a heap - if( initialPosition == index ) - { - // but the best is to throw an exception to give better formed information for end users - // rather than access violations - DEBUG_WARN(( "Thread temporary string buffer is full.\n" )); - throw CSError(LOGL_FATAL, 0, "Thread temporary string buffer is full"); - } - } + ThreadHolder::get().remove(this); + m_threadSystemId = 0; } -char *AbstractSphereThread::Strings::allocateBuffer() noexcept -{ - auto& tsholder = TemporaryStringsThreadSafeStateHolder::get(); - SimpleThreadLock stlBuffer(tsholder.g_tmpCStringMutex); - - char * buffer = nullptr; - tsholder.g_tmpCStringIndex += 1; - - if (tsholder.g_tmpCStringIndex >= THREAD_TEMPSTRING_C_STORAGE ) - { - tsholder.g_tmpCStringIndex = tsholder.g_tmpCStringIndex % THREAD_TEMPSTRING_C_STORAGE; - } +// ----- AbstractSphereThread ----- - //buffer = tsholder->g_tmpStrings.get() + (tsholder->g_tmpStringIndex * THREAD_STRING_LENGTH); - if (!tsholder.g_tmpCStrings) { - // I shouldn't even try to do this. Maybe i'm at the end of server shutdown. - RaiseImmediateAbort(20); - //return nullptr; - } - - buffer = &(tsholder.g_tmpCStrings[tsholder.g_tmpCStringIndex * THREAD_TEMPSTRING_C_STORAGE]); - *buffer = '\0'; - - return buffer; -} - -void AbstractSphereThread::Strings::getBufferForStringObject(TemporaryString &string) noexcept +AbstractSphereThread::AbstractSphereThread(const char *name, ThreadPriority priority) + : AbstractThread(name, priority) + , m_pExpr{std::make_unique()} { - ADDTOCALLSTACK("alloc"); - auto& tsholder = TemporaryStringsThreadSafeStateHolder::get(); - SimpleThreadLock stlBuffer(tsholder.g_tmpTemporaryStringMutex); - - auto* store = getThreadRawStringBuffer(); - string.init(store->m_buffer, &store->m_state); + m_profile.EnableProfile(PROFILE_IDLE); + m_profile.EnableProfile(PROFILE_OVERHEAD); + m_profile.EnableProfile(PROFILE_STAT_FAULTS); } - -bool AbstractSphereThread::shouldExit() noexcept +AbstractSphereThread::~AbstractSphereThread() { - if ( g_Serv.GetExitFlag() != 0 ) - return true; + m_uiState = eRunningState::Closing; - return AbstractThread::shouldExit(); + if (g_tlsCurrentSphereThread == this) + g_tlsCurrentSphereThread = nullptr; } #ifdef THREAD_TRACK_CALLSTACK void AbstractSphereThread::signalExceptionCaught() noexcept { - if (m_iStackPos < 0 || (m_iStackPos >= (ssize_t)ARRAY_COUNT(m_stackInfo))) + if (m_iStackPos < 0) return; - m_iCaughtExceptionStackPos = std::max(m_iStackPos, m_iCaughtExceptionStackPos); + m_iCaughtExceptionStackPos = std::max(m_iStackPos, m_iCaughtExceptionStackPos); printStackTrace(); - memset(m_stackInfoCopy, 0, sizeof(m_stackInfo)); - m_iCaughtExceptionStackPos = -1; - m_iStackUnwindingStackPos = -1; + std::memset(m_stackInfoCopy, 0, sizeof(m_stackInfoCopy)); + m_iCaughtExceptionStackPos = -1; + m_iStackUnwindingStackPos = -1; } void AbstractSphereThread::signalExceptionStackUnwinding() noexcept { - //ASSERT(isCurrentThread()); - if (m_iStackPos < 0 || (m_iStackPos >= (ssize_t)ARRAY_COUNT(m_stackInfo))) + if (m_iStackPos < 0) return; + m_iStackUnwindingStackPos = std::max(m_iStackPos, m_iStackUnwindingStackPos); - memcpy(m_stackInfoCopy, m_stackInfo, sizeof(m_stackInfo)); + std::memcpy(m_stackInfoCopy, m_stackInfo, sizeof(m_stackInfo)); } -static thread_local ssize_t _stackpos = -1; void AbstractSphereThread::pushStackCall(const char *name) noexcept { - if (m_fFreezeCallStack == true) [[unlikely]] { + if (m_fFreezeCallStack) return; - } #ifdef _DEBUG - if (m_iStackPos < -1) [[unlikely]] { + if (m_iStackPos < -1) RaiseImmediateAbort(16); - } - if (m_iStackPos >= (ssize_t)ARRAY_COUNT(m_stackInfo)) [[unlikely]] { + if (m_iStackPos >= (ssize_t)ARRAY_COUNT(m_stackInfo) - 1) RaiseImmediateAbort(17); - } #endif ++m_iStackPos; - _stackpos = m_iStackPos; m_stackInfo[m_iStackPos].functionName = name; } void AbstractSphereThread::popStackCall() NOEXCEPT_NODEBUG { - if (m_fFreezeCallStack == true) + if (m_fFreezeCallStack) return; - --m_iStackPos; - _stackpos = m_iStackPos; - DEBUG_ASSERT(m_iStackPos >= -1); - +#ifdef _DEBUG + ASSERT(m_iStackPos >= 0); +#endif + if (m_iStackPos >= 0) + { + m_stackInfo[m_iStackPos].functionName = nullptr; + --m_iStackPos; + } } void AbstractSphereThread::printStackTrace() noexcept { - // don't allow call stack to be modified whilst we're printing it - freezeCallStack(true); - - const uint64_t threadId = static_cast(getId()); - const lpctstr threadName = getName(); + freezeCallStack(true); - //EXC_NOTIFY_DEBUGGER; + uint64_t threadId = +#if defined(_WIN32) || defined(__APPLE__) + static_cast(getId() ? getId() : getCurrentThreadSystemId()); +#else + reinterpret_cast(getId() ? getId() : getCurrentThreadSystemId()); +#endif + const lpctstr threadName = getName(); auto& stackInfo = (m_stackInfoCopy[0].functionName != nullptr) ? m_stackInfoCopy : m_stackInfo; - g_Log.EventDebug("Printing STACK TRACE for debugging purposes.\n"); + g_Log.EventDebug("Printing STACK TRACE for debugging purposes.\n"); g_Log.EventDebug(" ______ thread (id) name _____ | # | _____________ function _____________ |\n"); - for ( ssize_t i = 0; i < (ssize_t)ARRAY_COUNT(m_stackInfoCopy); ++i ) - { - if( stackInfo[i].functionName == nullptr ) - break; + + for (ssize_t i = 0; i < (ssize_t)ARRAY_COUNT(m_stackInfoCopy); ++i) + { + if (stackInfo[i].functionName == nullptr) + break; lpctstr extra = ""; if (i == m_iStackUnwindingStackPos) - { extra = "<-- last tracked function call (stack unwinding detected here)"; - } else if (i == m_iCaughtExceptionStackPos) - { - if (m_iStackUnwindingStackPos == -1) - extra = "<-- exception catch point (below is guessed and could be incorrect!)"; - else - extra = "<-- exception catch point"; - } - /* - const bool origin = (i == (m_iStackPos - 1)); - if (origin) - { - if (m_uisignalExceptionStackUnwinding) - extra = "<-- last tracked function call (stack unwinding began here)"; - else - extra = "<-- exception catch point (below is guessed and could be incorrect!)"; - } - */ + extra = (m_iStackUnwindingStackPos == -1) + ? "<-- exception catch point (below is guessed and could be incorrect!)" + : "<-- exception catch point"; g_Log.EventDebug("(%" PRIx64 ") %15.15s | %3u | %36.36s | %s\n", threadId, threadName, (uint)i, stackInfo[i].functionName, extra); if (i == m_iStackUnwindingStackPos) - { - // Stop logging/writing functions called after the exception throw... break; - } - } + } - freezeCallStack(false); + freezeCallStack(false); } #endif -/* - * DummySphereThread -*/ -DummySphereThread *DummySphereThread::_instance = nullptr; +bool AbstractSphereThread::shouldExit() noexcept +{ + if (g_Serv.GetExitFlag() != 0) + return true; + return AbstractThread::shouldExit(); +} + +// ----- DummySphereThread ----- DummySphereThread::DummySphereThread() : AbstractSphereThread("dummy", ThreadPriority::Normal) { } -void DummySphereThread::createInstance() // static +void DummySphereThread::createInstance() { - // This dummy thread is created at the very beginning of the application startup. - // Before the server becomes operational, this won't be used anymore, since fully functional threads will be created. - - // Create this only once, it has to be one of the first operations to be done when the application starts. - ASSERT(_instance == nullptr); - _instance = new DummySphereThread(); + ASSERT(_instance == nullptr); + _instance = new DummySphereThread(); } -DummySphereThread *DummySphereThread::getInstance() noexcept // static +DummySphereThread* DummySphereThread::getInstance() noexcept { - return _instance; + return _instance; } void DummySphereThread::tick() { + // No-op } +// ----- Temporary string pool helpers ----- -/* -* StackDebugInformation -*/ +static TemporaryStringsThreadSafeStateHolder::TemporaryStringStorage* +getThreadRawStringBuffer() CANTHROW +{ + auto& tsholder = TemporaryStringsThreadSafeStateHolder::get(); + SimpleThreadLock stlBuffer(tsholder.g_tmpTemporaryStringMutex); -#ifdef THREAD_TRACK_CALLSTACK + const int initialPosition = static_cast(tsholder.g_tmpTemporaryStringIndex.load(std::memory_order_relaxed)); + int index = initialPosition; -StackDebugInformation::StackDebugInformation(const char *name) noexcept - : m_context(nullptr) + for (;;) + { + index = static_cast(++tsholder.g_tmpTemporaryStringIndex); + if (index >= (int)THREAD_TEMPSTRING_OBJ_STORAGE) + { + index %= THREAD_TEMPSTRING_OBJ_STORAGE; + tsholder.g_tmpTemporaryStringIndex.store(index, std::memory_order_relaxed); + } + + auto* store = &(tsholder.g_tmpTemporaryStringStorage[index]); + if (store->m_state == 0) + { + store->m_state = 1; + store->m_buffer[0] = '\0'; + return store; + } + + if (index == initialPosition) + { + DEBUG_WARN(("Thread temporary string buffer is full.\n")); + throw CSError(LOGL_FATAL, 0, "Thread temporary string buffer is full"); + } + } +} + +char* AbstractSphereThread::Strings::allocateBuffer() noexcept +{ + auto& tsholder = TemporaryStringsThreadSafeStateHolder::get(); + SimpleThreadLock stlBuffer(tsholder.g_tmpCStringMutex); + + uint index = ++tsholder.g_tmpCStringIndex; + if (index >= THREAD_TEMPSTRING_C_STORAGE) + { + index %= THREAD_TEMPSTRING_C_STORAGE; + tsholder.g_tmpCStringIndex.store(index, std::memory_order_relaxed); + } + + if (!tsholder.g_tmpCStrings) + RaiseImmediateAbort(20); + + char* buffer = &(tsholder.g_tmpCStrings[index * THREAD_STRING_LENGTH]); + buffer[0] = '\0'; + return buffer; +} + +void AbstractSphereThread::Strings::getBufferForStringObject(TemporaryString &string) CANTHROW { - STATIC_ASSERT_NOEXCEPT_CONSTRUCTOR(StackDebugInformation, const char*); - STATIC_ASSERT_NOEXCEPT_MEMBER_FUNCTION(AbstractSphereThread, pushStackCall, const char*); - STATIC_ASSERT_NOEXCEPT_FREE_FUNCTION(ThreadHolder::get); - STATIC_ASSERT_NOEXCEPT_MEMBER_FUNCTION(ThreadHolder, current); + ADDTOCALLSTACK("AbstractSphereThread::Strings::alloc"); + auto* store = getThreadRawStringBuffer(); // may throw + string.init(store->m_buffer, &store->m_state); +} - auto& th = ThreadHolder::get(); - if (th.closing()) [[unlikely]] +// ----- StackDebugInformation (RAII) ----- +#ifdef THREAD_TRACK_CALLSTACK +StackDebugInformation::StackDebugInformation(const char *name) noexcept + : m_context(nullptr) +{ + // Fast path: TLS + if (g_tlsCurrentSphereThread && !g_tlsCurrentSphereThread->isClosing()) + { + m_context = g_tlsCurrentSphereThread; + m_context->pushStackCall(name); return; + } + + // Fallback: use current() (still lock-free and fast) + AbstractThread *icontext = ThreadHolder::current(); + if (!icontext) + return; // TODO: log this? - AbstractThread *icontext = th.current(); - if (icontext == nullptr) - [[unlikely]] - { - // Thread was deleted, manually or by app closing signal. - return; - } - - m_context = static_cast(icontext); - if (m_context != nullptr && !m_context->closing()) [[likely]] - { + m_context = dynamic_cast(icontext); + if (m_context && !m_context->isClosing()) m_context->pushStackCall(name); - } } StackDebugInformation::~StackDebugInformation() noexcept { - if (!m_context || m_context->closing()) [[unlikely]] - return; + if (!m_context || m_context->isClosing()) + return; if (!m_context->isExceptionStackUnwinding()) { - if (std::uncaught_exceptions() != 0) [[unlikely]] - { - // Exception was thrown and stack unwinding is in progress. + if (std::uncaught_exceptions() != 0) m_context->signalExceptionStackUnwinding(); - } } m_context->popStackCall(); } -void StackDebugInformation::printStackTrace() noexcept // static +void StackDebugInformation::printStackTrace() noexcept { - AbstractThread* pThreadState = ThreadHolder::get().current(); - if (pThreadState) - static_cast(pThreadState)->printStackTrace(); + AbstractThread* pThreadState = ThreadHolder::current(); + if (pThreadState) + if (auto* s = dynamic_cast(pThreadState)) + s->printStackTrace(); } -void StackDebugInformation::freezeCallStack(bool freeze) noexcept // static +void StackDebugInformation::freezeCallStack(bool freeze) noexcept { - AbstractThread* pThreadState = ThreadHolder::get().current(); - if (pThreadState) - static_cast(pThreadState)->freezeCallStack(freeze); + AbstractThread* pThreadState = ThreadHolder::current(); + if (pThreadState) + if (auto* s = dynamic_cast(pThreadState)) + s->freezeCallStack(freeze); } - -#endif // THREAD_TRACK_CALLSTACK +#endif diff --git a/src/sphere/threads.h b/src/sphere/threads.h index 3601efe55..34402f458 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -1,202 +1,125 @@ /** -* @file threads.h -* -*/ + * @file threads.h + */ #ifndef _INC_THREADS_H #define _INC_THREADS_H #include "../common/common.h" #include "../sphere/ProfileData.h" + #include +#include +#include +#include #include #ifndef _WIN32 - #include +#include #endif class CExpression; class TemporaryString; - -/** - * Sphere threading system - * Threads should be inherited from AbstractThread with overridden tick() method - * Also useful to override onStart() in order to initialise class data variables for ticking - * which is triggered whenever the thread is starting/restarting -**/ - -// Types definition for different platforms -#ifdef _WIN32 - typedef HANDLE spherethread_t; - typedef DWORD threadid_t; - #define SPHERE_THREADENTRY_RETNTYPE unsigned - #define SPHERE_THREADENTRY_CALLTYPE __stdcall - #define SPHERE_THREADT_NULL nullptr -#else - typedef pthread_t spherethread_t; -# ifdef __APPLE__ - typedef uint64_t threadid_t; -# else - typedef pthread_t threadid_t; -#endif - - #define SPHERE_THREADENTRY_RETNTYPE void * - #define SPHERE_THREADENTRY_CALLTYPE -#endif - -class AbstractThread; class AutoResetEvent; class ManualResetEvent; -/* -// stores a value unique to each thread, intended to hold -// a pointer (e.g. the current AbstractThread instance) -template -class TlsValue -{ -private: -#ifdef _WIN32 - dword _key; -#else - pthread_key_t _key; -#endif - bool _ready; - -public: - TlsValue(); - ~TlsValue(); +// Forward decl for TLS pointer to speed up current() hot path. +class AbstractSphereThread; +extern thread_local AbstractSphereThread* g_tlsCurrentSphereThread; - TlsValue(const TlsValue& copy) = delete; - TlsValue& operator=(const TlsValue& other) = delete; - -public: - // allows assignment to set the current value - TlsValue& operator=(const T& value) - { - set(value); - return *this; - } - - // allows a cast to get current value - operator T() const { return get(); } - -public: - void set(const T value); // set the value for the current thread - T get() const; // get the value for the current thread -}; - -template -TlsValue::TlsValue() -{ - // allocate thread storage -#ifdef _WIN32 - _key = TlsAlloc(); - _ready = (_key != TLS_OUT_OF_INDEXES); -#else - _key = 0; - _ready = (pthread_key_create(&_key, nullptr) == 0); -#endif -} - -template -TlsValue::~TlsValue() -{ - // free the thread storage - if (_ready) +/* + * Platform types for OS thread identity and entry points. + */ #ifdef _WIN32 - TlsFree(_key); +#include +typedef HANDLE spherethread_t; +typedef DWORD threadid_t; +#define SPHERE_THREADENTRY_RETNTYPE unsigned +#define SPHERE_THREADENTRY_CALLTYPE __stdcall +#define SPHERE_THREADT_NULL nullptr #else - pthread_key_delete(_key); -#endif - _ready = false; -} - -template -void TlsValue::set(const T value) -{ - ASSERT(_ready); -#ifdef _WIN32 - TlsSetValue(_key, value); +//#include +#include +typedef pthread_t spherethread_t; +#ifdef __APPLE__ +typedef uint64_t threadid_t; // from pthread_threadid_np #else - pthread_setspecific(_key, value); +typedef pthread_t threadid_t; #endif -} - -template -T TlsValue::get() const -{ - if (_ready == false) - return nullptr; -#ifdef _WIN32 - return reinterpret_cast(TlsGetValue(_key)); -#else - return reinterpret_cast(pthread_getspecific(_key)); +#define SPHERE_THREADENTRY_RETNTYPE void* +#define SPHERE_THREADENTRY_CALLTYPE #endif -} -*/ +/* + * Priority → tick cadence mapping (ms). + */ enum class ThreadPriority : int { - Idle, // tick 1000ms - Low, // tick 200ms - Normal, // tick 100ms - High, // tick 50ms - Highest, // tick 5ms - RealTime, // tick almost instantly - Disabled = 0xFF // tick never + Idle, + Low, + Normal, + High, + Highest, + RealTime, + Disabled = 0xFF }; -// Thread base implementation, without Sphere "extensions". class AbstractThread { friend class ThreadHolder; static int m_threadsAvailable; + public: - static constexpr uint m_nameMaxLength = 16; // Unix support a max 16 bytes thread name. + static constexpr uint m_nameMaxLength = 16; // OS limits for linux/bsd pthread names protected: - threadid_t m_threadSystemId; - int m_threadHolderId; - - bool _fKeepAliveAtShutdown; - volatile std::atomic_bool _thread_selfTerminateAfterThisTick; - volatile std::atomic_bool _fIsClosing; + // TODO: move at least m_threadHolderId to AbstractSphereThread? + threadid_t m_threadSystemId{0}; // OS thread id for this object (0 => not bound) + int m_threadHolderId{-1}; // logical id in ThreadHolder + + bool m_fKeepAliveAtShutdown{false}; + std::atomic_bool m_fTerminateRequested{false}; + std::atomic_bool m_fThread_selfTerminateAfterThisTick{false}; + + enum class eRunningState : uchar { + NeverStarted, + Running, + Closing + }; + std::atomic m_uiState{eRunningState::NeverStarted}; - volatile std::atomic_bool m_terminateRequested; private: - char m_name[30]; - - // pthread_t type is opaque (platform-defined). It can be an integer, a struct, a ptr something. Memset is the safest and more portable way. - //spherethread_t m_handle; - // Or, since we need here an "invalid" value, just use optional. - std::optional m_handle; - - uint m_hangCheck; - ThreadPriority m_priority; - uint m_tickPeriod; - // PImpl - std::unique_ptr m_sleepEvent; + char m_name[30]{}; // internal name (OS name uses trimmed variant) + + std::optional m_handle; // present if start() spawned an OS thread + + uint m_uiHangCheck{0}; + ThreadPriority m_priority{ThreadPriority::Normal}; + uint m_uiTickPeriod{100}; + + std::unique_ptr m_sleepEvent; std::unique_ptr m_terminateEvent; public: AbstractThread(const char *name, ThreadPriority priority = ThreadPriority::Normal); - virtual ~AbstractThread(); + virtual ~AbstractThread(); - AbstractThread(const AbstractThread& copy) = delete; - AbstractThread& operator=(const AbstractThread& other) = delete; + AbstractThread(const AbstractThread&) = delete; + AbstractThread& operator=(const AbstractThread&) = delete; public: - threadid_t getId() const noexcept { return m_threadSystemId; } + threadid_t getId() const noexcept { return m_threadSystemId; } virtual const char *getName() const noexcept { return m_name; } - virtual bool isActive() const; - virtual bool checkStuck(); + bool isActive() const; + bool checkStuck(); + bool isClosing() const; virtual void start(); virtual void terminate(bool ended); virtual void waitForClose(); - void awaken(); + void awaken(); void setPriority(ThreadPriority pri); ThreadPriority getPriority() const { return m_priority; } @@ -204,44 +127,39 @@ class AbstractThread void overwriteInternalThreadName(const char* name) noexcept; bool isCurrentThread() const noexcept; - protected: +protected: virtual void tick() = 0; - // NOTE: this should not be too long-lasted function, so no world loading, etc here!!! + // Called on the OS thread that will execute tick(). virtual void onStart(); virtual bool shouldExit() noexcept; - private: +private: void run(); static SPHERE_THREADENTRY_RETNTYPE SPHERE_THREADENTRY_CALLTYPE runner(void *callerThread); - public: +public: static void setThreadName(const char* name); - bool closing() const noexcept - { - return _fIsClosing; - } - static inline threadid_t getCurrentThreadSystemId() noexcept { #if defined(_WIN32) return ::GetCurrentThreadId(); #elif defined(__APPLE__) - // On OSX, 'threadid_t' is not an integer but a '_opaque_pthread_t *'), so we need to resort to another method. - uint64_t threadid = 0; - pthread_threadid_np(pthread_self(), &threadid); - return threadid; + uint64_t tid = 0; + pthread_threadid_np(pthread_self(), &tid); + return tid; #else return pthread_self(); #endif } + static inline bool isSameThreadId(threadid_t firstId, threadid_t secondId) noexcept { #if defined(_WIN32) || defined(__APPLE__) return (firstId == secondId); #else - return pthread_equal(firstId,secondId); + return pthread_equal(firstId, secondId); #endif } @@ -249,26 +167,37 @@ class AbstractThread { return isSameThreadId(getCurrentThreadSystemId(), otherThreadId); } -}; + // Inline binding helper: attach/detach without creating a new OS thread. + class ThreadBindingScope + { + AbstractThread* m_pAbstractThread; + public: + explicit ThreadBindingScope(AbstractThread& t, const char* pcOsName = nullptr) noexcept + : m_pAbstractThread(&t) { m_pAbstractThread->attachToCurrentThread(pcOsName); } + ~ThreadBindingScope() noexcept { if (m_pAbstractThread) m_pAbstractThread->detachFromCurrentThread(); } + ThreadBindingScope(const ThreadBindingScope&) = delete; + ThreadBindingScope& operator=(const ThreadBindingScope&) = delete; + }; + + void attachToCurrentThread(const char* osThreadName = nullptr) noexcept; + void detachFromCurrentThread() noexcept; +}; -// Sphere thread. Have some sphere-specific class AbstractSphereThread : public AbstractThread { - friend class ThreadHolder; + friend class ThreadHolder; #ifdef THREAD_TRACK_CALLSTACK - struct STACK_INFO_REC - { - const char *functionName; - }; - - STACK_INFO_REC m_stackInfo[0x500]; - STACK_INFO_REC m_stackInfoCopy[0x500]; - ssize_t m_iStackPos; - ssize_t m_iStackUnwindingStackPos; - ssize_t m_iCaughtExceptionStackPos; - bool m_fFreezeCallStack; + struct STACK_INFO_REC { const char *functionName{}; }; + + STACK_INFO_REC m_stackInfo[0x500]{}; + STACK_INFO_REC m_stackInfoCopy[0x500]{}; + + ssize_t m_iStackPos{-1}; + ssize_t m_iStackUnwindingStackPos{-1}; + ssize_t m_iCaughtExceptionStackPos{-1}; + bool m_fFreezeCallStack{false}; #endif public: @@ -276,85 +205,80 @@ class AbstractSphereThread : public AbstractThread public: AbstractSphereThread(const char *name, ThreadPriority priority = ThreadPriority::Normal); - virtual ~AbstractSphereThread(); + virtual ~AbstractSphereThread(); - AbstractSphereThread(const AbstractSphereThread& copy) = delete; - AbstractSphereThread& operator=(const AbstractSphereThread& other) = delete; + AbstractSphereThread(const AbstractSphereThread&) = delete; + AbstractSphereThread& operator=(const AbstractSphereThread&) = delete; class Strings { friend class TemporaryString; friend tchar* Str_GetTemp() noexcept; - // allocates a char* with size of THREAD_MAX_LINE_LENGTH characters from the thread local storage static char *allocateBuffer() noexcept; - - // allocates a manageable String from the thread local storage - static void getBufferForStringObject(TemporaryString &string) noexcept; + static void getBufferForStringObject(TemporaryString &string) CANTHROW; }; public: #ifdef THREAD_TRACK_CALLSTACK void signalExceptionCaught() noexcept; void signalExceptionStackUnwinding() noexcept; - inline bool isExceptionCaught() const noexcept; - inline bool isExceptionStackUnwinding() const noexcept; - inline void freezeCallStack(bool freeze) noexcept; + inline bool isExceptionCaught() const noexcept { return (m_iCaughtExceptionStackPos >= 0); } + inline bool isExceptionStackUnwinding() const noexcept { return (m_iStackUnwindingStackPos >= 0); } - void pushStackCall(const char *name) noexcept; - void popStackCall() NOEXCEPT_NODEBUG; + inline void freezeCallStack(bool freeze) noexcept { m_fFreezeCallStack = freeze; } - void printStackTrace() noexcept; + void pushStackCall(const char *name) noexcept; + void popStackCall() NOEXCEPT_NODEBUG; + + void printStackTrace() noexcept; #endif - ProfileData m_profile; // the current active statistical profile. + ProfileData m_profile; protected: - virtual bool shouldExit() noexcept; + virtual bool shouldExit() noexcept; }; -#ifdef THREAD_TRACK_CALLSTACK -bool AbstractSphereThread::isExceptionCaught() const noexcept -{ - return (m_iCaughtExceptionStackPos >= 0); -} - -bool AbstractSphereThread::isExceptionStackUnwinding() const noexcept -{ - return (m_iStackUnwindingStackPos >= 0); -} - -void AbstractSphereThread::freezeCallStack(bool freeze) noexcept -{ - m_fFreezeCallStack = freeze; -} -#endif - -// Dummy thread for context when no thread really exists. To be called only once, at startup. +/* + * DummySphereThread: + * - Exists at startup so code has a safe expression/call-stack context. + * - After entering Run, missing contexts return nullptr by policy. + */ class DummySphereThread : public AbstractSphereThread { private: - static DummySphereThread *_instance; + friend class GlobalInitializer; + static DummySphereThread *_instance; public: - static void createInstance(); - static DummySphereThread *getInstance() noexcept; + static void createInstance(); + static DummySphereThread *getInstance() noexcept; protected: - DummySphereThread(); - virtual void tick(); + DummySphereThread(); + virtual void tick(); }; - -// Singleton utility class for working with threads. Holds all running threads inside. +/* + * ThreadHolder: registry and state flags. The “current()” hot path is fully inline and lock-free. + * + * Fast-path policy: + * - If TLS is set (g_tlsCurrentSphereThread != nullptr) → return it. O(1). + * - Else, if servClosing → return nullptr. + * - Else, if still in startup → return Dummy (if exists). + * - Else (Run mode) → return nullptr. + * + * Slow-path operations (push/remove/getThreadAt) use a mutex; NEVER log while holding it. + */ class ThreadHolder { friend class AbstractThread; struct SphereThreadData { - AbstractThread *m_ptr; + AbstractThread * m_ptr; bool m_closed; }; using spherethreadlist_t = std::vector; @@ -363,77 +287,78 @@ class ThreadHolder using spherethreadpair_t = std::pair; std::vector m_spherethreadpairs_systemid_ptr; - int m_threadCount; - volatile std::atomic_bool m_closingThreads; - mutable std::shared_mutex m_mutex; + int m_threadCount; + mutable std::shared_mutex m_mutex; - ThreadHolder() noexcept; + ThreadHolder() noexcept; ~ThreadHolder() noexcept = default; - friend void atexit_handler(void); - friend void Sphere_ExitServer(void); - void markThreadsClosing() CANTHROW; - - //SphereThreadData* findThreadData(AbstractThread* thread) noexcept; - public: - static constexpr lpctstr m_sClassName = "ThreadHolder"; + static constexpr lpctstr m_sClassName = "ThreadHolder"; static constexpr int m_kiInvalidThreadID = -1; - static ThreadHolder& get() noexcept; + // Global state flags (C++17 inline, no function call needed in fast path) + inline static std::atomic_bool sm_inStartup{true}; + inline static std::atomic_bool sm_servClosing{false}; - bool closing() noexcept; - // returns current working thread or DummySphereThread * if no AbstractThread threads are running - AbstractThread *current() noexcept; - // records a thread to the list. Sould NOT be called, internal usage - void push(AbstractThread *thread) noexcept; - // removes a thread from the list. Sould NOT be called, internal usage - void remove(AbstractThread *thread) CANTHROW; - // returns thread at i pos - AbstractThread * getThreadAt(size_t at) noexcept; + // Singleton instance for slow-path ops + static ThreadHolder& get() noexcept; - // returns number of running threads. Sould NOT be called, unit tests usage - inline size_t getActiveThreads() noexcept { return m_threadCount; } -}; + // High-performance fast path for “current” thread lookup. + static AbstractThread *current() noexcept; + // Record that the server entered Run mode (disable startup fallback in fast path). + static void markServEnteredRunMode() noexcept; + + // Record that threads are servClosing (fast-path observable). + static void markServservClosing() noexcept; + + static bool isServClosing() noexcept; + + // Slow-path registry ops + void push(AbstractThread *thread) noexcept; + void remove(AbstractThread *thread) CANTHROW; + + AbstractThread * getThreadAt(size_t at) noexcept; + inline size_t getActiveThreads() noexcept { return static_cast(m_threadCount); } + + void markThreadStarted(AbstractThread* pThr) CANTHROW; + + // Helper to mark servClosing and set flags. + void markThreadsClosing() CANTHROW; +}; -// used to hold debug information for the function call stack #ifdef THREAD_TRACK_CALLSTACK class StackDebugInformation { private: - AbstractSphereThread* m_context; + AbstractSphereThread* m_context; public: - StackDebugInformation(const char *name) noexcept; - ~StackDebugInformation() noexcept; + StackDebugInformation(const char *name) noexcept; + ~StackDebugInformation() noexcept; - StackDebugInformation(const StackDebugInformation& copy) = delete; - StackDebugInformation& operator=(const StackDebugInformation& other) = delete; + StackDebugInformation(const StackDebugInformation&) = delete; + StackDebugInformation& operator=(const StackDebugInformation&) = delete; public: - static void printStackTrace() noexcept; - static void freezeCallStack(bool freeze) noexcept; + static void printStackTrace() noexcept; + static void freezeCallStack(bool freeze) noexcept; }; -// Remember, call stack is disabled on Release builds! -#define ADDTOCALLSTACK(_function_) const StackDebugInformation debugStack(_function_) +#define ADDTOCALLSTACK(_function_) const StackDebugInformation debugStack(_function_) -// Add to the call stack these functions only in debug mode, to have the most precise call stack -// even if these functions are thought to be very safe and (nearly) exception-free. #ifdef _DEBUG - #define ADDTOCALLSTACK_DEBUG(_function_) ADDTOCALLSTACK(_function_) +#define ADDTOCALLSTACK_DEBUG(_function_) ADDTOCALLSTACK(_function_) #else - #define ADDTOCALLSTACK_DEBUG(_function_) (void)0 +#define ADDTOCALLSTACK_DEBUG(_function_) (void)0 #endif +#else -#else // THREAD_TRACK_CALLSTACK - -#define ADDTOCALLSTACK(_function_) (void)0 -#define ADDTOCALLSTACK_DEBUG(_function_) (void)0 - -#endif // THREAD_TRACK_CALLSTACK +#define ADDTOCALLSTACK(_function_) (void)0 +#define ADDTOCALLSTACK_DEBUG(_function_) (void)0 +#endif #endif // _INC_THREADS_H From c5d0fb3e8edd42801c74de51eb65af6ca256fdce Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sat, 18 Oct 2025 22:01:28 +0200 Subject: [PATCH 096/112] Updated threading. Fixed several threading system inconsistencies. Tested on Linux. --- src/CMakeSources.cmake | 1 + src/game/spheresvr.cpp | 98 +++--- src/game/spheresvr.h | 2 +- src/network/CNetworkManager.cpp | 2 +- src/network/CNetworkThread.cpp | 9 +- src/network/CNetworkThread.h | 3 +- src/sphere/StartupMonitorAPI.h | 11 + src/sphere/StartupMonitorThread.cpp | 9 + src/sphere/ntservice.cpp | 2 + src/sphere/ntwindow.cpp | 2 +- src/sphere/threads.cpp | 469 +++++++++++++++++++--------- src/sphere/threads.h | 37 +-- 12 files changed, 401 insertions(+), 244 deletions(-) create mode 100644 src/sphere/StartupMonitorAPI.h diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index ac6d2d961..88ac007d8 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -54,6 +54,7 @@ set(sphere_H src/sphere/MainThread.h src/sphere/ProfileData.h src/sphere/ProfileTask.h + src/sphere/StartupMonitorAPI.h src/sphere/StartupMonitorThread.h src/sphere/threads.h src/sphere/ntservice.h diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index f48774a8c..7f1bfa60f 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -105,6 +105,29 @@ CDataBaseAsyncHelper g_asyncHdb; //******************************************************************* +#ifdef _WIN32 +// Expose bootstrap-thread binding helpers for other TUs (e.g., WinMain/service). +#include "../sphere/StartupMonitorAPI.h" +extern "C" +{ + void Sphere_AttachBootstrapContext() + { + g_StartupMonitor.attachToCurrentThread("T_SphereStartup"); + } + + void Sphere_RenameBootstrapToMonitor() + { + g_StartupMonitor.renameAsMonitor(); + } + + void Sphere_RunMonitorLoop() + { + g_StartupMonitor.runMonitorLoop(); + } +} +#endif + + static bool WritePidFile(int iMode = 0) { lpctstr fileName = SPHERE_FILE ".pid"; @@ -297,47 +320,6 @@ bool Sphere_CheckMainStuckAndRestart() return g_Main.checkStuck(); } -static void Sphere_MainMonitorLoop() -{ - constexpr const char *m_sClassName = "SphereMonitor"; - // Just make sure the main loop is alive every so often. - // This should be the parent thread. try to restart it if it is not. - while ( !g_Serv.GetExitFlag() ) - { - EXC_TRY("MainMonitorLoop"); - - if ( g_Cfg.m_iFreezeRestartTime <= 0 ) - { - DEBUG_ERR(("Freeze Restart Time cannot be cleared at run time\n")); - g_Cfg.m_iFreezeRestartTime = 10; - } - - EXC_SET_BLOCK("Sleep"); - // only sleep 1 second at a time, to avoid getting stuck here when servClosing - // down with large m_iFreezeRestartTime values set - for (int i = 0; i < g_Cfg.m_iFreezeRestartTime; ++i) - { - if ( g_Serv.GetExitFlag() ) - break; - - SLEEP(1000); - } - - EXC_SET_BLOCK("Checks"); - // Don't look for freezing when doing certain things. - if ( g_Serv.IsLoadingGeneric() || ! g_Cfg.m_fSecure || g_Serv.IsValidBusy() ) - continue; - -#ifndef _DEBUG - EXC_SET_BLOCK("Check Stuck"); - if (g_Main.checkStuck() == true) - g_Log.Event(LOGL_CRIT, "'%s' thread hang, restarting...\n", g_Main.getName()); -#endif - EXC_CATCH; - } - -} - static void atexit_handler() { ThreadHolder::get().markThreadsClosing(); @@ -442,21 +424,25 @@ int main( int argc, char * argv[] ) // an instance of CNetworkInput nad CNetworkOutput, which support working in a multi threaded way (declarations and definitions in network_multithreaded.h/.cpp) g_NetworkManager.start(); - const bool fShouldCoreRunInSeparateThread = ( g_Cfg.m_iFreezeRestartTime > 0 ); - if (fShouldCoreRunInSeparateThread) - { - g_Main.start(); // Starts another thread to do all the work (it does Sphere_OnTick()) - AbstractThread::setThreadName("T_Monitor"); - Sphere_MainMonitorLoop(); // Use this thread to monitor if the others are stuck - } - else - { - while ( !g_Serv.GetExitFlag() ) - { - g_Main.tick(); // Use this thread to do all the work, without monitoring the other threads state - } - } - } + const bool fShouldCoreRunInSeparateThread = (g_Cfg.m_iFreezeRestartTime != 0); + if (fShouldCoreRunInSeparateThread) + { + // Core runs on a separate OS thread. + g_Main.start(); + // Switch the bootstrap thread from startup duties to monitoring duties. + g_StartupMonitor.renameAsMonitor(); + // Blocking monitor loop on the bootstrap thread. + g_StartupMonitor.runMonitorLoop(); + } + else + { + // Inline core on the bootstrap thread: release the startup context and bind g_Main. + g_StartupMonitor.detachFromCurrentThread(); + AbstractThread::ThreadBindingScope bind(g_Main, "T_Main"); + while (!g_Serv.GetExitFlag()) + g_Main.tick(); + } + } exit_server: Sphere_ExitServer(); diff --git a/src/game/spheresvr.h b/src/game/spheresvr.h index 2243f0dea..a4ff904d4 100644 --- a/src/game/spheresvr.h +++ b/src/game/spheresvr.h @@ -11,9 +11,9 @@ extern std::string g_sServerDescription; extern CSStringList g_AutoComplete; +int Sphere_MainEntryPoint( int argc, char *argv[] ); int Sphere_InitServer( int argc, char *argv[] ); void Sphere_ExitServer(); -int Sphere_MainEntryPoint( int argc, char *argv[] ); bool Sphere_CheckMainStuckAndRestart(); #endif // _INC_SPHERESVR_H diff --git a/src/network/CNetworkManager.cpp b/src/network/CNetworkManager.cpp index 150d89f78..c8908cdea 100644 --- a/src/network/CNetworkManager.cpp +++ b/src/network/CNetworkManager.cpp @@ -402,7 +402,7 @@ void CNetworkManager::start(void) } */ - pThread->onStart(); // the thread structure (class) was created via createNetworkThreads, but we execute the worker method of that class in this thread + pThread->init(); // the thread structure (class) was created via createNetworkThreads, but we execute the worker method of that class in this thread } } } diff --git a/src/network/CNetworkThread.cpp b/src/network/CNetworkThread.cpp index 0ebb6db11..275614b97 100644 --- a/src/network/CNetworkThread.cpp +++ b/src/network/CNetworkThread.cpp @@ -92,9 +92,8 @@ void CNetworkThread::dropInvalidStates(void) } } -void CNetworkThread::onStart(void) +void CNetworkThread::init() { - AbstractSphereThread::onStart(); m_input.setOwner(this); m_output.setOwner(this); m_profile.EnableProfile(PROFILE_NETWORK_RX); @@ -103,6 +102,12 @@ void CNetworkThread::onStart(void) m_profile.EnableProfile(PROFILE_DATA_TX); } +void CNetworkThread::onStart(void) +{ + init(); + AbstractSphereThread::onStart(); +} + void CNetworkThread::tick(void) { // process periodic actions diff --git a/src/network/CNetworkThread.h b/src/network/CNetworkThread.h index 69ee3a58c..091cd6100 100644 --- a/src/network/CNetworkThread.h +++ b/src/network/CNetworkThread.h @@ -29,7 +29,7 @@ class CNetworkThread : public AbstractSphereThread ThreadSafeQueue m_assignQueue; // queue of states waiting to be taken by this thread CNetworkInput m_input; // handles data input - CNetworkOutput m_output; // handles data output + CNetworkOutput m_output; // handles data output public: size_t id(void) const { return m_id; } // network thread # @@ -77,6 +77,7 @@ class CNetworkThread : public AbstractSphereThread virtual void onStart(void); virtual void tick(void); + void init(); void flushAllClients(void); // flush all output private: diff --git a/src/sphere/StartupMonitorAPI.h b/src/sphere/StartupMonitorAPI.h new file mode 100644 index 000000000..0f67cb324 --- /dev/null +++ b/src/sphere/StartupMonitorAPI.h @@ -0,0 +1,11 @@ +// sphere/StartupMonitorAPI.h +#ifndef _INC_STARTUP_MONITOR_API_H +#define _INC_STARTUP_MONITOR_API_H + +extern "C" { + void Sphere_AttachBootstrapContext(); + void Sphere_RenameBootstrapToMonitor(); + void Sphere_RunMonitorLoop(); +} + +#endif diff --git a/src/sphere/StartupMonitorThread.cpp b/src/sphere/StartupMonitorThread.cpp index e26b51741..da5fd94db 100644 --- a/src/sphere/StartupMonitorThread.cpp +++ b/src/sphere/StartupMonitorThread.cpp @@ -32,6 +32,15 @@ void StartupMonitorThread::tick() void StartupMonitorThread::renameAsMonitor() { + // Debugging. + AbstractThread * pThread = ThreadHolder::current(); + if (pThread != this) + { + g_Log.EventWarn("renameAsMonitor called, but current Sphere context is %s; refusing rename.\n", + pThread ? pThread->getName() : ""); + return; + } + // Rename the current OS thread (bootstrap thread) to “T_Monitor” // for better visibility in debuggers and logs. AbstractThread::setThreadName("T_Monitor"); diff --git a/src/sphere/ntservice.cpp b/src/sphere/ntservice.cpp index 3e5dd2840..faf1c90aa 100644 --- a/src/sphere/ntservice.cpp +++ b/src/sphere/ntservice.cpp @@ -10,6 +10,7 @@ #include "../game/CObjBase.h" #include "../game/CServer.h" #include "../game/spheresvr.h" +#include "StartupMonitorAPI.h" #include "ntwindow.h" #include "ntservice.h" @@ -460,6 +461,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine { UnreferencedParameter(hPrevInstance); AbstractThread::setThreadName("T_SphereStartup"); + Sphere_AttachBootstrapContext(); // bind bootstrap OS thread to StartupMonitor context TCHAR *argv[32]; argv[0] = nullptr; diff --git a/src/sphere/ntwindow.cpp b/src/sphere/ntwindow.cpp index e70946e15..7fc33885e 100644 --- a/src/sphere/ntwindow.cpp +++ b/src/sphere/ntwindow.cpp @@ -188,7 +188,7 @@ void CNTWindow::exitActions() { g_Serv.SetExitFlag(5); NTWindow_DeleteIcon(); - _thread_selfTerminateAfterThisTick = true; + m_fThreadSelfTerminateAfterThisTick = true; } void CNTWindow::onStart() diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index 1841bf400..5f9789112 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -1,25 +1,24 @@ /** * @file threads.cpp - * - * Implementation details for Sphere threading: - * - O(1) current() via TLS fast path (zero locking). - * - Startup and shutdown flags are atomics (no subsystem calls in fast path). - * - No logging while holding ThreadHolder locks (avoid re-entrancy). - * - Windows thread naming fix (dwThreadID = -1). - * - Correct pthread equality on POSIX. - * - Safe temporary string pools. */ #ifdef _WIN32 -#define _WIN32_DCOM -#include -#include +# define _WIN32_DCOM +# include +# include #else -#include -#if !defined(_BSD) && !defined(__APPLE__) -#include +# include +# if !defined(_BSD) && !defined(__APPLE__) +# include +# endif +# include #endif -#include + +#if defined(__linux__) +# include +# include +#elif defined(__FreeBSD__) +# include #endif #include "../common/sphere_library/sresetevents.h" @@ -42,7 +41,10 @@ extern CServer g_Serv; extern CLog g_Log; // Thread-local pointer for fast “current thread” lookup. -thread_local AbstractSphereThread* g_tlsCurrentSphereThread = nullptr; +static thread_local AbstractSphereThread* sg_tlsCurrentSphereThread = nullptr; +// Avoid trying to get thread context while binding it. +static thread_local bool sg_tlsBindingInProgress = false; + // Constants #define THREAD_EXCEPTIONS_ALLOWED 10 @@ -86,7 +88,7 @@ struct TemporaryStringsThreadSafeStateHolder } }; -// ----- ThreadHolder (slow-path ops) ----- +// ----- ThreadHolder ----- ThreadHolder::ThreadHolder() noexcept : m_threadCount(0) @@ -101,62 +103,122 @@ ThreadHolder& ThreadHolder::get() noexcept return instance; } -void ThreadHolder::push(AbstractThread *thread) noexcept +bool ThreadHolder::isSystemIdRegistered(threadid_t sysId, AbstractSphereThread** outExisting) const noexcept +{ + std::shared_lock lock(m_mutex); + auto it = std::find_if( + m_spherethreadpairs_systemid_ptr.begin(), + m_spherethreadpairs_systemid_ptr.end(), + [sysId](const spherethreadpair_t& elem) noexcept { + return elem.first == sysId; + } + ); + if (it == m_spherethreadpairs_systemid_ptr.end()) + return false; + if (outExisting) + *outExisting = it->second; + return true; +} + +void ThreadHolder::push(AbstractThread* thread) noexcept { if (!thread) { - stderrLog("ThreadHolder::push: trying to push nullptr AbstractThread* ?.\n"); + stderrLog("ThreadHolder::push: nullptr thread.\n"); return; } - // Stash debug info for post-unlock logging. - const char* pcThreadNameForLog = thread->getName(); - int iThreadIdForLog = -1; - bool fShouldLog = false; + auto* pSphereThread = dynamic_cast(thread); + if (!pSphereThread) + { + stderrLog("ThreadHolder::push: not an AbstractSphereThread.\n"); + return; + } + + const char* nameForLog = thread->getName(); + int idForLog = -1; + bool shouldLog = false; try { - auto* pSphereThread = dynamic_cast(thread); - if (!pSphereThread) - { - stderrLog("ThreadHolder::push: AbstractThread is not an AbstractSphereThread.\n"); - return; - } + std::unique_lock lock(m_mutex); + // 1) Idempotency: same pointer already registered. + auto itExistingPtr = std::find_if( + m_spherethreadpairs_systemid_ptr.begin(), + m_spherethreadpairs_systemid_ptr.end(), + [pSphereThread](const spherethreadpair_t& elem) noexcept { + return elem.second == pSphereThread; + } + ); + if (itExistingPtr != m_spherethreadpairs_systemid_ptr.end()) { - std::unique_lock lock(m_mutex); + // Refresh holder id from current slot if present. + auto itSlot = std::find_if( + m_threads.begin(), + m_threads.end(), + [thread](const SphereThreadData& s) noexcept { return s.m_ptr == thread; } + ); + if (itSlot != m_threads.end()) + thread->m_threadHolderId = static_cast(std::distance(m_threads.begin(), itSlot)); #ifdef _DEBUG - auto itp = std::find_if( - m_spherethreadpairs_systemid_ptr.begin(), m_spherethreadpairs_systemid_ptr.end(), - [pSphereThread](spherethreadpair_t const &elem) noexcept -> bool { - return (elem.second == pSphereThread); - }); - DEBUG_ASSERT(itp == m_spherethreadpairs_systemid_ptr.end()); + idForLog = thread->m_threadHolderId; + shouldLog = true; #endif + lock.unlock(); +#ifdef _DEBUG + if (shouldLog) + g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, + "ThreadHolder already registered: %s (ThreadHolder-ID %d).\n", + nameForLog, idForLog); +#endif + return; + } - m_threads.emplace_back(SphereThreadData{.m_ptr = thread, .m_closed = false }); - thread->m_threadHolderId = m_threadCount; + // 2) System-id uniqueness: refuse a second Sphere context on the same OS thread. + auto itExistingSys = std::find_if( + m_spherethreadpairs_systemid_ptr.begin(), + m_spherethreadpairs_systemid_ptr.end(), + [pSphereThread](const spherethreadpair_t& elem) noexcept { + // Correct equality for pthread_t vs DWORD handled by threadid_t typedef. + return elem.first == pSphereThread->m_threadSystemId; + } + ); + if (itExistingSys != m_spherethreadpairs_systemid_ptr.end()) + { + // Another object is already attached to this OS thread; refuse the second. + auto* other = itExistingSys->second; + lock.unlock(); + g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, + "ThreadHolder: refusing to register %s on OS thread already owned by %s.\n", + nameForLog, other ? other->getName() : ""); + EXC_NOTIFY_DEBUGGER; + return; + } - m_spherethreadpairs_systemid_ptr.emplace_back(pSphereThread->m_threadSystemId, pSphereThread); - ++m_threadCount; + // 3) New registration. + m_threads.emplace_back(SphereThreadData{ thread, false }); + thread->m_threadHolderId = m_threadCount; + m_spherethreadpairs_systemid_ptr.emplace_back(pSphereThread->m_threadSystemId, pSphereThread); + ++m_threadCount; - iThreadIdForLog = thread->m_threadHolderId; - fShouldLog = true; - } +#ifdef _DEBUG + idForLog = thread->m_threadHolderId; + shouldLog = true; +#endif + lock.unlock(); #ifdef _DEBUG - if (fShouldLog) - { - // Logging AFTER releasing the lock avoids re-entrancy into ThreadHolder. - g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, - "ThreadHolder: registered '%s' (ThreadHolder-ID %d).\n", pcThreadNameForLog, iThreadIdForLog); - } + if (shouldLog) + g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, + "ThreadHolder registered %s (ThreadHolder-ID %d).\n", + nameForLog, idForLog); #endif } catch (const std::exception& e) { - stderrLog("ThreadHolder::push: exception: '%s'.\n", e.what()); + stderrLog("ThreadHolder::push: exception: %s.\n", e.what()); } catch (...) { @@ -164,48 +226,47 @@ void ThreadHolder::push(AbstractThread *thread) noexcept } } -void ThreadHolder::remove(AbstractThread *pAbstractThread) CANTHROW + +void ThreadHolder::remove(AbstractThread* pAbstractThread) CANTHROW { if (!pAbstractThread) throw CSError(LOGL_FATAL, 0, "ThreadHolder::remove: thread == nullptr"); - const char* pcThreadNameForLog = pAbstractThread->getName(); - int iThreadIdForLog = pAbstractThread->m_threadHolderId; - bool fShouldLog = false; + AbstractSphereThread* pSphereThread = dynamic_cast(pAbstractThread); + threadid_t sysId = pSphereThread ? pSphereThread->m_threadSystemId : threadid_t{}; + const char* nm = pAbstractThread->getName(); - { - std::unique_lock lock(m_mutex); + std::unique_lock lock(m_mutex); - auto* pSphereThread = dynamic_cast(pAbstractThread); - auto itp = std::find_if( - m_spherethreadpairs_systemid_ptr.begin(), m_spherethreadpairs_systemid_ptr.end(), - [pSphereThread](spherethreadpair_t const &elem) noexcept -> bool { - return (elem.second == pSphereThread); - }); - if (itp != m_spherethreadpairs_systemid_ptr.end()) - m_spherethreadpairs_systemid_ptr.erase(itp); - - auto itd = std::find_if(m_threads.begin(), m_threads.end(), - [pAbstractThread](SphereThreadData const& elem) { return elem.m_ptr == pAbstractThread; }); - if (itd != m_threads.end()) - m_threads.erase(itd); - - if (m_threadCount > 0) - --m_threadCount; - - pAbstractThread->m_threadHolderId = m_kiInvalidThreadID; - fShouldLog = true; - } + // Remove from main list. + auto it = std::find_if(m_threads.begin(), m_threads.end(), + [pAbstractThread](const SphereThreadData& s) noexcept { return s.m_ptr == pAbstractThread; }); + if (it != m_threads.end()) + m_threads.erase(it); -#ifdef _DEBUG - if (fShouldLog) + // Remove from sys-id map. + for (auto it2 = m_spherethreadpairs_systemid_ptr.begin(); it2 != m_spherethreadpairs_systemid_ptr.end(); ) { - g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, - "ThreadHolder: removed '%s' (former ThreadHolder-ID %d).\n", pcThreadNameForLog, iThreadIdForLog); + if (it2->second == pSphereThread) + it2 = m_spherethreadpairs_systemid_ptr.erase(it2); + else + ++it2; } + + // Mark as no longer registered to aid destructor diagnostics. + pAbstractThread->m_threadHolderId = ThreadHolder::m_kiInvalidThreadID; + + lock.unlock(); + +#ifdef _DEBUG + g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, + "ThreadHolder removed %s with sys-id %llu.\n", + nm, (unsigned long long)sysId); #endif } + + void ThreadHolder::markThreadsClosing() CANTHROW { // Flip fast-path flag first so concurrent readers immediately see “servClosing”. @@ -238,7 +299,7 @@ AbstractThread * ThreadHolder::getThreadAt(size_t at) noexcept AbstractThread * ThreadHolder::current() noexcept // static { // TLS fast path: the absolutely hottest path in the system. - AbstractSphereThread* tls = g_tlsCurrentSphereThread; + AbstractSphereThread* tls = sg_tlsCurrentSphereThread; if (tls != nullptr) [[likely]] return tls; @@ -290,6 +351,97 @@ bool ThreadHolder::isServClosing() noexcept { // static int AbstractThread::m_threadsAvailable = 0; +static inline bool is_same_thread_id(threadid_t firstId, threadid_t secondId) noexcept +{ +#if defined(_WIN32) || defined(__APPLE__) + return (firstId == secondId); +#else + return pthread_equal(firstId, secondId); +#endif +} + +static uint64_t os_current_tid() noexcept // Equivalent to old getCurrentThreadSystemId() +{ +#if defined(_WIN32) + return static_cast(::GetCurrentThreadId()); +#elif defined(__APPLE__) + uint64_t tid = 0; + (void)pthread_threadid_np(nullptr, &tid); + return tid; +#elif defined(__linux__) + // gettid is the kernel TID seen by system tools + return static_cast(::syscall(SYS_gettid)); +#elif defined(__FreeBSD__) // TODO: does it work also for other BSD systems? + return static_cast(::pthread_getthreadid_np()); +#else + // Last-resort fallback: make a printable token from pthread_self() + return static_cast(reinterpret_cast(::pthread_self())); +#endif +} + +static void os_set_thread_name_portable(const char* name) noexcept +{ +#if defined(_WIN32) + // Prefer SetThreadDescription (Windows 10+) if available + using SetThreadDescription_t = HRESULT (WINAPI *)(HANDLE, PCWSTR); + static SetThreadDescription_t pSetThreadDescription = + reinterpret_cast( + GetProcAddress(GetModuleHandleW(L"Kernel32.dll"), "SetThreadDescription")); + if (pSetThreadDescription) + { + wchar_t wname[64]; + ::MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, static_cast(std::size(wname))); + pSetThreadDescription(GetCurrentThread(), wname); + } + else + { +# if defined(_MSC_VER) +# pragma pack(push, 8) + typedef struct tagTHREADNAME_INFO { + DWORD dwType; // 0x1000 + LPCSTR szName; + DWORD dwThreadID; // -1 => current thread + DWORD dwFlags; + } THREADNAME_INFO; +# pragma pack(pop) + + static constexpr DWORD MS_VC_EXCEPTION = 0x406D1388; + THREADNAME_INFO info{}; + info.dwType = 0x1000; + info.szName = name_trimmed; + info.dwThreadID = static_cast(-1); // CRITICAL: current thread + info.dwFlags = 0; + + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } __except(EXCEPTION_EXECUTE_HANDLER) { + } +# else + stderrLog("WARN: no available implementation to set the thread name.\n"); +# endif + } +#elif defined(__APPLE__) + // macOS: pthread_setname_np only sets the current thread, 64-char limit + (void)pthread_setname_np(name); +#elif defined(__linux__) +// Linux: prctl(PR_SET_NAME) sets current thread name, 16-byte limit incl. NUL +# if !defined(_BSD) && !defined(__APPLE__) + ::prctl(PR_SET_NAME, name, 0, 0, 0); +# endif +// Also attempt pthread_setname_np where available for consistency +# if defined(__GLIBC__) || defined(__ANDROID__) + (void)pthread_setname_np(pthread_self(), name); +# endif +#elif defined(__FreeBSD__) + (void)pthread_set_name_np(pthread_self(), name); +#else + // TODO: support other BSD systems + // No-op on unknown platforms + (void)name; + stderrLog("WARN: no available implementation to set the thread name for the current platform (unknown/unsupported).\n"); +#endif +} + AbstractThread::AbstractThread(const char *name, ThreadPriority priority) { if (AbstractThread::m_threadsAvailable == 0) @@ -302,7 +454,7 @@ AbstractThread::AbstractThread(const char *name, ThreadPriority priority) ++AbstractThread::m_threadsAvailable; Str_CopyLimitNull(m_name, name, sizeof(m_name)); - m_fThread_selfTerminateAfterThisTick = true; + m_fThreadSelfTerminateAfterThisTick = true; m_fTerminateRequested = true; setPriority(priority); @@ -314,40 +466,53 @@ AbstractThread::AbstractThread(const char *name, ThreadPriority priority) AbstractThread::~AbstractThread() { #ifdef _DEBUG - lpctstr ptcState; - if (m_threadHolderId == ThreadHolder::m_kiInvalidThreadID) { + // Resolve a safe thread name for diagnostics. + const char* name = getName(); + if (!name || !name[0]) + name = "(unnamed)"; + + // Derive a readable lifecycle state. + const char* state = ""; + if (m_threadHolderId == ThreadHolder::m_kiInvalidThreadID) + { if (m_uiState == eRunningState::Closing) - ptcState = "[previously registered]"; + state = "[detached/closing]"; else if (m_uiState == eRunningState::NeverStarted) - ptcState = "[unregistered, never started]"; + state = "[unregistered, never started]"; else - stderrLog("~AbstractThread: Thread never registered in ThreadHolder and still running at this stage?\n"); + state = "[unregistered]"; } else { if (m_uiState == eRunningState::NeverStarted) - stderrLog("~AbstractThread: Thread registered in ThreadHolder and never started?\n"); + state = "[registered, never started]"; else if (m_uiState == eRunningState::Running) - stderrLog("~AbstractThread: Thread registered in ThreadHolder and still running at this stage?\n"); + state = "[registered, still running]"; + else + state = "[registered, closing]"; } - fprintf(stdout, "DEBUG: Destroying thread '%s' (ThreadHolder-ID %d)%s, sys-id %" PRIu64 ".\n", - getName(), m_threadHolderId, - ptcState, - (uint64)getId()); + + // Print the OS thread id captured at onStart(); may be 0 if never registered. + const uint64_t tid64 = static_cast(m_threadSystemId); + + fprintf(stdout, + "DEBUG: Destroying thread '%s' (ThreadHolder-ID %d) %s, sys-id %" PRIu64 ".\n", + name, m_threadHolderId, state, tid64); fflush(stdout); #endif + // Final teardown. terminate(true); --AbstractThread::m_threadsAvailable; - if (AbstractThread::m_threadsAvailable == 0) - { #ifdef _WIN32 + if (AbstractThread::m_threadsAvailable == 0) CoUninitialize(); #endif - } } + + void AbstractThread::overwriteInternalThreadName(const char* name) noexcept { Str_CopyLimitNull(m_name, name, sizeof(m_name)); @@ -427,7 +592,7 @@ void AbstractThread::run() int exceptions = 0; bool lastWasException = false; - m_fThread_selfTerminateAfterThisTick = false; + m_fThreadSelfTerminateAfterThisTick = false; m_fTerminateRequested = false; setThreadName(getName()); @@ -574,30 +739,43 @@ void AbstractThread::awaken() bool AbstractThread::isCurrentThread() const noexcept { - return isSameThreadId(getId(), getCurrentThreadSystemId()); + return is_same_thread_id(getId(), os_current_tid()); } void AbstractThread::onStart() { - m_threadSystemId = getCurrentThreadSystemId(); + // Mark registration window so diagnostics don’t warn while TLS is not yet published. + sg_tlsBindingInProgress = true; + // Capture OS thread id first. + m_threadSystemId = os_current_tid(); + + // Try to register in the holder. ThreadHolder& th = ThreadHolder::get(); th.push(this); - th.markThreadStarted(this); -#ifdef THREAD_TRACK_CALLSTACK - g_tlsCurrentSphereThread = dynamic_cast(this); -#else - g_tlsCurrentSphereThread = dynamic_cast(this); -#endif + // If push was refused (e.g., this OS thread already owned), leave clean. + if (m_threadHolderId == ThreadHolder::m_kiInvalidThreadID) + { + m_threadSystemId = 0; // rollback sys-id stamp + return; // nothing else to do + } + + // Mark started and publish TLS fast-path. + th.markThreadStarted(this); + sg_tlsCurrentSphereThread = dynamic_cast(this); #ifdef _DEBUG - g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, + g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, "Started thread loop for '%s' (ThreadHolder-ID %d), sys-id %" PRIu64 ".\n", getName(), m_threadHolderId, (uint64)m_threadSystemId); #endif + + // End of the small registration window. + sg_tlsBindingInProgress = false; } + void AbstractThread::setPriority(ThreadPriority pri) { ASSERT(((pri >= ThreadPriority::Idle) && (pri <= ThreadPriority::RealTime)) || (pri == ThreadPriority::Disabled)); @@ -619,7 +797,7 @@ bool AbstractThread::shouldExit() noexcept { return (m_uiState == eRunningState::Closing) || m_fTerminateRequested.load(std::memory_order_relaxed) - || m_fThread_selfTerminateAfterThisTick; + || m_fThreadSelfTerminateAfterThisTick; } void AbstractThread::setThreadName(const char* name) @@ -627,38 +805,7 @@ void AbstractThread::setThreadName(const char* name) char name_trimmed[m_nameMaxLength] = {'\0'}; Str_CopyLimitNull(name_trimmed, name, m_nameMaxLength); -#if defined(_WIN32) -#if defined(_MSC_VER) -#pragma pack(push, 8) - typedef struct tagTHREADNAME_INFO { - DWORD dwType; // 0x1000 - LPCSTR szName; - DWORD dwThreadID; // -1 => current thread - DWORD dwFlags; - } THREADNAME_INFO; -#pragma pack(pop) - - static constexpr DWORD MS_VC_EXCEPTION = 0x406D1388; - THREADNAME_INFO info{}; - info.dwType = 0x1000; - info.szName = name_trimmed; - info.dwThreadID = static_cast(-1); // CRITICAL: current thread - info.dwFlags = 0; - - __try { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); - } __except(EXCEPTION_EXECUTE_HANDLER) { - } -#endif -#elif defined(__APPLE__) - pthread_setname_np(name_trimmed); -#elif !defined(_BSD) - prctl(PR_SET_NAME, name_trimmed, 0, 0, 0); -#elif defined(__FreeBSD__) || defined(__OpenBSD__) - pthread_set_name_np(pthread_self(), name_trimmed); -#elif defined(__NetBSD__) - pthread_setname_np(pthread_self(), "%s", name_trimmed); -#endif + os_set_thread_name_portable(name_trimmed); // Update internal name only if a context exists if (auto* cur = ThreadHolder::current()) @@ -669,22 +816,40 @@ void AbstractThread::setThreadName(const char* name) void AbstractThread::attachToCurrentThread(const char* osThreadName) noexcept { lpctstr ptcThreadName = (osThreadName && osThreadName[0]) ? osThreadName : getName(); - g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, + + // Refuse if another Sphere context already owns this OS thread. + if (sg_tlsCurrentSphereThread && sg_tlsCurrentSphereThread != this) + { + g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, + "attachToCurrentThread refused: current OS thread already bound to '%s'; wanted '%s'.\n", + sg_tlsCurrentSphereThread->getName(), ptcThreadName); + return; + } + + g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, "Binding current context to thread '%s'...\n", ptcThreadName); + + // Attempt to bind; onStart will self-guard and leave this object clean on refusal. onStart(); - setThreadName(ptcThreadName); + + // Only set the OS-visible name if registration succeeded. + if (m_threadHolderId != ThreadHolder::m_kiInvalidThreadID) + setThreadName(ptcThreadName); } + void AbstractThread::detachFromCurrentThread() noexcept { if (auto* s = dynamic_cast(this)) - if (g_tlsCurrentSphereThread == s) - g_tlsCurrentSphereThread = nullptr; + if (sg_tlsCurrentSphereThread == s) + sg_tlsCurrentSphereThread = nullptr; ThreadHolder::get().remove(this); m_threadSystemId = 0; + m_threadHolderId = ThreadHolder::m_kiInvalidThreadID; // make diagnostics unambiguous } + // ----- AbstractSphereThread ----- AbstractSphereThread::AbstractSphereThread(const char *name, ThreadPriority priority) @@ -700,8 +865,8 @@ AbstractSphereThread::~AbstractSphereThread() { m_uiState = eRunningState::Closing; - if (g_tlsCurrentSphereThread == this) - g_tlsCurrentSphereThread = nullptr; + if (sg_tlsCurrentSphereThread == this) + sg_tlsCurrentSphereThread = nullptr; } #ifdef THREAD_TRACK_CALLSTACK @@ -766,7 +931,7 @@ void AbstractSphereThread::printStackTrace() noexcept #if defined(_WIN32) || defined(__APPLE__) static_cast(getId() ? getId() : getCurrentThreadSystemId()); #else - reinterpret_cast(getId() ? getId() : getCurrentThreadSystemId()); + reinterpret_cast(getId() ? getId() : os_current_tid()); #endif const lpctstr threadName = getName(); @@ -898,17 +1063,21 @@ StackDebugInformation::StackDebugInformation(const char *name) noexcept : m_context(nullptr) { // Fast path: TLS - if (g_tlsCurrentSphereThread && !g_tlsCurrentSphereThread->isClosing()) + if (sg_tlsCurrentSphereThread && !sg_tlsCurrentSphereThread->isClosing()) { - m_context = g_tlsCurrentSphereThread; + m_context = sg_tlsCurrentSphereThread; m_context->pushStackCall(name); return; } - // Fallback: use current() (still lock-free and fast) + // If a thread is in the middle of binding, don’t warn about missing context. + if (sg_tlsBindingInProgress) + return; + + // Fallback: use current() AbstractThread *icontext = ThreadHolder::current(); if (!icontext) - return; // TODO: log this? + return; m_context = dynamic_cast(icontext); if (m_context && !m_context->isClosing()) diff --git a/src/sphere/threads.h b/src/sphere/threads.h index 34402f458..37531c0ec 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -23,10 +23,6 @@ class TemporaryString; class AutoResetEvent; class ManualResetEvent; -// Forward decl for TLS pointer to speed up current() hot path. -class AbstractSphereThread; -extern thread_local AbstractSphereThread* g_tlsCurrentSphereThread; - /* * Platform types for OS thread identity and entry points. */ @@ -80,7 +76,7 @@ class AbstractThread bool m_fKeepAliveAtShutdown{false}; std::atomic_bool m_fTerminateRequested{false}; - std::atomic_bool m_fThread_selfTerminateAfterThisTick{false}; + std::atomic_bool m_fThreadSelfTerminateAfterThisTick{false}; enum class eRunningState : uchar { NeverStarted, @@ -141,33 +137,6 @@ class AbstractThread public: static void setThreadName(const char* name); - static inline threadid_t getCurrentThreadSystemId() noexcept - { -#if defined(_WIN32) - return ::GetCurrentThreadId(); -#elif defined(__APPLE__) - uint64_t tid = 0; - pthread_threadid_np(pthread_self(), &tid); - return tid; -#else - return pthread_self(); -#endif - } - - static inline bool isSameThreadId(threadid_t firstId, threadid_t secondId) noexcept - { -#if defined(_WIN32) || defined(__APPLE__) - return (firstId == secondId); -#else - return pthread_equal(firstId, secondId); -#endif - } - - inline bool isSameThread(threadid_t otherThreadId) const noexcept - { - return isSameThreadId(getCurrentThreadSystemId(), otherThreadId); - } - // Inline binding helper: attach/detach without creating a new OS thread. class ThreadBindingScope { @@ -326,6 +295,10 @@ class ThreadHolder // Helper to mark servClosing and set flags. void markThreadsClosing() CANTHROW; + +private: + bool isSystemIdRegistered(threadid_t sysId, AbstractSphereThread** outExisting) const noexcept; + }; #ifdef THREAD_TRACK_CALLSTACK From 9d0bea24600d33a7e6e402e5c0942b281a2946f5 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 23 Oct 2025 19:35:36 +0200 Subject: [PATCH 097/112] Fixed: CScriptTriggerArgs pool exhausted too soon by CResourceScript not releasing instance to the pool --- src/common/CScript.cpp | 21 ++++++++++++++++----- src/common/CScript.h | 1 + src/common/CScriptParserBufs.cpp | 4 ++++ src/common/resource/CResourceScript.cpp | 17 ++++++++++++++++- src/common/resource/CResourceScript.h | 16 +++------------- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/common/CScript.cpp b/src/common/CScript.cpp index ed38bb0c7..8224bce78 100644 --- a/src/common/CScript.cpp +++ b/src/common/CScript.cpp @@ -306,7 +306,8 @@ tchar * CScriptKeyAlloc::_GetKeyBufferRaw() //ADDTOCALLSTACK_DEBUG("CScriptKeyAlloc::_GetKeyBufferRaw"); // iLen = length of the string we want to hold. - m_TextBuf = CScriptParserBufs::GetScriptKeyArgBufPtr(); + if (!m_TextBuf) + m_TextBuf = CScriptParserBufs::GetScriptKeyArgBufPtr(); m_pszKey = m_pszArg = GetKeyBuffer(); m_pszKey[0] = '\0'; @@ -320,6 +321,11 @@ tchar * CScriptKeyAlloc::GetKeyBufferRaw() } */ +void CScriptKeyAlloc::_FreeKeyBuffer() +{ + m_TextBuf.reset(); +} + tchar * CScriptKeyAlloc::GetKeyBuffer() { // Get the buffer the key is in. @@ -795,12 +801,16 @@ void CScript::_Close() ADDTOCALLSTACK("CScript::_Close"); // EndSection(); CCacheableScriptFile::_Close(); + + // If i don't have to use this to read/parse stuff anymore (example: after i have cached a script file), just free the text buffer. + // It will be created again if necessary by _GetKeyBufferRaw(). + m_TextBuf.reset(); } void CScript::Close() { ADDTOCALLSTACK("CScript::Close"); - // EndSection(); - CCacheableScriptFile::Close(); + MT_UNIQUE_LOCK_SET(this); + CScript::_Close(); } void CScript::CloseForce() @@ -827,7 +837,7 @@ CScriptLineContext CScript::_GetContext() const LineContext.m_iOffset = _GetPosition(); return LineContext; */ - return {m_iLineNum, _GetPosition()}; + return {_GetPosition(), m_iLineNum}; } CScriptLineContext CScript::GetContext() const @@ -839,7 +849,8 @@ CScriptLineContext CScript::GetContext() const LineContext.m_iOffset = _GetPosition(); return LineContext; */ - return {m_iLineNum, _GetPosition()}; + auto ret = CScriptLineContext(_GetPosition(), m_iLineNum); + return ret; } bool CScript::WriteSection( lpctstr ptcSection, ... ) diff --git a/src/common/CScript.h b/src/common/CScript.h index c8c2df484..1c2928404 100644 --- a/src/common/CScript.h +++ b/src/common/CScript.h @@ -84,6 +84,7 @@ class CScriptKeyAlloc : public CScriptKey protected: tchar * _GetKeyBufferRaw(); //tchar * GetKeyBufferRaw(); + void _FreeKeyBuffer(); size_t ParseKeyEnd(); public: diff --git a/src/common/CScriptParserBufs.cpp b/src/common/CScriptParserBufs.cpp index 402d4f207..6bd4bd5a1 100644 --- a/src/common/CScriptParserBufs.cpp +++ b/src/common/CScriptParserBufs.cpp @@ -11,9 +11,11 @@ auto CScriptParserBufs::GetCScriptTriggerArgsPtr() -> CScriptTriggerArgsPtr if (!pool.isFromPool(ptr)) { static_assert(CScriptParserBufs::sm_allow_fallback_objects); +#ifdef _DEBUG g_Log.EventDebug( "Requesting CScriptTriggerArgs from an exhausted pool (max size: %" PRIu32 "). Alive new heap-allocated fallback objects: %" PRIu32 ".\n", pool.sm_pool_size, pool.getFallbackCount()); +#endif } ptr->Clear(); @@ -27,9 +29,11 @@ auto CScriptParserBufs::GetScriptKeyArgBufPtr() -> CScriptKeyArgBufPtr if (!pool.isFromPool(ptr)) { static_assert(CScriptParserBufs::sm_allow_fallback_objects); +#ifdef _DEBUG g_Log.EventDebug( "Requesting CScriptKeyArgBuf from an exhausted pool (max size: %" PRIu32 "). Alive new heap-allocated fallback objects: %" PRIu32 ".\n", pool.sm_pool_size, pool.getFallbackCount()); +#endif } //ptr.get()->fill('\0'); diff --git a/src/common/resource/CResourceScript.cpp b/src/common/resource/CResourceScript.cpp index 93833d500..c0db21cd1 100644 --- a/src/common/resource/CResourceScript.cpp +++ b/src/common/resource/CResourceScript.cpp @@ -10,6 +10,16 @@ #include "../CLog.h" #include "CResourceScript.h" +CResourceScript::CResourceScript(lpctstr pszFileName) // explicit +{ + _Init(); + _SetFilePath(pszFileName); +} + +CResourceScript::CResourceScript() +{ + _Init(); +} bool CResourceScript::_CheckForChange() { @@ -47,6 +57,11 @@ bool CResourceScript::CheckForChange() MT_UNIQUE_LOCK_RETURN(this, CResourceScript::_CheckForChange()); } +bool CResourceScript::IsFirstCheck() const noexcept +{ + return (m_dwSize == UINT32_MAX && !m_dateChange.IsTimeValid()); +} + void CResourceScript::ReSync() { ADDTOCALLSTACK("CResourceScript::ReSync"); @@ -99,8 +114,8 @@ void CResourceScript::Close() // Close it later when we know it has not been used for a bit. if ( ! IsFileOpen()) return; - --m_iOpenCount; + -- m_iOpenCount; if ( ! m_iOpenCount ) { // Just leave it open for caching purposes diff --git a/src/common/resource/CResourceScript.h b/src/common/resource/CResourceScript.h index 9fa4da247..b1243e153 100644 --- a/src/common/resource/CResourceScript.h +++ b/src/common/resource/CResourceScript.h @@ -31,15 +31,8 @@ class CResourceScript : public CScript public: static const char *m_sClassName; - explicit CResourceScript(lpctstr pszFileName) - { - _Init(); - _SetFilePath(pszFileName); - } - CResourceScript() - { - _Init(); - } + explicit CResourceScript(lpctstr pszFileName); + CResourceScript(); virtual ~CResourceScript() = default; private: bool _CheckForChange(); @@ -49,10 +42,7 @@ public: bool CheckForChange(); CResourceScript(const CResourceScript& copy) = delete; CResourceScript& operator=(const CResourceScript& other) = delete; - bool IsFirstCheck() const noexcept - { - return (m_dwSize == UINT32_MAX && !m_dateChange.IsTimeValid()); - } + bool IsFirstCheck() const noexcept; void ReSync(); virtual bool Open( lpctstr pszFilename = nullptr, uint wFlags = OF_READ ) override; virtual void Close() override; From 50bde77cda064db3ef74277c3984d0c5515cf862 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 23 Oct 2025 19:36:41 +0200 Subject: [PATCH 098/112] Minimal threading refactoring --- src/common/CPointBase.h | 2 +- src/common/sphere_library/CSTime.h | 2 +- src/common/sphere_library/sstring.cpp | 8 +- src/network/CNetworkManager.cpp | 6 +- src/network/CNetworkThread.cpp | 7 +- src/sphere/GlobalInitializer.h | 1 + src/sphere/ntwindow.cpp | 2 +- src/sphere/threads.cpp | 144 +++++++++++++------------- src/sphere/threads.h | 19 ++-- 9 files changed, 99 insertions(+), 92 deletions(-) diff --git a/src/common/CPointBase.h b/src/common/CPointBase.h index fa8281107..530936768 100644 --- a/src/common/CPointBase.h +++ b/src/common/CPointBase.h @@ -23,7 +23,7 @@ DIR_TYPE GetDirTurn( DIR_TYPE dir, int offset ); struct CPointBase // Non initialized 3d point. { private: - friend class GlobalInitializer; + friend struct GlobalInitializer; static void InitRuntimeDefaultValues(); public: diff --git a/src/common/sphere_library/CSTime.h b/src/common/sphere_library/CSTime.h index ab332b240..aa02cc8a4 100644 --- a/src/common/sphere_library/CSTime.h +++ b/src/common/sphere_library/CSTime.h @@ -28,7 +28,7 @@ class CSTime #undef GetCurrentTime // Set once at server startup; used for Windows high-resolution timer - friend class GlobalInitializer; + friend struct GlobalInitializer; static llong _kllTimeProfileFrequency; #endif diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index dbd9081e4..efdf3b050 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -489,22 +489,22 @@ tchar* Str_FromULL_Fast (ullong val, tchar* buf, size_t buf_length, uint base) n void Str_FromI(int val, tchar* buf, size_t buf_length, uint base) noexcept { - Str_FromI_Fast(val, buf, buf_length, base); + (void) Str_FromI_Fast(val, buf, buf_length, base); } void Str_FromUI(uint val, tchar* buf, size_t buf_length, uint base) noexcept { - Str_FromUI_Fast(val, buf, buf_length, base); + (void) Str_FromUI_Fast(val, buf, buf_length, base); } void Str_FromLL(llong val, tchar* buf, size_t buf_length, uint base) noexcept { - Str_FromLL_Fast(val, buf, buf_length, base); + (void) Str_FromLL_Fast(val, buf, buf_length, base); } void Str_FromULL(ullong val, tchar* buf, size_t buf_length, uint base) noexcept { - Str_FromULL_Fast(val, buf, buf_length, base); + (void) Str_FromULL_Fast(val, buf, buf_length, base); } diff --git a/src/network/CNetworkManager.cpp b/src/network/CNetworkManager.cpp index c8908cdea..aee45b5af 100644 --- a/src/network/CNetworkManager.cpp +++ b/src/network/CNetworkManager.cpp @@ -369,7 +369,7 @@ void CNetworkManager::start(void) m_isThreaded = g_Cfg._uiNetworkThreads > 0; if (isThreaded()) { - // start network threads + // Start/spawn network threads for (NetworkThreadList::iterator it = m_threads.begin(), end = m_threads.end(); it != end; ++it) { // The thread structure (class) was created via createNetworkThreads, now spawn a new thread and do the work inside there. @@ -382,7 +382,9 @@ void CNetworkManager::start(void) } else { - // initialise network threads (if g_Cfg._uiNetworkThreads is == 0 then we'll have only 1 CNetworkThread) + // In the non-threaded configuration, T_Net #0 is just a worker object; its init()/tick()/processInput()/processOutput() run on the T_Main thread. + + // Initialise network threads (if g_Cfg._uiNetworkThreads is == 0 then we'll have only 1 CNetworkThread) size_t ntCount = m_threads.size(); UnreferencedParameter(ntCount); for (NetworkThreadList::iterator it = m_threads.begin(), end = m_threads.end(); it != end; ++it) diff --git a/src/network/CNetworkThread.cpp b/src/network/CNetworkThread.cpp index 275614b97..f7e9ca87f 100644 --- a/src/network/CNetworkThread.cpp +++ b/src/network/CNetworkThread.cpp @@ -16,6 +16,9 @@ static const char* GenerateNetworkThreadName(size_t id) return name; } +// A CNetworkThread is network worker abstraction that owns client state lists, queues, and APIs +// (processInput/processOutput/flush/assignNetworkState), so the manager can use the same code path in both modes; +// in non-threaded mode the object runs inline on T_Main, and in threaded mode the exact same object is started with its own OS thread. CNetworkThread::CNetworkThread(CNetworkManager* manager, size_t id) : AbstractSphereThread(GenerateNetworkThreadName(id), ThreadPriority::Disabled), @@ -129,9 +132,9 @@ void CNetworkThread::tick(void) // we're active, take priority setPriority(static_cast(g_Cfg._iNetworkThreadPriority)); - static constexpr int64 kiStateDataCheckPeriod = 10 * 1000; // 10 seconds, expressed in milliseconds + static constexpr int64 kiStateDataCheckPeriodMilli = 10 * 1000; // 10 seconds, expressed in milliseconds const int64 iTimeCur = CSTime::GetMonotonicSysTimeMilli(); - if (iTimeCur - _iTimeLastStateDataCheck > kiStateDataCheckPeriod) + if (iTimeCur - _iTimeLastStateDataCheck > kiStateDataCheckPeriodMilli) { _iTimeLastStateDataCheck = iTimeCur; diff --git a/src/sphere/GlobalInitializer.h b/src/sphere/GlobalInitializer.h index e845d6b83..cff3ab89c 100644 --- a/src/sphere/GlobalInitializer.h +++ b/src/sphere/GlobalInitializer.h @@ -2,6 +2,7 @@ struct GlobalInitializer { GlobalInitializer(); ~GlobalInitializer() = default; + static void InitRuntimeDefaultValues(); static void PeriodicSyncTimeConstants(); }; diff --git a/src/sphere/ntwindow.cpp b/src/sphere/ntwindow.cpp index 7fc33885e..3d90997bf 100644 --- a/src/sphere/ntwindow.cpp +++ b/src/sphere/ntwindow.cpp @@ -166,7 +166,7 @@ BOOL CNTWindow::CStatusDlg::DefDialogProc( UINT message, WPARAM wParam, LPARAM l CNTWindow::CNTWindow() : AbstractSphereThread("T_ConsoleWindow", ThreadPriority::Highest), _NTWInitParams{}, m_zCommands {{}} { - _fKeepAliveAtShutdown = true; + m_fKeepAliveAtShutdown = true; m_iLogTextLen = 0; m_fLogScrollLock = false; diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index 5f9789112..1d9fa69e0 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -45,10 +45,13 @@ static thread_local AbstractSphereThread* sg_tlsCurrentSphereThread = nullptr; // Avoid trying to get thread context while binding it. static thread_local bool sg_tlsBindingInProgress = false; +// Global state flags +static std::atomic_bool sg_inStartup{true}; +static std::atomic_bool sg_servClosing{false}; // Constants #define THREAD_EXCEPTIONS_ALLOWED 10 -#define THREADJOIN_TIMEOUT 60000 +#define THREAD_JOIN_TIMEOUT 60000 #define THREAD_TEMPSTRING_C_STORAGE 2048 #define THREAD_TEMPSTRING_OBJ_STORAGE 1024 @@ -64,10 +67,14 @@ struct TemporaryStringsThreadSafeStateHolder std::unique_ptr g_tmpCStrings; // TemporaryString buffers with “in-use” flags - SimpleMutex g_tmpTemporaryStringMutex; - std::atomic g_tmpTemporaryStringIndex; - struct TemporaryStringStorage { char m_buffer[THREAD_STRING_LENGTH]; char m_state; }; - std::unique_ptr g_tmpTemporaryStringStorage; + SimpleMutex g_tmpTemporaryStringMutex; + std::atomic g_tmpTemporaryStringIndex; + struct TemporaryStringStorage + { + char m_buffer[THREAD_STRING_LENGTH]; + char m_state; + }; + std::unique_ptr g_tmpTemporaryStringStorage; private: TemporaryStringsThreadSafeStateHolder() @@ -234,7 +241,7 @@ void ThreadHolder::remove(AbstractThread* pAbstractThread) CANTHROW AbstractSphereThread* pSphereThread = dynamic_cast(pAbstractThread); threadid_t sysId = pSphereThread ? pSphereThread->m_threadSystemId : threadid_t{}; - const char* nm = pAbstractThread->getName(); + const char* ptcName = pAbstractThread->getName(); std::unique_lock lock(m_mutex); @@ -259,9 +266,19 @@ void ThreadHolder::remove(AbstractThread* pAbstractThread) CANTHROW lock.unlock(); #ifdef _DEBUG - g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, - "ThreadHolder removed %s with sys-id %llu.\n", - nm, (unsigned long long)sysId); + if (!isServClosing()) + { + g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, + "ThreadHolder removed %s with sys-id %" PRIu64 ".\n", + ptcName, (uint64_t)sysId); + } + else + { + // Logger may be shutting down; print directly to stdout to avoid loss. + fprintf(stdout, "DEBUG: ThreadHolder removed %s with sys-id %" PRIu64 ".\n", + ptcName, (uint64_t)sysId); + fflush(stdout); + } #endif } @@ -269,8 +286,8 @@ void ThreadHolder::remove(AbstractThread* pAbstractThread) CANTHROW void ThreadHolder::markThreadsClosing() CANTHROW { - // Flip fast-path flag first so concurrent readers immediately see “servClosing”. - ThreadHolder::markServservClosing(); + // Flip fast-path flag first so concurrent readers immediately see “server is Closing”. + ThreadHolder::markServClosing(); std::unique_lock lock(m_mutex); @@ -312,10 +329,10 @@ AbstractThread * ThreadHolder::current() noexcept // static */ // No TLS => not a Sphere thread or not yet attached. - if (sm_servClosing.load(std::memory_order_relaxed)) [[unlikely]] + if (sg_servClosing.load(std::memory_order_relaxed) == true) [[unlikely]] return nullptr; - if (sm_inStartup.load(std::memory_order_relaxed)) [[unlikely]] + if (sg_inStartup.load(std::memory_order_relaxed) == true) [[unlikely]] return DummySphereThread::getInstance(); // may be null extremely early // Runtime (Run mode) and not Closing: missing context => nullptr (by policy). @@ -334,17 +351,17 @@ void ThreadHolder::markThreadStarted(AbstractThread* pThr) CANTHROW // Record that the server entered Run mode (disable startup fallback in fast path). void ThreadHolder::markServEnteredRunMode() noexcept // static { - sm_inStartup.store(false, std::memory_order_relaxed); + sg_inStartup.store(false, std::memory_order_relaxed); } // Record that threads are servClosing (fast-path observable). -void ThreadHolder::markServservClosing() noexcept // static +void ThreadHolder::markServClosing() noexcept // static { - sm_servClosing.store(true, std::memory_order_relaxed); + sg_servClosing.store(true, std::memory_order_relaxed); } bool ThreadHolder::isServClosing() noexcept { // static - return sm_servClosing.load(std::memory_order_relaxed); + return sg_servClosing.load(std::memory_order_relaxed); } // ----- AbstractThread ----- @@ -363,7 +380,7 @@ static inline bool is_same_thread_id(threadid_t firstId, threadid_t secondId) no static uint64_t os_current_tid() noexcept // Equivalent to old getCurrentThreadSystemId() { #if defined(_WIN32) - return static_cast(::GetCurrentThreadId()); + return static_cast(::GetCurrentThreadId()); // Ret type: DWORD. #elif defined(__APPLE__) uint64_t tid = 0; (void)pthread_threadid_np(nullptr, &tid); @@ -379,7 +396,7 @@ static uint64_t os_current_tid() noexcept // Equivalent to old getCurrentThre #endif } -static void os_set_thread_name_portable(const char* name) noexcept +static void os_set_thread_name_portable(const char* name_trimmed) noexcept { #if defined(_WIN32) // Prefer SetThreadDescription (Windows 10+) if available @@ -390,7 +407,7 @@ static void os_set_thread_name_portable(const char* name) noexcept if (pSetThreadDescription) { wchar_t wname[64]; - ::MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, static_cast(std::size(wname))); + ::MultiByteToWideChar(CP_UTF8, 0, name_trimmed, -1, wname, static_cast(std::size(wname))); pSetThreadDescription(GetCurrentThread(), wname); } else @@ -422,22 +439,22 @@ static void os_set_thread_name_portable(const char* name) noexcept } #elif defined(__APPLE__) // macOS: pthread_setname_np only sets the current thread, 64-char limit - (void)pthread_setname_np(name); + (void)pthread_setname_np(name_trimmed); #elif defined(__linux__) // Linux: prctl(PR_SET_NAME) sets current thread name, 16-byte limit incl. NUL # if !defined(_BSD) && !defined(__APPLE__) - ::prctl(PR_SET_NAME, name, 0, 0, 0); + ::prctl(PR_SET_NAME, name_trimmed, 0, 0, 0); # endif // Also attempt pthread_setname_np where available for consistency # if defined(__GLIBC__) || defined(__ANDROID__) - (void)pthread_setname_np(pthread_self(), name); + (void)pthread_setname_np(pthread_self(), name_trimmed); # endif #elif defined(__FreeBSD__) - (void)pthread_set_name_np(pthread_self(), name); + (void)pthread_set_name_np(pthread_self(), name_trimmed); #else // TODO: support other BSD systems // No-op on unknown platforms - (void)name; + (void)name_trimmed; stderrLog("WARN: no available implementation to set the thread name for the current platform (unknown/unsupported).\n"); #endif } @@ -463,47 +480,33 @@ AbstractThread::AbstractThread(const char *name, ThreadPriority priority) m_terminateEvent = std::make_unique(); } +// threads.cpp — robust, flag-free destructor classification AbstractThread::~AbstractThread() { #ifdef _DEBUG - // Resolve a safe thread name for diagnostics. const char* name = getName(); - if (!name || !name[0]) - name = "(unnamed)"; + if (!name || !name[0]) name = "(unnamed)"; - // Derive a readable lifecycle state. - const char* state = ""; - if (m_threadHolderId == ThreadHolder::m_kiInvalidThreadID) - { - if (m_uiState == eRunningState::Closing) - state = "[detached/closing]"; - else if (m_uiState == eRunningState::NeverStarted) - state = "[unregistered, never started]"; - else - state = "[unregistered]"; - } - else - { - if (m_uiState == eRunningState::NeverStarted) - state = "[registered, never started]"; - else if (m_uiState == eRunningState::Running) - state = "[registered, still running]"; - else - state = "[registered, closing]"; - } + const bool stillRegistered = (m_threadHolderId != ThreadHolder::m_kiInvalidThreadID); + const bool everBound = (m_threadSystemId != 0); + const bool everRanLoop = (m_uiState != eRunningState::NeverStarted); - // Print the OS thread id captured at onStart(); may be 0 if never registered. - const uint64_t tid64 = static_cast(m_threadSystemId); + const char* state = + stillRegistered ? "[registered, closing]" : + (everBound && everRanLoop) ? "[detached, closed]" : + (everBound && !everRanLoop) ? "[attached-only, closed]" : + "[not started]"; - fprintf(stdout, - "DEBUG: Destroying thread '%s' (ThreadHolder-ID %d) %s, sys-id %" PRIu64 ".\n", - name, m_threadHolderId, state, tid64); + if (everBound) + fprintf(stdout, "DEBUG: Destroying thread '%s' (ThreadHolder-ID %d) %s, sys-id %" PRIu64 ".\n", + name, m_threadHolderId, state, (uint64_t)m_threadSystemId); + else + fprintf(stdout, "DEBUG: Destroying thread '%s' (ThreadHolder-ID %d) %s, sys-id n/a.\n", + name, m_threadHolderId, state); fflush(stdout); #endif - // Final teardown. terminate(true); - --AbstractThread::m_threadsAvailable; #ifdef _WIN32 if (AbstractThread::m_threadsAvailable == 0) @@ -511,8 +514,6 @@ AbstractThread::~AbstractThread() #endif } - - void AbstractThread::overwriteInternalThreadName(const char* name) noexcept { Str_CopyLimitNull(m_name, name, sizeof(m_name)); @@ -570,10 +571,8 @@ void AbstractThread::terminate(bool ended) } } - ThreadHolder::get().remove(this); - m_threadSystemId = 0; + detachFromCurrentThread(); m_handle = std::nullopt; - m_terminateEvent->set(); if (ended == false && wasCurrentThread) @@ -727,7 +726,7 @@ void AbstractThread::waitForClose() m_fTerminateRequested = true; awaken(); - m_terminateEvent->wait(THREADJOIN_TIMEOUT); + m_terminateEvent->wait(THREAD_JOIN_TIMEOUT); terminate(false); } } @@ -739,7 +738,10 @@ void AbstractThread::awaken() bool AbstractThread::isCurrentThread() const noexcept { - return is_same_thread_id(getId(), os_current_tid()); +#ifdef _WIN32 + static_assert(sizeof(threadid_t) == sizeof(DWORD)); +#endif + return is_same_thread_id(getId(), static_cast(os_current_tid())); } void AbstractThread::onStart() @@ -748,7 +750,7 @@ void AbstractThread::onStart() sg_tlsBindingInProgress = true; // Capture OS thread id first. - m_threadSystemId = os_current_tid(); + m_threadSystemId = static_cast(os_current_tid()); // Try to register in the holder. ThreadHolder& th = ThreadHolder::get(); @@ -757,8 +759,10 @@ void AbstractThread::onStart() // If push was refused (e.g., this OS thread already owned), leave clean. if (m_threadHolderId == ThreadHolder::m_kiInvalidThreadID) { - m_threadSystemId = 0; // rollback sys-id stamp - return; // nothing else to do + m_threadSystemId = 0; + sg_tlsBindingInProgress = false; + EXC_NOTIFY_DEBUGGER; + return; } // Mark started and publish TLS fast-path. @@ -796,7 +800,7 @@ void AbstractThread::setPriority(ThreadPriority pri) bool AbstractThread::shouldExit() noexcept { return (m_uiState == eRunningState::Closing) - || m_fTerminateRequested.load(std::memory_order_relaxed) + || m_fTerminateRequested || m_fThreadSelfTerminateAfterThisTick; } @@ -844,12 +848,12 @@ void AbstractThread::detachFromCurrentThread() noexcept if (sg_tlsCurrentSphereThread == s) sg_tlsCurrentSphereThread = nullptr; + // Keep m_threadSystemId as historical OS TID; invalidation is done via m_threadHolderId. ThreadHolder::get().remove(this); - m_threadSystemId = 0; - m_threadHolderId = ThreadHolder::m_kiInvalidThreadID; // make diagnostics unambiguous } + // ----- AbstractSphereThread ----- AbstractSphereThread::AbstractSphereThread(const char *name, ThreadPriority priority) @@ -927,9 +931,9 @@ void AbstractSphereThread::printStackTrace() noexcept { freezeCallStack(true); - uint64_t threadId = + uint64_t threadId = //static_cast(getId()); #if defined(_WIN32) || defined(__APPLE__) - static_cast(getId() ? getId() : getCurrentThreadSystemId()); + static_cast(getId() ? getId() : os_current_tid()); #else reinterpret_cast(getId() ? getId() : os_current_tid()); #endif diff --git a/src/sphere/threads.h b/src/sphere/threads.h index 37531c0ec..4b7714a81 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -70,13 +70,14 @@ class AbstractThread static constexpr uint m_nameMaxLength = 16; // OS limits for linux/bsd pthread names protected: - // TODO: move at least m_threadHolderId to AbstractSphereThread? + // TODO: move at least m_threadHolderId to AbstractSphereThread, + // since it should theoretically be used only by Sphere custom thread class/AbstractSphereThread? threadid_t m_threadSystemId{0}; // OS thread id for this object (0 => not bound) int m_threadHolderId{-1}; // logical id in ThreadHolder - bool m_fKeepAliveAtShutdown{false}; - std::atomic_bool m_fTerminateRequested{false}; - std::atomic_bool m_fThreadSelfTerminateAfterThisTick{false}; + bool m_fKeepAliveAtShutdown{false}; + bool m_fThreadSelfTerminateAfterThisTick{false}; + bool m_fTerminateRequested{false}; enum class eRunningState : uchar { NeverStarted, @@ -218,7 +219,7 @@ class AbstractSphereThread : public AbstractThread class DummySphereThread : public AbstractSphereThread { private: - friend class GlobalInitializer; + friend struct GlobalInitializer; static DummySphereThread *_instance; public: @@ -266,10 +267,6 @@ class ThreadHolder static constexpr lpctstr m_sClassName = "ThreadHolder"; static constexpr int m_kiInvalidThreadID = -1; - // Global state flags (C++17 inline, no function call needed in fast path) - inline static std::atomic_bool sm_inStartup{true}; - inline static std::atomic_bool sm_servClosing{false}; - // Singleton instance for slow-path ops static ThreadHolder& get() noexcept; @@ -280,7 +277,7 @@ class ThreadHolder static void markServEnteredRunMode() noexcept; // Record that threads are servClosing (fast-path observable). - static void markServservClosing() noexcept; + static void markServClosing() noexcept; static bool isServClosing() noexcept; @@ -327,7 +324,7 @@ class StackDebugInformation #define ADDTOCALLSTACK_DEBUG(_function_) (void)0 #endif -#else +#else // ! THREAD_TRACK_CALLSTACK #define ADDTOCALLSTACK(_function_) (void)0 #define ADDTOCALLSTACK_DEBUG(_function_) (void)0 From 38331de949870e3789a67af33daf636afdb3ea15 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 24 Oct 2025 07:51:09 +0200 Subject: [PATCH 099/112] Fixed compilation warning/error for non-debug builds --- src/sphere/threads.cpp | 43 +++++++++++++++++++++++------------------- src/sphere/threads.h | 2 +- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index 1d9fa69e0..b8a452834 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -127,24 +127,26 @@ bool ThreadHolder::isSystemIdRegistered(threadid_t sysId, AbstractSphereThread** return true; } -void ThreadHolder::push(AbstractThread* thread) noexcept +void ThreadHolder::push(AbstractThread* pAbstractThread) noexcept { - if (!thread) + if (!pAbstractThread) { stderrLog("ThreadHolder::push: nullptr thread.\n"); return; } - auto* pSphereThread = dynamic_cast(thread); + auto* pSphereThread = dynamic_cast(pAbstractThread); if (!pSphereThread) { stderrLog("ThreadHolder::push: not an AbstractSphereThread.\n"); return; } - const char* nameForLog = thread->getName(); - int idForLog = -1; - bool shouldLog = false; +#ifdef _DEBUG + const char* ptcNameForLog = pAbstractThread->getName(); + int iIdForLog = -1; + bool fShouldLog = false; +#endif try { @@ -164,21 +166,21 @@ void ThreadHolder::push(AbstractThread* thread) noexcept auto itSlot = std::find_if( m_threads.begin(), m_threads.end(), - [thread](const SphereThreadData& s) noexcept { return s.m_ptr == thread; } + [pAbstractThread](const SphereThreadData& s) noexcept { return s.m_ptr == pAbstractThread; } ); if (itSlot != m_threads.end()) - thread->m_threadHolderId = static_cast(std::distance(m_threads.begin(), itSlot)); + pAbstractThread->m_threadHolderId = static_cast(std::distance(m_threads.begin(), itSlot)); #ifdef _DEBUG - idForLog = thread->m_threadHolderId; - shouldLog = true; + iIdForLog = pAbstractThread->m_threadHolderId; + fShouldLog = true; #endif lock.unlock(); #ifdef _DEBUG - if (shouldLog) + if (fShouldLog) g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, "ThreadHolder already registered: %s (ThreadHolder-ID %d).\n", - nameForLog, idForLog); + ptcNameForLog, iIdForLog); #endif return; } @@ -199,28 +201,28 @@ void ThreadHolder::push(AbstractThread* thread) noexcept lock.unlock(); g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, "ThreadHolder: refusing to register %s on OS thread already owned by %s.\n", - nameForLog, other ? other->getName() : ""); + ptcNameForLog, other ? other->getName() : ""); EXC_NOTIFY_DEBUGGER; return; } // 3) New registration. - m_threads.emplace_back(SphereThreadData{ thread, false }); - thread->m_threadHolderId = m_threadCount; + m_threads.emplace_back(SphereThreadData{ pAbstractThread, false }); + pAbstractThread->m_threadHolderId = m_threadCount; m_spherethreadpairs_systemid_ptr.emplace_back(pSphereThread->m_threadSystemId, pSphereThread); ++m_threadCount; #ifdef _DEBUG - idForLog = thread->m_threadHolderId; - shouldLog = true; + iIdForLog = pAbstractThread->m_threadHolderId; + fShouldLog = true; #endif lock.unlock(); #ifdef _DEBUG - if (shouldLog) + if (fShouldLog) g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, "ThreadHolder registered %s (ThreadHolder-ID %d).\n", - nameForLog, idForLog); + ptcNameForLog, iIdForLog); #endif } catch (const std::exception& e) @@ -240,8 +242,11 @@ void ThreadHolder::remove(AbstractThread* pAbstractThread) CANTHROW throw CSError(LOGL_FATAL, 0, "ThreadHolder::remove: thread == nullptr"); AbstractSphereThread* pSphereThread = dynamic_cast(pAbstractThread); + +#ifdef _DEBUG threadid_t sysId = pSphereThread ? pSphereThread->m_threadSystemId : threadid_t{}; const char* ptcName = pAbstractThread->getName(); +#endif std::unique_lock lock(m_mutex); diff --git a/src/sphere/threads.h b/src/sphere/threads.h index 4b7714a81..e2f88e0c0 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -282,7 +282,7 @@ class ThreadHolder static bool isServClosing() noexcept; // Slow-path registry ops - void push(AbstractThread *thread) noexcept; + void push(AbstractThread *pAbstractThread) noexcept; void remove(AbstractThread *thread) CANTHROW; AbstractThread * getThreadAt(size_t at) noexcept; From 535bf67a99fb41ec1ef3f6141776e4e9dbaa8d35 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 28 Sep 2025 17:00:54 +0200 Subject: [PATCH 100/112] Added Github Issues templates (courtesy of Canerksk). --- .github/ISSUE_TEMPLATE/bug-report.yml | 50 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 14 +++++++ .github/ISSUE_TEMPLATE/feature-request.md | 14 +++++++ 3 files changed, 78 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.md diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 000000000..3855583ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,50 @@ +name: 🐛 Bug Report +description: Report unexpected behavior +labels: ["bug"] +#assignees: +# - cbnolok +body: + - type: markdown + attributes: + value: | + Before opening a bug report, please search for the behaviour in the existing issues. + --- + Thank you for taking the time to file a bug report. To address this bug as fast as possible, we need some information. + - type: textarea + id: bug-description + attributes: + label: Bug description + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: Step-by-step guide for reproducing the bug in a vanilla SphereServer instance. If addressing a regression, please provide the sphere revision number or commit since when the bug started to occur. + validations: + required: false + - type: textarea + id: logs + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + validations: + required: false + - type: input + id: sphere-rev + attributes: + label: Revision Number and Git Version Hash + placeholder: "e.g. 4031 bbfc587bb2c270c53ecb54cb5e1ea2f54cd9031f" + validations: + required: true + - type: dropdown + id: sphere-branch + attributes: + label: Branch + description: "Select the branch you are working with" + options: + - "master" + - "dev" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..7a3b1dbdf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: false +contact_links: + - name: Support & Discussions & More + url: https://discord.gg/ZrMTXrs + about: '' + - name: Documentation (Wiki) + url: https://wiki.spherecommunity.net/ + about: '' + - name: Changelogs (Master branch) + url: https://github.com/Sphereserver/Source-X/blob/master/Changelog.txt + about: '' + - name: Changelogs (Dev branch) + url: https://github.com/Sphereserver/Source-X/blob/dev/Changelog.txt + about: '' diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 000000000..2c7f08c55 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,14 @@ +--- +name: "\U0001F4A1 Feature Request" +about: Suggest an idea. +title: '' +labels: feature +#assignees: cbnolok + +--- + +#### Description + + +#### Why this should be added + From 0c67435782ac86f576dfa91e8e6bbc21e3c3fe17 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 28 Sep 2025 17:14:11 +0200 Subject: [PATCH 101/112] Fixed Release build compilation error. --- src/common/CException.h | 6 +++++- src/sphere/threads.cpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/CException.h b/src/common/CException.h index 1de7652c4..dde06502a 100644 --- a/src/common/CException.h +++ b/src/common/CException.h @@ -133,7 +133,11 @@ class CAssert : public CSError #endif // _EXC_CAUGHT -#define _EXC_CAUGHT static_cast(ThreadHolder::get().current())->signalExceptionCaught() +#if defined THREAD_TRACK_CALLSTACK && defined _EXCEPTIONS_DEBUG +# define _EXC_CAUGHT static_cast(ThreadHolder::get().current())->signalExceptionCaught() +#else +# define _EXC_CAUGHT +#endif /*--- Main (non SUB) macros ---*/ diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index b8a452834..c7c9bc77e 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -878,7 +878,7 @@ AbstractSphereThread::~AbstractSphereThread() sg_tlsCurrentSphereThread = nullptr; } -#ifdef THREAD_TRACK_CALLSTACK +#if defined THREAD_TRACK_CALLSTACK && defined _EXCEPTIONS_DEBUG void AbstractSphereThread::signalExceptionCaught() noexcept { if (m_iStackPos < 0) From e90ca922b140ab2d7e89c474090bf4eac6a24d51 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Sun, 28 Sep 2025 18:05:52 +0200 Subject: [PATCH 102/112] Actually use x and y arguments to CSectorBase::Init method. --- src/game/CSectorList.cpp | 13 ++++++++----- src/game/CSectorTemplate.cpp | 22 ++++++++++------------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index b629de25a..ebfb93f3e 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -69,15 +69,18 @@ void CSectorList::Init() short iSectorX = 0, iSectorY = 0; for (; iSectorIndex < iSectorQty; ++iSectorIndex) { - if (iSectorX >= iMaxX) - { - iSectorX = 0; - ++iSectorY; - } + // Map sectors are added in row-major order. + if (iSectorX >= iMaxX) + { + iSectorX = 0; + ++iSectorY; + } CSector* pSector = &(sd._pSectors[iSectorIndex]); ASSERT(pSector); pSector->Init(iSectorIndex, (uchar)iMap, iSectorX, iSectorY); + + ++iSectorX; } } diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index f5ce9a004..76908d285 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -250,26 +250,24 @@ void CSectorBase::Init(int index, uchar map, short x, short y) // Set BasePoint. auto const& sd = CSectorList::Get().GetMapSectorData(map); - DEBUG_ASSERT((m_index >= 0) && (m_index < sd.iSectorQty) ); + DEBUG_ASSERT((m_index >= 0) && (m_index < sd.iSectorQty)); - const int iCols = sd.iSectorColumns, iSize = sd.iSectorSize; - const int iQuot = (m_index % iCols), iRem = (m_index / iCols); // Help the compiler to optimize the division m_BasePoint = // Initializer list for CPointMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) { - (short)(iQuot * iSize), // x - (short)(iRem * iSize), // y - 0, // z - (uint8)map // m + x, // x + y, // y + 0, // z + (uint8)map // m }; // Set MapRect. m_MapRect = // Initializer list for CRectMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) CRectMap { - m_BasePoint.m_x, // left - m_BasePoint.m_y, // yop - m_BasePoint.m_x + iSize, // right: East - m_BasePoint.m_y + iSize, // bottom: South - m_BasePoint.m_map // map + m_BasePoint.m_x, // left + m_BasePoint.m_y, // yop + m_BasePoint.m_x + sd.iSectorSize, // right: East + m_BasePoint.m_y + sd.iSectorSize, // bottom: South + m_BasePoint.m_map // map }; } From a52f027d30991ef2ae177dd6b3b84ce95c07f19d Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 24 Oct 2025 13:47:27 +0200 Subject: [PATCH 103/112] Another compilation error fix --- src/sphere/threads.cpp | 22 +++++++++++----------- src/sphere/threads.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index c7c9bc77e..d9e289549 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -143,10 +143,10 @@ void ThreadHolder::push(AbstractThread* pAbstractThread) noexcept } #ifdef _DEBUG - const char* ptcNameForLog = pAbstractThread->getName(); - int iIdForLog = -1; - bool fShouldLog = false; + int iIdForLogDebug = -1; + bool fShouldLogDebug = false; #endif + const char* ptcNameForLog = pAbstractThread->getName(); try { @@ -172,15 +172,15 @@ void ThreadHolder::push(AbstractThread* pAbstractThread) noexcept pAbstractThread->m_threadHolderId = static_cast(std::distance(m_threads.begin(), itSlot)); #ifdef _DEBUG - iIdForLog = pAbstractThread->m_threadHolderId; - fShouldLog = true; + iIdForLogDebug = pAbstractThread->m_threadHolderId; + fShouldLogDebug = true; #endif lock.unlock(); #ifdef _DEBUG - if (fShouldLog) + if (fShouldLogDebug) g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, "ThreadHolder already registered: %s (ThreadHolder-ID %d).\n", - ptcNameForLog, iIdForLog); + ptcNameForLog, iIdForLogDebug); #endif return; } @@ -213,16 +213,16 @@ void ThreadHolder::push(AbstractThread* pAbstractThread) noexcept ++m_threadCount; #ifdef _DEBUG - iIdForLog = pAbstractThread->m_threadHolderId; - fShouldLog = true; + iIdForLogDebug = pAbstractThread->m_threadHolderId; + fShouldLogDebug = true; #endif lock.unlock(); #ifdef _DEBUG - if (fShouldLog) + if (fShouldLogDebug) g_Log.Event(LOGM_DEBUG | LOGL_EVENT | LOGF_CONSOLE_ONLY, "ThreadHolder registered %s (ThreadHolder-ID %d).\n", - ptcNameForLog, iIdForLog); + ptcNameForLog, iIdForLogDebug); #endif } catch (const std::exception& e) diff --git a/src/sphere/threads.h b/src/sphere/threads.h index e2f88e0c0..30a643cab 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -159,7 +159,7 @@ class AbstractSphereThread : public AbstractThread friend class ThreadHolder; #ifdef THREAD_TRACK_CALLSTACK - struct STACK_INFO_REC { const char *functionName{}; }; + struct STACK_INFO_REC { const char *functionName; }; STACK_INFO_REC m_stackInfo[0x500]{}; STACK_INFO_REC m_stackInfoCopy[0x500]{}; From 4cc071f77f4b8a0de1fb0db34e4a7a2edc19e6dd Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 24 Oct 2025 20:14:15 +0200 Subject: [PATCH 104/112] Fixed int to str conversion. --- src/common/CExpression.cpp | 248 ++++++----- src/common/CExpression.h | 10 +- src/common/CFloatMath.cpp | 17 +- src/common/CVarDefMap.cpp | 4 +- src/common/CVarDefMap.h | 8 +- src/common/sphere_library/sstring.cpp | 589 ++++++++++++++++++-------- src/sphere/threads.cpp | 4 +- 7 files changed, 560 insertions(+), 320 deletions(-) diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index 01a4ebd4c..a7611c3ff 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -643,46 +643,43 @@ CExpression& CExpression::GetExprParser() // static */ } -llong CExpression::GetSingle(lpctstr & refStrExpr) +int64 CExpression::GetSingle(lpctstr & refStrExpr) { ADDTOCALLSTACK("CExpression::GetSingle"); ASSERT(refStrExpr); GETNONWHITESPACE(refStrExpr); - lpctstr const ptcStart = refStrExpr; + tchar ptcOrigExpr[SCRIPT_MAX_LINE_LEN]; + Str_CopyLimitNull(ptcOrigExpr, refStrExpr, sizeof(ptcOrigExpr)); + + // Differences between GetSingle and cstr_to_num (sstring.cpp) in parsing numbers: + // - The first "consumes" the string (advances the pointer) as it is parsed. + // - The first supports only base 16 and 10. + // - The first stops when finding a '.' during hex parsing. The second stops and warns. // Legacy: allow/skip a leading '.' before parsing numbers if (refStrExpr[0] == '.') ++refStrExpr; - // Hex path: a leading '0' indicates hex unless it is "0." (decimal disambiguation) - if (refStrExpr[0] == '0') + if (refStrExpr[0] == '0' && refStrExpr[1] != '.') { - if (refStrExpr[1] == '.') - { - // "0." means decimal - refStrExpr += 2; - goto try_decimal; - } - - // Consume the '0' hex marker + // HEX PATH: consume the leading '0' marker, then scan [0-9 A-F a-f] until a non-hex char or '.' ++refStrExpr; - // Parse hex digits; significant digits start at first non-zero nibble - uint64 uiVal = 0; + uint64 uiVal = 0; // accumulates significant nibbles only uint uiSig = 0; // count of significant hex digits (max 16) bool fSeenNonZero = false; bool fOverflow = false; lpctstr p = refStrExpr; - for (; ; ++p) + for (;; ++p) { - tchar ch = *p; + const tchar ch = *p; uint v; - if (ch >= '0' && ch <= '9') v = (uint)(ch - '0'); - else if (ch >= 'A' && ch <= 'F') v = (uint)(ch - 'A' + 10); - else if (ch >= 'a' && ch <= 'f') v = (uint)(ch - 'a' + 10); - else break; // end of hex token + if (ch >= '0' && ch <= '9') v = (uint)(ch - '0'); + else if (ch >= 'A' && ch <= 'F') v = (uint)(ch - 'A' + 10); + else if (ch >= 'a' && ch <= 'f') v = (uint)(ch - 'a' + 10); + else break; // stop on non-hex (including '.') if (!fSeenNonZero) { @@ -692,95 +689,93 @@ llong CExpression::GetSingle(lpctstr & refStrExpr) uiSig = 1; uiVal = v; // first non-zero nibble } - // else: ignore additional zeros before first non-zero; they don't affect width or value + // leading zeros before first non-zero nibble do not affect width or value } else { if (uiSig == 16) { - fOverflow = true; // keep consuming to end + // More than 16 significant nibbles; flag overflow, keep consuming token for caller. + fOverflow = true; } else { - uiVal = (uiVal << 4) | v; + uiVal = (uiVal << 4) | v; // safe: uiSig < 16 guarantees no shift past 60 bits here ++uiSig; } } } - // Consume the entire hex token (including after overflow) - refStrExpr = p;// + ((*p == '\0') ? 0 : 1); + // Consume the entire hex token (up to, not including, the first non-hex char) + refStrExpr = p; - // No significant hex nibble after the marker → value is zero (e.g., "0", "-00", "0000") + // If no significant nibble after the marker, the numeric value is zero (e.g., "0", "0000") if (!fSeenNonZero) return 0; if (fOverflow) { - g_Log.EventWarn("Hexadecimal value parsing will overflow 64 bits: %s.\n", ptcStart); + g_Log.EventWarn("Hexadecimal value parsing will overflow 64 bits: %s.\n", ptcOrigExpr); return -1; } - // Width by significant hex digits: <=8 → 32-bit signed; 9..16 → 64-bit signed + // Decide width by significant hex digits: + // ≤ 8 → signed 32-bit reinterpretation, then widen; 9..16 → signed 64-bit reinterpretation if (uiSig <= 8) { - // Two's complement 32-bit interpretation - const uint32 u32 = (uint32)uiVal; - const int32 s32 = (int32)u32; - return (llong)s32; + const uint32 u32 = static_cast(uiVal); + const int32 s32 = static_cast(u32); // two's complement reinterpretation + return static_cast(s32); } else { - // Two's complement 64-bit interpretation + // two's-complement 64-bit reinterpretation without implementation-defined casts if (uiVal & (1ull << 63)) { - const uint64 mag = (~uiVal) + 1; - const int64 neg = -(int64)mag; - return (llong)neg; - } - else - { - return (llong)(int64)uiVal; + const uint64 mag = (~uiVal) + 1; // absolute magnitude + const int64 neg = -static_cast(mag);// well-defined negate in 64-bit domain + return neg; } + return (llong)(int64)uiVal; } } - else if (refStrExpr[0] == '.' || (refStrExpr[0] >= '0' && refStrExpr[0] <= '9')) + else if ((refStrExpr[0] >= '0' && refStrExpr[0] <= '9') || + (refStrExpr[0] == '.' && (refStrExpr[1] >= '0' && refStrExpr[1] <= '9'))) { - // Decimal (with '.' separators allowed) - try_decimal: + // DECIMAL PATH: digits, optionally with '.' separators that are ignored // Overflow guard for INT64_MAX (9223372036854775807) - constexpr int64 LIM10 = INT64_MAX / 10; // 922337203685477580 - constexpr int LIMDG = (int)(INT64_MAX % 10); // 7 + constexpr int64 LIM10 = INT64_MAX / 10; // 922337203685477580 + constexpr int LIMDG = (int)(INT64_MAX % 10); // 7 int64 val = 0; bool overflow = false; lpctstr p = refStrExpr; - for (; ; ++p) + for (;; ++p) { - tchar ch = *p; + const tchar ch = *p; if (ch == '.') - continue; // skip separators + continue; // skip grouping separators if (ch < '0' || ch > '9') break; - int d = ch - '0'; + const int d = ch - '0'; if (!overflow && (val > LIM10 || (val == LIM10 && d > LIMDG))) { - overflow = true; // but keep consuming the whole token + overflow = true; // keep consuming the whole token for the caller } else if (!overflow) { - val = val * 10 + d; + val = val * 10 + d; // safe: guarded to avoid signed overflow } } // Consume the entire decimal token - refStrExpr = p;// + ((*p == '\0') ? 0 : 1); + refStrExpr = p; if (overflow) { - g_Log.EventWarn("Decimal value parsing will overflow 64 bits: %s.\n", ptcStart); + g_Log.EventWarn("Decimal value parsing will overflow 64 bits: %s.\n", ptcOrigExpr); return -1; } return (llong)val; @@ -1128,10 +1123,10 @@ llong CExpression::GetSingle(lpctstr & refStrExpr) if ( iCount == 2 ) { int64 val2 = GetVal( ppCmd[1] ); - iResult = g_Rand.GetLLVal2( val1, val2 ); + iResult = g_Rand.GetLLVal2( val1, val2 ); } else - iResult = g_Rand.GetLLVal(val1); + iResult = g_Rand.GetLLVal(val1); } } break; @@ -1230,38 +1225,41 @@ llong CExpression::GetSingle(lpctstr & refStrExpr) auto reader = g_ExprGlobals.mtEngineLockedReader(); lpctstr ptcArgsOriginal = refStrExpr; - llong llVal; - if ( reader->m_VarGlobals.GetParseVal_Advance( refStrExpr, &llVal ) ) // VAR. - return llVal; - if ( reader->m_VarResDefs.GetParseVal( ptcArgsOriginal, &llVal ) ) // RESDEF. - return llVal; - if ( reader->m_VarDefs.GetParseVal( ptcArgsOriginal, &llVal ) ) // DEF. - return llVal; + int64 iVal; + // VAR. + if ( reader->m_VarGlobals.GetParseVal_Advance( refStrExpr, &iVal ) ) + return iVal; + // RESDEF. + if ( reader->m_VarResDefs.GetParseVal( ptcArgsOriginal, &iVal ) ) + return iVal; + // DEF. + if ( reader->m_VarDefs.GetParseVal( ptcArgsOriginal, &iVal ) ) + return iVal; } //#pragma endregion intrinsics // MSVC specific // hard end ! Error of some sort. - if (ptcStart[0] != '\0') + if (ptcOrigExpr[0] != '\0') { tchar szTag[EXPRESSION_MAX_KEY_LEN]; - GetIdentifierString(szTag, ptcStart); - const lpctstr ptcLast = (ptcStart[0] == '<') ? ">'" : "'"; - g_Log.EventError("Undefined symbol '%s' [Evaluated expression: '%s%s].\n", szTag, ptcStart, ptcLast); + GetIdentifierString(szTag, ptcOrigExpr); + //const lpctstr ptcLast = (ptcOrigExpr[0] == '<') ? ">'" : "'"; + g_Log.EventError("Undefined symbol '%s' [Evaluated expression: '%s'].\n", szTag, ptcOrigExpr); } else { g_Log.EventError("Undefined symbol (empty parameter?).\n"); - } + } return 0; } -llong CExpression::GetValMath(llong llVal, lpctstr & refStrExpr ) +int64 CExpression::GetValMath(int64 iVal, lpctstr & refStrExpr ) { ADDTOCALLSTACK("CExpression::GetValMath"); GETNONWHITESPACE(refStrExpr); // Look for math type operator and eventually apply it to the second operand (which we evaluate here if a valid operator is found). - llong llValSecond; + int64 iValSecond; switch ( refStrExpr[0] ) { case '\0': @@ -1275,19 +1273,19 @@ llong CExpression::GetValMath(llong llVal, lpctstr & refStrExpr ) case '+': ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal += llValSecond; + iValSecond = GetVal(refStrExpr); + iVal += iValSecond; break; case '-': - llValSecond = GetVal(refStrExpr); + iValSecond = GetVal(refStrExpr); //++pExpr; No need to consume the negative sign, we need to keep it! - llVal += llValSecond; // a subtraction is an addiction with a negative number. + iVal += iValSecond; // a subtraction is an addiction with a negative number. break; case '*': ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal *= llValSecond; + iValSecond = GetVal(refStrExpr); + iVal *= iValSecond; break; case '|': @@ -1295,13 +1293,13 @@ llong CExpression::GetValMath(llong llVal, lpctstr & refStrExpr ) if ( refStrExpr[0] == '|' ) // boolean ? { ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal = (llValSecond || llVal); + iValSecond = GetVal(refStrExpr); + iVal = (iValSecond || iVal); } else // bitwise { - llValSecond = GetVal(refStrExpr); - llVal |= llValSecond; + iValSecond = GetVal(refStrExpr); + iVal |= iValSecond; } break; @@ -1310,42 +1308,42 @@ llong CExpression::GetValMath(llong llVal, lpctstr & refStrExpr ) if ( refStrExpr[0] == '&' ) // boolean ? { ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal = (llValSecond && llVal); // tricky stuff here. logical ops must come first or possibly not get processed. + iValSecond = GetVal(refStrExpr); + iVal = (iValSecond && iVal); // tricky stuff here. logical ops must come first or possibly not get processed. } else // bitwise { - llValSecond = GetVal(refStrExpr); - llVal &= llValSecond; + iValSecond = GetVal(refStrExpr); + iVal &= iValSecond; } break; case '/': ++refStrExpr; - llValSecond = GetVal(refStrExpr); - if (!llValSecond) + iValSecond = GetVal(refStrExpr); + if (!iValSecond) { g_Log.EventError("Evaluating math: Divide by 0\n"); break; } - llVal /= llValSecond; + iVal /= iValSecond; break; case '%': ++refStrExpr; - llValSecond = GetVal(refStrExpr); - if (!llValSecond) + iValSecond = GetVal(refStrExpr); + if (!iValSecond) { g_Log.EventError("Evaluating math: Modulo 0\n"); break; } - llVal %= llValSecond; + iVal %= iValSecond; break; case '^': ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal ^= llValSecond; + iValSecond = GetVal(refStrExpr); + iVal ^= iValSecond; break; case '>': // boolean @@ -1353,19 +1351,19 @@ llong CExpression::GetValMath(llong llVal, lpctstr & refStrExpr ) if ( refStrExpr[0] == '=' ) // boolean ? { ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal = ( llVal >= llValSecond ); + iValSecond = GetVal(refStrExpr); + iVal = ( iVal >= iValSecond ); } else if ( refStrExpr[0] == '>' ) // shift { ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal >>= llValSecond; + iValSecond = GetVal(refStrExpr); + iVal >>= iValSecond; } else { - llValSecond = GetVal(refStrExpr); - llVal = (llVal > llValSecond); + iValSecond = GetVal(refStrExpr); + iVal = (iVal > iValSecond); } break; @@ -1374,19 +1372,19 @@ llong CExpression::GetValMath(llong llVal, lpctstr & refStrExpr ) if ( refStrExpr[0] == '=' ) // boolean ? { ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal = ( llVal <= llValSecond ); + iValSecond = GetVal(refStrExpr); + iVal = ( iVal <= iValSecond ); } else if ( refStrExpr[0] == '<' ) // shift { ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal <<= llValSecond; + iValSecond = GetVal(refStrExpr); + iVal <<= iValSecond; } else { - llValSecond = GetVal(refStrExpr); - llVal = (llVal < llValSecond); + iValSecond = GetVal(refStrExpr); + iVal = (iVal < iValSecond); } break; @@ -1395,38 +1393,38 @@ llong CExpression::GetValMath(llong llVal, lpctstr & refStrExpr ) if ( refStrExpr[0] != '=' ) break; // boolean ! is handled as a single expresion. ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal = ( llVal != llValSecond ); + iValSecond = GetVal(refStrExpr); + iVal = ( iVal != iValSecond ); break; case '=': // boolean while ( refStrExpr[0] == '=' ) ++refStrExpr; - llValSecond = GetVal(refStrExpr); - llVal = ( llVal == llValSecond ); + iValSecond = GetVal(refStrExpr); + iVal = ( iVal == iValSecond ); break; case '@': ++refStrExpr; - llValSecond = GetVal(refStrExpr); - if (llVal < 0) + iValSecond = GetVal(refStrExpr); + if (iVal < 0) { - llVal = cexpression_power(llVal, llValSecond); + iVal = cexpression_power(iVal, iValSecond); break; } - else if ((llVal == 0) && (llValSecond <= 0)) //The information from https://en.cppreference.com/w/cpp/numeric/math/pow says if both input are 0, it can cause errors too. + else if ((iVal == 0) && (iValSecond <= 0)) //The information from https://en.cppreference.com/w/cpp/numeric/math/pow says if both input are 0, it can cause errors too. { g_Log.EventError("Power of zero with zero or negative exponent is undefined.\n"); break; } - llVal = cexpression_power(llVal, llValSecond); + iVal = cexpression_power(iVal, iValSecond); break; } - return llVal; + return iVal; } -llong CExpression::GetVal(lpctstr & refStrExpr ) +int64 CExpression::GetVal(lpctstr & refStrExpr ) { // This function moves the pointer forward, so you can retrieve the value only once! @@ -1464,14 +1462,14 @@ llong CExpression::GetVal(lpctstr & refStrExpr ) ++_iGetVal_Reentrant; // Get the first operand value: it may be a number or an expression - llong llVal = GetSingle(refStrExpr); + int64 iVal = GetSingle(refStrExpr); // Check if there is an operator (mathematical or logical), in that case apply it to the second operand (which we evaluate again with GetSingle). - llVal = GetValMath(llVal, refStrExpr); + iVal = GetValMath(iVal, refStrExpr); --_iGetVal_Reentrant; - return llVal; + return iVal; } int CExpression::GetRangeVals(lpctstr & refStrExpr, int64 * piVals, int iMaxQty, bool fNoWarn) @@ -2008,7 +2006,7 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) ptcToParse[uiToParseLen] = '\0'; lptstr pToParseCasted = static_cast(ptcToParse); - llong llValFirst = GetSingle(pToParseCasted); + int64 iValFirst = GetSingle(pToParseCasted); // Copy the second element in a new string uiToParseLen = std::min( @@ -2018,15 +2016,15 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) ptcToParse[uiToParseLen] = '\0'; pToParseCasted = static_cast(ptcToParse); - llong llValSecond = GetSingle(pToParseCasted); + int64 iValSecond = GetSingle(pToParseCasted); - if (llValSecond < llValFirst) // the first value has to be < than the second before passing it to g_Rand.GetLLVal2 + if (iValSecond < iValFirst) // the first value has to be < than the second before passing it to g_Rand.GetLLVal2 { - const llong llValTemp = llValFirst; - llValFirst = llValSecond; - llValSecond = llValTemp; + const int64 iValTemp = iValFirst; + iValFirst = iValSecond; + iValSecond = iValTemp; } - return g_Rand.GetLLVal2(llValFirst, llValSecond); + return g_Rand.GetLLVal2(iValFirst, iValSecond); } // First get the total of the weights @@ -2053,7 +2051,7 @@ int64 CExpression::GetRangeNumber(lpctstr & refStrExpr) } // Now roll the dice to see what value to pick - llTotalWeight = g_Rand.GetLLVal(llTotalWeight) + 1; + llTotalWeight = g_Rand.GetLLVal(llTotalWeight) + 1; // Now loop to that value int i = 1; diff --git a/src/common/CExpression.h b/src/common/CExpression.h index 6c9908f04..b6e44a256 100644 --- a/src/common/CExpression.h +++ b/src/common/CExpression.h @@ -223,10 +223,10 @@ class CExpression static const char *m_sClassName; // Evaluate using the stuff we know. - llong GetSingle(lpctstr & refStrExpr); + int64 GetSingle(lpctstr & refStrExpr); - llong GetValMath(llong llVal, lpctstr & refStrExpr); - llong GetVal(lpctstr & refStrExpr); + int64 GetValMath(int64 iVal, lpctstr & refStrExpr); + int64 GetVal(lpctstr & refStrExpr); int GetRangeVals(lpctstr& refStrExpr, int64* piVals, int iMaxQty, bool fNoWarn = false); int64 GetRangeNumber(lpctstr& refStrExpr); // Evaluate a { } range @@ -234,10 +234,10 @@ class CExpression // Strict G++ Prototyping produces an error when not casting char*& to const char*& // So this is a rather lazy and const-UNsafe workaround - inline llong GetSingle(lptstr &refArgs) { + inline int64 GetSingle(lptstr &refArgs) { return GetSingle(const_cast(refArgs)); } - inline llong GetVal(lptstr& refArgs) { + inline int64 GetVal(lptstr& refArgs) { return GetVal(const_cast(refArgs)); } inline int GetRangeVals(lptstr &refStrExpr, int64 * piVals, int iMaxQty, bool fNoWarn = false) { diff --git a/src/common/CFloatMath.cpp b/src/common/CFloatMath.cpp index 74eea4bbe..d032eb11f 100644 --- a/src/common/CFloatMath.cpp +++ b/src/common/CFloatMath.cpp @@ -674,13 +674,16 @@ realtype CFloatMath::GetSingle( lpctstr & ptcRefArgs ) } auto gReader = g_ExprGlobals.mtEngineLockedReader(); - llong llVal; - if ( gReader->m_VarGlobals.GetParseVal( ptcRefArgs, &llVal ) ) - return (int)llVal; - if ( gReader->m_VarResDefs.GetParseVal( ptcRefArgs, &llVal ) ) - return (int)llVal; - if ( gReader->m_VarDefs.GetParseVal( ptcRefArgs, &llVal ) ) - return (int)llVal; + int64 iVal; + // VAR. + if ( gReader->m_VarGlobals.GetParseVal( ptcRefArgs, &iVal ) ) + return (int)iVal; + // RESDEF. + if ( gReader->m_VarResDefs.GetParseVal( ptcRefArgs, &iVal ) ) + return (int)iVal; + // DEF. + if ( gReader->m_VarDefs.GetParseVal( ptcRefArgs, &iVal ) ) + return (int)iVal; return 0; } diff --git a/src/common/CVarDefMap.cpp b/src/common/CVarDefMap.cpp index 5a3d7da2e..a7134fc6c 100644 --- a/src/common/CVarDefMap.cpp +++ b/src/common/CVarDefMap.cpp @@ -586,13 +586,13 @@ CVarDefCont * CVarDefMap::GetParseKey_Advance( lpctstr & pszArgs ) const return nullptr; } -bool CVarDefMap::GetParseVal_Advance( lpctstr & pszArgs, llong * pllVal ) const +bool CVarDefMap::GetParseVal_Advance( lpctstr & pszArgs, int64 * piVal ) const { ADDTOCALLSTACK_DEBUG("CVarDefMap::GetParseVal_Advance"); CVarDefCont * pVarBase = GetParseKey_Advance( pszArgs ); if ( pVarBase == nullptr ) return false; - *pllVal = pVarBase->GetValNum(); + *piVal = pVarBase->GetValNum(); return true; } diff --git a/src/common/CVarDefMap.h b/src/common/CVarDefMap.h index 93bc6c2ea..c008cc1eb 100644 --- a/src/common/CVarDefMap.h +++ b/src/common/CVarDefMap.h @@ -169,8 +169,8 @@ class CVarDefMap CVarDefCont * CheckParseKey( lpctstr pszArgs ) const; CVarDefCont * GetParseKey_Advance( lpctstr & pArgs ) const; inline CVarDefCont * GetParseKey( lpctstr pArgs ) const; - bool GetParseVal_Advance( lpctstr & pArgs, llong * pllVal ) const; - inline bool GetParseVal( lpctstr pArgs, llong * plVal ) const; + bool GetParseVal_Advance( lpctstr & pArgs, int64 * piVal ) const; + inline bool GetParseVal( lpctstr pArgs, int64 * plVal ) const; void DumpKeys( CTextConsole * pSrc, lpctstr pszPrefix = nullptr ) const; void ClearKeys(lpctstr mask = nullptr); @@ -204,9 +204,9 @@ CVarDefCont * CVarDefMap::GetParseKey(lpctstr pArgs) const return GetParseKey_Advance(pArgs); } -bool CVarDefMap::GetParseVal(lpctstr pArgs, llong * pllVal) const +bool CVarDefMap::GetParseVal(lpctstr pArgs, int64 *piVal) const { - return GetParseVal_Advance(pArgs, pllVal); + return GetParseVal_Advance(pArgs, piVal); } CVarDefMap::iterator CVarDefMap::begin() { return m_Container.begin(); } diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index efdf3b050..cce8d53f9 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -2,6 +2,7 @@ //#include "../../common/CLog.h" #include "../../sphere/ProfileTask.h" #include "../CExpression.h" // included in the precompiled header +#include // for std::countl_zero #ifdef MSVC_COMPILER @@ -65,14 +66,13 @@ bool cstr_to_num( if (!str || !out || base == 1 || base > 16) [[unlikely]] return false; - // 1) Skip leading spaces - while (*str == ' ' || *str == '\t' || *str == '\r' || *str == '\n') { // Instead of using ISWHITESPACE + // Skip leading whitespace + while (*str == ' ' || *str == '\t' || *str == '\r' || *str == '\n') ++str; - } if (*str == '\0') return false; - // 2) Optional sign + // Optional sign (only for signed types) bool neg = false; if constexpr (std::is_signed_v<_IntType>) { @@ -90,18 +90,21 @@ bool cstr_to_num( } } - // 3) Hex‐prefix via leading '0' + // Auto-detect base or handle explicit hex prefix bool hex = false; if (base == 0) { - // Auto-detect. + // Auto-detect: '0' followed by hex digit → base 16; else base 10 if (*str == '0' && str[1] != '\0' && str[1] != '.') { - if (IsHexNumDigit(str[1])) + const char next = str[1]; + if ((next >= '0' && next <= '9') || + (next >= 'A' && next <= 'F') || + (next >= 'a' && next <= 'f')) { hex = true; base = 16; - ++str; // skip the '0' prefix + ++str; // skip '0' prefix } else { @@ -113,100 +116,209 @@ bool cstr_to_num( base = 10; } } - if (base == 16 && str[0] == '0' && str[1] != '\0' && str[1] != '.') + else if (base == 16 && *str == '0' && str[1] != '\0' && str[1] != '.') { hex = true; - ++str; + ++str; // consume '0' prefix } using _UIntType = std::make_unsigned_t<_IntType>; - const _UIntType base_casted = uint8_t(base); - const _UIntType maxDiv = std::numeric_limits<_UIntType>::max() / _UIntType(base_casted); - const _UIntType maxRem = std::numeric_limits<_UIntType>::max() % _UIntType(base_casted); - _UIntType acc = 0; // accumulator - // 4) Parse digits - bool parse_more; + // Compute overflow limits based on target type and sign + _UIntType limit; + if constexpr (std::is_signed_v<_IntType>) + { + // For negative: can go one past max (to represent min value in two's complement) + // For positive: limited to max + limit = neg ? (_UIntType(std::numeric_limits<_IntType>::max()) + 1u) + : _UIntType(std::numeric_limits<_IntType>::max()); + } + else + { + limit = std::numeric_limits<_UIntType>::max(); + } + + _UIntType acc = 0; ushort ndigits = 0; const char* startDigits = str; - do + + switch (base) { - const char c = *str; - parse_more = (size_t(++str - startDigits) < stop_at_len); - _UIntType digit; - if (c >= '0' && c <= '9') - digit = c - '0'; - else if (hex && c >= 'A' && c <= 'F') - digit = c - 'A' + 10; - else if (hex && c >= 'a' && c <= 'f') - digit = c - 'a' + 10; - else if (!hex && c == '.') - continue; // Skip decimal point - else - break; - if (acc > maxDiv || (acc == maxDiv && digit > maxRem)) - return false; // overflow + // Fast path: base 10 (most common) + case 10: + { + const _UIntType maxDiv = limit / 10u; + const _UIntType maxRem = limit % 10u; - acc = acc * base_casted + digit; - ++ndigits; - } while (*str && (!stop_at_len || parse_more)); + while (*str) + { + if (stop_at_len && (size_t(str - startDigits) >= stop_at_len)) + break; - if (str == startDigits) - return false; // no digits consumed + const char c = *str; - // 5) Trailing‐space or end‐of‐string - if (!ignore_trailing_extra_chars && parse_more) - { - // Some old code expects string to num conversion to tolerate trailing whitespaces or even extra chars - // (like the atoi C function). - constexpr bool tolerate_trailing_whitespaces = true; - if constexpr (tolerate_trailing_whitespaces) + // Skip dots (Sphere convention: no true floats) + if (c == '.') + { + ++str; + continue; + } + + if (c < '0' || c > '9') + break; + + const _UIntType digit = c - '0'; + + // Check overflow before multiplying + if (acc > maxDiv || (acc == maxDiv && digit > maxRem)) + return false; + + acc = acc * 10u + digit; + ++ndigits; + ++str; + } + } + // Fast path: base 16 (second most common) + case 16: + { + const _UIntType maxDiv = limit / 16u; + const _UIntType maxRem = limit % 16u; + + // Skip other leading zeroes. + while (*str == '0') + ++str; + startDigits = str; + + while (*str) + { + if (stop_at_len && (size_t(str - startDigits) >= stop_at_len)) + break; + + const char c = *str; + _UIntType digit; + + if (c >= '0' && c <= '9') + digit = c - '0'; + else if (c >= 'A' && c <= 'F') + digit = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + digit = c - 'a' + 10; + else + break; + + // Check overflow + if (acc > maxDiv || (acc == maxDiv && digit > maxRem)) + return false; + + acc = acc * 16u + digit; + ++ndigits; + ++str; + } + } + // Generic path: other bases (2-15, cold path) + default: { - while (*str == ' ' || *str == '\t' || *str == '\r' || *str == '\n') + const _UIntType base_casted = uint8_t(base); + const _UIntType maxDiv = limit / base_casted; + const _UIntType maxRem = limit % base_casted; + + while (*str) + { + if (stop_at_len && (size_t(str - startDigits) >= stop_at_len)) + break; + + const char c = *str; + _UIntType digit; + + if (c >= '0' && c <= '9') + digit = c - '0'; + else if (c >= 'A' && c <= 'F') + digit = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + digit = c - 'a' + 10; + else + break; + + // Validate digit is within base + if (digit >= base_casted) + return false; + + // Check overflow + if (acc > maxDiv || (acc == maxDiv && digit > maxRem)) + return false; + + acc = acc * base_casted + digit; + ++ndigits; ++str; + } } + } // switch + + if (ndigits == 0) + return false; // no valid digits consumed + + // Check trailing characters + if (!ignore_trailing_extra_chars) + { + // Skip trailing whitespace + while (*str == ' ' || *str == '\t' || *str == '\r' || *str == '\n') + ++str; + if (*str != '\0') - return false; + return false; // unexpected trailing characters } - // 6) Make short (< UINT32_MAX, or <= 8 hex digits) hex numbers behave like 32 bits numbers. - /* - * Why Sphere expects this and works like this is unknown to me (maybe TUS/Grayworld or - * prehistoric Sphere versions worked with 32 bit numbers instead of 64 bit). - * - */ - if (hex && ndigits<=8 && std::is_signed_v<_IntType>) + // Sphere hex convention: ≤8 hex digits → interpret as signed 32-bit, then extend + if (hex && ndigits <= 8) { // Reinterpret the bits as a signed 32 bit number, then expand that value to a 32 bit number. // if ndigits <= 8 (so number is always <= 0xFFFF FFFF), it will always be < UINT32_MAX. - int32_t v32 = int32_t(uint32_t(acc)); - *out = _IntType(v32); + + // ..Why Sphere expects this and works like this is unknown to me (maybe TUS/Grayworld or + // prehistoric Sphere versions worked with 32 bit numbers instead of 64 bit). + + const int32_t v32 = static_cast(static_cast(acc)); + + // Apply the leading minus if present + const int64_t v = neg ? -static_cast(v32) : static_cast(v32); + // branchless: + //uint64_t m = 0 - static_cast(neg); // 0 or 0xFFFF..FFFF, defined modulo 2^64 + //int64_t v = static_cast((static_cast(v64) ^ m) - m); // relies only on defined unsigned wraparound + + // Verify it fits in the target type (important for int8_t, int16_t) + if constexpr (sizeof(_IntType) < sizeof(int32_t)) + { + if (v32 < std::numeric_limits<_IntType>::min() || + v32 > std::numeric_limits<_IntType>::max()) + return false; + } + + *out = static_cast<_IntType>(v); return true; } - // 7) Store result + // Non-hex path. Store final result if constexpr (std::is_signed_v<_IntType>) { if (neg) { - if (acc > _UIntType(std::numeric_limits<_IntType>::max()) + 1u) - return false; // too negative - *out = _IntType(0) - _IntType(acc); + // Negate using well-defined unsigned arithmetic, then cast + *out = static_cast<_IntType>(_UIntType(0) - acc); } else { - if (acc > _UIntType(std::numeric_limits<_IntType>::max())) - return false; // too large positive - *out = _IntType(acc); + *out = static_cast<_IntType>(acc); } } else { - *out = _IntType(acc); + *out = static_cast<_IntType>(acc); } + return true; } +// Wrapper functions std::optional Str_ToI8 (const tchar * ptcStr, uint base, size_t uiStopAtLen, bool fIgnoreExcessChars) noexcept { @@ -293,176 +405,303 @@ Count significant hex digits starting at the first non-zero nibble: 16 significant digits → warn about 64-bit overflow, return −1, and consume the entire hex token. */ -static constexpr tchar DIGITS_UPPER[] = "0123456789ABCDEF"; +namespace str2int_detail +{ + +// Uppercase hex digit table +static constexpr char DIGITS_UPPER[16] = { + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' +}; + +// Map a nibble [0,15] to its uppercase hex character +static inline char hexdig_upper(uint32_t v) noexcept +{ + return DIGITS_UPPER[v & 0xF]; +} -// Return how many hex digits should be emitted for value 'u' within a fixed width (8 or 16 nibbles), -// after trimming leading zero nibbles within that width. -static inline int _hex_num_digits_from_width(uint64 u, int width_nibbles) noexcept +// Compute hex digits to emit within a fixed width (8 or 16 nibbles) after +// trimming leading zero nibbles within that width. +// Special-case zero as exactly one hex digit ("0"), so final hex "00". +static inline int hex_digits_from_width(uint64_t u, int iWidthNibbles) noexcept { - int shift = (width_nibbles - 1) * 4; - while (shift > 0 && ((u >> shift) & 0xFull) == 0) - shift -= 4; - return (shift / 4) + 1; + if (iWidthNibbles == 8) + { + uint32 v = static_cast(u); + if (v == 0) + return 1; // represent 0 as "0" (the caller will prefix a single '0' for "00") + // Count leading zero bits in 32-bit domain, then convert to nibbles. + // countl_zero(0) would be 32, but we have v != 0 here. + int lz = std::countl_zero(v); + return 8 - (lz / 4); + } + else + { + if (u == 0) + return 1; + int lz = std::countl_zero(u); // counts in 64-bit domain + return 16 - (lz / 4); + } } -// Template entry point: returns out_buf on success, nullptr on failure. +// Base-10 two-digit lookup table (LUT): "00", "01", ..., "99" laid out consecutively. +// Emission logic divides by 100, uses remainder r in [0,99], and copies DEC_00_99[2*r+0..1]. +// This halves the number of division/mod operations versus single-digit steps. +static constexpr std::array DEC_00_99 = []{ + std::array a{}; + for (int i = 0; i < 100; ++i) { + const int tens = i / 10; // exact for 0..99 + const int ones = i - tens * 10; // exact for 0..99 + a[2*i + 0] = char('0' + tens); + a[2*i + 1] = char('0' + ones); + } + return a; +}(); + +} // namespace + +// Template entry point: returns ptcOutBuf on success, nullptr on failure. +// uiBase must be in [2,16]. Hex path (uiBase 16) emits uppercase and starts with '0'. +// Decimal path (uiBase 10) uses a two-digit LUT for throughput. +// Other bases use a generic fallback (reverse into tmp, then write forward). template -tchar* Str_FromInt_Fast(_IntType val, tchar* out_buf, size_t buf_length, uint base) noexcept +tchar* Str_FromInt_Fast(_IntType val, tchar* ptcOutBuf, size_t uiBufLength, uint32 uiBase) noexcept { static_assert(std::is_integral_v<_IntType>, "Str_FromInt_Fast requires an integral type"); + static_assert(sizeof(_IntType) <= 8, "Only up to 64-bit integers are supported"); - if (!out_buf || buf_length == 0) + if (!ptcOutBuf || uiBufLength == 0) { - g_Log.EventWarn("Str_FromInt_Fast: null buffer or zero length.\n"); +#ifdef _DEBUG + g_Log.EventError("Str_FromInt_Fast: null buffer or zero length.\n"); +#endif return nullptr; } - if (base < 2 || base > 16) + if (uiBase < 2 || uiBase > 16) { - g_Log.EventWarn("Str_FromInt_Fast: invalid base %u (supported 2..16).\n", base); +#ifdef _DEBUG + g_Log.EventError("Str_FromInt_Fast: invalid base %u (supported 2..16).\n", uiBase); +#endif return nullptr; } - const bool fHex = (base == 16); - - // Zero fast path + // Zero fast path matches existing semantics exactly. if (val == 0) { - if (fHex) + if (uiBase == 16) { - // Hex zero is "00" - if (buf_length < 3) + // Hex zero as "00" + if (uiBufLength < 3) { - g_Log.EventWarn("Str_FromInt_Fast[hex]: insufficient buffer (need 3 for \"00\\0\").\n"); +#ifdef _DEBUG + g_Log.EventError("Str_FromInt_Fast[hex]: insufficient buffer (need 3 for \"00\\0\").\n"); +#endif return nullptr; } - out_buf[0] = '0'; out_buf[1] = '0'; out_buf[2] = '\0'; - return out_buf; + ptcOutBuf[0] = '0'; + ptcOutBuf[1] = '0'; + ptcOutBuf[2] = '\0'; + return ptcOutBuf; } else { - if (buf_length < 2) + if (uiBufLength < 2) { - g_Log.EventWarn("Str_FromInt_Fast[base=%u]: insufficient buffer (need 2 for \"0\\0\").\n", base); +#ifdef _DEBUG + g_Log.EventError("Str_FromInt_Fast[base=%u]: insufficient buffer (need 2 for \"0\\0\").\n", uiBase); +#endif return nullptr; } - out_buf[0] = '0'; out_buf[1] = '\0'; - return out_buf; + ptcOutBuf[0] = '0'; + ptcOutBuf[1] = '\0'; + return ptcOutBuf; } } - if (fHex) + // Specialize only 16 and 10; generic fallback for others. + switch (uiBase) { // Sphere hex formatting: + // - Always consider an input hex number in scripts as the representation of an unsigned number, but keep in mind that internally + // everything is converted to a signed number, so the upper numeric limit is INT64_MAX, not UINT64_MAX. + // - The string is written as a 32 bit number if number < UINT32_MAX. Using INT32_MAX as upper limit is wrong, + // since we said that we consider the input string to be the representation of an unsigned number. + // So, if value fits in int32 (<= 0xFFFFFFFF) → 32-bit t.c.; else 64-bit t.c. // - Uppercase, prefix '0' // - Variable width after trimming within chosen width: - // Signed types: if value fits in int32 → 32-bit t.c.; else 64-bit t.c. - // Unsigned types: <= 0xFFFFFFFF → 32-bit; else 64-bit - uint64 u = 0; - int width_nibbles = 0; + case 16: + { + // Sphere hex formatting (uppercase, prefix '0', trimmed within 32/64-bit width). + uint64_t u = 0; + int width_nibbles = 0; + + // If type is <=32-bit or runtime value fits 32 bit integer, use 32-bit two's-complement view; else 64-bit. + if (sizeof(_IntType) <= 4 + || static_cast(val) <= static_cast(UINT32_MAX)) + { + // Casting through signed then to unsigned preserves the bit pattern modulo 2^32. + const uint32_t u32 = static_cast(static_cast(val)); + u = static_cast(u32); + width_nibbles = 8; + } + else + { + // View through 64-bit signed then to unsigned to preserve bit pattern. + u = static_cast(static_cast(val)); + width_nibbles = 16; + } + + // Compute number of significant hex digits using count-leading-zero in the selected width. + const int iDigits = str2int_detail::hex_digits_from_width(u, width_nibbles); + + // Required: '0' prefix + digits + NUL. + const size_t need = static_cast(iDigits) + 2; + if (uiBufLength < need) + { +#ifdef _DEBUG + g_Log.EventWarn("Str_FromInt_Fast[hex]: insufficient buffer (need %" PRIuSIZE_T ", have %" PRIuSIZE_T ").\n", need, uiBufLength); +#endif + return nullptr; + } + + // Emit: '0' + exactly 'digits' hex characters (no re-emitted trimmed zeros). + // Right-shift on unsigned is well-defined and zero-filling on the left. + ptcOutBuf[0] = '0'; + size_t uiPos = 1; + int iShift = (iDigits - 1) * 4; // start at the most significant non-zero nibble + for (; iShift >= 0; iShift -= 4) + ptcOutBuf[uiPos++] = str2int_detail::hexdig_upper(static_cast((u >> iShift) & 0xFull)); + ptcOutBuf[uiPos] = '\0'; + return ptcOutBuf; + } - if constexpr (std::is_signed_v<_IntType>) + case 10: { - if (sizeof(_IntType) <= 4 || (val >= (llong)INT32_MIN && val <= (llong)INT32_MAX)) + // In Sphere scripting decimal numbers are always signed. There's no concept of unsigned number. + // Base-10 fast path using two-digit LUT: + // Strategy: + // 1) Convert to unsigned magnitude |val| in a way that is correct even for INT_MIN. + // 2) While magnitude >= 100: divide by 100 => quotient q and remainder r in [0,99]. + // 3) Use r to copy two chars from DEC_00_99 to a small reverse buffer. + // 4) Emit the final one or two digits. + // 5) Copy forward to out_buf (prepend '-' if original value was negative). + using _UInt = std::make_unsigned_t<_IntType>; + + _UInt uiMagnitude; + bool fNeg = false; + + if constexpr (std::is_signed_v<_IntType>) { - uint32 u32 = (uint32)(int32)val; // two's complement 32-bit view - u = (uint64)u32; - width_nibbles = 8; + // Two's-complement safe: interpret val as unsigned (no UB), then negate if negative. + _UInt utwos = static_cast<_UInt>(val); + fNeg = (val < 0); + uiMagnitude = fNeg ? (_UInt(0) - utwos) : utwos; } else { - u = (uint64)(int64)val; // two's complement 64-bit view - width_nibbles = 16; + uiMagnitude = static_cast<_UInt>(val); } - } - else - { - ullong uv = (ullong)val; - if (sizeof(_IntType) <= 4 || uv <= 0xFFFFFFFFull) + + // Reverse buffer; 32 bytes sufficient for 64-bit decimal (max 20 digits). + tchar ptcTemp[32]; + size_t uiPos = 0; + + // Emit two digits per iteration using /100, significantly reducing division/mod count. + while (uiMagnitude >= 100) + { + const _UInt q = uiMagnitude / 100; // quotient + const uint32_t r = static_cast(uiMagnitude - q * 100); // remainder in [0,99] + // Store in reverse order: ones then tens, since we're building least significant first. + ptcTemp[uiPos + 0] = str2int_detail::DEC_00_99[2 * r + 1]; + ptcTemp[uiPos + 1] = str2int_detail::DEC_00_99[2 * r + 0]; + uiPos += 2; + uiMagnitude = q; + } + + // Handle the last one or two digits without branching on zero. + if (uiMagnitude < 10) { - u = (uint64)(uint32)uv; - width_nibbles = 8; + ptcTemp[uiPos++] = static_cast('0' + static_cast(uiMagnitude)); } else { - u = (uint64)uv; - width_nibbles = 16; + const uint32_t r = static_cast(uiMagnitude); // 10..99 + ptcTemp[uiPos + 0] = str2int_detail::DEC_00_99[2 * r + 1]; + ptcTemp[uiPos + 1] = str2int_detail::DEC_00_99[2 * r + 0]; + uiPos += 2; } + + // Buffer need: optional '-' + digits + NUL. + const size_t uiNeed = (fNeg ? 1 : 0) + uiPos + 1; + if (uiBufLength < uiNeed) + { +#ifdef _DEBUG + g_Log.EventWarn("Str_FromInt_Fast[base=10]: insufficient buffer (need %" PRIuSIZE_T ", have %" PRIuSIZE_T ").\n", uiNeed, uiBufLength); +#endif + return nullptr; + } + + // Write sign and digits forward. + size_t n = uiPos; // save digit count before resetting + size_t pos = 0; + if (fNeg) + ptcOutBuf[pos++] = '-'; + while (n) + ptcOutBuf[pos++] = ptcTemp[--n]; + ptcOutBuf[pos] = '\0'; } - const int digits = _hex_num_digits_from_width(u, width_nibbles); - const size_t need = (size_t)digits + 2; // '0' + digits + NUL - if (buf_length < need) + default: { - g_Log.EventWarn("Str_FromInt_Fast[hex]: insufficient buffer (need %" PRIuSIZE_T ", have %" PRIuSIZE_T ").\n", need, buf_length); - return nullptr; - } + // Generic fallback for other bases in [2,16], reverse-then-forward. + using _UInt = std::make_unsigned_t<_IntType>; - out_buf[0] = '0'; - int shift = (width_nibbles - digits) * 4; - size_t pos = 1; - for (; shift >= 0; shift -= 4) - out_buf[pos++] = DIGITS_UPPER[(u >> shift) & 0xFull]; - out_buf[pos] = '\0'; - return out_buf; - } - else - { - // General base 2..16. - using _UInt = std::make_unsigned_t<_IntType>; - _UInt uiMagnitude; - bool fNeg = false; + _UInt uiMagnitude; + bool fNeg = false; - if constexpr (std::is_signed_v<_IntType>) - { - // two's complement magnitude (works for INT_MIN) - _UInt utwos = (_UInt)val; - fNeg = (val < 0); - uiMagnitude = fNeg ? (_UInt(0) - utwos) : utwos; - } - else - { - uiMagnitude = (_UInt)val; - } + if constexpr (std::is_signed_v<_IntType>) + { + const _UInt utwos = static_cast<_UInt>(val); + fNeg = (val < 0); + uiMagnitude = fNeg ? (_UInt(0) - utwos) : utwos; + } + else + { + uiMagnitude = static_cast<_UInt>(val); + } - // Accumulate digits in reverse in a small stack buffer; then write forward. - tchar tmp[64]; - size_t n = 0; - const _UInt B = (_UInt)base; + // 65 bytes: 64 bits in base 2 = 64 digits max, plus room for sign handling if needed + tchar ptcTemp[65]; + size_t n = 0; + const _UInt B = static_cast<_UInt>(uiBase); - if (base == 10) - { + // Repeated division/modulo; acceptable since this path is cold in your workload. do { - const _UInt r = uiMagnitude % 10u; - uiMagnitude /= 10u; - tmp[n++] = DIGITS_UPPER[(size_t)r]; - } while (uiMagnitude); - } - else - { - do + const _UInt q = uiMagnitude / B; + const uint32_t r = static_cast(uiMagnitude - q * B); + uiMagnitude = q; + ptcTemp[n++] = str2int_detail::DIGITS_UPPER[r]; + } + while (uiMagnitude); + + const size_t uiNeed = (fNeg ? 1 : 0) + n + 1; + if (uiBufLength < uiNeed) { - const _UInt r = uiMagnitude % B; - uiMagnitude /= B; - tmp[n++] = DIGITS_UPPER[(size_t)r]; - } while (uiMagnitude); - } +#ifdef _DEBUG + g_Log.EventWarn("Str_FromInt_Fast[base=%u]: insufficient buffer (need %" PRIuSIZE_T ", have %" PRIuSIZE_T ").\n", uiBase, uiNeed, uiBufLength); +#endif + return nullptr; + } - const size_t uiNeed = (fNeg ? 1 : 0) + n + 1; - if (buf_length < uiNeed) - { - g_Log.EventWarn("Str_FromInt_Fast[base=%u]: insufficient buffer (need %" PRIuSIZE_T ", have %" PRIuSIZE_T ").\n", base, uiNeed, buf_length); - return nullptr; + size_t uiPos = 0; + if (fNeg) + ptcOutBuf[uiPos++] = '-'; + while (n) + ptcOutBuf[uiPos++] = ptcTemp[--n]; + ptcOutBuf[uiPos] = '\0'; + return ptcOutBuf; } - - size_t uiPos = 0; - if (fNeg) - out_buf[uiPos++] = '-'; - while (n) - out_buf[uiPos++] = tmp[--n]; - out_buf[uiPos] = '\0'; - return out_buf; - } + } // switch } // Typed front-writing wrappers: they return buf on success, nullptr on failure. diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index d9e289549..5e4897aa1 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -503,10 +503,10 @@ AbstractThread::~AbstractThread() "[not started]"; if (everBound) - fprintf(stdout, "DEBUG: Destroying thread '%s' (ThreadHolder-ID %d) %s, sys-id %" PRIu64 ".\n", + fprintf(stdout, "DEBUG: Destroying AbstractThread '%s' (ThreadHolder-ID %d) %s, sys-id %" PRIu64 ".\n", name, m_threadHolderId, state, (uint64_t)m_threadSystemId); else - fprintf(stdout, "DEBUG: Destroying thread '%s' (ThreadHolder-ID %d) %s, sys-id n/a.\n", + fprintf(stdout, "DEBUG: Destroying AbstractThread '%s' (ThreadHolder-ID %d) %s, sys-id n/a.\n", name, m_threadHolderId, state); fflush(stdout); #endif From 2c1c05efb878e63a0ac716ffe96337cc10af140c Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 29 Oct 2025 07:28:32 +0100 Subject: [PATCH 105/112] Fixed: changing char flags didn't always trigger a full update packet. --- src/game/chars/CChar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index bf247ce6e..caa426626 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -3872,7 +3872,7 @@ bool CChar::r_LoadVal( CScript & s ) _uiStatFlag = (_uiStatFlag & uiFlagsNoChange) | (uiNewFlags & ~uiFlagsNoChange); if (uiCurFlags != uiNewFlags) { - const bool fDoFullUpdate = (uiCurFlags & uiFlagsRequireFullUpdate) != (uiNewFlags & uiFlagsRequireFullUpdate); + const bool fDoFullUpdate = HAS_FLAGS_ANY(uiCurFlags, uiFlagsRequireFullUpdate); NotoSave_Update(fDoFullUpdate); } break; From c3f65146c7b1f95d1ec3d23cd06f93e0589b8582 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 29 Oct 2025 19:42:31 +0100 Subject: [PATCH 106/112] Minor changes, uniform some data types in related code(llong to int64) --- src/common/CFloatMath.cpp | 6 ++--- src/common/CScriptTriggerArgs.cpp | 7 +++--- src/common/resource/sections/CDialogDef.cpp | 27 +++++++++++---------- src/common/sphere_library/CSString.cpp | 3 ++- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/common/CFloatMath.cpp b/src/common/CFloatMath.cpp index d032eb11f..60b90b567 100644 --- a/src/common/CFloatMath.cpp +++ b/src/common/CFloatMath.cpp @@ -677,13 +677,13 @@ realtype CFloatMath::GetSingle( lpctstr & ptcRefArgs ) int64 iVal; // VAR. if ( gReader->m_VarGlobals.GetParseVal( ptcRefArgs, &iVal ) ) - return (int)iVal; + return static_cast(static_cast(iVal)); // RESDEF. if ( gReader->m_VarResDefs.GetParseVal( ptcRefArgs, &iVal ) ) - return (int)iVal; + return static_cast(static_cast(iVal)); // DEF. if ( gReader->m_VarDefs.GetParseVal( ptcRefArgs, &iVal ) ) - return (int)iVal; + return static_cast(static_cast(iVal)); return 0; } diff --git a/src/common/CScriptTriggerArgs.cpp b/src/common/CScriptTriggerArgs.cpp index 4fc6dff01..216a3aff6 100644 --- a/src/common/CScriptTriggerArgs.cpp +++ b/src/common/CScriptTriggerArgs.cpp @@ -116,15 +116,16 @@ void CScriptTriggerArgs::Init( lpctstr pszStr ) if ( IsDigit(*pszStr) || ((*pszStr == '-') && IsDigit(*(pszStr+1))) ) { - m_iN1 = Exp_GetSingle(pszStr); + auto& gExprParser = CExpression::GetExprParser(); + m_iN1 = gExprParser.GetSingle(pszStr); SKIP_ARGSEP( pszStr ); if ( IsDigit(*pszStr) || ((*pszStr == '-') && IsDigit(*(pszStr+1))) ) { - m_iN2 = Exp_GetSingle(pszStr); + m_iN2 = gExprParser.GetSingle(pszStr); SKIP_ARGSEP( pszStr ); if ( IsDigit(*pszStr) || ((*pszStr == '-') && IsDigit(*(pszStr+1))) ) { - m_iN3 = Exp_GetSingle(pszStr); + m_iN3 = gExprParser.GetSingle(pszStr); } } } diff --git a/src/common/resource/sections/CDialogDef.cpp b/src/common/resource/sections/CDialogDef.cpp index 19d96d43b..9dd70ef8f 100644 --- a/src/common/resource/sections/CDialogDef.cpp +++ b/src/common/resource/sections/CDialogDef.cpp @@ -142,6 +142,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t return m_pObj->r_Verb(s, pSrc); } + auto &gExprParser = CExpression::GetExprParser(); lptstr ptcArgs = s.GetArgStr(); //g_Log.EventDebug("Dialog index %d, KEY %s ARG %s.\n", index, ptcKey, ptcArgs); @@ -151,25 +152,25 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t GETNONWHITESPACE(ptcArgs_); }; - const auto _CalcRelative = [](lptstr& ptcArgs_, int &iCoordBase_) -> int + const auto _CalcRelative = [&gExprParser](lptstr& ptcArgs_, int &iCoordBase_) -> int { int c; - if ( *ptcArgs_ == '-' && IsSpace(ptcArgs_[1])) + if ( *ptcArgs_ == '-' && IsSpace(ptcArgs_[1])) c = iCoordBase_, ++ptcArgs_; else if ( *ptcArgs_ == '+' ) - c = iCoordBase_ + Exp_GetSingle( ++ptcArgs_ ); + c = iCoordBase_ + gExprParser.GetSingle(++ptcArgs_ ); else if ( *ptcArgs_ == '-' ) - c = iCoordBase_ - Exp_GetSingle( ++ptcArgs_ ); + c = iCoordBase_ - gExprParser.GetSingle(++ptcArgs_ ); else if ( *ptcArgs_ == '*' ) - iCoordBase_ = c = iCoordBase_ + Exp_GetSingle( ++ptcArgs_ ); + iCoordBase_ = c = iCoordBase_ + gExprParser.GetSingle( ++ptcArgs_ ); else - c = Exp_GetSingle( ptcArgs_ ); + c = gExprParser.GetSingle( ptcArgs_ ); return c; }; -# define GET_ABSOLUTE(c) _SkipAll(ptcArgs); int c = Exp_GetSingle(ptcArgs); -# define GET_EVAL(c) _SkipAll(ptcArgs); int c = Exp_GetVal(ptcArgs); -# define GET_RELATIVE(c, base) _SkipAll(ptcArgs); int c = _CalcRelative(ptcArgs, base); +# define GET_RELATIVE(c, base) _SkipAll(ptcArgs); const int c = _CalcRelative(ptcArgs, base); +# define GET_ABSOLUTE(c) _SkipAll(ptcArgs); const int c = gExprParser.GetSingle(ptcArgs); +# define GET_EVAL(c) _SkipAll(ptcArgs); const int c = gExprParser.GetVal(ptcArgs); switch( index ) { @@ -431,17 +432,17 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t if ( *ptcArgs == '-' && (IsSpace( ptcArgs[1] ) || !ptcArgs[1]) ) ++ptcArgs; else if ( *ptcArgs == '*' ) - m_iOriginX += Exp_GetSingle( ++ptcArgs ); + m_iOriginX += gExprParser.GetSingle( ++ptcArgs ); else - m_iOriginX = Exp_GetSingle( ptcArgs ); + m_iOriginX = gExprParser.GetSingle( ptcArgs ); _SkipAll( ptcArgs ); if ( *ptcArgs == '-' && (IsSpace( ptcArgs[1] ) || !ptcArgs[1]) ) ++ptcArgs; else if ( *ptcArgs == '*' ) - m_iOriginY += Exp_GetSingle( ++ptcArgs ); + m_iOriginY += gExprParser.GetSingle( ++ptcArgs ); else - m_iOriginY = Exp_GetSingle( ptcArgs ); + m_iOriginY = gExprParser.GetSingle( ptcArgs ); return true; } diff --git a/src/common/sphere_library/CSString.cpp b/src/common/sphere_library/CSString.cpp index 71c875968..a9a604a77 100644 --- a/src/common/sphere_library/CSString.cpp +++ b/src/common/sphere_library/CSString.cpp @@ -429,7 +429,8 @@ void CSString::FormatULLVal(ullong uiVal) } void CSString::FormatSTVal(size_t uiVal) { - static_assert(sizeof(size_t) <= sizeof(ullong), "You can't use StrFromULL with a size_t argument on this architecture. Use the old call to Format instead."); + static_assert(sizeof(size_t) <= sizeof(ullong), + "You can't use FormatSTVal on this architecture (it uses internally Str_FromULL_Fast). Use the old call to Format instead."); //Format("%" PRIuSIZE_T, iVal); FORMATNUM_WRAPPER(Str_FromULL_Fast, uiVal, 10); } From b2b5b8998a03ac69b48b6d99018cf08ea5c33c0c Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 30 Oct 2025 16:26:26 +0100 Subject: [PATCH 107/112] CSector/CPointBase: added some safety checks --- src/common/CPointBase.cpp | 11 ----------- src/common/CPointBase.h | 28 +++++++++++++++++---------- src/common/CRect.cpp | 35 +++++++++++++++++++--------------- src/game/CRegion.cpp | 6 ++++-- src/game/CSector.cpp | 2 +- src/game/CSectorList.cpp | 29 +++++++++++++++++++++++----- src/game/CSectorList.h | 8 +++++--- src/game/CSectorTemplate.cpp | 37 ++++++++++++++++++------------------ src/game/CServerConfig.cpp | 4 ++-- src/game/CWorld.cpp | 18 +++++++++--------- src/game/CWorldMap.cpp | 24 +++++++++++++++++------ 11 files changed, 120 insertions(+), 82 deletions(-) diff --git a/src/common/CPointBase.cpp b/src/common/CPointBase.cpp index 7c1eb3f3d..10821a167 100644 --- a/src/common/CPointBase.cpp +++ b/src/common/CPointBase.cpp @@ -96,17 +96,6 @@ lpctstr const CPointBase::sm_szLoadKeys[PT_QTY+1] = nullptr }; -CPointBase::CPointBase() noexcept : - m_x(-1), m_y(-1), m_z(0), m_map(0) // Same thing as calling InitPoint(), but without this extra cuntion call -{ - //InitPoint(); -} - -CPointBase::CPointBase(short x, short y, char z, uchar map) noexcept : - m_x(x), m_y(y), m_z(z), m_map(map) -{ -} - bool CPointBase::operator== ( const CPointBase & pt ) const noexcept { return( m_x == pt.m_x && m_y == pt.m_y && m_z == pt.m_z && m_map == pt.m_map ); diff --git a/src/common/CPointBase.h b/src/common/CPointBase.h index 530936768..544392241 100644 --- a/src/common/CPointBase.h +++ b/src/common/CPointBase.h @@ -42,19 +42,27 @@ struct CPointBase // Non initialized 3d point. CPointBase& InitPoint() noexcept; CPointBase& ZeroPoint() noexcept; - CPointBase() noexcept; - - // This destructor (and the ones of the child classes) are willingly NOT virtual. - // If the class had any virtual method, its size will increase of at least 4-8 bytes (size of a pointer to the virtual table). - // It matters the most because CPointBase is used in the union inside CItem. Increasing the size of CPointBase will increase the size of the union. - // Moreover, it will disalign the addresses of the data inside the other structs of the union. - // Last but not least, remember that deleting an inheriting class without a virtual destructor will delete only a part of the class! It will not call the topmost destructor! - ~CPointBase() noexcept = default; + inline CPointBase() noexcept : + m_x(-1), m_y(-1), m_z(0), m_map(0) // Same thing as calling InitPoint(), but without this extra function call + { + //InitPoint(); + } - CPointBase(short x, short y, char z = 0, uchar map = 0) noexcept; - CPointBase(const CPointBase&) noexcept = default; + inline CPointBase(short x, short y, char z, uchar map) noexcept : + m_x(x), m_y(y), m_z(z), m_map(map) + { + } + CPointBase(const CPointBase&) noexcept = default; CPointBase(CPointBase&&) noexcept = default; + // This destructor (and the ones of the child classes) are willingly NOT virtual. + // If the class had any virtual method, its size will increase of at least 4-8 bytes (size of a pointer to the virtual table). + // It matters the most because CPointBase is used in the union inside CItem. Increasing the size of CPointBase will increase the size of the union. + // Moreover, it will disalign the addresses of the data inside the other structs of the union. + // Last but not least, remember that deleting an inheriting class without a virtual destructor will delete only a part of the class! It will not call the topmost destructor! + ~CPointBase() noexcept = default; + + CPointBase& operator = (const CPointBase&) noexcept = default; bool operator == ( const CPointBase & pt ) const noexcept; bool operator != ( const CPointBase & pt ) const noexcept; diff --git a/src/common/CRect.cpp b/src/common/CRect.cpp index 3ab4a941b..98c9c986d 100644 --- a/src/common/CRect.cpp +++ b/src/common/CRect.cpp @@ -286,7 +286,7 @@ CPointBase CRect::GetRectCorner( DIR_TYPE dir ) const return pt; } -CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that make up this rect. +CSector * CRect::GetSector( int i ) const noexcept // get all the sectors that make up this rect. { //ADDTOCALLSTACK_DEBUG("CRect::GetSector"); // get all the CSector(s) that overlap this rect. @@ -294,17 +294,22 @@ CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that ma // Align new rect. const CSectorList &pSectors = CSectorList::Get(); - const MapSectorsData& sd = pSectors.GetMapSectorData(m_map); - const int iSectorSize = sd.iSectorSize; - const int iSectorCols = sd.iSectorColumns; - const uint uiSectorShift = sd.uiSectorDivShift; - - CRectMap rect; - rect.m_left = m_left & ~(iSectorSize-1); // aligns the left boundary down to the nearest multiple of iSectorSize. - rect.m_right = (m_right | (iSectorSize-1)) + 1; // rounds the right boundary up to cover the full last sector. - rect.m_top = m_top & ~(iSectorSize-1); - rect.m_bottom = (m_bottom | (iSectorSize-1)) + 1; - rect.m_map = m_map; + const MapSectorsData* pSecData = pSectors.GetMapSectorData(m_map); + if (!pSecData) [[unlikely]] + return nullptr; + + const int iSectorSize = pSecData->iSectorSize; + const int iSectorCols = pSecData->iSectorColumns; + const uint uiSectorShift = pSecData->uiSectorDivShift; + + // CRectMap(int left, int top, int right, int bottom, int map) + CRectMap rect( + (m_left & ~(iSectorSize-1)), // aligns the left boundary down to the nearest multiple of iSectorSize. + (m_top & ~(iSectorSize-1)), + (m_right | (iSectorSize-1)) + 1, // rounds the right boundary up to cover the full last sector. + (m_bottom | (iSectorSize-1)) + 1, + m_map + ); rect.NormalizeRectMax(); //const int width = (rect.GetWidth()) / iSectorSize; @@ -314,7 +319,7 @@ CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that ma #ifdef _DEBUG ASSERT(width <= iSectorCols); - const int iSectorRows = sd.iSectorRows; + const int iSectorRows = pSecData->iSectorRows; ASSERT(height <= iSectorRows); #endif @@ -332,10 +337,10 @@ CSector * CRect::GetSector( int i ) const noexcept // ge all the sectors that ma const int iBase = (baseRow * iSectorCols) + baseCol; if (i >= (height * width)) - return i ? nullptr : pSectors.GetSectorByIndex(m_map, iBase); + return i ? nullptr : pSectors.GetSectorByIndexUnchecked(m_map, iBase); const int indexoffset = ((i / width) * iSectorCols) + (i % width); - return pSectors.GetSectorByIndex(m_map, iBase + indexoffset); + return pSectors.GetSectorByIndexUnchecked(m_map, iBase + indexoffset); } diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 471c6acf4..ec581af85 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -68,15 +68,17 @@ bool CRegion::RealizeRegion() // Link the region to the world. RETURN: false = not valid. if ( IsRegionEmpty() ) return false; + + ASSERT(g_MapList.IsMapSupported(m_pt.m_map)); if ( !m_pt.IsValidPoint() ) m_pt = GetRegionCorner( DIR_QTY ); // center // Attach to all sectors that i overlap. ASSERT( m_iLinkedSectors == 0 ); const CSectorList& pSectors = CSectorList::Get(); - for ( int i = 0, iMax = pSectors.GetMapSectorData(m_pt.m_map).iSectorQty; i < iMax; ++i ) + for ( int i = 0, iMax = pSectors.GetMapSectorDataUnchecked(m_pt.m_map).iSectorQty; i < iMax; ++i ) { - CSector *pSector = pSectors.GetSectorByIndex(m_pt.m_map, i); + CSector *pSector = pSectors.GetSectorByIndexUnchecked(m_pt.m_map, i); if ( pSector && IsOverlapped(pSector->GetRect()) ) { diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index 71e391ac9..5a6e3a17a 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -625,7 +625,7 @@ int CSector::GetLocalTime() const } else { - const MapSectorsData& sd = pSectors.GetMapSectorData(pt.m_map); + const MapSectorsData& sd = pSectors.GetMapSectorDataUnchecked(pt.m_map); // Time difference between adjacent sectors in minutes const int iSectorTimeDiff = (24*60) / sd.iSectorColumns; diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index ebfb93f3e..af4bcf691 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -69,7 +69,7 @@ void CSectorList::Init() short iSectorX = 0, iSectorY = 0; for (; iSectorIndex < iSectorQty; ++iSectorIndex) { - // Map sectors are added in row-major order. + // Map sectors are added in row-major order (fill a column, then increment row count and fill columns at that row, and so on). if (iSectorX >= iMaxX) { iSectorX = 0; @@ -120,27 +120,46 @@ void CSectorList::Close(bool fClosingWorld) [[nodiscard]] -const MapSectorsData& CSectorList::GetMapSectorData(int map) const +const MapSectorsData* CSectorList::GetMapSectorData(int map) const noexcept { - DEBUG_ASSERT(g_MapList.IsMapSupported(map)); + if (!g_MapList.IsMapSupported(map)) + return nullptr; + + return &_SectorData[map]; +} + +[[nodiscard]] +const MapSectorsData& CSectorList::GetMapSectorDataUnchecked(int map) const noexcept +{ + // We assume we HAVE checked that's a valid map and that we are not indexing the _SectorData out of bounds. + //if (!g_MapList.IsMapSupported(map)) + // return nullptr; + return _SectorData[map]; } -CSector* CSectorList::GetSectorByIndex(int map, int index) const noexcept +CSector* CSectorList::GetSectorByIndexUnchecked(int map, int index) const noexcept { // We call this method very often and from places we ASSUME have already checked that the map number is legit. + // (it's done in CWorldMap::GetSectorByIndex). + // Micro-optimization: Skip the function call and the if statement to avoid the possible cost of a branch misprediction. //if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) // return nullptr; + //if ((map < 0) || ((size_t)map < sizeof(_SectorData)) ) + // return nullptr; const MapSectorsData& sd = _SectorData[map]; + //return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; return (index < sd.iSectorQty) ? &(sd._pSectors.get()[index]) : nullptr; } -CSector* CSectorList::GetSectorByCoordsUnchecked(int map, short x, short y) const noexcept +CSector* CSectorList::GetSectorByCoordsUnchecked(int map, short x, short y) const NOEXCEPT_NODEBUG { // We call this method very often and from places we ASSUME have already checked that the map number is legit. + // (it's done in CPointBase::GetSector()) + // Micro-optimization: Skip the IsMapSupported function call and the if statement to avoid the possible cost of a branch misprediction. //if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) // return nullptr; diff --git a/src/game/CSectorList.h b/src/game/CSectorList.h index df943b854..9f267f320 100644 --- a/src/game/CSectorList.h +++ b/src/game/CSectorList.h @@ -49,10 +49,12 @@ class CSectorList void Close(bool fClosingWorld); [[nodiscard]] - const MapSectorsData& GetMapSectorData(int map) const; + const MapSectorsData* GetMapSectorData(int map) const noexcept; + [[nodiscard]] + const MapSectorsData& GetMapSectorDataUnchecked(int map) const noexcept; - CSector* GetSectorByIndex(int map, int index) const noexcept; // gets sector # from one map - CSector* GetSectorByCoordsUnchecked(int map, short x, short y) const noexcept; + CSector* GetSectorByIndexUnchecked(int map, int index) const noexcept; // gets sector # from one map + CSector* GetSectorByCoordsUnchecked(int map, short x, short y) const NOEXCEPT_NODEBUG; int GetSectorAbsoluteQty() const noexcept; CSector* GetSectorAbsolute(int index) noexcept; diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index 76908d285..7e8931b36 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -156,16 +156,18 @@ void CItemsList::AddItemToSector( CItem * pItem ) void CSectorBase::SetAdjacentSectors() { const CSectorList& pSectors = CSectorList::Get(); - auto const& sd = pSectors.GetMapSectorData(m_BasePoint.m_map); + ASSERT_ALWAYS(g_MapList.IsMapSupported(m_BasePoint.m_map)); + auto const& sd = pSectors.GetMapSectorDataUnchecked(m_BasePoint.m_map); const int iMaxX = sd.iSectorColumns; ASSERT(iMaxX > 0); - [[maybe_unused]] const int iMaxY = sd.iSectorRows; + const int iMaxY = sd.iSectorRows; ASSERT(iMaxY > 0); const int iMaxSectors = sd.iSectorQty; + ASSERT(iMaxSectors > 9); - // Sectors are layed out in the array horizontally: when the row is complete (X), the subsequent sector is placed in - // the column below (Y). + // Sectors are laid out in the array horizontally (row-major order): when the row is complete (X), + // the subsequent sector is placed in the row below (Y). // Between each X coordinate there's a single sector index difference; // between each Y coordinate there's a number of sectors equal to the sectors in a row. /* @@ -197,22 +199,23 @@ void CSectorBase::SetAdjacentSectors() for (int i = 0; i < (int)DIR_QTY; ++i) { // out of bounds checks - const int iAdjX = m_BasePoint.m_x + _xyDir[i].x; // These checks are needed or the negative iAdjX/iAdjY can lead to a wrong index if the sector is near the map borders. // For instance m_index = 0, iAdjY > 0 and iAdjX < 0, SW check, the index start from the first column of the second row and it goes back to the first row because there is no SW sector + const int iAdjX = m_BasePoint.m_x + _xyDir[i].x; if ((iAdjX < 0) || (iAdjX >= iMaxX)) continue; + const int iAdjY = m_BasePoint.m_y + _xyDir[i].y; if ((iAdjY < 0) || (iAdjY >= iMaxY)) continue; - int index = m_index; - index += ((iAdjY * iMaxX) + iAdjX); - if (index < 0 || (index > iMaxSectors)) - { + const int iAdjIndex = m_index + ((iAdjY * iMaxX) + iAdjX); + if ((iAdjIndex < 0) || (iAdjIndex > iMaxSectors)) continue; - } - _ppAdjacentSectors[(DIR_TYPE)i] = pSectors.GetSectorByIndex(m_BasePoint.m_map, index); + + _ppAdjacentSectors[(DIR_TYPE)i] = pSectors.GetSectorByIndexUnchecked(m_BasePoint.m_map, iAdjIndex); + + //g_Log.EventDebug("Sector %d, Setting adjacent sector %d.\n", m_index, iAdjIndex); } } @@ -238,7 +241,7 @@ void CSectorBase::Init(int index, uchar map, short x, short y) g_Log.EventError("Trying to initalize a sector %d in unsupported map #%d. Defaulting to 0,0.\n", index, map); return; } - if (( index < 0 ) || ( index >= CSectorList::Get().GetMapSectorData(map).iSectorQty )) + if (( index < 0 ) || ( index >= CSectorList::Get().GetMapSectorDataUnchecked(map).iSectorQty )) { m_BasePoint.m_map = map; g_Log.EventError("Trying to initalize a sector by sector number %d out-of-range for map #%d. Defaulting to 0,%d.\n", index, map, map); @@ -248,11 +251,10 @@ void CSectorBase::Init(int index, uchar map, short x, short y) ASSERT(x >= 0 && y >= 0); m_index = index; - // Set BasePoint. - auto const& sd = CSectorList::Get().GetMapSectorData(map); + auto const& sd = CSectorList::Get().GetMapSectorDataUnchecked(map); DEBUG_ASSERT((m_index >= 0) && (m_index < sd.iSectorQty)); - m_BasePoint = // Initializer list for CPointMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) + m_BasePoint = CPointBase { x, // x y, // y @@ -260,11 +262,10 @@ void CSectorBase::Init(int index, uchar map, short x, short y) (uint8)map // m }; - // Set MapRect. - m_MapRect = // Initializer list for CRectMap, it's the fastest way to return an object (requires less optimizations, which aren't used in debug build) + m_MapRect = CRectMap { m_BasePoint.m_x, // left - m_BasePoint.m_y, // yop + m_BasePoint.m_y, // top m_BasePoint.m_x + sd.iSectorSize, // right: East m_BasePoint.m_y + sd.iSectorSize, // bottom: South m_BasePoint.m_map // map diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index eb55c3d87..8127a4cd5 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -1103,7 +1103,7 @@ bool CServerConfig::r_LoadVal( CScript &s ) { if ( !strnicmp(pszStr, "ALLSECTORS", 10) ) { - const int nSectors = CSectorList::Get().GetMapSectorData(nMapNumber).iSectorQty; + const int nSectors = CSectorList::Get().GetMapSectorDataUnchecked(nMapNumber).iSectorQty; pszStr = s.GetArgRaw(); if ( pszStr && *pszStr ) @@ -1734,7 +1734,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * { pszCmd += 7; const CSectorList& pSectors = CSectorList::Get(); - const MapSectorsData& sd = pSectors.GetMapSectorData(iNumber); + const MapSectorsData& sd = pSectors.GetMapSectorDataUnchecked(iNumber); if (!strnicmp(pszCmd, "SIZE", 4)) sVal.FormatVal(sd.iSectorSize); diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index f4cbbd90d..18aab3863 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -1260,9 +1260,9 @@ void CWorld::SaveStatics() if ( !g_MapList.IsMapSupported(m) ) continue; - for (int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s) + for (int s = 0, qty = _Sectors.GetMapSectorDataUnchecked(m).iSectorQty; s < qty; ++s) { - CSector* pSector = _Sectors.GetSectorByIndex(m, s); + CSector* pSector = _Sectors.GetSectorByIndexUnchecked(m, s); if ( !pSector ) continue; @@ -1282,7 +1282,7 @@ void CWorld::SaveStatics() m_FileStatics.WriteSection( "EOF" ); m_FileStatics.Close(); - g_Log.Event(LOGM_SAVE, "Statics data saved (%s).\n", static_cast(m_FileStatics.GetFilePath())); + g_Log.Event(LOGM_SAVE, "Statics data saved (%s).\n", m_FileStatics.GetFilePath()); } catch (const CSError& e) { @@ -1470,11 +1470,11 @@ bool CWorld::LoadAll() // Load world from script if (!g_MapList.IsMapSupported(m)) continue; - for (int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s) + for (int s = 0, qty = _Sectors.GetMapSectorDataUnchecked(m).iSectorQty; s < qty; ++s) { EXC_TRYSUB("Load"); - CSector* pSector = _Sectors.GetSectorByIndex(m, s); + CSector* pSector = _Sectors.GetSectorByIndexUnchecked(m, s); ASSERT(pSector); if (!pSector->IsLightOverriden()) @@ -1658,11 +1658,11 @@ void CWorld::RespawnDeadNPCs() if ( !g_MapList.IsMapSupported(m) ) continue; - for (int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s) + for (int s = 0, qty = _Sectors.GetMapSectorDataUnchecked(m).iSectorQty; s < qty; ++s) { EXC_TRY("OnSector"); - CSector* pSector = _Sectors.GetSectorByIndex(m, s); + CSector* pSector = _Sectors.GetSectorByIndexUnchecked(m, s); ASSERT(pSector); pSector->RespawnDeadNPCs(); @@ -1698,11 +1698,11 @@ void CWorld::Restock() if ( !g_MapList.IsMapSupported(m) ) continue; - for ( int s = 0, qty = _Sectors.GetMapSectorData(m).iSectorQty; s < qty; ++s ) + for ( int s = 0, qty = _Sectors.GetMapSectorDataUnchecked(m).iSectorQty; s < qty; ++s ) { EXC_TRY("OnSector"); - CSector *pSector = _Sectors.GetSectorByIndex(m, s); + CSector *pSector = _Sectors.GetSectorByIndexUnchecked(m, s); ASSERT(pSector); pSector->Restock(); diff --git a/src/game/CWorldMap.cpp b/src/game/CWorldMap.cpp index 2cd569990..f14ebe125 100644 --- a/src/game/CWorldMap.cpp +++ b/src/game/CWorldMap.cpp @@ -230,14 +230,21 @@ CSector* CWorldMap::GetSectorByIndex(int map, int index) noexcept // static { //ADDTOCALLSTACK_DEBUG("CWorldMap::GetSectorByIndex(index)"); - const int iMapSectorQty = g_World._Sectors.GetMapSectorData(map).iSectorQty; + const MapSectorsData * pSd = g_World._Sectors.GetMapSectorData(map); + if (!pSd) + { + g_Log.EventError("Requested sector #%d for unsupported map #%d.\n", index, map); + return nullptr; + } + + const int iMapSectorQty = pSd->iSectorQty; if (index >= iMapSectorQty) { g_Log.EventError("Unsupported sector #%d for map #%d specified.\n", index, map); return nullptr; } - return g_World._Sectors.GetSectorByIndex(map, index); + return g_World._Sectors.GetSectorByIndexUnchecked(map, index); } CSector* CWorldMap::GetSectorByCoordsUnchecked(int map, short x, short y) noexcept // static @@ -293,7 +300,10 @@ const CUOMapMeter* CWorldMap::GetMapMeter(const CPointMap& pt) // static if (!pMapBlock) return nullptr; - return pMapBlock->GetTerrain(UO_BLOCK_OFFSET(pt.m_x), UO_BLOCK_OFFSET(pt.m_y)); + return pMapBlock->GetTerrain( + UO_BLOCK_OFFSET(pt.m_x), + UO_BLOCK_OFFSET(pt.m_y) + ); } std::optional CWorldMap::GetMapMeterAdjusted(const CPointMap& pt) @@ -385,7 +395,8 @@ CPointMap CWorldMap::FindTypeNear_Top( const CPointMap & pt, IT_TYPE iType, int pDupeDef = CItemBaseDupe::GetDupeRef((ITEMID_TYPE)(pItem->GetDispID())); if ( ! pDupeDef ) { - g_Log.EventDebug("Failed to get non-parent reference (dynamic) (DispID 0%x) (X: %d Y: %d Z: %d)\n",pItem->GetDispID(),ptTest.m_x,ptTest.m_y,ptTest.m_z); + g_Log.EventDebug("Failed to get non-parent reference (dynamic) (DispID 0%x) (X: %d Y: %d Z: %d)\n", + pItem->GetDispID(),ptTest.m_x,ptTest.m_y,ptTest.m_z); Height = pItemDef->GetHeight(); } else @@ -398,10 +409,11 @@ CPointMap CWorldMap::FindTypeNear_Top( const CPointMap & pt, IT_TYPE iType, int continue; //if ( ((( pItem->GetTopPoint().m_z - pt.m_z ) > 0) && ( pItem->GetTopPoint().m_z - pt.m_z ) > RESOURCE_Z_CHECK ) || ((( pt.m_z - pItem->GetTopPoint().m_z ) < 0) && (( pt.m_z - pItem->GetTopPoint().m_z ) < - RESOURCE_Z_CHECK ))) - if ( ((( z - pt.m_z ) > 0) && ( z - pt.m_z ) > RESOURCE_Z_CHECK ) || ((( pt.m_z - z ) < 0) && (( pt.m_z - z ) < - RESOURCE_Z_CHECK ))) + if ( ((( z - pt.m_z ) > 0) && ( z - pt.m_z ) > RESOURCE_Z_CHECK ) || + ((( pt.m_z - z ) < 0) && (( pt.m_z - z ) < - RESOURCE_Z_CHECK )) ) continue; - if (( z < ptElem[0].m_z ) || (( z == ptElem[0].m_z ) && ( fElem[0] ))) + if (( z < ptElem[0].m_z ) || (( z == ptElem[0].m_z ) && fElem[0])) continue; ptElem[0] = ptItemTop; From 061dbd6a480542505b3178ba846b1931e77d6d5a Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 30 Oct 2025 16:49:22 +0100 Subject: [PATCH 108/112] Potentially fixed sector issue added by e90ca922 --- Changelog.txt | 7 +++++++ src/game/CSectorList.cpp | 13 +++++++------ src/game/CSectorTemplate.cpp | 3 +++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 796b515e0..0053aadbd 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -4062,3 +4062,10 @@ When setting a property like MORE to the a spell or skill defname, trying to rea 30-09-2025, Mulambo - Changed: Login rejection (packet `0x82`) is now sent to all clients trying to log in (used to be only to clients, that Sphere could identify). + +30-10-2025, Nolok +- Fixed: Corner cases issues with number parsing from scripts. +- Fixed: Function call stack messed up ordering. +- Fixed: Wrong script line pointed to in console log messages. +- Fixed: Changing Char flags didn't always trigger a full update packet. +- Changed: Made both Windows and Linux load scripts in a folder in alphabetical order. diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index af4bcf691..bd2b7cae6 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -36,7 +36,7 @@ void CSectorList::Init() we need to (re)set iSectorIndex to 0 when Sphere finish to initialize every sectors in a map, otherwise iSectorIndex will have the same value of iSectorQty when Sphere finish loading map0. */ - int iSectorIndex = 0; + int iSectorIndex = 0; MapSectorsData& sd = _SectorData[iMap]; sd.iSectorSize = sd.iSectorColumns = sd.iSectorRows = sd.iSectorQty = 0; @@ -53,6 +53,8 @@ void CSectorList::Init() Str_ConcatLimitNull(tsConcat.buffer(), ts.buffer(), tsConcat.capacity()); sd._pSectors = std::make_unique(iSectorQty); + ASSERT(sd._pSectors); + sd.iSectorSize = g_MapList.GetSectorSize(iMap); sd.iSectorQty = iSectorQty; sd.iSectorColumns = iMaxX; @@ -70,17 +72,16 @@ void CSectorList::Init() for (; iSectorIndex < iSectorQty; ++iSectorIndex) { // Map sectors are added in row-major order (fill a column, then increment row count and fill columns at that row, and so on). - if (iSectorX >= iMaxX) + if (iSectorY >= iMaxY) { - iSectorX = 0; - ++iSectorY; + iSectorY = 0; + ++iSectorX; } CSector* pSector = &(sd._pSectors[iSectorIndex]); - ASSERT(pSector); pSector->Init(iSectorIndex, (uchar)iMap, iSectorX, iSectorY); - ++iSectorX; + ++iSectorY; } } diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index 7e8931b36..af209be92 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -241,6 +241,9 @@ void CSectorBase::Init(int index, uchar map, short x, short y) g_Log.EventError("Trying to initalize a sector %d in unsupported map #%d. Defaulting to 0,0.\n", index, map); return; } + + // Sooner or later we have to change those signed values to unsigned... they should never be negative in any case + // and we're doing a number of potentially useless checks. if (( index < 0 ) || ( index >= CSectorList::Get().GetMapSectorDataUnchecked(map).iSectorQty )) { m_BasePoint.m_map = map; From e74d51df7347f6aff0057b6a4efe8621b8341b0f Mon Sep 17 00:00:00 2001 From: cbnolok Date: Thu, 30 Oct 2025 17:32:47 +0100 Subject: [PATCH 109/112] Fixed compilation error, moved some stypecast.h non templated functions to .cpp --- src/common/resource/sections/CDialogDef.cpp | 30 ++- src/common/sphere_library/stypecast.cpp | 124 ++++++++++++ src/common/sphere_library/stypecast.h | 210 ++++++-------------- 3 files changed, 204 insertions(+), 160 deletions(-) diff --git a/src/common/resource/sections/CDialogDef.cpp b/src/common/resource/sections/CDialogDef.cpp index 9dd70ef8f..038aed079 100644 --- a/src/common/resource/sections/CDialogDef.cpp +++ b/src/common/resource/sections/CDialogDef.cpp @@ -158,19 +158,27 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t if ( *ptcArgs_ == '-' && IsSpace(ptcArgs_[1])) c = iCoordBase_, ++ptcArgs_; else if ( *ptcArgs_ == '+' ) - c = iCoordBase_ + gExprParser.GetSingle(++ptcArgs_ ); + c = iCoordBase_ + n64_narrow_n32_checked(gExprParser.GetSingle(++ptcArgs_ )); else if ( *ptcArgs_ == '-' ) - c = iCoordBase_ - gExprParser.GetSingle(++ptcArgs_ ); + c = iCoordBase_ - n64_narrow_n32_checked(gExprParser.GetSingle(++ptcArgs_ )); else if ( *ptcArgs_ == '*' ) - iCoordBase_ = c = iCoordBase_ + gExprParser.GetSingle( ++ptcArgs_ ); + iCoordBase_ = c = iCoordBase_ + n64_narrow_n32_checked(gExprParser.GetSingle( ++ptcArgs_ )); else - c = gExprParser.GetSingle( ptcArgs_ ); + c = n64_narrow_n32_checked(gExprParser.GetSingle( ptcArgs_ )); return c; }; -# define GET_RELATIVE(c, base) _SkipAll(ptcArgs); const int c = _CalcRelative(ptcArgs, base); -# define GET_ABSOLUTE(c) _SkipAll(ptcArgs); const int c = gExprParser.GetSingle(ptcArgs); -# define GET_EVAL(c) _SkipAll(ptcArgs); const int c = gExprParser.GetVal(ptcArgs); +# define GET_RELATIVE(c, base) \ + _SkipAll(ptcArgs); \ + const int c = _CalcRelative(ptcArgs, base); + +# define GET_ABSOLUTE(c) \ + _SkipAll(ptcArgs); \ + const int c = n64_narrow_n32_checked(gExprParser.GetSingle(ptcArgs), false); + +# define GET_EVAL(c) \ + _SkipAll(ptcArgs); \ + const int c = n64_narrow_n32_checked(gExprParser.GetVal(ptcArgs), false); switch( index ) { @@ -432,17 +440,17 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t if ( *ptcArgs == '-' && (IsSpace( ptcArgs[1] ) || !ptcArgs[1]) ) ++ptcArgs; else if ( *ptcArgs == '*' ) - m_iOriginX += gExprParser.GetSingle( ++ptcArgs ); + m_iOriginX += n64_narrow_n32_checked(gExprParser.GetSingle( ++ptcArgs ), false); else - m_iOriginX = gExprParser.GetSingle( ptcArgs ); + m_iOriginX = n64_narrow_n32_checked(gExprParser.GetSingle( ptcArgs ), false); _SkipAll( ptcArgs ); if ( *ptcArgs == '-' && (IsSpace( ptcArgs[1] ) || !ptcArgs[1]) ) ++ptcArgs; else if ( *ptcArgs == '*' ) - m_iOriginY += gExprParser.GetSingle( ++ptcArgs ); + m_iOriginY += n64_narrow_n32_checked(gExprParser.GetSingle( ++ptcArgs ), false); else - m_iOriginY = gExprParser.GetSingle( ptcArgs ); + m_iOriginY = n64_narrow_n32_checked(gExprParser.GetSingle( ptcArgs ), false); return true; } diff --git a/src/common/sphere_library/stypecast.cpp b/src/common/sphere_library/stypecast.cpp index f0e0c3de4..ed118100c 100644 --- a/src/common/sphere_library/stypecast.cpp +++ b/src/common/sphere_library/stypecast.cpp @@ -9,3 +9,127 @@ void LogEventWarnWrapper(const char* warn_str) } } + +[[nodiscard]] +uint32 usize_narrow_u32_checked(const size_t source_val, bool should_assert) +{ + if (should_assert) { + ASSERT(source_val <= std::numeric_limits::max()); + } + else if (source_val > std::numeric_limits::max()) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from size_t to 32 bits unsigned integer will overflow.\n"); + } + return usize_narrow_u32(source_val); +} + +[[nodiscard]] +int8 i8_from_u8_checked(const uint8 source_val, bool should_assert) // not clamping/capping +{ + const bool would_overflow = (source_val > (uint8)std::numeric_limits::max()); + if (should_assert) { + ASSERT(!would_overflow); + } + else if (would_overflow) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 8 bits unsigned integer to 8 bits signed integer will overflow.\n"); + } + + return static_cast(source_val); +} + +[[nodiscard]] +int16 i8_from_u16_checked(const uint16 source_val, bool should_assert) // not clamping/capping +{ + const bool would_overflow = (source_val > (uint16)std::numeric_limits::max()); + if (should_assert) { + ASSERT(!would_overflow); + } + else if (would_overflow) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 16 bits unsigned integer to 8 bits signed integer will overflow.\n"); + } + + return static_cast(source_val); +} + +[[nodiscard]] +int16 i16_from_u16_checked(const uint16 source_val, bool should_assert) // not clamping/capping +{ + const bool would_overflow = (source_val > (uint16)std::numeric_limits::max()); + if (should_assert) { + ASSERT(!would_overflow); + } + else if (would_overflow) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 16 bits unsigned integer to 16 bits signed integer will overflow.\n"); + } + + return static_cast(source_val); +} + +[[nodiscard]] +int16 i16_from_u32_checked(const uint32 source_val, bool should_assert) // not clamping/capping +{ + const bool would_overflow = (source_val > (uint32)std::numeric_limits::max()); + if (should_assert) { + ASSERT(!would_overflow); + } + else if (would_overflow) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 32 bits unsigned integer to 16 bits signed integer will overflow.\n"); + } + + return static_cast(source_val); +} + +[[nodiscard]] +int16 i16_from_u64_checked(const uint64 source_val, bool should_assert) // not clamping/capping +{ + const bool would_overflow = (source_val > (uint64)std::numeric_limits::max()); + if (should_assert) { + ASSERT(!would_overflow); + } + else if (would_overflow) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 bits unsigned integer to 16 bits signed integer will overflow.\n"); + } + + return static_cast(source_val); +} + +[[nodiscard]] +int32 i32_from_u32_checked(const uint32 source_val, bool should_assert) // not clamping/capping +{ + const bool would_overflow = (source_val > (uint32)std::numeric_limits::max()); + if (should_assert) { + ASSERT(!would_overflow); + } + else if (would_overflow) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 32 bits unsigned integer to 32 bits signed integer will overflow.\n"); + } + + return static_cast(source_val); +} + +[[nodiscard]] +int32 i32_from_u64_checked(const uint64 source_val, bool should_assert) // not clamping/capping +{ + const bool would_overflow = (source_val > (uint64)std::numeric_limits::max()); + if (should_assert) { + ASSERT(!would_overflow); + } + else if (would_overflow) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 bits unsigned integer to 32 bits signed integer will overflow.\n"); + } + + return static_cast(source_val); +} + +[[nodiscard]] +int64 i64_from_u64_checked(const uint64 source_val, bool should_assert) // not clamping/capping +{ + const bool would_overflow = (source_val > (uint64)std::numeric_limits::max()); + if (should_assert) { + ASSERT(!would_overflow); + } + else if (would_overflow) { + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 bits unsigned integer to 64 bits signed integer will overflow.\n"); + } + + return static_cast(source_val); +} diff --git a/src/common/sphere_library/stypecast.h b/src/common/sphere_library/stypecast.h index 278cb03d8..2de4c06f6 100644 --- a/src/common/sphere_library/stypecast.h +++ b/src/common/sphere_library/stypecast.h @@ -7,13 +7,8 @@ namespace detail_stypecast { -void LogEventWarnWrapper(const char* warn_str); -} - -// Helper macros -//#define n64_narrow_n32_assert(source_val) (n64_narrow_n32_checked(source_val, true)) -//#define n64_narrow_n32_warn(source_val) (n64_narrow_n32_checked(source_val, false)) +void LogEventWarnWrapper(const char* warn_str); // Helper template to work on enum types. @@ -35,6 +30,12 @@ struct underlying_or_self>> template using underlying_or_self_t = typename underlying_or_self::type; +} + +// Helper macros +//#define n64_narrow_n32_assert(source_val) (n64_narrow_n32_checked(source_val, true)) +//#define n64_narrow_n32_warn(source_val) (n64_narrow_n32_checked(source_val, false)) + // Use this as a double check, to be sure at compile time that the two variables have the same size and sign. template @@ -58,8 +59,12 @@ constexpr Tout enum_alias_cast(const Tin source_val) noexcept static_assert(std::is_arithmetic_v || std::is_enum_v, "Output variable is not a numeric type."); static_assert(sizeof(Tin) == sizeof(Tout), "Input and output types do not have the same size."); - static_assert(!(std::is_signed_v> && std::is_unsigned_v>), "Casting signed to unsigned."); - static_assert(!(std::is_signed_v> && std::is_unsigned_v> ), "Casting unsigned to signed."); + static_assert(!(std::is_signed_v > && + std::is_unsigned_v >), + "Casting signed to unsigned."); + static_assert(!(std::is_signed_v > && + std::is_unsigned_v > ), + "Casting unsigned to signed."); /* static_assert(std::is_enum_v || @@ -167,8 +172,8 @@ constexpr auto n64_narrow_n32(const T source_val) noexcept // ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow, otherwise // otherwise just print an error if it overflows (wise to do this if the values depends from user input/scripts). template -[[nodiscard]] inline - auto n64_narrow_n32_checked(const T source_val, bool should_assert) +[[nodiscard]] + auto n64_narrow_n32_checked(const T source_val, bool should_assert = false) { if (should_assert) { @@ -195,7 +200,7 @@ template template [[nodiscard]] -constexpr auto enum64_narrow_n32_checked(const T source_val, bool should_assert) noexcept +constexpr auto enum64_narrow_n32_checked(const T source_val, bool should_assert = false) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); return n64_narrow_n32_checked(static_cast>(source_val, should_assert)); @@ -226,8 +231,8 @@ constexpr auto n64_narrow_n16(const T source_val) noexcept // ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow, otherwise // otherwise just print an error if it overflows (wise to do this if the values depends from user input/scripts). template -[[nodiscard]] inline - auto n64_narrow_n16_checked(const T source_val, bool should_assert) +[[nodiscard]] + auto n64_narrow_n16_checked(const T source_val, bool should_assert = false) { if (should_assert) { @@ -255,7 +260,7 @@ template template [[nodiscard]] -constexpr auto enum64_narrow_n16_checked(const T source_val, bool should_assert) noexcept +constexpr auto enum64_narrow_n16_checked(const T source_val, bool should_assert = false) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); return n64_narrow_n16_checked(static_cast>(source_val, should_assert)); @@ -283,8 +288,8 @@ constexpr auto n64_narrow_n8(const T source_val) noexcept // ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow, otherwise // otherwise just print an error if it overflows (wise to do this if the values depends from user input/scripts). template -[[nodiscard]] inline - auto n64_narrow_n8_checked(const T source_val, bool should_assert) +[[nodiscard]] + auto n64_narrow_n8_checked(const T source_val, bool should_assert = false) { if (should_assert) { @@ -311,7 +316,7 @@ template template [[nodiscard]] -constexpr auto enum64_narrow_n8_checked(const T source_val, bool should_assert) noexcept +constexpr auto enum64_narrow_n8_checked(const T source_val, bool should_assert = false) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); return n64_narrow_n8_checked(static_cast>(source_val, should_assert)); @@ -342,8 +347,8 @@ constexpr auto n32_narrow_n16(const T source_val) noexcept // ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow, otherwise // otherwise just print an error if it overflows (wise to do this if the values depends from user input/scripts). template -[[nodiscard]] inline - auto n32_narrow_n16_checked(const T source_val, bool should_assert) +[[nodiscard]] + auto n32_narrow_n16_checked(const T source_val, bool should_assert = false) { if (should_assert) { @@ -370,7 +375,7 @@ template template [[nodiscard]] -constexpr auto enum32_narrow_n16_checked(const T source_val, bool should_assert) noexcept +constexpr auto enum32_narrow_n16_checked(const T source_val, bool should_assert = false) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); return n32_narrow_n16_checked(static_cast>(source_val, should_assert)); @@ -396,8 +401,8 @@ constexpr auto n32_narrow_n8(const T source_val) noexcept // ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow, otherwise // otherwise just print an error if it overflows (wise to do this if the values depends from user input/scripts). template -[[nodiscard]] inline - auto n32_narrow_n8_checked(const T source_val, bool should_assert) +[[nodiscard]] + auto n32_narrow_n8_checked(const T source_val, bool should_assert = false) { if (should_assert) { @@ -424,7 +429,7 @@ template template [[nodiscard]] -constexpr auto enum32_narrow_n8_checked(const T source_val, bool should_assert) noexcept +constexpr auto enum32_narrow_n8_checked(const T source_val, bool should_assert = false) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); return n32_narrow_n8_checked(static_cast>(source_val, should_assert)); @@ -451,8 +456,8 @@ constexpr auto n16_narrow_n8(const T source_val) noexcept // ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow, otherwise // otherwise just print an error if it overflows (wise to do this if the values depends from user input/scripts). template -[[nodiscard]] inline - auto n16_narrow_n8_checked(const T source_val, bool should_assert) +[[nodiscard]] + auto n16_narrow_n8_checked(const T source_val, bool should_assert = false) { if (should_assert) { @@ -479,7 +484,7 @@ template template [[nodiscard]] -constexpr auto enum16_narrow_n8_checked(const T source_val, bool should_assert) noexcept +constexpr auto enum16_narrow_n8_checked(const T source_val, bool should_assert= false) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); return n16_narrow_n8_checked(static_cast>(source_val, should_assert)); @@ -506,17 +511,8 @@ constexpr uint32 usize_narrow_u32(const size_t source_val) noexcept } // If size_t is bigger than a 32 bits number, narrow it to a 32 bits number and ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow. If size_t has 32 bits size, plain return the same value. -[[nodiscard]] inline -uint32 usize_narrow_u32_checked(const size_t source_val, bool should_assert) -{ - if (should_assert) { - ASSERT(source_val <= std::numeric_limits::max()); - } - else if (source_val > std::numeric_limits::max()) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from size_t to 32 bits unsigned integer will overflow.\n"); - } - return usize_narrow_u32(source_val); -} +[[nodiscard]] +uint32 usize_narrow_u32_checked(const size_t source_val, bool should_assert = false); /* Unsigned (and size_t) to signed, clamping. */ @@ -569,132 +565,45 @@ uint32 usize_narrow_u32_checked(const size_t source_val, bool should_assert) /* Unsigned (and size_t) to signed, checked for overflows. */ -[[nodiscard]] inline - int8 i8_from_u8_checked(const uint8 source_val, bool should_assert) // not clamping/capping -{ - const bool would_overflow = (source_val > (uint8)std::numeric_limits::max()); - if (should_assert) { - ASSERT(!would_overflow); - } - else if (would_overflow) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 8 bits unsigned integer to 8 bits signed integer will overflow.\n"); - } - - return static_cast(source_val); -} +[[nodiscard]] + int8 i8_from_u8_checked(const uint8 source_val, bool should_assert = false); // not clamping/capping template int8 i8_from_u8_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -[[nodiscard]] inline - int16 i8_from_u16_checked(const uint16 source_val, bool should_assert) // not clamping/capping -{ - const bool would_overflow = (source_val > (uint16)std::numeric_limits::max()); - if (should_assert) { - ASSERT(!would_overflow); - } - else if (would_overflow) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 16 bits unsigned integer to 8 bits signed integer will overflow.\n"); - } - - return static_cast(source_val); -} +[[nodiscard]] + int16 i8_from_u16_checked(const uint16 source_val, bool should_assert = false); // not clamping/capping template int16 i8_from_u16_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -[[nodiscard]] inline - int16 i16_from_u16_checked(const uint16 source_val, bool should_assert) // not clamping/capping -{ - const bool would_overflow = (source_val > (uint16)std::numeric_limits::max()); - if (should_assert) { - ASSERT(!would_overflow); - } - else if (would_overflow) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 16 bits unsigned integer to 16 bits signed integer will overflow.\n"); - } - - return static_cast(source_val); -} +[[nodiscard]] + int16 i16_from_u16_checked(const uint16 source_val, bool should_assert = false); // not clamping/capping template int16 i16_from_u16_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -[[nodiscard]] inline - int16 i16_from_u32_checked(const uint32 source_val, bool should_assert) // not clamping/capping -{ - const bool would_overflow = (source_val > (uint32)std::numeric_limits::max()); - if (should_assert) { - ASSERT(!would_overflow); - } - else if (would_overflow) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 32 bits unsigned integer to 16 bits signed integer will overflow.\n"); - } - - return static_cast(source_val); -} +[[nodiscard]] + int16 i16_from_u32_checked(const uint32 source_val, bool should_assert = false); // not clamping/capping template int16 i16_from_u32_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -[[nodiscard]] inline - int16 i16_from_u64_checked(const uint64 source_val, bool should_assert) // not clamping/capping -{ - const bool would_overflow = (source_val > (uint64)std::numeric_limits::max()); - if (should_assert) { - ASSERT(!would_overflow); - } - else if (would_overflow) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 bits unsigned integer to 16 bits signed integer will overflow.\n"); - } - - return static_cast(source_val); -} +[[nodiscard]] + int16 i16_from_u64_checked(const uint64 source_val, bool should_assert = false); // not clamping/capping template int16 i16_from_u64_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument // Convert a 32 bits unsigned value to signed and ASSERT (because you're reasonably sure but not absolutely certain) that it will fit into its signed datatype counterpart (unsigned variables can store greater values than signed ones). -[[nodiscard]] inline - int32 i32_from_u32_checked(const uint32 source_val, bool should_assert) // not clamping/capping -{ - const bool would_overflow = (source_val > (uint32)std::numeric_limits::max()); - if (should_assert) { - ASSERT(!would_overflow); - } - else if (would_overflow) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 32 bits unsigned integer to 32 bits signed integer will overflow.\n"); - } - - return static_cast(source_val); -} +[[nodiscard]] + int32 i32_from_u32_checked(const uint32 source_val, bool should_assert = false); // not clamping/capping template int32 i32_from_u32_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -[[nodiscard]] inline - int32 i32_from_u64_checked(const uint64 source_val, bool should_assert) // not clamping/capping -{ - const bool would_overflow = (source_val > (uint64)std::numeric_limits::max()); - if (should_assert) { - ASSERT(!would_overflow); - } - else if (would_overflow) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 bits unsigned integer to 32 bits signed integer will overflow.\n"); - } - - return static_cast(source_val); -} +[[nodiscard]] + int32 i32_from_u64_checked(const uint64 source_val, bool should_assert = false); // not clamping/capping template int32 i32_from_u64_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument // Convert a 64 bits unsigned value to signed and ASSERT (because you're reasonably sure but not absolutely certain) that it will fit into its signed datatype counterpart (unsigned variables can store greater values than signed ones). -[[nodiscard]] inline - int64 i64_from_u64_checked(const uint64 source_val, bool should_assert) // not clamping/capping -{ - const bool would_overflow = (source_val > (uint64)std::numeric_limits::max()); - if (should_assert) { - ASSERT(!would_overflow); - } - else if (would_overflow) { - detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 bits unsigned integer to 64 bits signed integer will overflow.\n"); - } - - return static_cast(source_val); -} +[[nodiscard]] + int64 i64_from_u64_checked(const uint64 source_val, bool should_assert = false); // not clamping/capping template int64 i64_from_u64_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument // size_t conversions -[[nodiscard]] inline - int8 i8_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping +[[nodiscard]] +inline + int8 i8_from_usize_checked(const size_t source_val, bool should_assert = false) // not clamping/capping { #if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return n64_narrow_n8_checked(source_val, should_assert); @@ -707,8 +616,9 @@ template int64 i64_from_u64_checked(T, bool) = delete; // disable i template int8 i8_from_usize_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -[[nodiscard]] inline - int16 i16_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping +[[nodiscard]] +inline + int16 i16_from_usize_checked(const size_t source_val, bool should_assert = false) // not clamping/capping { #if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return n64_narrow_n16_checked(source_val, should_assert); @@ -720,8 +630,9 @@ template int8 i8_from_usize_checked(T, bool) = delete; // disable i } template int16 i16_from_usize_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -[[nodiscard]] inline - int32 i32_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping +[[nodiscard]] +inline + int32 i32_from_usize_checked(const size_t source_val, bool should_assert = false) // not clamping/capping { #if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return i32_from_u32_clamping(n64_narrow_n32_checked(source_val, should_assert)); @@ -733,8 +644,9 @@ template int16 i16_from_usize_checked(T, bool) = delete; // disable } template int32 i32_from_usize_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -[[nodiscard]] inline - int64 i64_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping +[[nodiscard]] +inline + int64 i64_from_usize_checked(const size_t source_val, bool should_assert = false) // not clamping/capping { #if (__SIZEOF_POINTER__ == 8) //SIZE_MAX == UINT64_MAX return i64_from_u64_checked(n_alias_cast(source_val), should_assert); From 5c82639dba06712eebeec2c21f3a439a282e9bab Mon Sep 17 00:00:00 2001 From: cbnolok Date: Mon, 3 Nov 2025 11:00:44 +0100 Subject: [PATCH 110/112] Fixed: Sector indexing issue --- Changelog.txt | 3 ++- src/game/CRegion.cpp | 22 ++++++++++++--- src/game/CRegion.h | 50 +++++++++++++++------------------- src/game/CRegionBase.cpp | 6 +++++ src/game/CRegionBase.h | 11 +++----- src/game/CSector.cpp | 35 +++++++++++++----------- src/game/CSectorList.cpp | 15 +++-------- src/game/CSectorTemplate.cpp | 52 ++++++++++++++++++++++++------------ src/game/CSectorTemplate.h | 10 ++++--- src/game/CServerConfig.cpp | 20 +++++++++----- 10 files changed, 128 insertions(+), 96 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 0053aadbd..01b71f701 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -4063,9 +4063,10 @@ When setting a property like MORE to the a spell or skill defname, trying to rea 30-09-2025, Mulambo - Changed: Login rejection (packet `0x82`) is now sent to all clients trying to log in (used to be only to clients, that Sphere could identify). -30-10-2025, Nolok +03-11-2025, Nolok - Fixed: Corner cases issues with number parsing from scripts. - Fixed: Function call stack messed up ordering. - Fixed: Wrong script line pointed to in console log messages. - Fixed: Changing Char flags didn't always trigger a full update packet. +- Fixed: Sector indexing issue. - Changed: Made both Windows and Linux load scripts in a folder in alphabetical order. diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index ec581af85..8371782e9 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -54,7 +54,7 @@ void CRegion::UnRealizeRegion() if ( pSector == nullptr ) break; // Does the rect overlap ? - if ( ! IsOverlapped( pSector->GetRect())) + if ( ! IsOverlapped( pSector->GetRectWorldUnits())) continue; if ( pSector->UnLinkRegion( this )) --m_iLinkedSectors; @@ -80,9 +80,9 @@ bool CRegion::RealizeRegion() { CSector *pSector = pSectors.GetSectorByIndexUnchecked(m_pt.m_map, i); - if ( pSector && IsOverlapped(pSector->GetRect()) ) + if ( pSector && IsOverlapped(pSector->GetRectWorldUnits()) ) { - // Yes, this sector overlapped, so add it to the sector list + // Yes, this sector is overlapped, so add it to the sector list if ( !pSector->LinkRegion(this) ) { g_Log.EventError("Linking sector #%d (map %d) for region %s failed (fatal for this region).\n", i, int(m_pt.m_map), GetName()); @@ -109,6 +109,12 @@ bool CRegion::AddRegionRect( const CRectMap & rect ) return true; } +bool CRegion::SetRegionRect( const CRectMap & rect ) +{ + EmptyRegion(); + return AddRegionRect( rect ); +} + void CRegion::SetName( lpctstr pszName ) { ADDTOCALLSTACK("CRegion::SetName"); @@ -692,6 +698,14 @@ void CRegion::r_Write( CScript &s ) r_WriteBase( s ); } +void CRegion::TogRegionFlags( dword dwFlags, bool fSet ) noexcept +{ + if ( fSet ) + m_dwFlags |= dwFlags; + else + m_dwFlags &= ~dwFlags; + SetModified( REGMOD_FLAGS ); +} bool CRegion::IsGuarded() const { @@ -841,7 +855,7 @@ bool CRegion::SendSectorsVerb( lpctstr pszVerb, lpctstr pszArgs, CTextConsole * break; // Does the rect overlap ? - if ( IsOverlapped( pSector->GetRect() ) ) + if ( IsOverlapped( pSector->GetRectWorldUnits() ) ) { CScript script( pszVerb, pszArgs ); fRet |= pSector->r_Verb( script, pSrc ); diff --git a/src/game/CRegion.h b/src/game/CRegion.h index fd8db13fc..e73c06ff2 100644 --- a/src/game/CRegion.h +++ b/src/game/CRegion.h @@ -62,17 +62,17 @@ class CRegion : public CResourceDef, public CRegionBase public: static const char *m_sClassName; CPointMap m_pt; // safe point in the region. (for teleporting to) - int m_iLinkedSectors; // just for statistics tracking. How many sectors are linked ? + int m_iLinkedSectors; // just for statistics tracking. How many sectors are linked ? dword m_dwModifiedFlags; + CItemMulti* _pMultiLink; // Does this region belong to a multi? static lpctstr const sm_szLoadKeys[]; static lpctstr const sm_szTrigName[RTRIG_QTY+1]; static lpctstr const sm_szVerbKeys[]; - CResourceRefArray m_Events; // trigger [REGION x] when entered or exited RES_REGIONTYPE - CVarDefMap m_TagDefs; // attach extra tags here. + CResourceRefArray m_Events; // trigger [REGION x] when entered or exited RES_REGIONTYPE + CVarDefMap m_TagDefs; // attach extra tags here. CVarDefMap m_BaseDefs; // New Variable storage system - CItemMulti* _pMultiLink; // Does this region belong to a multi? TRIGRET_TYPE OnRegionTrigger( CTextConsole * pChar, RTRIG_TYPE trig ); @@ -106,8 +106,6 @@ class CRegion : public CResourceDef, public CRegionBase bool SendSectorsVerb( lpctstr pszVerb, lpctstr pszArgs, CTextConsole * pSrc ); // distribute to the CSectors public: - virtual bool RealizeRegion(); - void UnRealizeRegion(); #define REGMOD_FLAGS 0x0001 #define REGMOD_EVENTS 0x0002 #define REGMOD_TAGS 0x0004 @@ -134,39 +132,33 @@ class CRegion : public CResourceDef, public CRegionBase virtual bool r_Verb( CScript & s, CTextConsole * pSrc ) override; // Execute command from script virtual void r_Write( CScript & s ); + virtual bool IsValid() const noexcept + { + return m_sName.IsValid(); + } + + virtual bool RealizeRegion(); + void UnRealizeRegion(); + virtual bool AddRegionRect( const CRectMap & rect ) override; - bool SetRegionRect( const CRectMap & rect ) - { - EmptyRegion(); - return AddRegionRect( rect ); - } - inline dword GetRegionFlags() const noexcept + bool SetRegionRect( const CRectMap & rect ); + + inline dword GetRegionFlags() const noexcept { return m_dwFlags; } - bool IsFlag( dword dwFlags ) const noexcept + inline bool IsFlag( dword dwFlags ) const noexcept { - return (( m_dwFlags & dwFlags ) ? true : false ); + return (bool( m_dwFlags & dwFlags )); } - bool IsGuarded() const; - void SetRegionFlags( dword dwFlags ) noexcept + inline void SetRegionFlags( dword dwFlags ) noexcept { m_dwFlags |= dwFlags; } - void TogRegionFlags( dword dwFlags, bool fSet ) noexcept - { - if ( fSet ) - m_dwFlags |= dwFlags; - else - m_dwFlags &= ~dwFlags; - SetModified( REGMOD_FLAGS ); - } + void TogRegionFlags( dword dwFlags, bool fSet ) noexcept; - bool CheckAntiMagic( SPELL_TYPE spell ) const; - virtual bool IsValid() const noexcept - { - return m_sName.IsValid(); - } + bool IsGuarded() const; + bool CheckAntiMagic( SPELL_TYPE spell ) const; bool MakeRegionDefname(); diff --git a/src/game/CRegionBase.cpp b/src/game/CRegionBase.cpp index 6364ac578..6137a6777 100644 --- a/src/game/CRegionBase.cpp +++ b/src/game/CRegionBase.cpp @@ -10,6 +10,12 @@ CRegionBase::CRegionBase() m_rectUnion.SetRectEmpty(); } +void CRegionBase::EmptyRegion() +{ + m_rectUnion.SetRectEmpty(); + m_Rects.clear(); +} + size_t CRegionBase::GetRegionRectCount() const { ADDTOCALLSTACK("CRegionBase::GetRegionRectCount"); diff --git a/src/game/CRegionBase.h b/src/game/CRegionBase.h index 28202f871..67a691147 100644 --- a/src/game/CRegionBase.h +++ b/src/game/CRegionBase.h @@ -23,11 +23,7 @@ class CRegionBase { return m_rectUnion.IsRectEmpty(); } - void EmptyRegion() - { - m_rectUnion.SetRectEmpty(); - m_Rects.clear(); - } + void EmptyRegion(); size_t GetRegionRectCount() const; CRectMap & GetRegionRect(size_t i); const CRectMap & GetRegionRect(size_t i) const; @@ -52,9 +48,8 @@ class CRegionBase CRegionBase(); virtual ~CRegionBase() = default; -private: - CRegionBase(const CRegionBase& copy); - CRegionBase& operator=(const CRegionBase& other); + CRegionBase(const CRegionBase& copy) = delete; + CRegionBase& operator=(const CRegionBase& other) = delete; }; diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index 5a6e3a17a..a96c26867 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -434,7 +434,7 @@ void CSector::r_Write() if ( m_fSaveParity == g_World.m_fSaveParity ) return; // already saved. - CPointMap const& pt = m_BasePoint; + CPointMap const& pt = m_BasePointSectUnits; m_fSaveParity = g_World.m_fSaveParity; bool fHeaderCreated = false; @@ -615,23 +615,23 @@ int CSector::GetLocalTime() const { ADDTOCALLSTACK("CSector::GetLocalTime"); // Get local time of the day (in minutes) + const CPointMap& pt = m_BasePointSectUnits; const CSectorList& pSectors = CSectorList::Get(); - const CPointMap& pt = m_BasePoint; - int64 iLocalTime = CWorldGameTime::GetCurrentTimeInGameMinutes(); + const MapSectorsData& sd = pSectors.GetMapSectorDataUnchecked(pt.m_map); + int64 iLocalTime = CWorldGameTime::GetCurrentTimeInGameMinutes(); if ( !g_Cfg.m_fAllowLightOverride ) { - iLocalTime += ( pt.m_x * 24*60 ) / g_MapList.GetMapSizeX(pt.m_map); + iLocalTime += ( pt.m_x * 24*60 ) / sd.iSectorColumns; } else { - const MapSectorsData& sd = pSectors.GetMapSectorDataUnchecked(pt.m_map); - // Time difference between adjacent sectors in minutes const int iSectorTimeDiff = (24*60) / sd.iSectorColumns; // Calculate the # of columns between here and Castle Britannia ( x = 1400 ) - const int iSectorOffset = ( pt.m_x / sd.iSectorSize); + // TODO: This code doesn't actually do that... + const int iSectorOffset = pt.m_x; // Calculate the time offset from global time const int iTimeOffset = iSectorOffset * iSectorTimeDiff; @@ -835,8 +835,9 @@ void CSector::SetLight( int light ) void CSector::SetDefaultWeatherChance() { ADDTOCALLSTACK("CSector::SetDefaultWeatherChance"); - CPointMap const& pt = m_BasePoint; - byte iPercent = (byte)(IMulDiv( pt.m_y, 100, g_MapList.GetMapSizeY(pt.m_map) )); // 100 = south + const CSectorList& pSectors = CSectorList::Get(); + const MapSectorsData& sd = pSectors.GetMapSectorDataUnchecked(m_BasePointSectUnits.m_map); + byte iPercent = (byte)(IMulDiv( m_BasePointSectUnits.m_y, 100, sd.iSectorRows )); // 100 = south if ( iPercent < 50 ) { // Anywhere north of the Britain Moongate is a good candidate for snow @@ -949,7 +950,7 @@ bool CSector::IsInDungeon() const ADDTOCALLSTACK("CSector::IsInDungeon"); // What part of the maps are filled with dungeons. // Used for light / weather calcs. - CRegion *pRegion = GetRegion(m_BasePoint, REGION_TYPE_AREA); + CRegion *pRegion = GetRegion(GetBasePointMapUnits(), REGION_TYPE_AREA); return ( pRegion && pRegion->IsFlag(REGION_FLAG_UNDERGROUND) ); } @@ -1122,7 +1123,7 @@ void CSector::RespawnDeadNPCs() { ADDTOCALLSTACK("CSector::RespawnDeadNPCs"); // skip sectors in unsupported maps - if ( !g_MapList.IsMapSupported(m_BasePoint.m_map) ) + if ( !g_MapList.IsMapSupported(m_BasePointSectUnits.m_map) ) return; // Respawn dead NPCs @@ -1201,7 +1202,7 @@ bool CSector::_OnTick() */ // do not tick sectors on maps not supported by server - if ( !g_MapList.IsMapSupported(m_BasePoint.m_map) ) + if ( !g_MapList.IsMapSupported(m_BasePointSectUnits.m_map) ) return true; EXC_TRY("_OnTick"); @@ -1337,9 +1338,10 @@ bool CSector::_OnTick() EXC_CATCHSUB("Sector"); EXC_DEBUGSUB_START; - CPointMap const& pt = m_BasePoint; + CPointMap const& pt = m_BasePointSectUnits; g_Log.EventDebug("#0 char 0%x '%s'\n", (dword)(pChar->GetUID()), pChar->GetName()); g_Log.EventDebug("#0 sector #%d [%d,%d,%d,%d]\n", GetIndex(), pt.m_x, pt.m_y, pt.m_z, pt.m_map); + // TODO: add rect cords? EXC_DEBUGSUB_END; } @@ -1348,8 +1350,9 @@ bool CSector::_OnTick() _SetTimeout(SECTOR_TICKING_PERIOD); // Sector is Awake, make it tick after 30 seconds. EXC_DEBUG_START; - CPointMap const& pt = m_BasePoint; + CPointMap const& pt = m_BasePointSectUnits; g_Log.EventError("#4 sector #%d [%hd,%hd,%hhd,%hhu]\n", GetIndex(), pt.m_x, pt.m_y, pt.m_z, pt.m_map); + // TODO: add rect coords? EXC_DEBUG_END; return true; } @@ -1422,7 +1425,7 @@ bool CSector::CheckItemComplexity() const noexcept const size_t uiCount = GetItemComplexity(); if (uiCount > g_Cfg.m_iMaxSectorComplexity) { - g_Log.Event(LOGL_WARN, "%" PRIuSIZE_T " items at %s. Sector too complex!\n", uiCount, m_BasePoint.WriteUsed()); + g_Log.Event(LOGL_WARN, "%" PRIuSIZE_T " items at %s. Sector too complex!\n", uiCount, GetBasePointMapUnits().WriteUsed()); return true; } return false; @@ -1473,7 +1476,7 @@ bool CSector::CheckCharComplexity() const noexcept const size_t uiCount = GetCharComplexity(); if (uiCount > g_Cfg.m_iMaxCharComplexity) { - g_Log.Event(LOGL_WARN, "%" PRIuSIZE_T " chars at %s. Sector too complex!\n", uiCount, m_BasePoint.WriteUsed()); + g_Log.Event(LOGL_WARN, "%" PRIuSIZE_T " chars at %s. Sector too complex!\n", uiCount, GetBasePointMapUnits().WriteUsed()); return true; } return false; diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index bd2b7cae6..ff5e956a4 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -31,13 +31,6 @@ void CSectorList::Init() for (int iMap = 0; iMap < MAP_SUPPORTED_QTY; ++iMap) { - /* - Before iSectorIndex was declared and set to 0 outside the FOR, so I moved it inside because - we need to (re)set iSectorIndex to 0 when Sphere finish to initialize every sectors in a map, otherwise - iSectorIndex will have the same value of iSectorQty when Sphere finish loading map0. - */ - int iSectorIndex = 0; - MapSectorsData& sd = _SectorData[iMap]; sd.iSectorSize = sd.iSectorColumns = sd.iSectorRows = sd.iSectorQty = 0; sd._pSectors.reset(); @@ -69,9 +62,9 @@ void CSectorList::Init() short iSectorX = 0, iSectorY = 0; - for (; iSectorIndex < iSectorQty; ++iSectorIndex) + for (int iSectorIndex = 0; iSectorIndex < iSectorQty; ++iSectorIndex) { - // Map sectors are added in row-major order (fill a column, then increment row count and fill columns at that row, and so on). + // Map sectors are added in row-major order (fill every row for a column, then increment column index and fill its rows, and so on). if (iSectorY >= iMaxY) { iSectorY = 0; @@ -179,11 +172,11 @@ CSector* CSectorList::GetSectorByCoordsUnchecked(int map, short x, short y) cons if ((xSect >= sd.iSectorColumns) || (ySect >= sd.iSectorRows)) return nullptr; - const int index = ((ySect * sd.iSectorColumns) + xSect); + const int index = ((xSect * sd.iSectorRows) + ySect); return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; #else */ - const int index = ((ySect * sd.iSectorColumns) + xSect); + const int index = ((xSect * sd.iSectorRows) + ySect); DEBUG_ASSERT(index < sd.iSectorQty); return &(sd._pSectors[index]); //#endif diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index af209be92..57356287c 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -156,8 +156,8 @@ void CItemsList::AddItemToSector( CItem * pItem ) void CSectorBase::SetAdjacentSectors() { const CSectorList& pSectors = CSectorList::Get(); - ASSERT_ALWAYS(g_MapList.IsMapSupported(m_BasePoint.m_map)); - auto const& sd = pSectors.GetMapSectorDataUnchecked(m_BasePoint.m_map); + ASSERT_ALWAYS(g_MapList.IsMapSupported(m_BasePointSectUnits.m_map)); + auto const& sd = pSectors.GetMapSectorDataUnchecked(m_BasePointSectUnits.m_map); const int iMaxX = sd.iSectorColumns; ASSERT(iMaxX > 0); @@ -201,19 +201,19 @@ void CSectorBase::SetAdjacentSectors() // out of bounds checks // These checks are needed or the negative iAdjX/iAdjY can lead to a wrong index if the sector is near the map borders. // For instance m_index = 0, iAdjY > 0 and iAdjX < 0, SW check, the index start from the first column of the second row and it goes back to the first row because there is no SW sector - const int iAdjX = m_BasePoint.m_x + _xyDir[i].x; + const int iAdjX = m_BasePointSectUnits.m_x + _xyDir[i].x; if ((iAdjX < 0) || (iAdjX >= iMaxX)) continue; - const int iAdjY = m_BasePoint.m_y + _xyDir[i].y; + const int iAdjY = m_BasePointSectUnits.m_y + _xyDir[i].y; if ((iAdjY < 0) || (iAdjY >= iMaxY)) continue; - const int iAdjIndex = m_index + ((iAdjY * iMaxX) + iAdjX); + const int iAdjIndex = m_index + ((iAdjX * iMaxX) + iAdjY); if ((iAdjIndex < 0) || (iAdjIndex > iMaxSectors)) continue; - _ppAdjacentSectors[(DIR_TYPE)i] = pSectors.GetSectorByIndexUnchecked(m_BasePoint.m_map, iAdjIndex); + _ppAdjacentSectors[(DIR_TYPE)i] = pSectors.GetSectorByIndexUnchecked(m_BasePointSectUnits.m_map, iAdjIndex); //g_Log.EventDebug("Sector %d, Setting adjacent sector %d.\n", m_index, iAdjIndex); } @@ -246,7 +246,7 @@ void CSectorBase::Init(int index, uchar map, short x, short y) // and we're doing a number of potentially useless checks. if (( index < 0 ) || ( index >= CSectorList::Get().GetMapSectorDataUnchecked(map).iSectorQty )) { - m_BasePoint.m_map = map; + m_BasePointSectUnits.m_map = map; g_Log.EventError("Trying to initalize a sector by sector number %d out-of-range for map #%d. Defaulting to 0,%d.\n", index, map, map); return; } @@ -257,7 +257,8 @@ void CSectorBase::Init(int index, uchar map, short x, short y) auto const& sd = CSectorList::Get().GetMapSectorDataUnchecked(map); DEBUG_ASSERT((m_index >= 0) && (m_index < sd.iSectorQty)); - m_BasePoint = CPointBase + m_BasePointSectUnits = + CPointBase { x, // x y, // y @@ -265,16 +266,27 @@ void CSectorBase::Init(int index, uchar map, short x, short y) (uint8)map // m }; - m_MapRect = - CRectMap { - m_BasePoint.m_x, // left - m_BasePoint.m_y, // top - m_BasePoint.m_x + sd.iSectorSize, // right: East - m_BasePoint.m_y + sd.iSectorSize, // bottom: South - m_BasePoint.m_map // map + m_MapRectWorldUnits = + CRectMap + { + m_BasePointSectUnits.m_x * sd.iSectorSize, // left + m_BasePointSectUnits.m_y * sd.iSectorSize, // top + m_BasePointSectUnits.m_x * sd.iSectorSize + sd.iSectorSize, // right: East + m_BasePointSectUnits.m_y * sd.iSectorSize + sd.iSectorSize, // bottom: South + m_BasePointSectUnits.m_map // map }; } +CPointBase CSectorBase::GetBasePointMapUnits() const noexcept +{ + return CPointBase { + (int16)m_MapRectWorldUnits.m_left, + (int16)m_MapRectWorldUnits.m_top, + 0, + (uint8)m_MapRectWorldUnits.m_map + }; +} + CRegion * CSectorBase::GetRegion( const CPointBase & pt, dword dwType ) const { ADDTOCALLSTACK_DEBUG("CSectorBase::GetRegion"); @@ -387,7 +399,7 @@ bool CSectorBase::LinkRegion( CRegion * pRegionNew ) // Later added regions from the MAP file should be the smaller ones, // according to the old rules. ASSERT(pRegionNew); - ASSERT( pRegionNew->IsOverlapped(GetRect()) ); + ASSERT( pRegionNew->IsOverlapped(GetRectWorldUnits()) ); size_t iQty = m_RegionLinks.size(); for ( size_t i = 0; i < iQty; ++i ) @@ -411,11 +423,17 @@ bool CSectorBase::LinkRegion( CRegion * pRegionNew ) // it is accurate in the TRUE case. if ( pRegionNew->IsInside(pRegion)) + { + g_Log.EventWarn("IsInside.\n"); continue; + } // keep item (multi) regions on top if ( pRegion->GetResourceID().IsUIDItem() && !pRegionNew->GetResourceID().IsUIDItem() ) - continue; + { + g_Log.EventWarn("TopItem.\n"); + continue; + } // must insert before this. m_RegionLinks.emplace(m_RegionLinks.begin() + i, pRegionNew); diff --git a/src/game/CSectorTemplate.h b/src/game/CSectorTemplate.h index 2f552385f..0f6414055 100644 --- a/src/game/CSectorTemplate.h +++ b/src/game/CSectorTemplate.h @@ -100,8 +100,8 @@ class CSectorBase // world sector protected: int m_index; // Sector index in the 'sector grid' - CPointBase m_BasePoint; // Sector coordinates in the 'sector grid'. - CRectMap m_MapRect; // Map area inside this sector. + CPointBase m_BasePointSectUnits; // Sector coordinates in the 'sector grid'. + CRectMap m_MapRectWorldUnits; // Map area inside this sector, in map coordinates. private: // TODO: store indices instead of pointers, to make the class smaller? @@ -138,8 +138,10 @@ class CSectorBase // world sector // Location map units. int GetIndex() const noexcept { return m_index; } - int GetMap() const noexcept { return m_BasePoint.m_map; } - constexpr inline const CRectMap& GetRect() const noexcept { return m_MapRect; } + int GetMap() const noexcept { return m_BasePointSectUnits.m_map; } + CPointBase GetBasePointMapUnits() const noexcept; + constexpr const CPointBase& GetBasePointSectUnits() noexcept { return m_BasePointSectUnits; } + constexpr const CRectMap& GetRectWorldUnits() const noexcept { return m_MapRectWorldUnits; } // CRegion CRegion * GetRegion( const CPointBase & pt, dword dwType ) const; diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 8127a4cd5..f62fceb54 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -3327,7 +3327,8 @@ bool CServerConfig::LoadResourceSection( CScript * pScript, bool fInsertSorted ) // Create a new index for the block. // NOTE: rid is not created for all types. // NOTE: GetArgStr() is not always the DEFNAME - rid = ResourceGetNewID( restype, pScript->GetArgStr(), &pVarNum, fNewStyleDef ); + lpctstr ptcScriptArg = pScript->GetArgStr(); + rid = ResourceGetNewID( restype, ptcScriptArg, &pVarNum, fNewStyleDef ); } if ( !rid.IsValidUID() ) @@ -3760,7 +3761,8 @@ bool CServerConfig::LoadResourceSection( CScript * pScript, bool fInsertSorted ) } else { - CRegionWorld * pRegion = new CRegionWorld( rid, pScript->GetArgStr()); + lpctstr ptcScriptArg = pScript->GetArgStr(); + CRegionWorld * pRegion = new CRegionWorld(rid, ptcScriptArg); pRegion->r_Load( *pScript ); if (!pRegion->RealizeRegion()) { @@ -3792,7 +3794,8 @@ bool CServerConfig::LoadResourceSection( CScript * pScript, bool fInsertSorted ) } else { - CRegion * pRegion = new CRegion( rid, pScript->GetArgStr()); + lpctstr ptcScriptArg = pScript->GetArgStr(); + CRegion * pRegion = new CRegion( rid, ptcScriptArg ); pNewDef = pRegion; ASSERT(pNewDef); pRegion->r_Load(*pScript); @@ -4097,11 +4100,13 @@ bool CServerConfig::LoadResourceSection( CScript * pScript, bool fInsertSorted ) return true; case RES_WORLDLISTS: { - CListDefCont* pListBase = g_ExprGlobals.mtEngineLockedWriter()->m_ListGlobals.AddList(pScript->GetArgStr()); + lpctstr ptcScriptArg = pScript->GetArgStr(); + auto gWriter = g_ExprGlobals.mtEngineLockedWriter(); + CListDefCont* pListBase = gWriter->m_ListGlobals.AddList(ptcScriptArg); if ( !pListBase ) { - DEBUG_ERR(("Unable to create list '%s'...\n", pScript->GetArgStr())); + DEBUG_ERR(("Unable to create list '%s'...\n", ptcScriptArg)); return false; } @@ -4197,7 +4202,10 @@ bool CServerConfig::LoadResourceSection( CScript * pScript, bool fInsertSorted ) EXC_CATCH; EXC_DEBUG_START; - g_Log.EventDebug("ExcInfo: section '%s' key '%s' args '%s'\n", pszSection, pScript ? pScript->GetKey() : "", pScript ? pScript->GetArgStr() : ""); + g_Log.EventDebug("ExcInfo: section '%s' key '%s' args '%s'\n", + pszSection, + pScript ? pScript->GetKey() : "", + pScript ? pScript->GetArgStr() : ""); EXC_DEBUG_END; return false; } From 15fef5fb060f18477fe8d83b444c2ddfd04e70d5 Mon Sep 17 00:00:00 2001 From: cbnolok Date: Wed, 12 Nov 2025 20:43:21 +0100 Subject: [PATCH 111/112] More sector indexing fixes. Added garbage collection safety checks. --- src/common/CRect.cpp | 99 +++++++++++++++++++++-------------- src/common/CRect.h | 29 +++++++--- src/common/CScriptObj.cpp | 2 +- src/common/CUID.h | 2 +- src/game/CObjBase.cpp | 4 +- src/game/CRegion.cpp | 12 +++-- src/game/CRegionBase.cpp | 2 +- src/game/CRegionBase.h | 10 ++-- src/game/CSectorList.cpp | 27 +++++----- src/game/CSectorList.h | 12 ++++- src/game/CSectorTemplate.cpp | 22 +++----- src/game/CSectorTemplate.h | 2 +- src/game/CWorld.cpp | 14 +++-- src/game/CWorldSearch.cpp | 7 ++- src/game/CWorldSearch.h | 4 +- src/game/items/CItem.cpp | 16 +++--- src/game/items/CItemMulti.cpp | 23 ++++++-- src/game/spheresvr.cpp | 2 +- src/sphere/threads.cpp | 4 +- utilities/configure-asan.sh | 2 +- 20 files changed, 185 insertions(+), 110 deletions(-) diff --git a/src/common/CRect.cpp b/src/common/CRect.cpp index 98c9c986d..cad104411 100644 --- a/src/common/CRect.cpp +++ b/src/common/CRect.cpp @@ -286,22 +286,59 @@ CPointBase CRect::GetRectCorner( DIR_TYPE dir ) const return pt; } -CSector * CRect::GetSector( int i ) const noexcept // get all the sectors that make up this rect. +// get the n-th sector that makes up this rect. +CSector * CRect::GetSectorAtIndex( int i ) const noexcept { - //ADDTOCALLSTACK_DEBUG("CRect::GetSector"); - // get all the CSector(s) that overlap this rect. - // RETURN: nullptr = no more + //ADDTOCALLSTACK_DEBUG("CRect::GetSectorAtIndexWithHints"); + // get all the CSector(s) that overlap this rect. + // RETURN: nullptr = no more + + if (g_MapList.IsMapSupported(m_map) == false) + return nullptr; + + SectIndexingHints hints = PrecomputeSectorIndexingHints(); + return GetSectorAtIndexWithHints(i, hints); +} + +// get the n-th sector that makes up this rect. +CSector * CRect::GetSectorAtIndexWithHints(int i, SectIndexingHints hints) const noexcept +{ + //if (g_MapList.IsMapSupported(m_map) == false) + // return nullptr; - // Align new rect. + const CSectorList &pSectors = CSectorList::Get(); + + if (i >= hints.iRectSectorCount) + return i ? nullptr : pSectors.GetSectorByIndexUnchecked(m_map, hints.iBaseSectorIndex); + + // From now on we need only height and sectorcols. We'll use precomputed data when checking + // incremental sector indices for the same CRect. + + // Column-major + // col = i / height + // row = i % height + // indexoffset = col * sectorRows + row + // Row-major + const int row = i / hints.iRectWidth; + const int col = i % hints.iRectWidth; + const int iSectorIndexOffset = CSectorList::CalcSectorIndex(hints.iRectMapSectorCols, col, row); + //const int iSectorIndexOffset = row * hints.iRectMapSectorCols + col; + const int iSectorIndex = hints.iBaseSectorIndex + iSectorIndexOffset; + return pSectors.GetSectorByIndexUnchecked(m_map, iSectorIndex); +} + +CRect::SectIndexingHints +CRect::PrecomputeSectorIndexingHints() const noexcept +{ const CSectorList &pSectors = CSectorList::Get(); const MapSectorsData* pSecData = pSectors.GetMapSectorData(m_map); - if (!pSecData) [[unlikely]] - return nullptr; + //ASSERT(pSecData); const int iSectorSize = pSecData->iSectorSize; const int iSectorCols = pSecData->iSectorColumns; - const uint uiSectorShift = pSecData->uiSectorDivShift; + const uint uiSectorShift = pSecData->uiSectorSizeDivShift; + // Align new rect. // CRectMap(int left, int top, int right, int bottom, int map) CRectMap rect( (m_left & ~(iSectorSize-1)), // aligns the left boundary down to the nearest multiple of iSectorSize. @@ -310,37 +347,33 @@ CSector * CRect::GetSector( int i ) const noexcept // get all the sectors that m (m_bottom | (iSectorSize-1)) + 1, m_map ); - rect.NormalizeRectMax(); + //g_Log.EventDebug("Precomputing hints. Starting rect: %d,%d, %d,%d. Aligned rect: %d,%d, %d,%d.\n", + // m_left, m_top, m_right, m_bottom, rect.m_left, rect.m_top, rect.m_right, rect.m_bottom); + rect.NormalizeRectMax(); //const int width = (rect.GetWidth()) / iSectorSize; //const int height = (rect.GetHeight()) / iSectorSize; + // Bit-shifting is faster than plain division. We can use it because sector size is guaranteed to be a multiple of 2. const int width = rect.GetWidth() >> uiSectorShift; const int height = rect.GetHeight() >> uiSectorShift; #ifdef _DEBUG - ASSERT(width <= iSectorCols); const int iSectorRows = pSecData->iSectorRows; - ASSERT(height <= iSectorRows); + ASSERT(width <= iSectorCols); + ASSERT(height <= iSectorRows); #endif - /* - const int iBase = (( rect.m_top / iSectorSize) * iSectorCols) + ( rect.m_left / iSectorSize ); - if ( i >= ( height * width )) - return i ? nullptr : pSectors->GetSector(m_map, iBase); - - const int indexoffset = (( i / width ) * iSectorCols) + ( i % width ); - return pSectors->GetSector(m_map, iBase + indexoffset); - */ - const int baseRow = rect.m_top >> uiSectorShift; const int baseCol = rect.m_left >> uiSectorShift; - const int iBase = (baseRow * iSectorCols) + baseCol; - - if (i >= (height * width)) - return i ? nullptr : pSectors.GetSectorByIndexUnchecked(m_map, iBase); - - const int indexoffset = ((i / width) * iSectorCols) + (i % width); - return pSectors.GetSectorByIndexUnchecked(m_map, iBase + indexoffset); + const int iBase = CSectorList::CalcSectorIndex(iSectorCols, baseCol, baseRow); + + return SectIndexingHints { + .iBaseSectorIndex = iBase, + .iRectWidth = width, + .iRectHeight = height, + .iRectSectorCount = width * height, + .iRectMapSectorCols = iSectorCols + }; } @@ -367,18 +400,6 @@ bool CRectMap::IsValid() const noexcept const int iSizeY = GetHeight(); return (iSizeX >= 0 && iSizeX <= g_MapList.GetMapSizeX(m_map)) && (iSizeY >= 0 && iSizeY <= g_MapList.GetMapSizeY(m_map)); - - /* - const int iSizeX = GetWidth(); - if ( (iSizeX < 0) || (iSizeX > g_MapList.GetMapSizeX(m_map)) ) - return false; - const int iSizeY = GetHeight(); - return !( (iSizeY < 0) || (iSizeY > g_MapList.GetMapSizeY(m_map)) ); - */ - - //if ( (iSizeY < 0) || (iSizeY > g_MapList.GetMapSizeY(m_map)) ) - // return false; - //return true; } void CRectMap::NormalizeRect() noexcept diff --git a/src/common/CRect.h b/src/common/CRect.h index 534d10da4..680448dd8 100644 --- a/src/common/CRect.h +++ b/src/common/CRect.h @@ -21,6 +21,17 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) int m_bottom; // South ( NON INCLUSIVE !) int m_map; + + struct SectIndexingHints + { + int iBaseSectorIndex; // Sector index at origin, top-left x,y coords of the rect + int iRectWidth; // x1 - x: how much x units (columns) are inside the rect + int iRectHeight; // y1 - y: how much y units (rows) are inside the rect + int iRectSectorCount; // How much sectors are inside this rect + int iRectMapSectorCols; // Number of sectors columns (X) per each row in the map (so, map max X) + }; + + void SetRectEmpty() noexcept; CRect() noexcept; @@ -32,16 +43,16 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) CRect& operator = (const CRect&) = default; const CRect& operator += (const CRect& rect); - constexpr inline int GetWidth() const noexcept + constexpr int GetWidth() const noexcept { return( m_right - m_left ); } - constexpr inline int GetHeight() const noexcept + constexpr int GetHeight() const noexcept { return( m_bottom - m_top ); } - constexpr inline bool IsRectEmpty() const noexcept + constexpr bool IsRectEmpty() const noexcept { return( m_left >= m_right || m_top >= m_bottom ); } @@ -49,11 +60,11 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) void OffsetRect( int x, int y ); void UnionPoint( int x, int y ); - constexpr inline bool IsInsideX( int x ) const noexcept + constexpr bool IsInsideX( int x ) const noexcept { // non-inclusive return( x >= m_left && x < m_right ); } - constexpr inline bool IsInsideY( int y ) const noexcept + constexpr bool IsInsideY( int y ) const noexcept { // non-inclusive return( y >= m_top && y < m_bottom ); } @@ -74,7 +85,13 @@ struct CRect // Basic rectangle, similar to _WIN32 RECT (May not be on the map) CPointBase GetCenter() const noexcept; CPointBase GetRectCorner( DIR_TYPE dir ) const; - CSector * GetSector( int i ) const noexcept; // ge all the sectors that make up this rect. + + // get all the sectors that make up this rect. + CSector * GetSectorAtIndex( int i ) const noexcept; + + // get all the sectors that make up this rect (using cached precomputed data, that's faster if the use case allows its usage). + CSector * GetSectorAtIndexWithHints(int i, SectIndexingHints hints) const noexcept; + SectIndexingHints PrecomputeSectorIndexingHints() const noexcept; void SetRect( int left, int top, int right, int bottom, int map ) noexcept; diff --git a/src/common/CScriptObj.cpp b/src/common/CScriptObj.cpp index a56b86602..6450d2211 100644 --- a/src/common/CScriptObj.cpp +++ b/src/common/CScriptObj.cpp @@ -1513,7 +1513,7 @@ bool CScriptObj::Execute_Call(CScript& s, CScriptTriggerArgsPtr const& pScriptAr // Parse object references, src.* is not parsed // by r_GetRef so do it manually - r_GetRef(const_cast(static_cast(argRaw)), pRef); + r_GetRef(const_cast(reinterpret_cast(argRaw)), pRef); if (!strnicmp("SRC.", argRaw, 4)) { argRaw += 4; diff --git a/src/common/CUID.h b/src/common/CUID.h index 8e80006e8..a11bec11e 100644 --- a/src/common/CUID.h +++ b/src/common/CUID.h @@ -81,7 +81,7 @@ class CUID // A unique system serial id. 4 bytes long return IsChar(m_dwInternalVal); } - inline constexpr bool IsObjDisconnected() const noexcept // Called very frequently + constexpr bool IsObjDisconnected() const noexcept // Called very frequently { // Not in the game world for some reason. return ((m_dwInternalVal & (UID_F_RESOURCE | UID_O_DISCONNECT)) == UID_O_DISCONNECT); diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index 6c928509a..a97cdd9f6 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -1962,7 +1962,9 @@ bool CObjBase::r_LoadVal( CScript & s ) SetName( s.GetArgStr()); fResendTooltip = true; break; - case OC_P: // Must set the point via the CItem or CChar methods. + case OC_P: + // Need to use it in r_LoadVal only for server load stage. + // Must set the point/position via the CItem or CChar methods. return false; case OC_SPEED: { diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 8371782e9..2c36de8c6 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -50,13 +50,15 @@ void CRegion::UnRealizeRegion() for ( int i = 0; ; ++i ) { - CSector * pSector = GetSector(i); + CSector * pSector = GetSectorAtIndex(i); if ( pSector == nullptr ) break; + // Does the rect overlap ? if ( ! IsOverlapped( pSector->GetRectWorldUnits())) continue; - if ( pSector->UnLinkRegion( this )) + + if ( pSector->UnLinkRegion( this )) --m_iLinkedSectors; } @@ -78,6 +80,7 @@ bool CRegion::RealizeRegion() const CSectorList& pSectors = CSectorList::Get(); for ( int i = 0, iMax = pSectors.GetMapSectorDataUnchecked(m_pt.m_map).iSectorQty; i < iMax; ++i ) { + // TODO: maybe provide a hint on where to start checking, instead of checking every sector in the map... CSector *pSector = pSectors.GetSectorByIndexUnchecked(m_pt.m_map, i); if ( pSector && IsOverlapped(pSector->GetRectWorldUnits()) ) @@ -323,7 +326,7 @@ bool CRegion::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, int iClients = 0; for (int i = 0; ; ++i) { - CSector *pSector = GetSector(i); + CSector *pSector = GetSectorAtIndex(i); if (pSector == nullptr) break; iClients += pSector->m_Chars_Active.GetClientsNumber(); @@ -850,7 +853,8 @@ bool CRegion::SendSectorsVerb( lpctstr pszVerb, lpctstr pszArgs, CTextConsole * bool fRet = false; for ( int i=0; ; ++i ) { - CSector * pSector = GetSector(i); + // TODO: optimization, use GetSectorAtIndexWithHints instead. + CSector * pSector = GetSectorAtIndex(i); if ( pSector == nullptr ) break; diff --git a/src/game/CRegionBase.cpp b/src/game/CRegionBase.cpp index 6137a6777..6330fef93 100644 --- a/src/game/CRegionBase.cpp +++ b/src/game/CRegionBase.cpp @@ -136,7 +136,7 @@ bool CRegionBase::IsOverlapped( const CRectMap & rect ) const noexcept return false; const uint iQty = (uint)m_Rects.size(); - if ( iQty <= 0 ) + if ( iQty <= 0 ) // TODOC: Usually happens for a multi-bound CRegion? return true; // Avoid the cost of the IsOverlapped function call and just slap the content here. diff --git a/src/game/CRegionBase.h b/src/game/CRegionBase.h index 67a691147..0ea4ed34b 100644 --- a/src/game/CRegionBase.h +++ b/src/game/CRegionBase.h @@ -39,10 +39,14 @@ class CRegionBase bool IsOverlapped( const CRegionBase * pRegionTest ) const; bool IsEqualRegion( const CRegionBase * pRegionTest ) const; - inline CSector * GetSector( int i ) const // get all the sectors that make up this rect. - { - return m_rectUnion.GetSector(i); + [[nodiscard]] + inline CSector * GetSectorAtIndex( int i ) const noexcept { + return m_rectUnion.GetSectorAtIndex(i); } + [[nodiscard]] + inline CSector * GetSectorAtIndexWithHints( int i, CRect::SectIndexingHints hints ) const noexcept { + return m_rectUnion.GetSectorAtIndexWithHints(i, std::move(hints)); + } public: CRegionBase(); diff --git a/src/game/CSectorList.cpp b/src/game/CSectorList.cpp index ff5e956a4..7a300667e 100644 --- a/src/game/CSectorList.cpp +++ b/src/game/CSectorList.cpp @@ -3,9 +3,9 @@ #include "CWorld.h" #include "CSectorList.h" -CSectorList::CSectorList() : _SectorData{} +CSectorList::CSectorList() : + _SectorData{}, _fInitialized(false) { - _fInitialized = false; } CSectorList::~CSectorList() @@ -58,23 +58,24 @@ void CSectorList::Init() uint32 sector_shift = 0; for (uint sz = sd.iSectorSize; sz > 1; sz >>= 1) ++sector_shift; - sd.uiSectorDivShift = (uint16)sector_shift; + sd.uiSectorSizeDivShift = (uint16)sector_shift; short iSectorX = 0, iSectorY = 0; for (int iSectorIndex = 0; iSectorIndex < iSectorQty; ++iSectorIndex) { - // Map sectors are added in row-major order (fill every row for a column, then increment column index and fill its rows, and so on). - if (iSectorY >= iMaxY) + // Map sectors are ordered in row-major order: + // consecutive elements from the same row are contiguous and the inner loop varies the column index. + if (iSectorX >= iMaxX) { - iSectorY = 0; - ++iSectorX; + iSectorX = 0; + ++iSectorY; } CSector* pSector = &(sd._pSectors[iSectorIndex]); pSector->Init(iSectorIndex, (uchar)iMap, iSectorX, iSectorY); - ++iSectorY; + ++iSectorX; } } @@ -137,15 +138,13 @@ CSector* CSectorList::GetSectorByIndexUnchecked(int map, int index) const noexce // We call this method very often and from places we ASSUME have already checked that the map number is legit. // (it's done in CWorldMap::GetSectorByIndex). - // Micro-optimization: Skip the function call and the if statement to avoid the possible cost of a branch misprediction. + // Micro-optimization: Skip the function call (with the call cost) and the if statement to avoid the possible cost of a branch misprediction. //if (!g_MapList.IsMapSupported(map) || !_SectorData[map]._pSectors) // return nullptr; //if ((map < 0) || ((size_t)map < sizeof(_SectorData)) ) // return nullptr; const MapSectorsData& sd = _SectorData[map]; - - //return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; return (index < sd.iSectorQty) ? &(sd._pSectors.get()[index]) : nullptr; } @@ -162,7 +161,7 @@ CSector* CSectorList::GetSectorByCoordsUnchecked(int map, short x, short y) cons // We know that sector sizes MUST be multiple of 2, so just use bit shifts. //const int xSectTest = (x / sd.iSectorSize), ySectTest = (y / sd.iSectorSize); - const int xSect = (x >> sd.uiSectorDivShift), ySect = (y >> sd.uiSectorDivShift); + const int xSect = (x >> sd.uiSectorSizeDivShift), ySect = (y >> sd.uiSectorSizeDivShift); //ASSERT(xSectTest == xSect); //ASSERT(ySectTest == ySect); @@ -172,11 +171,11 @@ CSector* CSectorList::GetSectorByCoordsUnchecked(int map, short x, short y) cons if ((xSect >= sd.iSectorColumns) || (ySect >= sd.iSectorRows)) return nullptr; - const int index = ((xSect * sd.iSectorRows) + ySect); + const int index = ((ySect * sd.iSectorColumns) + xSect); return (index < sd.iSectorQty) ? &(sd._pSectors[index]) : nullptr; #else */ - const int index = ((xSect * sd.iSectorRows) + ySect); + const int index = ((ySect * sd.iSectorColumns) + xSect); DEBUG_ASSERT(index < sd.iSectorQty); return &(sd._pSectors[index]); //#endif diff --git a/src/game/CSectorList.h b/src/game/CSectorList.h index 9f267f320..d94e8cac0 100644 --- a/src/game/CSectorList.h +++ b/src/game/CSectorList.h @@ -20,7 +20,7 @@ struct MapSectorsData int iSectorColumns; // how much sectors are in a column (x) in a given map int iSectorRows; // how much sectors are in a row (y) in a given map int iSectorQty; // how much sectors are in a map - uint16 uiSectorDivShift; // precalculated value to avoid a division in sector/rect calculations + uint16 uiSectorSizeDivShift; // precalculated value to avoid a division in sector/rect calculations private: std::unique_ptr _pSectors; @@ -31,7 +31,8 @@ class CSectorList public: static const char* m_sClassName; - //std::array _SectorData; // Use plain C-style vectors, to remove even the minimal overhead of std::array methods + // Use plain C-style vectors, to remove even the minimal overhead of std::array methods in debug builds. + //std::array _SectorData; MapSectorsData _SectorData[MAP_SUPPORTED_QTY]; bool _fInitialized; @@ -53,6 +54,13 @@ class CSectorList [[nodiscard]] const MapSectorsData& GetMapSectorDataUnchecked(int map) const noexcept; + [[nodiscard]] + static + constexpr int CalcSectorIndex(int maxX, int x, int y) noexcept { + // Row-major order: index = row * numColumns + column + return (y * maxX) + x; + } + CSector* GetSectorByIndexUnchecked(int map, int index) const noexcept; // gets sector # from one map CSector* GetSectorByCoordsUnchecked(int map, short x, short y) const NOEXCEPT_NODEBUG; diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index 57356287c..c1a0ebc6e 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -297,8 +297,8 @@ CRegion * CSectorBase::GetRegion( const CPointBase & pt, dword dwType ) const // REGION_TYPE_ROOM => RES_ROOM = NPC House areas only = CRegion. // REGION_TYPE_MULTI => RES_WORLDITEM = UID linked types in general = CRegionWorld - size_t iQty = m_RegionLinks.size(); - for ( size_t i = 0; i < iQty; ++i ) + const size_t uiQty = m_RegionLinks.size(); + for ( size_t i = 0; i < uiQty; ++i ) { CRegion * pRegion = m_RegionLinks[i]; ASSERT(pRegion); @@ -340,8 +340,8 @@ CRegion * CSectorBase::GetRegion( const CPointBase & pt, dword dwType ) const size_t CSectorBase::GetRegions( const CPointBase & pt, dword dwType, CRegionLinks *pRLinks ) const { //ADDTOCALLSTACK_DEBUG("CSectorBase::GetRegions"); // Called very frequently - size_t iQty = m_RegionLinks.size(); - for ( size_t i = 0; i < iQty; ++i ) + const size_t uiQty = m_RegionLinks.size(); + for ( size_t i = 0; i < uiQty; ++i ) { CRegion * pRegion = m_RegionLinks[i]; ASSERT(pRegion); @@ -400,9 +400,9 @@ bool CSectorBase::LinkRegion( CRegion * pRegionNew ) // according to the old rules. ASSERT(pRegionNew); ASSERT( pRegionNew->IsOverlapped(GetRectWorldUnits()) ); - size_t iQty = m_RegionLinks.size(); + const size_t uiQty = m_RegionLinks.size(); - for ( size_t i = 0; i < iQty; ++i ) + for ( size_t i = 0; i < uiQty; ++i ) { CRegion * pRegion = m_RegionLinks[i]; ASSERT(pRegion); @@ -423,17 +423,11 @@ bool CSectorBase::LinkRegion( CRegion * pRegionNew ) // it is accurate in the TRUE case. if ( pRegionNew->IsInside(pRegion)) - { - g_Log.EventWarn("IsInside.\n"); continue; - } // keep item (multi) regions on top if ( pRegion->GetResourceID().IsUIDItem() && !pRegionNew->GetResourceID().IsUIDItem() ) - { - g_Log.EventWarn("TopItem.\n"); continue; - } // must insert before this. m_RegionLinks.emplace(m_RegionLinks.begin() + i, pRegionNew); @@ -450,7 +444,7 @@ CTeleport * CSectorBase::GetTeleport( const CPointMap & pt ) const ADDTOCALLSTACK("CSectorBase::GetTeleport"); // Any teleports here at this point ? - size_t i = m_Teleports.FindKey(pt.GetPointSortIndex()); + const size_t i = m_Teleports.FindKey(pt.GetPointSortIndex()); if ( i == sl::scont_bad_index() ) return nullptr; @@ -469,7 +463,7 @@ bool CSectorBase::AddTeleport( CTeleport * pTeleport ) // NOTE: can't be 2 teleports from the same place ! // ASSERT( Teleport is actually in this sector ! - size_t i = m_Teleports.FindKey( pTeleport->GetPointSortIndex()); + const size_t i = m_Teleports.FindKey( pTeleport->GetPointSortIndex()); if ( i != sl::scont_bad_index() ) { DEBUG_ERR(( "Conflicting teleport %s!\n", pTeleport->WriteUsed() )); diff --git a/src/game/CSectorTemplate.h b/src/game/CSectorTemplate.h index 0f6414055..2633c222a 100644 --- a/src/game/CSectorTemplate.h +++ b/src/game/CSectorTemplate.h @@ -154,7 +154,7 @@ class CSectorBase // world sector CTeleport * GetTeleport( const CPointMap & pt ) const; bool AddTeleport( CTeleport * pTeleport ); - bool IsFlagSet( dword dwFlag ) const noexcept { + constexpr bool IsFlagSet( dword dwFlag ) const noexcept { return (m_dwFlags & dwFlag); } diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index 18aab3863..1b1299cc8 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -615,20 +615,18 @@ void CWorldThread::GarbageCollection_UIDs() int iResultCode = FixObj(pObj, i); if ( iResultCode ) { - // FixObj directly calls Delete method - //if (pObj->_IsBeingDeleted() || pObj->IsDeleted()) - //{ + if (pObj->_IsBeingDeleted() || pObj->IsDeleted()) + { // Do an immediate delete here instead of Delete() delete pObj; FreeUID(i); // Get rid of junk uid if all fails.. - //} - /* - else + } + else { + // FixObj doesn't always delete the item on failure/result code != 0. pObj->Delete(); } - */ - continue; + continue; } if ((iCount & 0x1FF ) == 0) diff --git a/src/game/CWorldSearch.cpp b/src/game/CWorldSearch.cpp index e09e953f2..c631e6b42 100644 --- a/src/game/CWorldSearch.cpp +++ b/src/game/CWorldSearch.cpp @@ -42,6 +42,7 @@ class CWorldSearchHolderImpl CSReferenceCounted CWorldSearchHolder::GetInstance(const CPointMap& pt, int iDist) // static { + // Thread-unsafe! static CWorldSearchHolderImpl holder; return holder.GetOne(pt, iDist); } @@ -87,6 +88,7 @@ void CWorldSearch::Reset(const CPointMap& pt, int iDist) _pt = pt; _iDist = iDist; + _pSectorBase = _pSector = pt.GetSector(); _rectSector.SetRect( pt.m_x - iDist, @@ -95,6 +97,8 @@ void CWorldSearch::Reset(const CPointMap& pt, int iDist) pt.m_y + iDist + 1, pt.m_map); + _rectSectorIndexingHints = _rectSector.PrecomputeSectorIndexingHints(); + SetDistanceFunction(); } @@ -138,7 +142,8 @@ bool CWorldSearch::GetNextSector() while (true) { - _pSector = _rectSector.GetSector(_iSectorCur++); + _pSector = _rectSector.GetSectorAtIndexWithHints(_iSectorCur, _rectSectorIndexingHints); + _iSectorCur += 1; if (_pSector == nullptr) return false; // done searching. if (_pSectorBase == _pSector) diff --git a/src/game/CWorldSearch.h b/src/game/CWorldSearch.h index 97f8a4c1e..27121ad65 100644 --- a/src/game/CWorldSearch.h +++ b/src/game/CWorldSearch.h @@ -46,7 +46,7 @@ class CWorldSearch CSObjContRec** _ppCurContObjs; // Array with a copy of the pointers to the objects inside the sector we are in searching right now. CObjBase* _pObj; // The current object of interest. - size_t _uiCurObjIndex; + size_t _uiCurObjIndex; size_t _uiObjArrayCapacity; size_t _uiObjArraySize; @@ -54,6 +54,7 @@ class CWorldSearch CSector* _pSectorBase; // Don't search the center sector 2 times. CSector* _pSector; // current Sector CRectMap _rectSector; // A rectangle containing our sectors we can search. + CRectMap::SectIndexingHints _rectSectorIndexingHints; // Pointer to distance function: // The ::* operator means "pointer to member of CPointMap". @@ -86,6 +87,7 @@ class CWorldSearch struct CWorldSearchHolder { + // Thread-unsafe! static CSReferenceCounted GetInstance(const CPointMap& pt, int iDist = 0); }; diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index 1b464e7f1..09d90e718 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -863,7 +863,7 @@ int CItem::FixWeirdness() } else { - DEBUG_ERR(("'%s' Bad Link to 0%x\n", GetName(), (dword)(m_uidLink))); + g_Log.EventError("GC: Object '%s' has Bad Link to UID 0%" PRIx32 ".\n", GetName(), m_uidLink.GetObjUID()); m_uidLink.InitUID(); iResultCode = 0x2205; return iResultCode; // get rid of it. @@ -3479,12 +3479,13 @@ bool CItem::r_LoadVal( CScript & s ) // Load an item Script m_itNormal.m_morep.m_z = s.GetArgCVal(); break; case IC_P: - // Loading or import ONLY ! others use the r_Verb + // Loading or import ONLY ! others use CObjBase::r_Verb if ( ! IsDisconnected() && ! IsItemInContainer() ) return false; else { - // Will be placed in the world later. + // Will be placed in the world later (in CItem::r_Load): + // since we are loading the world, the parent region might not be created/"realized" yet. CPointMap pt; pt.Read( s.GetArgStr()); if (pt.IsValidPoint()) @@ -3520,13 +3521,16 @@ bool CItem::r_LoadVal( CScript & s ) // Load an item Script bool CItem::r_Load( CScript & s ) // Load an item from script { ADDTOCALLSTACK("CItem::r_Load"); - CScriptObj::r_Load( s ); - if ( GetContainer() == nullptr ) - { + CScriptObj::r_Load( s ); + + if ( GetContainer() == nullptr ) + { // Actually place the item into the world. if ( GetTopPoint().IsCharValid()) + { MoveToUpdate( GetTopPoint()); + } } int iResultCode = CObjBase::IsWeird(); diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index 3c9760ab8..43235c1d9 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -107,6 +107,7 @@ CItemMulti::~CItemMulti() } if (!_lAddons.empty()) { + // TODOC: add a comment... why aren't we deleting addons!? /* for (const CUID& itemUID : _lAddons) { @@ -249,8 +250,8 @@ bool CItemMulti::MultiRealizeRegion() m_pRegion->SetName(pszTemp); m_pRegion->_pMultiLink = this; - //We have to update the Characters if not moving around like Player Vendors. - //Otherwise, when you reboot server, the region.name of the characters returns as Region name instead of multis. + // We have to update the Characters if not moving around like Player Vendors. + // Otherwise, when you reboot server, the region.name of the characters returns as Region name instead of multis. auto Area = CWorldSearchHolder::GetInstance(m_pRegion->m_pt, Multi_GetDistanceMax()); Area->SetSearchSquare(true); for (;;) @@ -267,6 +268,9 @@ bool CItemMulti::MultiRealizeRegion() pChar->MoveToRegion(m_pRegion, false); //Move the character to house region. } + //g_Log.EventError("Realizing MULTI region at P=%hd,%hd,%hhd,%hhu. Multi uid=0%x. Multi rect: %d,%d, %d,%d.\n", + // pt.m_x, pt.m_y, pt.m_z, pt.m_map, (dword)GetUID(), + // rect.m_left, rect.m_top, rect.m_right, rect.m_bottom); return m_pRegion->RealizeRegion(); } @@ -279,6 +283,16 @@ void CItemMulti::MultiUnRealizeRegion() } m_pRegion->_pMultiLink = nullptr; + const CItemBaseMulti * pMultiDef = Multi_GetDef(); + ASSERT(pMultiDef); + + CRectMap rect = pMultiDef->m_rect; + + //auto pt = m_pRegion->m_pt; + //g_Log.EventError("UN-Realizing MULTI region at P=%hd,%hd,%hhd,%hhu. Multi uid=0%x. Multi rect: %d,%d, %d,%d.\n", + // pt.m_x, pt.m_y, pt.m_z, pt.m_map, (dword)GetUID(), + // rect.m_left, rect.m_top, rect.m_right, rect.m_bottom); + m_pRegion->UnRealizeRegion(); // find all creatures in the region and remove this from them. @@ -483,7 +497,7 @@ CItem * CItemMulti::Multi_FindItemType(IT_TYPE type) const } if (pItem->IsType(type)) { - return(pItem); + return pItem; } } } @@ -522,6 +536,7 @@ void CItemMulti::OnMoveFrom() bool CItemMulti::MoveTo(const CPointMap& pt, bool fForceFix) // Put item on the ground here. { ADDTOCALLSTACK("CItemMulti::MoveTo"); + // Move this item to it's point in the world. (ground/top level) if (!CItem::MoveTo(pt, fForceFix)) { @@ -562,6 +577,7 @@ CItem * CItemMulti::Multi_GetSign() { ADDTOCALLSTACK("CItemMulti::Multi_GetSign"); // Get my sign or tiller link. + CItem * pTiller = m_uidLink.ItemFind(); if (pTiller == nullptr) { @@ -695,6 +711,7 @@ CUID CItemMulti::GetOwner() const void CItemMulti::SetGuild(const CUID& uidGuild) { ADDTOCALLSTACK("CItemMulti::SetGuild"); + CItemStone *pGuildStone = static_cast(GetGuildStone().ItemFind()); _uidGuild.InitUID(); if (pGuildStone) // Old Guild may not exist, was it removed...? diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 7f1bfa60f..b98e6d81b 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -72,7 +72,7 @@ CAccounts g_Accounts; // All the player accounts. name sorted CAccount CWorld g_World; // the world. (we save this stuff) // Networking stuff. They are declared here (in the same file of the other global declarations) to control the order of construction -// and destruction of these classes. If this order is altered, you'll get segmentation faults (access violations) when the server is servClosing! +// and destruction of these classes. If this order is altered, you'll get segmentation faults (access violations) when the server is closing! #ifdef _LIBEV extern LinuxEv g_NetworkEvent; #endif diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index 5e4897aa1..6373063f2 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -527,7 +527,7 @@ void AbstractThread::overwriteInternalThreadName(const char* name) noexcept void AbstractThread::start() { g_Log.Event(LOGM_DEBUG|LOGL_EVENT|LOGF_CONSOLE_ONLY, - "Spawning new thread '%s' (0x% " PRIxSIZE_T ")...\n", + "Spawning new thread '%s' (AbstractThread* = 0x% " PRIxSIZE_T ")...\n", getName(), reinterpret_cast(this)); #ifdef _WIN32 @@ -947,7 +947,7 @@ void AbstractSphereThread::printStackTrace() noexcept auto& stackInfo = (m_stackInfoCopy[0].functionName != nullptr) ? m_stackInfoCopy : m_stackInfo; g_Log.EventDebug("Printing STACK TRACE for debugging purposes.\n"); - g_Log.EventDebug(" ______ thread (id) name _____ | # | _____________ function _____________ |\n"); + g_Log.EventDebug(" _ thread (id) name _ | # | _____________ function _____________ |\n"); for (ssize_t i = 0; i < (ssize_t)ARRAY_COUNT(m_stackInfoCopy); ++i) { diff --git a/utilities/configure-asan.sh b/utilities/configure-asan.sh index ece552bf6..57a6bbfd5 100644 --- a/utilities/configure-asan.sh +++ b/utilities/configure-asan.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env sh _SAN_COMMON_FLAGS=handle_abort=true:abort_on_error=false #ASAN_MORE=check_initialization_order=1 From 199f4964d75c48f64e36373969229a0901e7fc8e Mon Sep 17 00:00:00 2001 From: cbnolok Date: Fri, 14 Nov 2025 07:37:52 +0100 Subject: [PATCH 112/112] Fixed: changed column-major -> row-major indexing in setting adjacent sectors --- src/game/CSectorTemplate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/CSectorTemplate.cpp b/src/game/CSectorTemplate.cpp index c1a0ebc6e..64d9525ae 100644 --- a/src/game/CSectorTemplate.cpp +++ b/src/game/CSectorTemplate.cpp @@ -209,7 +209,7 @@ void CSectorBase::SetAdjacentSectors() if ((iAdjY < 0) || (iAdjY >= iMaxY)) continue; - const int iAdjIndex = m_index + ((iAdjX * iMaxX) + iAdjY); + const int iAdjIndex = CSectorList::CalcSectorIndex(iMaxX, iAdjX, iAdjY); if ((iAdjIndex < 0) || (iAdjIndex > iMaxSectors)) continue;