diff --git a/.clang-format b/.clang-format index b993d79f0..b85f0ee08 100644 --- a/.clang-format +++ b/.clang-format @@ -1,25 +1,35 @@ +# Based on LLVM style with C++20 support BasedOnStyle: LLVM Language: Cpp Standard: c++20 + +# Tab and indent settings TabWidth: 4 IndentWidth: 4 UseTab: Never ColumnLimit: 160 +# Text line management #LineEndingStyle: LF -#MaxEmptyLinesToKeep: 3 +MaxEmptyLinesToKeep: 2 +InsertNewlineAtEOF: true -##-- Alignment -AlignAfterOpenBracket: AlwaysBreak +# Alignment settings +AlignAfterOpenBracket: DontAlign AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: false -#AlignEscapedNewlinesLeft: false -AlignTrailingComments: true - +#AlignConsecutiveTableGenDefinitionColons: +# Enabled: true +# AcrossEmptyLines: true +# AcrossComments: false +AlignEscapedNewlines: Left +AlignTrailingComments: + Kind: Always + OverEmptyLines: 1 DerivePointerAlignment: false PointerAlignment: Right -##-- Single line statements +# Single-line statements settings AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None @@ -27,8 +37,8 @@ AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: Inline AllowShortLoopsOnASingleLine: false -##-- Braces -BreakBeforeBraces: Custom #Allman +# Braces and wrapping settings +BreakBeforeBraces: Custom BraceWrapping: AfterCaseLabel: true AfterClass: false @@ -47,77 +57,78 @@ BraceWrapping: SplitEmptyNamespace: false Cpp11BracedListStyle: false -##-- Line breaks -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None +# Line breaks and wrapping settings +#AllowAllParametersOfDeclarationOnNextLine: true +#AllowBreakBeforeNoexceptSpecifier: OnlyWithParen +AlwaysBreakAfterDefinitionReturnType: None # Keep return type on the same line if possible +AlwaysBreakAfterReturnType: None # Default value AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true -BreakAfterJavaFieldAnnotations: false -BreakBeforeBinaryOperators: None +BreakAfterAttributes: Always +BreakBeforeBinaryOperators: None # Default value: No line break before binary operators +BreakBeforeConceptDeclarations: Always BreakConstructorInitializers: AfterColon -#BreakInheritanceList: AfterColon -BreakBeforeTernaryOperators: false +BreakBeforeTernaryOperators: false # Default value BreakConstructorInitializersBeforeComma: true -BreakStringLiterals: false +#BreakInheritanceList: AfterColon +BreakStringLiterals: false # Default value +PackConstructorInitializers: NextLineOnly +PenaltyReturnTypeOnItsOwnLine: 200 # Prefer keeping return type on the same line if possible -##-- Indentation +# Indentation settings +AccessModifierOffset: -4 +IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: true IndentExternBlock: AfterExternBlock IndentGotoLabels: true IndentPPDirectives: AfterHash +IndentWrappedFunctionNames: false LambdaBodyIndentation: Signature NamespaceIndentation: Inner -##-- Spaces in/between statements/etc -SpaceAfterCStyleCast: false -SpaceAfterTemplateKeyword: false +# Spaces around statements settings +SpaceAfterCStyleCast: false # Default value +SpaceAfterTemplateKeyword: false # Default value #SpaceAroundPointerQualifiers: Left SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInContainerLiterals: false -SpacesInParentheses: false -SpacesInSquareBrackets: false +SpaceInEmptyParentheses: false # Default value +SpacesInAngles: false # Default value +SpacesInCStyleCastParentheses: false # Default value +SpacesInContainerLiterals: false # Default value +SpacesInParentheses: false # Default value +SpacesInSquareBrackets: false # Default value -##-- Misc -AllowAllParametersOfDeclarationOnNextLine: true -AccessModifierOffset: -4 -BinPackArguments: false -BinPackParameters: false -BreakAfterAttributes: Always -ExperimentalAutoDetectBinPacking: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false -DisableFormat: false -InsertNewlineAtEOF: true -KeepEmptyLinesAtTheStartOfBlocks: false -ReflowComments: false +# Macros settings +#MacroBlockBegin: [regex -> EXC_TRY...] +#MacroBlockEnd: +#StatementMacros: [-> ASSERT?] + +# Scopes settings +FixNamespaceComments: true +ShortNamespaceLines: 2 + +# Miscellaneous settings SeparateDefinitionBlocks: Always +ReflowComments: false -#-- Include order -#IncludeIsMainRegex: '$?' +# Include order #IncludeBlocks: Regroup +#IncludeIsMainRegex: '$?' IncludeCategories: #TODO: main include! - # Headers in "" with .h extension. - - Regex: '"([A-Za-z0-9\/-_])+\.h"' - Priority: 1 - # Headers in "" with .hpp extension. - - Regex: '"([A-Za-z0-9\/-_])+\.hpp"' - Priority: 2 - - # Headers in <> with .h extension. - - Regex: '<([A-Za-z0-9\/-_])+\.h>' - Priority: 10 - # Headers in <> with .hpp extension. - - Regex: '<([A-Za-z0-9\/-_])+\.hpp>' - Priority: 20 - # Headers in <> without extension. - - Regex: '<([A-Za-z0-9\/-_])+>' - Priority: 30 -SortIncludes: false #CaseSensitive + - Regex: '"([A-Za-z0-9\/-_])+\.h"' + Priority: 1 + - Regex: '"([A-Za-z0-9\/-_])+\.hpp"' + Priority: 2 + - Regex: '<([A-Za-z0-9\/-_])+\.h>' + Priority: 10 + - Regex: '<([A-Za-z0-9\/-_])+\.hpp>' + Priority: 20 + - Regex: '<([A-Za-z0-9\/-_])+>' + Priority: 30 +SortIncludes: false diff --git a/.clang-tidy b/.clang-tidy index d051527da..cfd245271 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -57,6 +57,8 @@ Checks: > CheckOptions: - key: 'clang-analyzer-core.NonNullParamChecker:assert_like_macro' value: 'DEBUG_ASSERT,ASSERT,PERSISTANT_ASSERT' + - key: 'clang-analyzer-config:noreturn_function' + value: 'RaiseImmediateAbort,RaiseRecoverableAbort' - { key: readability-identifier-naming.NamespaceCase, value: lower_case } - { key: readability-identifier-naming.ClassCase, value: CamelCase } - { key: readability-identifier-naming.StructCase, value: CamelCase } diff --git a/.clangd b/.clangd index 6b5f08c53..243a3c7f2 100644 --- a/.clangd +++ b/.clangd @@ -1,5 +1,6 @@ CompileFlags: Add: + # Exclude the following folders from the source code analysis. - -I!.codelite - -I!.ctagsd - -I!.git @@ -15,3 +16,5 @@ CompileFlags: # PathMatch: '.*/src/common/common\.h' # Diagnostics: # Suppress: ['-Wunused-include-directive'] + +# TODO: disable clazy-non-pod-global-static diff --git a/.gitignore b/.gitignore index 8fde82f30..a21d5394c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Ignore folders +/.cache /.git /.vscode *.vshistory* diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c7bb54b6..445f0334b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,39 +3,8 @@ cmake_minimum_required(VERSION 3.24) set(CMAKE_SUPPRESS_REGENERATION TRUE) # Supress the ZERO_CHECK generation - -# -------------------- GUI checkboxes -------------------- -option( - CMAKE_NO_GIT_REVISION - "Do not try to retrieve the current git revision. Useful for building source not git-cloned from Github." - FALSE -) -option(CROSSCOMPILE "Are we compiling for a different target architecture?" FALSE) -option( - RUNTIME_STATIC_LINK - "Statically link inside the executable the runtime libraries? (MSVC, libc/libcc, libstdc++)." - FALSE -) -option(CMAKE_EXPORT_COMPILE_COMMANDS "Export compiler commands to compile_commands.json" TRUE) - -set(predef_LIB_LIBEV_BUILD FALSE) -if(NOT WIN32) - set(predef_LIB_LIBEV_BUILD TRUE) -endif() -option(LIB_LIBEV_BUILD "Build libev." ${predef_LIB_LIBEV_BUILD}) -option(LIB_SQLITE_BUILD "Build sqlite." TRUE) -option(LIB_ZLIB_BUILD "Build zlib." TRUE) - -if(WIN32) - option(WIN32_SPAWN_CONSOLE "Spawn an additional console, useful for debugging." FALSE) -endif() - -option(USE_ASAN "Enable AddressSanitizer." FALSE) -option(USE_MSAN "Enable MemorySanitizer." FALSE) -option(USE_LSAN "Enable LeakSanitizer." FALSE) -option(USE_UBSAN "Enable Undefined Behavior Sanitizer." FALSE) - # -------------------- Utility functions -------------------- + function(booleanize_str_find VAR) if(${VAR} EQUAL -1) unset(${VAR} PARENT_SCOPE) @@ -44,7 +13,6 @@ function(booleanize_str_find VAR) endif() endfunction() - # -------------------- Start: initializations -------------------- # Should i load the toolchain passed as argument? @@ -54,9 +22,14 @@ if((NOT TOOLCHAIN_LOADED) AND (NOT ${CMAKE_TOOLCHAIN_FILE} STREQUAL "")) endif() 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) + include("cmake/DetectDefaultToolchain.cmake") +endif() + # Setup global flags, to be done before creating targets. if(NOT DEFINED CMAKE_CXX_STANDARD) @@ -76,7 +49,8 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") -# CMake also sets other predefined flags, which we don't want... +# 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 "") @@ -89,17 +63,65 @@ 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... +# We might want to delete only /RTC* flags, instead of erasing all of them altogether. +#STRING (REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") +#STRING (REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") -set(is_win32_app_linker) -if(WIN32 AND NOT ${WIN32_SPAWN_CONSOLE}) - set(is_win32_app_linker WIN32) # if not set, it defaults to console subsystem +# Also, it fails to create those predefined flags, which it needs, for custom build types (e.g. Nightly). +set(CMAKE_EXE_LINKER_FLAGS_NIGHTLY "") + +# -------------------- GUI checkboxes -------------------- + +option( + CMAKE_NO_GIT_REVISION + "Do not try to retrieve the current git revision. Useful for building source not git-cloned from Github." + FALSE +) +option(CROSSCOMPILE "Are we compiling for a different target architecture?" FALSE) +option( + RUNTIME_STATIC_LINK + "Statically link inside the executable the runtime libraries? (MSVC, libc/libcc, libstdc++)." + FALSE +) +option(CMAKE_EXPORT_COMPILE_COMMANDS "Export compiler commands to compile_commands.json" TRUE) + +set(predef_LIB_LIBEV_BUILD FALSE) +if(NOT WIN32) + set(predef_LIB_LIBEV_BUILD TRUE) endif() +option(LIB_LIBEV_BUILD "Build libev." ${predef_LIB_LIBEV_BUILD}) +option(LIB_SQLITE_BUILD "Build sqlite." TRUE) +option(LIB_ZLIB_BUILD "Build zlib." TRUE) -# If we have not specified a toolchain, let's detect which one we should use, using the detected arch. -if(NOT TOOLCHAIN_LOADED) - include("cmake/DetectDefaultToolchain.cmake") +if(WIN32) + option(WIN_SPAWN_CONSOLE "Spawn an additional console, useful for debugging." FALSE) + if(MSVC) + 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() + +if(NOT MSVC) + option(USE_COMPILER_HARDENING_OPTIONS "Enable compiler (even runtime) safety checks and code hardening." FALSE) +endif() +option(USE_ASAN "Enable AddressSanitizer." FALSE) +option(USE_UBSAN "Enable Undefined Behavior Sanitizer." FALSE) +option(USE_LSAN "Enable LeakSanitizer." FALSE) +option(USE_MSAN "Enable MemorySanitizer." FALSE) + +set(ENABLED_SANITIZER CACHE INTERNAL BOOL FALSE) +if(USE_ASAN OR USE_MSAN OR USE_LSAN OR USE_MSAN) + set(ENABLED_SANITIZER TRUE) +endif() + + +# -------------------- Stuff to set right away, after having parsed the checkboxes/CLI arguments. + +set(is_win32_app_linker) +if(WIN32 AND NOT ${WIN_SPAWN_CONSOLE}) + set(is_win32_app_linker WIN32) # if not set, it defaults to console subsystem endif() @@ -120,17 +142,17 @@ 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") if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "") if( - ( NOT ${CMAKE_BUILD_TYPE} STREQUAL "Release") + (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Release") AND (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") AND (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Nightly") ) - message(WARNING "Invalid parameter -DCMAKE_BUILD_TYPE, defaulting to Nightly.") + message(WARNING "Invalid parameter CMAKE_BUILD_TYPE (${CMAKE_BUILD_TYPE}), defaulting to Nightly.") # -> needed only for MAKEFILE-STYLE generators, which can't switch between different configs set(CMAKE_BUILD_TYPE "Nightly" CACHE STRING "" FORCE) else() @@ -157,11 +179,9 @@ else(SINGLE_TARGET) set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Nightly" CACHE STRING "" FORCE) endif(SINGLE_TARGET) - # Include the list of all Sphere source files include("src/CMakeSources.cmake") - # Configure output binaries set(TARGETS "" CACHE INTERNAL "List of Sphere targets to build.") if(SINGLE_TARGET) @@ -215,11 +235,28 @@ else(SINGLE_TARGET) target_precompile_headers(spheresvr ${pch_options}) endif(SINGLE_TARGET) +# -------------------- + +message(STATUS) +include("${CMAKE_SOURCE_DIR}/cmake/CompilerFlagsChecker.cmake") # -------------------- -#message(STATUS) -#include("${CMAKE_SOURCE_DIR}/cmake/CompilerFlagsChecker.cmake") +if(USE_ASAN) + set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} ADDRESS_SANITIZER) +endif() +if(USE_UBSAN) + set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} UNDEFINED_BEHAVIOR_SANITIZER) +endif() +if(USE_LSAN) + set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} LEAK_SANITIZER) +endif() +if(USE_MSAN) + set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} MEMORY_SANITIZER) +endif() +if(ENABLED_SANITIZER) + set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} _SANITIZERS) +endif() # -------------------- @@ -229,8 +266,6 @@ message(STATUS) message(STATUS "Configuring external libraries...") add_subdirectory(lib) - -message(STATUS) toolchain_exe_stuff() # stuff to be executed after ADD_EXECUTABLE # Get the Git revision number diff --git a/Changelog.txt b/Changelog.txt index 3e94dedc4..034689fd8 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -3903,5 +3903,14 @@ Added: 'H' shortcut for variables to get the value as hexadecimal. - Changed: @FollowerUpdate ARGN1 and ARGN2 are now read-only. Having them RW and actually get them working would have meant many other changes, breaking script compatibility. - Fixed: CURFOLLOWER.DEL/DELETE/CLEAR not working properly. -05-10-2024, Jhobean +03-10-2024, Nolok +- Fixed: LIST.ADD ignoring quoted arguments (Issue #1247). +- Fixed: FOOD and MAXFOOD not being stored in the worldsave file (Issue #1289). +- Added: CURFOLLOWER.CHARCOUNT, to return the raw number of characters you own/that are following you. Plain CURFOLLOWER returns the current total FOLLOWERSLOTS you own. +- Changed: removed CURFOLLOWER.DEL and CURFOLLOWER.DELETE in favor of the more specific: + CURFOLLOWER.DELUID: The same as .DEL/DELETE, just removed the pet via its UID. + CURFOLLOWER.DELINDEX: Removes the pet via its id in the (cur)follower list for that owner. +- Added: spells with SPELLFLAG_SUMMON now accept LOCAL.FollowerSlotsOverride in the triggers @Success and @SpellSuccess. Default value (do not override): -1. + +13-10-2024, Jhobean - Added: @PetRelease trigger work like @petdesert and return 1 to prevent the pet from being released. diff --git a/README.md b/README.md index 67aabfb3a..d2f5e46fa 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Most notable changes (right now) are: ### Required libraries (Windows) -+ `libmariadb.dll` (MariaDB Client v10.*package), found in `lib/bin/*cpu_architecture*/mariadb/libmariadb.dll` ++ `libmariadb.dll` (MariaDB Client v10.\* package), found in `lib/bin/*cpu_architecture*/mariadb/libmariadb.dll` ### Required libraries (Linux) @@ -156,7 +156,7 @@ Building will require more packages than the ones needed to run Sphere. #### Compiling on Linux -Just run the `make` command inside the `build` folder. You can pass the -jX argument (`make -jX`, where X is a number) to speed up the compilation and split the work between X threads. +Just run `cmake --build .` in the build directory (the one where you have asked CMake to create its files). ## Coding Notes (add as you wish to standardize the coding for new contributors) diff --git a/cmake/CompilerFlagsChecker.cmake b/cmake/CompilerFlagsChecker.cmake index 0e2e0399e..b0e183dc0 100644 --- a/cmake/CompilerFlagsChecker.cmake +++ b/cmake/CompilerFlagsChecker.cmake @@ -1,9 +1,30 @@ include(CheckCXXCompilerFlag) message(STATUS "Checking available compiler flags...") - -if (NOT MSVC) +if(NOT MSVC) message(STATUS "-- Compilation options:") + + # Compiler option flags. + list( + APPEND + compiler_options_base + -pthread + -fexceptions + -fnon-call-exceptions + -pipe + -ffast-math + ) + list(APPEND base_compiler_options_warning -Werror;-Wall;-Wextra;-Wpedantic) + + # Linker flags (warnings) + #check_cxx_compiler_flag("-Wl,--fatal-warnings" COMP_LINKER_HAS_FATAL_WARNINGS_V1) + #check_cxx_compiler_flag("-Wl,-fatal_warnings" COMP_LINKER_HAS_FATAL_WARNINGS_V2) + if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + set(COMP_LINKER_HAS_FATAL_WARNINGS_V2 TRUE) + else() + set(COMP_LINKER_HAS_FATAL_WARNINGS_V1 TRUE) + endif() + # Compiler option flags. Common to both compilers, but older versions might not support the following. #check_cxx_compiler_flag("" COMP_HAS_) @@ -11,38 +32,122 @@ 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("-fforce-emit-vtables" COMP_HAS_F_FORCE_EMIT_VTABLES) - - message(STATUS "-- Compilation options (Address Sanitizer):") - # Compiler option flags. Sanitizers related (ASAN). - #check_cxx_compiler_flag("-fsanitize=safe-stack" COMP_HAS_) # Can't be used with asan! - check_cxx_compiler_flag("-fsanitize-cfi" COMP_HAS_FSAN_CFI) # cfi: control flow integrity - check_cxx_compiler_flag("-fsanitize-address-use-after-scope" COMP_HAS_FSAN_ADDRESS_USE_AFTER_SCOPE) - check_cxx_compiler_flag("-fsanitize=pointer-compare" COMP_HAS_FSAN_PTR_COMP) - check_cxx_compiler_flag("-fsanitize=pointer-subtract" COMP_HAS_FSAN_PTR_SUB) - check_cxx_compiler_flag("-fsanitize-trap=all" COMP_HAS_FSAN_TRAP_ALL) - - message(STATUS "-- Compilation options (Sanitizers-related and safety checks):") - # Compiler option flags. Sanitizers related (other). - # fsanitize=signed-integer-overflow # -fno-strict-overflow (which implies -fwrapv-pointer and -fwrapv) - check_cxx_compiler_flag("-fcf-protection=full" COMP_HAS_FCF_PROTECTION_FULL) - check_cxx_compiler_flag("-fstack-check" COMP_HAS_F_STACK_CHECK) - check_cxx_compiler_flag("-fstack-protector-all" COMP_HAS_F_STACK_PROTECTOR_ALL) - check_cxx_compiler_flag("-fstack-protector-strong" COMP_HAS_F_STACK_PROTECTOR_STRONG) - check_cxx_compiler_flag("-fstack-protector" COMP_HAS_F_STACK_PROTECTOR) - check_cxx_compiler_flag("-fstack-clash-protection" COMP_HAS_F_STACK_CLASH_PROTECTION) - #Flags GCC specific? - check_cxx_compiler_flag("-fvtable-verify=preinit" COMP_HAS_F_VTABLE_VERIFY_PREINIT) - check_cxx_compiler_flag("-fharden-control-flow-redundancy" COMP_HAS_F_HARDEN_CFR) - check_cxx_compiler_flag("-fhardcfr-check-exceptions" COMP_HAS_F_HARDCFR_CHECK_EXCEPTIONS) - #check_cxx_compiler_flag("" COMP_HAS_) + # check_cxx_compiler_flag("-fforce-emit-vtables" COMP_HAS_F_FORCE_EMIT_VTABLES) + # -fwhole-program-vtables + # -fstrict-vtable-pointers + + if(${USE_ASAN}) + # Compiler option flags. Sanitizers related (ASAN). + message(STATUS "-- Compilation options (Address Sanitizer):") + + set(CMAKE_REQUIRED_LIBRARIES -fsanitize=address) # link against sanitizer lib, being required by the following compilation test + check_cxx_compiler_flag("-fsanitize=address" COMP_HAS_ASAN) + unset(CMAKE_REQUIRED_LIBRARIES) + if(NOT COMP_HAS_ASAN) + message(FATAL_ERROR "This compiler does not support Address Sanitizer. Disable it.") + endif() + + check_cxx_compiler_flag("-fsanitize-cfi" COMP_HAS_FSAN_CFI) # cfi: control flow integrity + check_cxx_compiler_flag("-fsanitize-address-use-after-scope" COMP_HAS_FSAN_ADDRESS_USE_AFTER_SCOPE) + check_cxx_compiler_flag("-fsanitize=pointer-compare" COMP_HAS_FSAN_PTR_COMP) + check_cxx_compiler_flag("-fsanitize=pointer-subtract" COMP_HAS_FSAN_PTR_SUB) + check_cxx_compiler_flag("-fsanitize-trap=all" COMP_HAS_FSAN_TRAP_ALL) + endif() + + if(${USE_UBSAN}) + # Compiler option flags. Sanitizers related (UBSAN). + message(STATUS "-- Compilation options (Undefined Behavior Sanitizer):") + + set(CMAKE_REQUIRED_LIBRARIES -fsanitize=undefined) # link against sanitizer lib, being required by the following compilation test + check_cxx_compiler_flag("-fsanitize=undefined" COMP_HAS_UBSAN) + unset(CMAKE_REQUIRED_LIBRARIES) + if(NOT COMP_HAS_UBSAN) + message(FATAL_ERROR "This compiler does not support Undefined Behavior Sanitizer. Disable it.") + 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=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) + endif() + + if(${USE_LSAN}) + # Compiler option flags. Sanitizers related (LSAN). + message(STATUS "-- Compilation options (Leak Sanitizer):") + + set(CMAKE_REQUIRED_LIBRARIES -fsanitize=leak) # link against sanitizer lib, being required by the following compilation test + check_cxx_compiler_flag("-fsanitize=leak" COMP_HAS_LSAN) + unset(CMAKE_REQUIRED_LIBRARIES) + if(NOT COMP_HAS_LSAN) + message(FATAL_ERROR "This compiler does not support Leak Sanitizer. Disable it.") + endif() + endif() + + if(${USE_MSAN}) + # Compiler option flags. Sanitizers related (UBSAN). + message(STATUS "-- Compilation options (Memory Behavior Sanitizer):") + + set(CMAKE_REQUIRED_LIBRARIES -fsanitize=memory) # link against sanitizer lib, being required by the following compilation test + check_cxx_compiler_flag("-fsanitize=memory" COMP_HAS_MSAN) + unset(CMAKE_REQUIRED_LIBRARIES) + if(NOT COMP_HAS_MSAN) + message(FATAL_ERROR "This compiler does not support Memory Sanitizer. Disable it.") + endif() + + # MemorySanitizer: it doesn't work out of the box. It needs to be linked to an MSAN-instrumented build of libc++ and libc++abi. + # This means: one should build them from LLVM source... + # https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo + #IF (${USE_MSAN}) + # SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + #ENDIF() + # Use "-stdlib=libstdc++" to link against GCC c/c++ libs (this is done by default) + # To use LLVM libc++ use "-stdlib=libc++", but you need to install it separately + + message( + WARNING + "You have enabled MSAN. Make sure you do know what you are doing. It doesn't work out of the box. \ +See comments in the toolchain and: https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo" + ) + + check_cxx_compiler_flag("-fsanitize=memory-track-origins" COMP_HAS_F_SANITIZE_MEMORY_TRACK_ORIGINS) + check_cxx_compiler_flag("-fPIE" COMP_HAS_F_PIE) + endif() + + if(ENABLED_SANITIZER) + if(RUNTIME_STATIC_LINK) + check_cxx_compiler_flag("-fstatic-libasan" COMP_HAS_F_STATIC_LIBASAN) + if(NOT COMP_HAS_F_STATIC_LIBASAN) + message(FATAL_ERROR "This compiler doesn't support statically linking libasan. Turn off RUNTIME_STATIC_LINK?") + endif() + endif() + endif() + + if(USE_COMPILER_HARDENING_OPTIONS) + message(STATUS "-- Compilation 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! + endif() + check_cxx_compiler_flag("-fcf-protection=full" COMP_HAS_FCF_PROTECTION_FULL) + check_cxx_compiler_flag("-fstack-check" COMP_HAS_F_STACK_CHECK) + check_cxx_compiler_flag("-fstack-protector-all" COMP_HAS_F_STACK_PROTECTOR_ALL) + check_cxx_compiler_flag("-fstack-protector-strong" COMP_HAS_F_STACK_PROTECTOR_STRONG) + check_cxx_compiler_flag("-fstack-protector" COMP_HAS_F_STACK_PROTECTOR) + check_cxx_compiler_flag("-fstack-clash-protection" COMP_HAS_F_STACK_CLASH_PROTECTION) + #Flags GCC specific? + check_cxx_compiler_flag("-fvtable-verify=preinit" COMP_HAS_F_VTABLE_VERIFY_PREINIT) + check_cxx_compiler_flag("-fharden-control-flow-redundancy" COMP_HAS_F_HARDEN_CFR) + check_cxx_compiler_flag("-fhardcfr-check-exceptions" COMP_HAS_F_HARDCFR_CHECK_EXCEPTIONS) + #check_cxx_compiler_flag("" COMP_HAS_) + endif() # Compiler warning flags. message(STATUS "-- Compilation options (warnings):") - check_cxx_compiler_flag("-Wuseless-cast" COMP_HAS_W_USELESS_CAST) + #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. - check_cxx_compiler_flag("-Wcast-qual" COMP_HAS_W_CAST_QUAL) # casts that remove a type's const or volatile qualifier. + #check_cxx_compiler_flag("-Wconversion" COMP_HAS_W_CONVERSION) # Temporarily disabled. Implicit type conversions that might change a value, such as narrowing conversions. + #check_cxx_compiler_flag("-Wcast-qual" COMP_HAS_W_CAST_QUAL) # casts that remove a type's const or volatile qualifier. check_cxx_compiler_flag("-Wzero-as-null-pointer-constant" COMP_HAS_W_ZERO_NULLPTR) check_cxx_compiler_flag("-Wdisabled-optimization" COMP_HAS_W_DISABLE_OPT) check_cxx_compiler_flag("-Winvalid-pch" COMP_HAS_W_INVALID_PCH) @@ -65,7 +170,6 @@ if (NOT MSVC) #check_cxx_compiler_flag("" COMP_HAS_) # Compiler warning flags. TODO: To be evaluated... - # -Wshadow # -Wfree-nonheap-object (good but false positive warnings on GCC?) # -Wcast-align=strict # -Wcast-user-defined @@ -73,170 +177,251 @@ if (NOT MSVC) # Compiler warning flags. To be disabled. message(STATUS "-- Compilation options (disable specific warnings):") - check_cxx_compiler_flag("-Wno-format-nonliteral" COMP_HAS_WNO_FORMAT_NONLITERAL) # Since -Wformat=2 is stricter, you would need to disable this warning. - check_cxx_compiler_flag("-Wno-nonnull-compare" COMP_HAS_WNO_NONNULL_COMPARE) - check_cxx_compiler_flag("-Wno-maybe-uninitialized" COMP_HAS_WNO_MAYBE_UNINIT) - check_cxx_compiler_flag("-Wno-switch" COMP_HAS_WNO_SWITCH) - check_cxx_compiler_flag("-Wno-implicit-fallthrough" COMP_HAS_WNO_IMPLICIT_FALLTHROUGH) - check_cxx_compiler_flag("-Wno-parentheses" COMP_HAS_WNO_PARENTHESES) - check_cxx_compiler_flag("-Wno-misleading-indentation" COMP_HAS_WNO_MISLEADING_INDENTATION) - check_cxx_compiler_flag("-Wno-unused-result" COMP_HAS_WNO_UNUSED_RESULT) - check_cxx_compiler_flag("-Wno-nested-anon-types" COMP_HAS_WNO_NESTED_ANON_TYPES) - check_cxx_compiler_flag("-Wno-format-security" COMP_HAS_WNO_FORMAT_SECURITY) # TODO: remove that when we'll have time to fix every printf format issue - check_cxx_compiler_flag("-Wno-deprecated-declarations" COMP_HAS_WNO_DEPRECATED_DECLARATIONS) - #check_cxx_compiler_flag("-Wno-unknown-pragmas" COMP_HAS_) + check_cxx_compiler_flag("-Wunused-function" COMP_HAS_WNO_UNUSED_FUNCTION) + check_cxx_compiler_flag("-Wformat-nonliteral" COMP_HAS_WNO_FORMAT_NONLITERAL) # Since -Wformat=2 is stricter, you would need to disable this warning. + 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("-Wswitch" COMP_HAS_WNO_SWITCH) + check_cxx_compiler_flag("-Wimplicit-fallthrough" COMP_HAS_WNO_IMPLICIT_FALLTHROUGH) + check_cxx_compiler_flag("-Wparentheses" COMP_HAS_WNO_PARENTHESES) + check_cxx_compiler_flag("-Wmisleading-indentation" COMP_HAS_WNO_MISLEADING_INDENTATION) + check_cxx_compiler_flag("-Wunused-result" COMP_HAS_WNO_UNUSED_RESULT) + check_cxx_compiler_flag("-Wnested-anon-types" COMP_HAS_WNO_NESTED_ANON_TYPES) + check_cxx_compiler_flag("-Wformat-security" COMP_HAS_WNO_FORMAT_SECURITY) # TODO: remove that when we'll have time to fix every printf format issue + check_cxx_compiler_flag("-Wdeprecated-declarations" COMP_HAS_WNO_DEPRECATED_DECLARATIONS) + check_cxx_compiler_flag("-Wlanguage-extension-token" COMP_HAS_WNO_LANGUAGE_EXTENSION_TOKEN) + #check_cxx_compiler_flag("-Wunknown-pragmas" COMP_HAS_) #check_cxx_compiler_flag("" COMP_HAS_) -else (NOT MSVC) +elseif(MSVC) # Compiler warning flags (MSVC). message(STATUS "-- Compilation options (MSVC):") - check_cxx_compiler_flag("/Wd5105" COMP_HAS_DEFINE_MACRO_EXPANSION) # False positive warning, maybe even a MSVC bug. + 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_) -endif() +endif() # ---- Append options into some lists. # ---- GCC/Clang -if(COMP_HAS_FNO_EXPENSIVE_OPTIMIZATIONS) - list(APPEND checked_compiler_options "-fno-expensive-optimizations") -endif() +if(NOT MSVC) + if(COMP_LINKER_HAS_FATAL_WARNINGS_V1) + list(APPEND checked_linker_options_all "-Wl,--fatal-warnings") + endif() + if(COMP_LINKER_HAS_FATAL_WARNINGS_V2) + list(APPEND checked_linker_options_all "-Wl,-fatal_warnings") + endif() -if(COMP_HAS_FSAN_CFI) - list(APPEND checked_compiler_options_asan "-fsanitize-cfi") -endif() -if(COMP_HAS_FSAN_ADDRESS_USE_AFTER_SCOPE) - list(APPEND checked_compiler_options_asan "-fsanitize-address-use-after-scope") -endif() -if(COMP_HAS_FSAN_PTR_COMP) - list(APPEND checked_compiler_options_asan "-fsanitize=pointer-compare" ) -endif() -if(COMP_HAS_FSAN_PTR_SUB) - list(APPEND checked_compiler_options_asan "-fsanitize=pointer-subtract" ) -endif() -if(COMP_HAS_FSAN_TRAP_ALL) - list(APPEND checked_compiler_options_asan "-fsanitize-trap=all" ) -endif() + if(COMP_HAS_FNO_EXPENSIVE_OPTIMIZATIONS) + list(APPEND checked_compiler_options "-fno-expensive-optimizations") + endif() -if(COMP_HAS_FCF_PROTECTION_FULL) - list(APPEND checked_compiler_options_sanitize_other "-fcf-protection=full") -endif() -if(COMP_HAS_F_STACK_CHECK) - list(APPEND checked_compiler_options_sanitize_other "-fstack-check") -endif() -if(COMP_HAS_F_STACK_PROTECTOR_ALL) - list(APPEND checked_compiler_options_sanitize_other "-fstack-protector-all") -elseif(COMP_HAS_F_STACK_PROTECTOR_STRONG) - list(APPEND checked_compiler_options_sanitize_other "-fstack-protector-strong") -elseif(COMP_HAS_F_STACK_PROTECTOR) - list(APPEND checked_compiler_options_sanitize_other "-fstack-protector") -endif() -if(COMP_HAS_F_STACK_CLASH_PROTECTION) - list(APPEND checked_compiler_options_sanitize_other "-fstack-clash-protection") -endif() -if(COMP_HAS_F_VTABLE_VERIFY_PREINIT) - list(APPEND checked_compiler_options_sanitize_other "-fvtable-verify=preinit") -endif() -if(COMP_HAS_F_HARDEN_CFR) - list(APPEND checked_compiler_options_sanitize_other "-fharden-control-flow-redundancy") -endif() -if(COMP_HAS_F_HARDCFR_CHECK_EXCEPTIONS) - list( - APPEND checked_compiler_options_sanitize_other "-fhardcfr-check-exceptions") -endif() + if(COMP_HAS_ASAN) + list(APPEND checked_compiler_options_asan "-fsanitize=address") + list(APPEND checked_linker_options_all "-fsanitize=address") + if(COMP_HAS_F_STATIC_LIBASAN) + list(APPEND checked_linker_options_all "-static-libasan") + endif() + if(COMP_HAS_FSAN_CFI) + list(APPEND checked_compiler_options_asan "-fsanitize-cfi") + endif() + if(COMP_HAS_FSAN_ADDRESS_USE_AFTER_SCOPE) + list(APPEND checked_compiler_options_asan "-fsanitize-address-use-after-scope") + endif() + if(COMP_HAS_FSAN_PTR_COMP) + list(APPEND checked_compiler_options_asan "-fsanitize=pointer-compare") + endif() + if(COMP_HAS_FSAN_PTR_SUB) + list(APPEND checked_compiler_options_asan "-fsanitize=pointer-subtract") + endif() + if(COMP_HAS_FSAN_TRAP_ALL) + list(APPEND checked_compiler_options_asan "-fsanitize-trap=all") + endif() + endif() -if(COMP_HAS_W_USELESS_CAST) - list(APPEND checked_compiler_warnings "-Wuseless-cast") -endif() -if(COMP_HAS_NULL_DEREFERENCE) - list(APPEND checked_compiler_warnings "-Wnull-dereference") -endif() -if(COMP_HAS_W_CONVERSION) - list(APPEND checked_compiler_warnings "-Wconversion") -endif() -if(COMP_HAS_W_CAST_QUAL) - list(APPEND checked_compiler_warnings "-Wcast-qual") -endif() -if(COMP_HAS_W_ZERO_NULLPTR) - list(APPEND checked_compiler_warnings "-Wzero-as-null-pointer-constant") -endif() -if(COMP_HAS_W_DISABLE_OPT) - list(APPEND checked_compiler_warnings "-Wdisabled-optimization") -endif() -if(COMP_HAS_W_INVALID_PCH) - list(APPEND checked_compiler_warnings "-Winvalid-pch") -endif() -if(COMP_HAS_W_SHADOW) - list(APPEND checked_compiler_warnings "-Wshadow") -endif() + if(COMP_HAS_UBSAN) + list(APPEND checked_compiler_options_ubsan "-fsanitize=undefined") + list(APPEND checked_linker_options_all "-fsanitize=undefined") + if(RUNTIME_STATIC_LINK) + list(APPEND checked_linker_options_all "-static-libubsan") + endif() + if(COMP_HAS_FSAN_FLOAT_DIVIDE_BY_ZERO) + list(APPEND checked_compiler_options_ubsan "-fsanitize=float-divide-by-zero") + endif() + if(COMP_HAS_FSAN_UNSIGNED_INTEGER_OVERFLOW) + list(APPEND checked_compiler_options_ubsan "-fsanitize=unsigned-integer-overflow") + endif() + if(COMP_HAS_FSAN_IMPLICIT_CONVERSION) + list(APPEND checked_compiler_options_ubsan "-fsanitize=implicit-conversion") + endif() + if(COMP_HAS_FSAN_NO_ENUM) + list(APPEND checked_compiler_options_ubsan "-fno-sanitize=enum") + endif() + if(COMP_HAS_FSAN_LOCAL_BOUNDS) + list(APPEND checked_compiler_options_ubsan "-fsanitize=local-bounds") + endif() + endif() -if(COMP_HAS_W_LTO_TYPE_MISMATCH) - list(APPEND checked_compiler_warnings "-Wlto-type-mismatch") -endif() -if(COMP_HAS_W_SHIFT_OVERFLOW) - list(APPEND checked_compiler_warnings "-Wshift-overflow=2") -endif() -if(COMP_HAS_W_DUPLICATED_COND) - list(APPEND checked_compiler_warnings "-Wduplicated-cond") -endif() -if(COMP_HAS_W_SIZED_DEALLOC) - list(APPEND checked_compiler_warnings "-Wsized-deallocation") -endif() -if(COMP_HAS_W_VECTOR_OP_PERF) - list(APPEND checked_compiler_warnings "-Wvector-operation-performance") -endif() -if(COMP_HAS_W_TRAMPOLINES) - list(APPEND checked_compiler_warnings "-Wtrampolines") -endif() -if(COMP_HAS_W_WEAK_VTABLES) - list(APPEND checked_compiler_warnings "-Wweak-vtables") -endif() -if(COMP_HAS_W_MISSING_PROTOTYPES) - list(APPEND checked_compiler_warnings "-Wmissing-prototypes") -endif() -if(COMP_HAS_W_MISSING_VARIABLE_DECL) - list(APPEND checked_compiler_warnings "-Wmissing-variable-declarations") -endif() + if(COMP_HAS_LSAN) + list(APPEND checked_compiler_options_lsan "-fsanitize=leak") + list(APPEND checked_linker_options_all "-fsanitize=leak") + if(RUNTIME_STATIC_LINK) + list(APPEND checked_linker_options_all "-static-liblsan") + endif() + endif() -if(COMP_HAS_WNO_FORMAT_NONLITERAL) - list(APPEND checked_compiler_warnings_disabled "-Wno-format-nonliteral") -endif() -if(COMP_HAS_WNO_NONNULL_COMPARE) - list(APPEND checked_compiler_warnings_disabled "-Wno-nonnull-compare") -endif() -if(COMP_HAS_WNO_MAYBE_UNINIT) - list(APPEND checked_compiler_warnings_disabled "-Wno-maybe-uninitialized") -endif() -if(COMP_HAS_WNO_SWITCH) - list(APPEND checked_compiler_warnings_disabled "-Wno-switch") -endif() -if(COMP_HAS_WNO_IMPLICIT_FALLTHROUGH) - list(APPEND checked_compiler_warnings_disabled "-Wno-implicit-fallthrough") -endif() -if(COMP_HAS_WNO_PARENTHESES) - list(APPEND checked_compiler_warnings_disabled "-Wno-parentheses") -endif() -if(COMP_HAS_WNO_MISLEADING_INDENTATION) - list(APPEND checked_compiler_warnings_disabled "-Wno-misleading-indentation") -endif() -if(COMP_HAS_WNO_UNUSED_RESULT) - list(APPEND checked_compiler_warnings_disabled "-Wno-unused-result") -endif() -if(COMP_HAS_WNO_NESTED_ANON_TYPES) - list(APPEND checked_compiler_warnings_disabled "-Wno-nested-anon-types") -endif() -if(COMP_HAS_WNO_FORMAT_SECURITY) - list(APPEND checked_compiler_warnings_disabled "-Wno-format-security") -endif() -if(COMP_HAS_WNO_DEPRECATED_DECLARATIONS) - list(APPEND checked_compiler_warnings_disabled "-Wno-deprecated-declarations") + if(COMP_HAS_MSAN) + list(APPEND checked_compiler_options_msan "-fsanitize=memory") + list(APPEND checked_linker_options_all "-fsanitize=memory") + if(RUNTIME_STATIC_LINK) + list(APPEND checked_linker_options_all "-static-libmsan") + endif() + if(COMP_HAS_F_SANITIZE_MEMORY_TRACK_ORIGINS) + list(APPEND checked_compiler_options_msan "-fsanitize=memory-track-origins") + endif() + if(COMP_HAS_F_PIE) + list(APPEND checked_compiler_options_msan "-fPIE") + list(APPEND checked_linker_options_all "-pie") + endif() + endif() + + if(COMP_HAS_F_SANITIZE_SAFE_STACK) + list(APPEND checked_compiler_options_sanitize_harden "-fsanitize=safe-stack") + endif() + if(COMP_HAS_FCF_PROTECTION_FULL) + list(APPEND checked_compiler_options_sanitize_harden "-fcf-protection=full") + endif() + if(COMP_HAS_F_STACK_CHECK) + list(APPEND checked_compiler_options_sanitize_harden "-fstack-check") + endif() + if(COMP_HAS_F_STACK_PROTECTOR_ALL) + list(APPEND checked_compiler_options_sanitize_harden "-fstack-protector-all") + elseif(COMP_HAS_F_STACK_PROTECTOR_STRONG) + list(APPEND checked_compiler_options_sanitize_harden "-fstack-protector-strong") + elseif(COMP_HAS_F_STACK_PROTECTOR) + list(APPEND checked_compiler_options_sanitize_harden "-fstack-protector") + endif() + if(COMP_HAS_F_STACK_CLASH_PROTECTION) + list(APPEND checked_compiler_options_sanitize_harden "-fstack-clash-protection") + endif() + if(COMP_HAS_F_VTABLE_VERIFY_PREINIT) + list(APPEND checked_compiler_options_sanitize_harden "-fvtable-verify=preinit") + endif() + if(COMP_HAS_F_HARDEN_CFR) + list(APPEND checked_compiler_options_sanitize_harden "-fharden-control-flow-redundancy") + endif() + if(COMP_HAS_F_HARDCFR_CHECK_EXCEPTIONS) + list(APPEND checked_compiler_options_sanitize_harden "-fhardcfr-check-exceptions") + endif() + + if(COMP_HAS_W_USELESS_CAST) + list(APPEND checked_compiler_warnings "-Wuseless-cast") + endif() + if(COMP_HAS_NULL_DEREFERENCE) + list(APPEND checked_compiler_warnings "-Wnull-dereference") + endif() + if(COMP_HAS_W_CONVERSION) + list(APPEND checked_compiler_warnings "-Wconversion") + endif() + if(COMP_HAS_W_CAST_QUAL) + list(APPEND checked_compiler_warnings "-Wcast-qual") + endif() + if(COMP_HAS_W_ZERO_NULLPTR) + list(APPEND checked_compiler_warnings "-Wzero-as-null-pointer-constant") + endif() + if(COMP_HAS_W_DISABLE_OPT) + list(APPEND checked_compiler_warnings "-Wdisabled-optimization") + endif() + if(COMP_HAS_W_INVALID_PCH) + list(APPEND checked_compiler_warnings "-Winvalid-pch") + endif() + if(COMP_HAS_W_SHADOW) + list(APPEND checked_compiler_warnings "-Wshadow") + endif() + + if(COMP_HAS_W_LTO_TYPE_MISMATCH) + list(APPEND checked_compiler_warnings "-Wlto-type-mismatch") + endif() + if(COMP_HAS_W_SHIFT_OVERFLOW) + list(APPEND checked_compiler_warnings "-Wshift-overflow=2") + endif() + if(COMP_HAS_W_DUPLICATED_COND) + list(APPEND checked_compiler_warnings "-Wduplicated-cond") + endif() + if(COMP_HAS_W_SIZED_DEALLOC) + list(APPEND checked_compiler_warnings "-Wsized-deallocation") + endif() + if(COMP_HAS_W_VECTOR_OP_PERF) + list(APPEND checked_compiler_warnings "-Wvector-operation-performance") + endif() + if(COMP_HAS_W_TRAMPOLINES) + list(APPEND checked_compiler_warnings "-Wtrampolines") + endif() + if(COMP_HAS_W_WEAK_VTABLES) + list(APPEND checked_compiler_warnings "-Wweak-vtables") + endif() + if(COMP_HAS_W_MISSING_PROTOTYPES) + list(APPEND checked_compiler_warnings "-Wmissing-prototypes") + endif() + if(COMP_HAS_W_MISSING_VARIABLE_DECL) + list(APPEND checked_compiler_warnings "-Wmissing-variable-declarations") + endif() + + if(COMP_HAS_WNO_UNUSED_FUNCTION) + list(APPEND checked_compiler_warnings_disabled "-Wno-unused-function") + endif() + if(COMP_HAS_WNO_FORMAT_NONLITERAL) + list(APPEND checked_compiler_warnings_disabled "-Wno-format-nonliteral") + endif() + if(COMP_HAS_WNO_NONNULL_COMPARE) + list(APPEND checked_compiler_warnings_disabled "-Wno-nonnull-compare") + endif() + if(COMP_HAS_WNO_MAYBE_UNINIT) + list(APPEND checked_compiler_warnings_disabled "-Wno-maybe-uninitialized") + endif() + if(COMP_HAS_WNO_SWITCH) + list(APPEND checked_compiler_warnings_disabled "-Wno-switch") + endif() + if(COMP_HAS_WNO_IMPLICIT_FALLTHROUGH) + list(APPEND checked_compiler_warnings_disabled "-Wno-implicit-fallthrough") + endif() + if(COMP_HAS_WNO_PARENTHESES) + list(APPEND checked_compiler_warnings_disabled "-Wno-parentheses") + endif() + if(COMP_HAS_WNO_MISLEADING_INDENTATION) + list(APPEND checked_compiler_warnings_disabled "-Wno-misleading-indentation") + endif() + if(COMP_HAS_WNO_UNUSED_RESULT) + list(APPEND checked_compiler_warnings_disabled "-Wno-unused-result") + endif() + if(COMP_HAS_WNO_NESTED_ANON_TYPES) + list(APPEND checked_compiler_warnings_disabled "-Wno-nested-anon-types") + endif() + if(COMP_HAS_WNO_FORMAT_SECURITY) + list(APPEND checked_compiler_warnings_disabled "-Wno-format-security") + endif() + if(COMP_HAS_WNO_DEPRECATED_DECLARATIONS) + list(APPEND checked_compiler_warnings_disabled "-Wno-deprecated-declarations") + endif() + if(COMP_HAS_WNO_LANGUAGE_EXTENSION_TOKEN) + list(APPEND checked_compiler_warnings_disabled "-Wno-language-extension-token") + endif() endif() # ---- MSVC -if(COMP_HAS_DEFINE_MACRO_EXPANSION) - list(APPEND checked_compiler_flags_msvc "/Wd5105") +if(MSVC) + #list( + # APPEND + # base_compiler_options_msvc + # "/wd5105" + #) + + if(COMP_HAS_W_DEFINE_MACRO_EXPANSION) + list(APPEND checked_compiler_options_msvc "/wd5105") + endif() + endif() # ---- Double checks @@ -250,7 +435,6 @@ if( unset(COMPILER_SUPPORTS_NULL_DEREFERENCE CACHE) endif() - # ---- #[[ @@ -258,18 +442,81 @@ set(checked_compiler_options) set(checked_compiler_options_asan) #set(checked_compiler_options_ubsan) #set(checked_compiler_options_lsan) -set(checked_compiler_options_sanitize_other) +set(checked_compiler_options_sanitize_harden) set(checked_compiler_warnings) set(checked_compiler_warnings_disabled) -set(checked_compiler_flags_msvc) +set(checked_compiler_options_msvc) ]] -message(STATUS "Adding the following conditional compiler options: ${checked_compiler_options}") -message(STATUS "Adding the following conditional compiler options for ASan: ${checked_compiler_options_asan}") -message(STATUS "Adding the following conditional compiler options for sanitizers: ${checked_compiler_options_sanitize_other}") -message(STATUS "Adding the following conditional compiler warnings: ${checked_compiler_warnings}") -message(STATUS "Adding the following conditional compiler warnings ignore options: ${checked_compiler_warnings_disabled}") +## ---- + +# Now sum up, report and encapsulate all the options. + +if(NOT MSVC) + list( + APPEND + list_explicit_compiler_options_all + ${compiler_options_base} + ${base_compiler_options_warning} + ${checked_compiler_options} + ${checked_compiler_options_asan} + ${checked_compiler_options_ubsan} + ${checked_compiler_options_lsan} + ${checked_compiler_options_msan} + ${checked_compiler_options_sanitize_harden} + ${checked_compiler_warnings} + ${checked_compiler_warnings_disabled} + ) + + list(APPEND list_explicit_linker_options_all ${checked_linker_options_all};-pthread;-dynamic) + + #string(JOIN " " string_checked_compiler_options_all ${list_checked_compiler_options_all}) + #set(string_checked_compiler_options_all CACHE INTERNAL STRING) + set(list_explicit_compiler_options_all CACHE INTERNAL STRING) + set(list_explicit_linker_options_all CACHE INTERNAL STRING) + + # -- + + message(STATUS "Adding the following base compiler options: ${compiler_options_base}") + message(STATUS "Adding the following base compiler warning options: ${base_compiler_options_warning}") + + message(STATUS "Adding the following conditional compiler options: ${checked_compiler_options}.") + if(COMP_HAS_ASAN) + message(STATUS "Adding the following conditional compiler options for ASan: ${checked_compiler_options_asan}.") + endif() + if(COMP_HAS_UBSAN) + message( + STATUS + "Adding the following conditional compiler options for UBSan: ${checked_compiler_options_ubsan}." + ) + endif() + if(COMP_HAS_LSAN) + message(STATUS "Adding the following conditional compiler options for LSan: ${checked_compiler_options_lsan}.") + endif() + + if(COMP_HAS_MSAN) + message(STATUS "Adding the following conditional compiler options for MSan: ${checked_compiler_options_msan}.") + endif() + + message( + STATUS + "Adding the following conditional compiler options for hardening: ${checked_compiler_options_sanitize_harden}." + ) + message(STATUS "Adding the following conditional compiler warnings: ${checked_compiler_warnings}.") + message( + STATUS + "Adding the following conditional compiler warnings ignore options: ${checked_compiler_warnings_disabled}." + ) + message(STATUS "Adding the following linker options: ${list_explicit_linker_options_all}.") + +elseif(MSVC) + list( + APPEND + list_explicit_compiler_options_all + ${base_compiler_options_msvc} + ${checked_compiler_options_msvc} + ) + + message(STATUS "Adding the following conditional compiler options: ${list_explicit_compiler_options_all}.") -if(MSVC) - message(STATUS "Adding the following conditional compiler options: ${checked_compiler_flags_msvc}") endif() diff --git a/cmake/toolchains/Windows-GNU-x86_64.cmake b/cmake/toolchains/Windows-GNU-x86_64.cmake index 00b1fafc1..ec6ac7494 100644 --- a/cmake/toolchains/Windows-GNU-x86_64.cmake +++ b/cmake/toolchains/Windows-GNU-x86_64.cmake @@ -17,7 +17,7 @@ function(toolchain_after_project) # Do not set CMAKE_SYSTEM_NAME if compiling for the same OS, otherwise CMAKE_CROSSCOMPILING will be set to TRUE #unset(CMAKE_SYSTEM_NAME) #SET(CMAKE_SYSTEM_NAME "Windows" CACHE INTERNAL "" FORCE) - unset() + unset(CMAKE_SYSTEM_PROCESSOR) set(CMAKE_SYSTEM_PROCESSOR "x86_64" CACHE INTERNAL "" FORCE) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin-x86_64" PARENT_SCOPE) #set(ARCH_BITS 64 CACHE INTERNAL "" FORCE) # automatically gotten from CMAKE_SYSTEM_PROCESSOR diff --git a/cmake/toolchains/Windows-MSVC.cmake b/cmake/toolchains/Windows-MSVC.cmake index 3088faee3..4df3bad9f 100644 --- a/cmake/toolchains/Windows-MSVC.cmake +++ b/cmake/toolchains/Windows-MSVC.cmake @@ -34,7 +34,7 @@ function(toolchain_exe_stuff) #-- Configure the Windows application type and add global linker flags. - if(${WIN32_SPAWN_CONSOLE}) + if(${WIN_SPAWN_CONSOLE}) add_link_options("LINKER:/ENTRY:WinMainCRTStartup") # Handled by is_win32_app_linker -> "LINKER:/SUBSYSTEM:CONSOLE" set(PREPROCESSOR_DEFS_EXTRA _WINDOWS_CONSOLE) #ELSE () @@ -42,7 +42,7 @@ function(toolchain_exe_stuff) endif() #-- Validate sanitizers options and store them between the common compiler flags. - + # TODO: move sanitizers checks to CompilerFlagsChecker.cmake. set(ENABLED_SANITIZER false) if(${USE_ASAN}) if(${MSVC_TOOLSET_VERSION} LESS_EQUAL 141) # VS 2017 @@ -74,6 +74,7 @@ function(toolchain_exe_stuff) set(cxx_compiler_flags_common ${CXX_FLAGS_EXTRA} + ${list_explicit_compiler_options_all} /W4 /MP /GR @@ -101,16 +102,28 @@ function(toolchain_exe_stuff) endif() set(cxx_compiler_flags_common ${cxx_compiler_flags_common} /Zc:preprocessor ${local_msvc_compat_options}) - # Needed, otherwise throws a error... Regression? + # Needed, otherwise throws a error... Regression in CMake? set(CMAKE_C_FLAGS_DEBUG_INIT "" INTERNAL) set(CMAKE_CXX_FLAGS_DEBUG_INIT "" INTERNAL) + if(RUNTIME_STATIC_LINK) + set(local_msvc_cmdline_runtime_lib_debug /MTd) + set(local_msvc_cmdline_runtime_lib_nondebug /MT) + else() + set(local_msvc_cmdline_runtime_lib_debug /MDd) + set(local_msvc_cmdline_runtime_lib_nondebug /MD) + endif() + if(WIN_GENERATE_CRASHDUMP) + set(local_msvc_cmdline_exception_handler /EHa) + else() + set(local_msvc_exception_handler /EHsc) + endif() # gersemi: off target_compile_options(spheresvr PRIVATE ${cxx_compiler_flags_common} - $<$: $,/MT,/MD> /EHa /Oy /GL /GA /Gw /Gy /GF $,/O1 /Zi,/O2>> - $<$: $,/MT,/MD> /EHa /Oy /GL /GA /Gw /Gy /GF $,/O1 /Zi,/O2>> - $<$: $,/MTd,/MDd> /EHsc /Oy- /ob1 /Od /Gs $,/Zi,/ZI>> + $<$: ${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>> # ASan (and compilation for ARM arch) doesn't support edit and continue option (ZI) ) # gersemi: on @@ -118,7 +131,6 @@ function(toolchain_exe_stuff) if("${ARCH}" STREQUAL "x86_64") target_compile_options(spheresvr PRIVATE /arch:SSE2) endif() - #-- Apply linker flags. # For some reason only THIS one isn't created, and CMake complains with an error... @@ -126,10 +138,10 @@ function(toolchain_exe_stuff) # gersemi: off target_link_options(spheresvr PRIVATE - /WX # treat all warnings as errors + /WX # treat all warnings as errors $<$: ${EXE_LINKER_EXTRA} /NODEFAULTLIB:libcmtd /OPT:REF,ICF /LTCG /INCREMENTAL:NO> $<$: ${EXE_LINKER_EXTRA} /NODEFAULTLIB:libcmtd /OPT:REF,ICF /LTCG /INCREMENTAL:NO> - $<$: ${EXE_LINKER_EXTRA} /NODEFAULTLIB:libcmt /SAFESEH:NO /DEBUG /LTCG:OFF + $<$: ${EXE_LINKER_EXTRA} /NODEFAULTLIB:libcmt /SAFESEH:NO /DEBUG /LTCG:OFF $,/INCREMENTAL:NO /EDITANDCONTINUE:NO,/INCREMENTAL /EDITANDCONTINUE> > ) # gersemi: on @@ -142,11 +154,13 @@ function(toolchain_exe_stuff) #-- Set define macros. # Common defines + #gersemi: off target_compile_definitions( spheresvr PRIVATE ${PREPROCESSOR_DEFS_EXTRA} - $<$>:_GITVERSION> + $<$>: + _GITVERSION> _CRT_SECURE_NO_WARNINGS # Temporary setting _CRT_SECURE_NO_WARNINGS to do not spam so much in the build proccess. _EXCEPTIONS_DEBUG @@ -156,15 +170,18 @@ function(toolchain_exe_stuff) # Removing WINSOCK warnings until the code gets updated or reviewed. # Per-build defines $<$>: - NDEBUG> + NDEBUG> $<$: - _NIGHTLYBUILD - THREAD_TRACK_CALLSTACK> + _NIGHTLYBUILD + THREAD_TRACK_CALLSTACK> $<$: - _DEBUG - THREAD_TRACK_CALLSTACK - _PACKETDUMP> + _DEBUG + THREAD_TRACK_CALLSTACK + _PACKETDUMP> + $<$: + WINDOWS_GENERATE_CRASHDUMP> ) + #gersemi: on #-- Custom output directory. diff --git a/cmake/toolchains/include/Linux-Clang_common.inc.cmake b/cmake/toolchains/include/Linux-Clang_common.inc.cmake index 88695e547..a7304c79c 100644 --- a/cmake/toolchains/include/Linux-Clang_common.inc.cmake +++ b/cmake/toolchains/include/Linux-Clang_common.inc.cmake @@ -9,186 +9,54 @@ function(toolchain_exe_stuff_common) message(STATUS "Locating libraries to be linked to...") - set(LIBS_LINK_LIST mariadb dl) - foreach(LIB_NAME ${LIBS_LINK_LIST}) - find_library(LIB_${LIB_NAME}_WITH_PATH ${LIB_NAME} PATH ${lib_search_paths}) - message(STATUS "Library ${LIB_NAME}: ${LIB_${LIB_NAME}_WITH_PATH}") + set(libs_link_list mariadb dl) + foreach(lib_name ${libs_link_list}) + find_library(lib_${lib_name}_with_path ${lib_name} PATH ${lib_search_paths}) + message(STATUS "Library ${lib_name}: ${lib_${lib_name}_with_path}") endforeach() #-- Validate sanitizers options and store them between the common compiler flags. - set(ENABLED_SANITIZER false) # From https://clang.llvm.org/docs/ClangCommandLineReference.html # -static-libsan Statically link the sanitizer runtime (Not supported for ASan, TSan or UBSan on darwin) #string(REPLACE ";" " " CXX_FLAGS_EXTRA "${CXX_FLAGS_EXTRA}") - if(${USE_ASAN}) - set(CXX_FLAGS_EXTRA - ${CXX_FLAGS_EXTRA} - # -fsanitize=safe-stack # Can't be used with asan! - -fsanitize=address - -fno-sanitize-recover=address - #-fsanitize-cfi # cfi: control flow integrity - -fsanitize-address-use-after-scope - -fsanitize=pointer-compare - -fsanitize=pointer-subtract - # Flags for additional instrumentation not strictly needing asan to be enabled - -fcf-protection=full - -fstack-check - -fstack-protector-all - -fstack-clash-protection - # Not supported by Clang, but supported by GCC - #-fvtable-verify=preinit -fharden-control-flow-redundancy -fhardcfr-check-exceptions - # Other - #-fsanitize-trap=all - ) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=address) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} ADDRESS_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_MSAN}) - message( - WARNING - "You have enabled MSAN. Make sure you do know what you are doing. It doesn't work out of the box. \ -See comments in the toolchain and: https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo" - ) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=memory -fsanitize-memory-track-origins -fPIE) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=memory) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} MEMORY_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_LSAN}) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=leak) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=leak>) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} LEAK_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_UBSAN}) - set(UBSAN_FLAGS - -fsanitize=undefined,float-divide-by-zero,local-bounds - -fno-sanitize=enum - # Supported? - -fsanitize=unsigned-integer-overflow #Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. - -fsanitize=implicit-conversion - ) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} ${UBSAN_FLAGS} -fsanitize=return,vptr) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=undefined) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} UNDEFINED_BEHAVIOR_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - - if(${ENABLED_SANITIZER}) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} _SANITIZERS) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA - ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - $<$:-static-libsan> - ) - endif() - - #-- Store compiler flags common to all builds. - - set(cxx_local_opts_warnings - -Werror - -Wall - -Wextra - -Wpedantic - - -Wmissing-include-dirs # Warns when an include directory provided with -I does not exist. - -Wformat=2 - #-Wcast-qual # Warns about casts that remove a type's const or volatile qualifier. - #-Wconversion # Temporarily disabled. Warns about implicit type conversions that might change a value, such as narrowing conversions. - -Wdisabled-optimization - #-Winvalid-pch - -Wzero-as-null-pointer-constant - -Wnull-dereference - - # Clang-only - -Wweak-vtables - #-Wmissing-prototypes - #-Wmissing-variable-declarations - - # Unsupported by Clang, but supported by GCC: - #-fno-expensive-optimizations - #-Wtrampolines # Warns when trampolines (a technique to implement nested functions) are generated (don't want this for security reasons). - #-Wvector-operation-performance - #-Wsized-deallocation - #-Wduplicated-cond - #-Wshift-overflow=2 - #-Wno-maybe-uninitialized - #-Wno-nonnull-compare - - # Disable errors: - -Wno-format-nonliteral # Since -Wformat=2 is stricter, you would need to disable this warning. - -Wno-switch - -Wno-implicit-fallthrough - -Wno-parentheses - -Wno-misleading-indentation - -Wno-unused-result - -Wno-format-security # TODO: disable that when we'll have time to fix every printf format issue - -Wno-nested-anon-types - ) - set(cxx_local_opts - -std=c++20 - -pthread - -fexceptions - -fnon-call-exceptions - -pipe - -ffast-math - # clang -specific: - # -fforce-emit-vtables - ) - set(cxx_compiler_options_common ${cxx_local_opts_warnings} ${cxx_local_opts} ${CXX_FLAGS_EXTRA}) + set(cxx_compiler_options_common ${list_explicit_compiler_options_all} ${CXX_FLAGS_EXTRA}) #separate_arguments(cxx_compiler_options_common) - # GCC flags not supported by clang: - - # MemorySanitizer: it doesn't work out of the box. It needs to be linked to an MSAN-instrumented build of libc++ and libc++abi. - # This means: one should build them from LLVM source... - # https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo - #IF (${USE_MSAN}) - # SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") - #ENDIF() - # Use "-stdlib=libstdc++" to link against GCC c/c++ libs (this is done by default) - # To use LLVM libc++ use "-stdlib=libc++", but you need to install it separately - #-- Apply compiler flags, only the ones specific per build type. # -fno-omit-frame-pointer disables a good optimization which may corrupt the debugger stack trace. - set(COMPILE_OPTIONS_EXTRA) + set(local_compile_options_extra) if(ENABLED_SANITIZER OR TARGET spheresvr_debug) - set(COMPILE_OPTIONS_EXTRA -fno-omit-frame-pointer -fno-inline) + set(local_compile_options_extra -fno-omit-frame-pointer -fno-inline) endif() if(TARGET spheresvr_release) target_compile_options( spheresvr_release - PUBLIC -O3 -flto=full -fvirtual-function-elimination ${COMPILE_OPTIONS_EXTRA} + PUBLIC -O3 -flto=full -fvirtual-function-elimination ${local_compile_options_extra} ) endif() if(TARGET spheresvr_nightly) if(ENABLED_SANITIZER) - target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${local_compile_options_extra}) else() target_compile_options( spheresvr_nightly - PUBLIC -O3 -flto=full -fvirtual-function-elimination ${COMPILE_OPTIONS_EXTRA} + PUBLIC -O3 -flto=full -fvirtual-function-elimination ${local_compile_options_extra} ) endif() endif() if(TARGET spheresvr_debug) - target_compile_options(spheresvr_debug PUBLIC -ggdb3 -O0 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_debug PUBLIC -ggdb3 -O0 ${local_compile_options_extra}) endif() #-- Store common linker flags. - if(${USE_MSAN}) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -pie) - endif() set(cxx_linker_options_common + ${list_explicit_linker_options_all} ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - -pthread - -dynamic - -Wl,--fatal-warnings $<$: -static-libstdc++ -static-libgcc> # no way to safely statically link against libc @@ -222,7 +90,7 @@ See comments in the toolchain and: https://github.com/google/sanitizers/wiki/Mem target_compile_options(${tgt} PRIVATE ${cxx_compiler_options_common}) target_compile_definitions(${tgt} PRIVATE ${cxx_compiler_definitions_common}) target_link_options(${tgt} PRIVATE ${cxx_linker_options_common}) - target_link_libraries(${tgt} PRIVATE ${LIB_mariadb_WITH_PATH} ${LIB_dl_WITH_PATH}) + target_link_libraries(${tgt} PRIVATE ${lib_mariadb_with_path} ${lib_dl_with_path}) endforeach() #-- Set different output folders for each build type diff --git a/cmake/toolchains/include/Linux-GNU_common.inc.cmake b/cmake/toolchains/include/Linux-GNU_common.inc.cmake index 90c80b83f..19e8c3a71 100644 --- a/cmake/toolchains/include/Linux-GNU_common.inc.cmake +++ b/cmake/toolchains/include/Linux-GNU_common.inc.cmake @@ -9,164 +9,42 @@ function(toolchain_exe_stuff_common) message(STATUS "Locating libraries to be linked to...") - set(LIBS_LINK_LIST mariadb dl) - foreach(LIB_NAME ${LIBS_LINK_LIST}) - find_library(LIB_${LIB_NAME}_WITH_PATH ${LIB_NAME} PATH ${lib_search_paths}) - message(STATUS "Library ${LIB_NAME}: ${LIB_${LIB_NAME}_WITH_PATH}") + set(libs_link_list mariadb dl) + foreach(lib_name ${libs_link_list}) + find_library(lib_${lib_name}_with_path ${lib_name} PATH ${lib_search_paths}) + message(STATUS "Library ${lib_name}: ${lib_${lib_name}_with_path}") endforeach() - #-- Validate sanitizers options and store them between the common compiler flags. - - set(ENABLED_SANITIZER false) - - if(${USE_ASAN}) - set(CXX_FLAGS_EXTRA - ${CXX_FLAGS_EXTRA} - -fsanitize=address - -fno-sanitize-recover=address - #-fsanitize-cfi # cfi: control flow integrity, not supported by GCC - -fsanitize-address-use-after-scope - -fsanitize=pointer-compare - -fsanitize=pointer-subtract - # Flags for additional instrumentation not strictly needing asan to be enabled - #-fvtable-verify=preinit # This causes a GCC internal error! Tested with 13.2.0 - -fstack-check - -fstack-protector-all - -fcf-protection=full - # GCC 14? - #-fharden-control-flow-redundancy - #-fhardcfr-check-exceptions - # Other - #-fsanitize-trap=all - ) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA - ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - -fsanitize=address - $<$:-static-libasan> - ) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} ADDRESS_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_MSAN}) - message(FATAL_ERROR "Linux GCC doesn't yet support MSAN") - set(USE_MSAN false) - #SET (CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=memory -fsanitize-memory-track-origins=2 -fPIE) - #set (CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=memory )#$<$:-static-libmsan>) - #SET (PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} MEMORY_SANITIZER) - #SET (ENABLED_SANITIZER true) - endif() - if(${USE_LSAN}) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=leak) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA - ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - -fsanitize=leak - $<$:-static-liblsan> - ) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} LEAK_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_UBSAN}) - set(UBSAN_FLAGS - -fsanitize=undefined,float-divide-by-zero - -fno-sanitize=enum - # Unsupported (yet?) by GCC 13 - #-fsanitize=unsigned-integer-overflow #Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. - #-fsanitize=implicit-conversion, local-bounds - ) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} ${UBSAN_FLAGS} -fsanitize=return,vptr) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA - ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - -fsanitize=undefined - $<$:-static-libubsan> - ) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} UNDEFINED_BEHAVIOR_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - - if(${ENABLED_SANITIZER}) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} _SANITIZERS) - endif() - #-- Store compiler flags common to all builds. - set(cxx_local_opts_warnings - -Werror - -Wall - -Wextra - -Wpedantic - - -Wmissing-include-dirs # Warns when an include directory provided with -I does not exist. - -Wformat=2 - #-Wcast-qual # Warns about casts that remove a type's const or volatile qualifier. - #-Wconversion # Temporarily disabled. Warns about implicit type conversions that might change a value, such as narrowing conversions. - -Wdisabled-optimization - #-Winvalid-pch - -Wzero-as-null-pointer-constant - #-Wnull-dereference # Don't: on GCC 12 causes some false positives... - -Wduplicated-cond - - # Supported by Clang, but unsupported by GCC: - #-Wweak-vtables - - # Unsupported by Clang, but supported by GCC: - -Wtrampolines # Warns when trampolines (a technique to implement nested functions) are generated (don't want this for security reasons). - -Wvector-operation-performance - -Wsized-deallocation - -Wduplicated-cond - -Wshift-overflow=2 - - # Disable errors: - -Wno-format-nonliteral # Since -Wformat=2 is stricter, you would need to disable this warning. - -Wno-nonnull-compare # GCC only - -Wno-switch - -Wno-implicit-fallthrough - -Wno-parentheses - -Wno-misleading-indentation - -Wno-unused-result - -Wno-format-security # TODO: disable that when we'll have time to fix every printf format issue - -Wno-nested-anon-types - ) - set(cxx_local_opts - -std=c++20 - -pthread - -fexceptions - -fnon-call-exceptions - -pipe - -ffast-math - ) - set(cxx_compiler_options_common ${cxx_local_opts_warnings} ${cxx_local_opts} ${CXX_FLAGS_EXTRA}) + set(cxx_compiler_options_common ${list_explicit_compiler_options_all} ${CXX_FLAGS_EXTRA}) #-- Apply compiler flags, only the ones specific per build type. # -fno-omit-frame-pointer disables a good optimization which may corrupt the debugger stack trace. - set(COMPILE_OPTIONS_EXTRA) + set(local_compile_options_extra) if(ENABLED_SANITIZER OR TARGET spheresvr_debug) - set(COMPILE_OPTIONS_EXTRA -fno-omit-frame-pointer -fno-inline) + set(local_compile_options_extra -fno-omit-frame-pointer -fno-inline) endif() if(TARGET spheresvr_release) - target_compile_options(spheresvr_release PUBLIC -s -O3 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_release PUBLIC -s -O3 ${local_compile_options_extra}) endif() if(TARGET spheresvr_nightly) if(ENABLED_SANITIZER) - target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${local_compile_options_extra}) else() - target_compile_options(spheresvr_nightly PUBLIC -O3 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_nightly PUBLIC -O3 ${local_compile_options_extra}) endif() endif() if(TARGET spheresvr_debug) - target_compile_options(spheresvr_debug PUBLIC -ggdb3 -O0 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_debug PUBLIC -ggdb3 -O0 ${local_compile_options_extra}) endif() #-- Store common linker flags. - if(${USE_MSAN}) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -pie) - endif() set(cxx_linker_options_common + ${list_explicit_linker_options_all} ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - -pthread - -dynamic - -Wl,--fatal-warnings $<$: -static-libstdc++ -static-libgcc # no way to safely statically link against libc @@ -202,7 +80,7 @@ function(toolchain_exe_stuff_common) target_compile_options(${tgt} PRIVATE ${cxx_compiler_options_common}) target_compile_definitions(${tgt} PRIVATE ${cxx_compiler_definitions_common}) target_link_options(${tgt} PRIVATE ${cxx_linker_options_common}) - target_link_libraries(${tgt} PRIVATE ${LIB_mariadb_WITH_PATH} ${LIB_dl_WITH_PATH}) + target_link_libraries(${tgt} PRIVATE ${lib_mariadb_with_path} ${lib_dl_with_path}) endforeach() #-- Set different output folders for each build type diff --git a/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake b/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake index 62088924a..699b50881 100644 --- a/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake +++ b/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake @@ -9,143 +9,49 @@ function(toolchain_exe_stuff_common) message(STATUS "Locating libraries to be linked to...") - set(LIBS_LINK_LIST mariadb dl) - foreach(LIB_NAME ${LIBS_LINK_LIST}) + set(libs_link_list mariadb dl) + foreach(lib_name ${libs_link_list}) find_library( - LIB_${LIB_NAME}_WITH_PATH - ${LIB_NAME} + lib_${lib_name}_with_path + ${lib_name} HINT "/usr/local/opt/mariadb-connector-c/lib/mariadb" "/opt/homebrew/var/mariadb-connector-c/lib/mariadb" "/opt/homebrew/lib/mariadb" ) - message(STATUS "Library ${LIB_NAME}: ${LIB_${LIB_NAME}_WITH_PATH}") + message(STATUS "Library ${lib_name}: ${lib_${lib_name}_with_path}") endforeach() - #-- Validate sanitizers options and store them between the common compiler flags. - - set(ENABLED_SANITIZER false) - # From https://clang.llvm.org/docs/ClangCommandLineReference.html - # -static-libsan Statically link the sanitizer runtime (Not supported for ASan, TSan or UBSan on darwin) - - if(${USE_ASAN}) - set(CXX_FLAGS_EXTRA - ${CXX_FLAGS_EXTRA} # -fsanitize=safe-stack # Can't be used with asan! - -fsanitize=address - -fno-sanitize-recover=address #-fsanitize-cfi # cfi: control flow integrity - -fsanitize-address-use-after-scope - -fsanitize=pointer-compare - -fsanitize=pointer-subtract - # Flags for additional instrumentation not strictly needing asan to be enabled - -fcf-protection=full - -fstack-check - -fstack-protector-all - -fstack-clash-protection - # Not supported by Clang, but supported by GCC - #-fvtable-verify=preinit -fharden-control-flow-redundancy -fhardcfr-check-exceptions - # Other - #-fsanitize-trap=all - ) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=address) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} ADDRESS_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_MSAN}) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=memory -fsanitize-memory-track-origins=2 -fPIE) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=memory) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} MEMORY_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_LSAN}) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=leak) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=leak) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} LEAK_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_UBSAN}) - set(UBSAN_FLAGS - -fsanitize=undefined,float-divide-by-zero,local-bounds - -fno-sanitize=enum - # Supported? - -fsanitize=unsigned-integer-overflow #Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. - -fsanitize=implicit-conversion - ) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} ${UBSAN_FLAGS} -fsanitize=return,vptr) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=undefined) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} UNDEFINED_BEHAVIOR_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - - if(${ENABLED_SANITIZER}) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} _SANITIZERS) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA - ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - $<$:-static-libsan> - ) - endif() - #-- Store compiler flags common to all builds. - set(cxx_local_opts_warnings - -Werror - -Wall - -Wextra - -Wno-unknown-pragmas - -Wno-switch - -Wno-implicit-fallthrough - -Wno-parentheses - -Wno-misleading-indentation - -Wno-conversion-null - -Wno-unused-result - -Wno-format-security # TODO: disable that when we'll have time to fix every printf format issue - # clang-specific: - -Wno-deprecated-declarations # spams so much warnings for the use of sprintf - ) - set(cxx_local_opts - -std=c++20 - -pthread - -fexceptions - -fnon-call-exceptions - -pipe - -ffast-math - ) - set(cxx_compiler_options_common ${cxx_local_opts_warnings} ${cxx_local_opts} ${CXX_FLAGS_EXTRA}) - - # GCC flags not supported by clang: - # Warnings: "-Wno-nonnull-compare -Wno-maybe-uninitialized" - # Other: "-fno-expensive-optimizations" + set(cxx_compiler_options_common ${list_explicit_compiler_options_all} ${cxx_local_opts} ${CXX_FLAGS_EXTRA}) #-- Apply compiler flags, only the ones specific per build type. # -fno-omit-frame-pointer disables a good optimization which may corrupt the debugger stack trace. - set(COMPILE_OPTIONS_EXTRA) + set(local_compile_options_extra) if(ENABLED_SANITIZER OR TARGET spheresvr_debug) - set(COMPILE_OPTIONS_EXTRA -fno-omit-frame-pointer -fno-inline) + set(local_compile_options_extra -fno-omit-frame-pointer -fno-inline) endif() if(TARGET spheresvr_release) - target_compile_options(spheresvr_release PUBLIC -O3 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_release PUBLIC -O3 ${local_compile_options_extra}) endif() if(TARGET spheresvr_nightly) if(ENABLED_SANITIZER) - target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${local_compile_options_extra}) else() - target_compile_options(spheresvr_nightly PUBLIC -O3 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_nightly PUBLIC -O3 ${local_compile_options_extra}) endif() endif() if(TARGET spheresvr_debug) - target_compile_options(spheresvr_debug PUBLIC -ggdb3 -O0 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_debug PUBLIC -ggdb3 -O0 ${local_compile_options_extra}) endif() #-- Store common linker flags. - if(${USE_MSAN}) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -pie) - endif() set(cxx_linker_options_common + ${list_explicit_linker_options_all} ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - -pthread - -dynamic - -Wl,-fatal_warnings $<$: -static-libstdc++ -static-libgcc> # no way to statically link against libc @@ -188,7 +94,7 @@ function(toolchain_exe_stuff_common) target_compile_options(${tgt} PRIVATE ${cxx_compiler_options_common}) target_compile_definitions(${tgt} PRIVATE ${cxx_compiler_definitions_common}) target_link_options(${tgt} PRIVATE ${cxx_linker_options_common}) - target_link_libraries(${tgt} PRIVATE ${LIB_mariadb_WITH_PATH} ${LIB_dl_WITH_PATH}) + target_link_libraries(${tgt} PRIVATE ${lib_mariadb_with_path} ${lib_dl_with_path}) endforeach() #-- Set different output folders for each build type diff --git a/cmake/toolchains/include/Windows-Clang_common.inc.cmake b/cmake/toolchains/include/Windows-Clang_common.inc.cmake index a502a5185..bf2094af3 100644 --- a/cmake/toolchains/include/Windows-Clang_common.inc.cmake +++ b/cmake/toolchains/include/Windows-Clang_common.inc.cmake @@ -14,135 +14,36 @@ endfunction() function(toolchain_exe_stuff_common) #-- Configure the Windows application type and add global linker flags. - if(${WIN32_SPAWN_CONSOLE}) + if(${WIN_SPAWN_CONSOLE}) set(PREPROCESSOR_DEFS_EXTRA _WINDOWS_CONSOLE) if(${CLANG_USE_GCC_LINKER}) # --entry might not work - add_link_options("LINKER:--entry=WinMainCRTStartup") # Handled by is_win32_app_linker -> "LINKER:SHELL:-m$,CONSOLE,WINDOWS>" + add_link_options("LINKER:--entry=WinMainCRTStartup") # Handled by is_win32_app_linker -> "LINKER:SHELL:-m$,CONSOLE,WINDOWS>" else() - add_link_options("LINKER:/ENTRY:WinMainCRTStartup") # Handled by is_win32_app_linker -> "LINKER:SHELL:/SUBSYSTEM:$,CONSOLE,WINDOWS>" - endif() - endif() - - #-- Validate sanitizers options and store them between the common compiler flags. - - set(ENABLED_SANITIZER false) - # From https://clang.llvm.org/docs/ClangCommandLineReference.html - # -static-libsan Statically link the sanitizer runtime (Not supported for ASan, TSan or UBSan on darwin) - - if(${USE_ASAN}) - set(CXX_FLAGS_EXTRA - ${CXX_FLAGS_EXTRA} # -fsanitize=safe-stack # Can't be used with asan! - -fsanitize=address - -fno-sanitize-recover=address #-fsanitize-cfi # cfi: control flow integrity - -fsanitize-address-use-after-scope - -fsanitize=pointer-compare - -fsanitize=pointer-subtract - # Flags for additional instrumentation not strictly needing asan to be enabled - -fcf-protection=full - -fstack-check - -fstack-protector-all - -fstack-clash-protection - # Not supported by Clang, but supported by GCC - #-fvtable-verify=preinit -fharden-control-flow-redundancy -fhardcfr-check-exceptions - # Other - #-fsanitize-trap=all - ) - if(${CLANG_USE_GCC_LINKER}) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=address) - endif() - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} ADDRESS_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - if(${USE_MSAN}) - message(FATAL_ERROR "Windows Clang doesn't yet support MSAN") - set(USE_MSAN false) - #SET (CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=memory -fsanitize-memory-track-origins=2 -fPIE) - #IF (${CLANG_USE_GCC_LINKER}) - #set (CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=memory) - #ENDIF - #SET (PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} MEMORY_SANITIZER) - #SET (ENABLED_SANITIZER true) - endif() - if(${USE_LSAN}) - message(FATAL_ERROR "Windows Clang doesn't yet support LSAN") - set(USE_LSAN false) - #SET (CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=leak) - #IF (${CLANG_USE_GCC_LINKER}) - #set (CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=leak) - #ENDIF - #SET (PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} LEAK_SANITIZER) - #SET (ENABLED_SANITIZER true) - endif() - if(${USE_UBSAN}) - set(UBSAN_FLAGS - -fsanitize=undefined,float-divide-by-zero,local-bounds - -fno-sanitize=enum - # Supported? - -fsanitize=unsigned-integer-overflow #Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. - -fsanitize=implicit-conversion - ) - set(CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} ${UBSAN_FLAGS} -fsanitize=return) - #IF (${CLANG_USE_GCC_LINKER}) - #set (CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=undefined) - #ENDIF - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} UNDEFINED_BEHAVIOR_SANITIZER) - set(ENABLED_SANITIZER true) - endif() - - if(${ENABLED_SANITIZER}) - set(PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} _SANITIZERS) - if(${RUNTIME_STATIC_LINK}) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -static-libsan) - else() - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -shared-libsan) + add_link_options("LINKER:/ENTRY:WinMainCRTStartup") # Handled by is_win32_app_linker -> "LINKER:SHELL:/SUBSYSTEM:$,CONSOLE,WINDOWS>" endif() endif() #-- Store compiler flags common to all builds. - set(cxx_local_opts_warnings - -Werror - -Wall - -Wextra - -Wno-unknown-pragmas - -Wno-switch - -Wno-implicit-fallthrough - -Wno-parentheses - -Wno-misleading-indentation - -Wno-conversion-null - -Wno-unused-result - -Wno-format-security # TODO: disable that when we'll have time to fix every printf format issue - # clang -specific: - # -fforce-emit-vtables - ) set(cxx_local_opts - -std=c++20 - -fexceptions - -fnon-call-exceptions - -pipe - -ffast-math -mno-ms-bitfields # -mno-ms-bitfields is needed to fix structure packing; # -pthread unused here? we only need to specify that to the linker? - -Wno-format-security # TODO: disable that when we'll have time to fix every printf format issue - # clang-specific: - # - # Flags supported by GCC but not by Clang: -fno-expensive-optimizations, -Wno-error=maybe-uninitialized ) - set(cxx_compiler_options_common ${cxx_local_opts_warnings} ${cxx_local_opts} ${CXX_FLAGS_EXTRA}) + set(cxx_compiler_options_common ${list_explicit_compiler_options_all} ${cxx_local_opts} ${CXX_FLAGS_EXTRA}) #-- Apply compiler flags, only the ones specific per build type. # -fno-omit-frame-pointer disables a good optimization which may corrupt the debugger stack trace. - set(COMPILE_OPTIONS_EXTRA) + set(local_compile_options_extra) if(ENABLED_SANITIZER OR TARGET spheresvr_debug) - set(COMPILE_OPTIONS_EXTRA -fno-omit-frame-pointer -fno-inline) + set(local_compile_options_extra -fno-omit-frame-pointer -fno-inline) endif() if(TARGET spheresvr_release) target_compile_options( spheresvr_release - PUBLIC -O3 -flto=full -fvirtual-function-elimination ${COMPILE_OPTIONS_EXTRA} + PUBLIC -O3 -flto=full -fvirtual-function-elimination ${local_compile_options_extra} ) #[[ IF (NOT ${CLANG_USE_GCC_LINKER}) @@ -156,11 +57,11 @@ function(toolchain_exe_stuff_common) endif() if(TARGET spheresvr_nightly) if(ENABLED_SANITIZER) - target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${local_compile_options_extra}) else() target_compile_options( spheresvr_nightly - PUBLIC -O3 -flto=full -fvirtual-function-elimination ${COMPILE_OPTIONS_EXTRA} + PUBLIC -O3 -flto=full -fvirtual-function-elimination ${local_compile_options_extra} ) endif() #[[ @@ -188,17 +89,13 @@ function(toolchain_exe_stuff_common) #-- Store common linker flags. - if(${USE_MSAN}) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -pie) - endif() - set(cxx_linker_options_common ${CMAKE_EXE_LINKER_FLAGS_EXTRA}) if(${CLANG_USE_GCC_LINKER}) - set(cxx_linker_options_common ${cxx_linker_options_common} -pthread -dynamic -Wl,--fatal-warnings) + set(cxx_linker_options_common ${list_explicit_linker_options_all}) if(${RUNTIME_STATIC_LINK}) set(cxx_linker_options_common ${cxx_linker_options_common} -static-libstdc++ -static-libgcc) # no way to statically link against libc? maybe we can on windows? endif() - #[[ +#[[ else () if (${RUNTIME_STATIC_LINK}) set (cxx_linker_options_common ${cxx_linker_options_common} /MTd ) @@ -282,7 +179,7 @@ function(toolchain_exe_stuff_common) endif () endif() endif () -]] + ]] endif() set(libs_to_link_against ${libs_to_link_against} ws2_32 ${libs_prefix}mariadb) diff --git a/cmake/toolchains/include/Windows-GNU_common.inc.cmake b/cmake/toolchains/include/Windows-GNU_common.inc.cmake index efbffb09b..c2a90d51f 100644 --- a/cmake/toolchains/include/Windows-GNU_common.inc.cmake +++ b/cmake/toolchains/include/Windows-GNU_common.inc.cmake @@ -9,157 +9,45 @@ function(toolchain_exe_stuff_common) #-- Configure the Windows application type. # Subsystem is already managed by is_win32_app_linker. GCC doesn't need us to specify the entry point. - #IF (${WIN32_SPAWN_CONSOLE}) + #IF (${WIN_SPAWN_CONSOLE}) # add_link_options ("LINKER:SHELL:-mconsole") # SET (PREPROCESSOR_DEFS_EXTRA _WINDOWS_CONSOLE) ##ELSE () ## add_link_options ("LINKER:SHELL:-mwindows") #ENDIF () - #-- Validate sanitizers options and store them between the common compiler flags. - - set(ENABLED_SANITIZER false) - if(${USE_ASAN}) - message(FATAL_ERROR "MinGW-GCC doesn't yet support ASAN") - set(USE_ASAN false) - #[[ - SET (CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} - -fsanitize=address -fno-sanitize-recover=address #-fsanitize-cfi # cfi: control flow integrity, not currently supported by GCC (even on Linux) - -fsanitize-address-use-after-scope -fsanitize=pointer-compare -fsanitize=pointer-subtract - # Flags for additional instrumentation not strictly needing asan to be enabled - #-fvtable-verify=preinit # This causes a GCC internal error! Tested with 13.2.0 - -fstack-check -fstack-protector-all - -fcf-protection=full - # GCC 14? - #-fharden-control-flow-redundancy -fhardcfr-check-exceptions - # Other - #-fsanitize-trap=all - ) - ]] - #set (CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=address $<$:-static-libasan>) - #SET (PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} ADDRESS_SANITIZER) - #SET (ENABLED_SANITIZER true) - endif() - if(${USE_MSAN}) - message(FATAL_ERROR "MinGW-GCC doesn't yet support MSAN") - set(USE_MSAN false) - #SET (CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=memory -fsanitize-memory-track-origins=2 -fPIE) - #set (CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=memory )#$<$:-static-libmsan>) - #SET (PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} MEMORY_SANITIZER) - #SET (ENABLED_SANITIZER true) - endif() - if(${USE_LSAN}) - message(FATAL_ERROR "MinGW-GCC doesn't yet support LSAN") - set(USE_LSAN false) - #SET (CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} -fsanitize=leak) - #set (CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=leak #$<$:-static-liblsan>) - #SET (PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} LEAK_SANITIZER) - #SET (ENABLED_SANITIZER true) - endif() - if(${USE_UBSAN}) - message(FATAL_ERROR "MinGW-GCC doesn't yet support UBSAN") - set(USE_UBSAN false) - #[[ - SET (UBSAN_FLAGS - -fsanitize=undefined,float-divide-by-zero - -fno-sanitize=enum - # Unsupported (yet?) by GCC 13 - #-fsanitize=unsigned-integer-overflow #Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. - #-fsanitize=implicit-conversion, local-bounds - ) - ]] - #SET (CXX_FLAGS_EXTRA ${CXX_FLAGS_EXTRA} ${UBSAN_FLAGS} -fsanitize=return,vptr) - #set (CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -fsanitize=undefined #$<$:-static-libubsan>) - #SET (PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} UNDEFINED_BEHAVIOR_SANITIZER) - #SET (ENABLED_SANITIZER true) - endif() - - #IF (${ENABLED_SANITIZER}) - # SET (PREPROCESSOR_DEFS_EXTRA ${PREPROCESSOR_DEFS_EXTRA} _SANITIZERS) - #ENDIF () - - #-- Store compiler flags common to all builds. - - set(cxx_local_opts_warnings - -Werror - -Wall - -Wextra - -Wpedantic - - -Wmissing-include-dirs # Warns when an include directory provided with -I does not exist. - -Wformat=2 - #-Wcast-qual # Warns about casts that remove a type's const or volatile qualifier. - #-Wconversion # Temporarily disabled. Warns about implicit type conversions that might change a value, such as narrowing conversions. - -Wdisabled-optimization - #-Winvalid-pch - -Wzero-as-null-pointer-constant - #-Wnull-dereference # Don't: on GCC 12 causes some false positives... - -Wduplicated-cond - - # Supported by Clang, but unsupported by GCC: - #-Wweak-vtables - - # Unsupported by Clang, but supported by GCC: - -Wtrampolines # Warns when trampolines (a technique to implement nested functions) are generated (don't want this for security reasons). - -Wvector-operation-performance - -Wsized-deallocation - -Wduplicated-cond - -Wshift-overflow=2 - - # Disable errors: - -Wno-format-nonliteral # Since -Wformat=2 is stricter, you would need to disable this warning. - -Wno-nonnull-compare # GCC only - -Wno-switch - -Wno-implicit-fallthrough - -Wno-parentheses - -Wno-misleading-indentation - -Wno-unused-result - -Wno-format-security # TODO: disable that when we'll have time to fix every printf format issue - -Wno-nested-anon-types - ) set(cxx_local_opts - -std=c++20 - -pthread - -fexceptions - -fnon-call-exceptions - -mno-ms-bitfields # it's needed to fix structure packing - -pipe - -ffast-math + -mno-ms-bitfields # it's needed to fix structure packing ) - set(cxx_compiler_options_common ${cxx_local_opts_warnings} ${cxx_local_opts} ${CXX_FLAGS_EXTRA}) + set(cxx_compiler_options_common ${list_explicit_compiler_options_all} ${cxx_local_opts} ${CXX_FLAGS_EXTRA}) #-- Apply compiler flags, only the ones specific per build type. # -fno-omit-frame-pointer disables a good optimization which may corrupt the debugger stack trace. - set(COMPILE_OPTIONS_EXTRA) + set(local_compile_options_extra) if(ENABLED_SANITIZER OR TARGET spheresvr_debug) - set(COMPILE_OPTIONS_EXTRA -fno-omit-frame-pointer -fno-inline) + set(local_compile_options_extra -fno-omit-frame-pointer -fno-inline) endif() if(TARGET spheresvr_release) - target_compile_options(spheresvr_release PUBLIC -s -O3 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_release PUBLIC -s -O3 ${local_compile_options_extra}) endif() if(TARGET spheresvr_nightly) if(ENABLED_SANITIZER) - target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_nightly PUBLIC -ggdb3 -Og ${local_compile_options_extra}) else() - target_compile_options(spheresvr_nightly PUBLIC -O3 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_nightly PUBLIC -O3 ${local_compile_options_extra}) endif() endif() if(TARGET spheresvr_debug) - target_compile_options(spheresvr_debug PUBLIC -ggdb3 -O0 ${COMPILE_OPTIONS_EXTRA}) + target_compile_options(spheresvr_debug PUBLIC -ggdb3 -O0 ${local_compile_options_extra}) endif() #-- Store common linker flags. - if(${USE_MSAN}) - set(CMAKE_EXE_LINKER_FLAGS_EXTRA ${CMAKE_EXE_LINKER_FLAGS_EXTRA} -pie) - endif() set(cxx_linker_options_common + ${list_explicit_linker_options_all} ${CMAKE_EXE_LINKER_FLAGS_EXTRA} - -pthread - -dynamic - -Wl,--fatal-warnings $<$: -static-libstdc++ -static-libgcc> # no way to statically link against libc? maybe we can on windows? diff --git a/lib/bcrypt/bcrypt/wrapper.c b/lib/bcrypt/bcrypt/wrapper.c index 197475b8c..fe20e7577 100644 --- a/lib/bcrypt/bcrypt/wrapper.c +++ b/lib/bcrypt/bcrypt/wrapper.c @@ -49,8 +49,15 @@ extern struct crypt_data _ufc_foobar; char* mystrdup(const char* s) { - char* p = malloc(strlen(s)+1); - if (p) strcpy(p, s); + const size_t len = strlen(s); + char* p = (char*)(malloc(len + 1)); + if (p) + { + //strcpy(p, s); + strncpy(p, s, len); + p[len] = '\0'; + } + return p; } diff --git a/lib/lib_build_flags_common_c.cmake b/lib/lib_build_flags_common_c.cmake index d34668325..2d3cc4734 100644 --- a/lib/lib_build_flags_common_c.cmake +++ b/lib/lib_build_flags_common_c.cmake @@ -6,9 +6,9 @@ endif() if(MSVC) # gersemi: off set (c_compiler_options_common - /O2 /EHsc /GA /Gw /Gy /GF /GR- /GS- - $<$: $,/MT,/MD> $<$:/Zi>> - $<$: $,/MT,/MD> $<$:/Zi>> + /EHsc /GA /Gw /Gy /GF /GR- /GS- + $<$: $,/MT,/MD> $,/O1 /Zi,/O2>> + $<$: $,/MT,/MD> $,/O1 /Zi,/O2>> $<$: $,/MTd,/MDd> $,/Zi,/ZI>> ) set (c_linker_options_common @@ -17,7 +17,8 @@ if(MSVC) $<$: /DEBUG /LTCG:OFF /NODEFAULTLIB:libcmt> ) # gersemi: on -else(MSVC) + +elseif(NOT MSVC) set(c_compiler_options_common -pipe -fexceptions diff --git a/lib/libev/wrapper_ev.c b/lib/libev/wrapper_ev.c index c9a7cfae2..391df69e3 100644 --- a/lib/libev/wrapper_ev.c +++ b/lib/libev/wrapper_ev.c @@ -2,15 +2,22 @@ #define _LIBEV +// Clang with MSVC backend on Windows defines _MSC_VER! (But also __clang__) +#ifndef MSVC_COMPILER +# if defined(_MSC_VER) && !defined(__clang__) +# define MSVC_COMPILER 1 +# endif +#endif + // libev produces many warnings which isn't really appropriate for us to // address since it is 3rd party code that could be overwritten at any time // with a new version -#ifdef _MSC_VER +#ifdef MSVC_COMPILER #pragma warning(push) #pragma warning(disable:4013 4068 4100 4101 4127 4133 4189 4244 4245 4456 4457 4706 4996) #else - //#define _POSIX_C_SOURCE - #define _POSIX_SOURCE 1 + //#define _POSIX_C_SOURCE 1 + //#define _POSIX_SOURCE #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcomment" @@ -44,7 +51,7 @@ //config.h for ev.c is generated by cmake #include "libev/ev.c" -#ifdef _MSC_VER +#ifdef MSVC_COMPILER #pragma warning(pop) #else #pragma GCC diagnostic pop diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index d1b52321f..fbecf9e6b 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -8,7 +8,7 @@ set(pch_options PRIVATE src/common/sphere_library/sstringobjs.h PRIVATE src/common/CException.h PRIVATE src/common/CExpression.h - PRIVATE src/common/CLog.h + #PRIVATE src/common/CLog.h #PRIVATE src/common/CServerMap.h PRIVATE src/game/CObjBase.h #PRIVATE src/game/CSector.h @@ -297,6 +297,7 @@ set(spherelibrary_SRCS 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}) diff --git a/src/common/CDataBase.cpp b/src/common/CDataBase.cpp index 3266a643e..352359d24 100644 --- a/src/common/CDataBase.cpp +++ b/src/common/CDataBase.cpp @@ -2,15 +2,13 @@ #include "../game/CServer.h" #include "../game/CServerTime.h" #include "../sphere/asyncdb.h" -#include "resource/sections/CResourceNamedDef.h" +#include "resource/sections/CResourceNamedDef.h" // Needed because it was only forward declared. #include "CLog.h" #include "CException.h" #include "CExpression.h" #include "CScriptTriggerArgs.h" #include "CDataBase.h" -extern CDataBaseAsyncHelper g_asyncHdb; - CDataBase::CDataBase() { @@ -169,7 +167,7 @@ bool CDataBase::query(const char *query, CVarDefMap & mapQueryResult) return false; } -bool __cdecl CDataBase::queryf(CVarDefMap & mapQueryResult, char *fmt, ...) +bool CDataBase::queryf(CVarDefMap & mapQueryResult, char *fmt, ...) { ADDTOCALLSTACK("CDataBase::queryf"); TemporaryString tsBuf; @@ -216,7 +214,7 @@ bool CDataBase::exec(const char *query) return false; } -bool __cdecl CDataBase::execf(char *fmt, ...) +bool CDataBase::execf(char *fmt, ...) { ADDTOCALLSTACK("CDataBase::execf"); TemporaryString tsBuf; @@ -405,7 +403,7 @@ bool CDataBase::r_WriteVal(lpctstr ptcKey, CSString &sVal, CTextConsole *pSrc, b { ptcKey += strlen(sm_szLoadKeys[index]); GETNONWHITESPACE(ptcKey); - sVal.FormatVal(0); + sVal.SetValFalse(); if ( ptcKey[0] != '\0' ) { diff --git a/src/common/CDataBase.h b/src/common/CDataBase.h index ee084ae50..d166cd70d 100644 --- a/src/common/CDataBase.h +++ b/src/common/CDataBase.h @@ -37,9 +37,9 @@ class CDataBase : public CScriptObj // select bool query(const char *query, CVarDefMap & mapQueryResult); // proceeds the query for SELECT - bool __cdecl queryf(CVarDefMap & mapQueryResult, char *fmt, ...) __printfargs(3,4); + 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 __cdecl execf(char *fmt, ...) __printfargs(2,3); + bool execf(char *fmt, ...) SPHERE_PRINTFARGS(2,3); void addQueryResult(CSString & theFunction, CScriptTriggerArgs * theResult); // set / get / info methods diff --git a/src/common/CException.cpp b/src/common/CException.cpp index a29834e7c..6b41a9db0 100644 --- a/src/common/CException.cpp +++ b/src/common/CException.cpp @@ -1,6 +1,10 @@ #include "CException.h" +#ifdef WINDOWS_SHOULD_EMIT_CRASH_DUMP +#include "crashdump/crashdump.h" +#endif + #ifndef _WIN32 #include //#include // for pthread_exit @@ -45,7 +49,7 @@ void NotifyDebugger() { #ifdef _WIN32 - #ifdef _MSC_VER + #ifdef MSVC_COMPILER __debugbreak(); #else SetAbortImmediate(false); @@ -66,21 +70,27 @@ static bool* _GetAbortImmediate() noexcept static bool _fIsAbortImmediate = true; return &_fIsAbortImmediate; } + void SetAbortImmediate(bool on) noexcept { *_GetAbortImmediate() = on; } -bool IsAbortImmediate() noexcept +#ifndef _WIN32 +static bool IsAbortImmediate() noexcept { return *_GetAbortImmediate(); } +#endif +[[noreturn]] void RaiseRecoverableAbort() { EXC_NOTIFY_DEBUGGER; SetAbortImmediate(false); std::abort(); } + +[[noreturn]] void RaiseImmediateAbort() { EXC_NOTIFY_DEBUGGER; @@ -183,18 +193,18 @@ bool CAssert::GetErrorMessage(lptstr lpszError, uint uiMaxError) const // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- -#ifdef _WIN32 +#ifdef WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS -CWinException::CWinException(uint uCode, size_t pAddress) : +CWinStructuredException::CWinStructuredException(uint uCode, size_t pAddress) : CSError(LOGL_CRIT, uCode, "Exception"), m_pAddress(pAddress) { } -CWinException::~CWinException() +CWinStructuredException::~CWinStructuredException() { } -bool CWinException::GetErrorMessage(lptstr lpszError, uint uiMaxError) const +bool CWinStructuredException::GetErrorMessage(lptstr lpszError, uint uiMaxError) const { lpctstr zMsg; switch ( m_hError ) @@ -225,7 +235,7 @@ void Assert_Fail( lpctstr pExp, lpctstr pFile, long long llLine ) throw CAssert(LOGL_CRIT, pExp, pFile, llLine); } -void _cdecl Sphere_Purecall_Handler() +static void SPHERE_CDECL Sphere_Purecall_Handler() { // catch this special type of C++ exception as well. Assert_Fail("purecall", "unknown", 1); @@ -234,7 +244,8 @@ void _cdecl Sphere_Purecall_Handler() void SetPurecallHandler() { // We don't want sphere to immediately exit if something calls a pure virtual method. -#ifdef _MSC_VER + // Those functions set the behavior process-wide, so there's no need to call this on each thread. +#ifdef MSVC_COMPILER _set_purecall_handler(Sphere_Purecall_Handler); #else // GCC handler for pure calls is __cxxabiv1::__cxa_pure_virtual. @@ -243,42 +254,37 @@ void SetPurecallHandler() #endif } -#if defined(_WIN32) && !defined(_DEBUG) - -#include "crashdump/crashdump.h" - -void _cdecl Sphere_Exception_Windows( unsigned int id, struct _EXCEPTION_POINTERS* pData ) +#ifdef WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS +static void SPHERE_CDECL Sphere_Structured_Exception_Windows( unsigned int id, struct _EXCEPTION_POINTERS* pData ) { -#ifndef _NO_CRASHDUMP +# ifdef WINDOWS_SHOULD_EMIT_CRASH_DUMP if ( CrashDump::IsEnabled() ) CrashDump::StartCrashDump(GetCurrentProcessId(), GetCurrentThreadId(), pData); +# endif -#endif // WIN32 gets an exception. size_t pCodeStart = (size_t)(byte *) &globalstartsymbol; // sync up to my MAP file. size_t pAddr = (size_t)pData->ExceptionRecord->ExceptionAddress; pAddr -= pCodeStart; - throw CWinException(id, pAddr); + throw CWinStructuredException(id, pAddr); } -#endif // _WIN32 && !_DEBUG - -void SetExceptionTranslator() +void SetWindowsStructuredExceptionTranslator() { -#if defined(_MSC_VER) && !defined(_DEBUG) - _set_se_translator( Sphere_Exception_Windows ); -#endif + // Process-wide, no need to call this on each thread. + _set_se_translator( Sphere_Structured_Exception_Windows ); } +#endif #ifndef _WIN32 -void _cdecl Signal_Hangup(int sig = 0) noexcept // If shutdown is initialized +static void Signal_Hangup(int sig = 0) noexcept // If shutdown is initialized { UnreferencedParameter(sig); #ifdef THREAD_TRACK_CALLSTACK - static bool _Signal_Hangup_stack_printed = false; + static thread_local bool _Signal_Hangup_stack_printed = false; if (!_Signal_Hangup_stack_printed) { StackDebugInformation::printStackTrace(); @@ -292,13 +298,13 @@ void _cdecl Signal_Hangup(int sig = 0) noexcept // If shutdown is initialized g_Serv.SetExitFlag(SIGHUP); } -void _cdecl Signal_Terminate(int sig = 0) noexcept // If shutdown is initialized +static void Signal_Terminate(int sig = 0) noexcept // If shutdown is initialized { g_Log.Event(LOGL_FATAL, "Server Unstable: %s signal received\n", strsignal(sig)); #ifdef THREAD_TRACK_CALLSTACK - static bool _Signal_Terminate_stack_printed = false; + static thread_local bool _Signal_Terminate_stack_printed = false; if (!_Signal_Terminate_stack_printed) { StackDebugInformation::printStackTrace(); @@ -346,7 +352,7 @@ void _cdecl Signal_Terminate(int sig = 0) noexcept // If shutdown is initialized //exit(EXIT_FAILURE); // Having set the exit flag, all threads "should" terminate cleanly. } -void _cdecl Signal_Break(int sig = 0) // signal handler attached when using secure mode +static void Signal_Break(int sig = 0) // signal handler attached when using secure mode { // Shouldn't be needed, since gdb consumes the signals itself and this code won't be executed //#ifdef _DEBUG @@ -367,7 +373,7 @@ void _cdecl Signal_Break(int sig = 0) // signal handler attached when using sec } } -void _cdecl Signal_Illegal_Instruction(int sig = 0) +static void Signal_Illegal_Instruction(int sig = 0) { #ifdef THREAD_TRACK_CALLSTACK StackDebugInformation::freezeCallStack(true); @@ -394,7 +400,7 @@ void _cdecl Signal_Illegal_Instruction(int sig = 0) throw CSError(LOGL_FATAL, sig, strsignal(sig)); } -void _cdecl Signal_Children(int sig = 0) +static void Signal_Children(int sig = 0) { UnreferencedParameter(sig); while (waitpid((pid_t)(-1), nullptr, WNOHANG) > 0) {} @@ -406,15 +412,15 @@ void SetUnixSignals( bool fSet ) // Signal handlers are installed only in sec { #ifdef _SANITIZERS - const bool fSan = true; + constexpr bool fSan = true; #else - const bool fSan = false; + constexpr bool fSan = false; #endif #ifdef _DEBUG const bool fDebugger = IsDebuggerPresent(); #else - const bool fDebugger = false; + constexpr bool fDebugger = false; #endif if (!fDebugger && !fSan) diff --git a/src/common/CException.h b/src/common/CException.h index 7f2f9a0c8..4a121107f 100644 --- a/src/common/CException.h +++ b/src/common/CException.h @@ -6,9 +6,24 @@ #ifndef _INC_CEXCEPTION_H #define _INC_CEXCEPTION_H -#include "../sphere/threads.h" +//#include "../sphere/threads.h" #include "CLog.h" +#if defined(_WIN32) && (defined(MSVC_COMPILER) || (defined(__MINGW32__) && defined(__SEH__))) +# define WINDOWS_HAS_SEH 1 +#endif + +#if defined(WINDOWS_HAS_SEH) && defined(WINDOWS_GENERATE_CRASHDUMP) +// We don't want this for Release build because, in order to call _set_se_translator, we should set the /EHa +// compiler flag, which slows down code a bit. +# define WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS 1 +//#endif + +//2#if defined(WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS) && defined(WINDOWS_GENERATE_CRASHDUMP) +// Also, the crash dump generating code works only when Structured Exception Handling is enabled +# define WINDOWS_SHOULD_EMIT_CRASH_DUMP 1 +#endif + // ------------------------------------------------------------------- // ------------------------------------------------------------------- @@ -18,7 +33,9 @@ extern "C" } void SetPurecallHandler(); -void SetExceptionTranslator(); +#ifdef WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS +void SetWindowsStructuredExceptionTranslator(); +#endif #ifndef _WIN32 void SetUnixSignals( bool ); @@ -28,9 +45,9 @@ void SetExceptionTranslator(); void NotifyDebugger(); void SetAbortImmediate(bool on) noexcept; -bool AbortImmediate() noexcept; - +[[noreturn]] void RaiseRecoverableAbort(); +[[noreturn]] void RaiseImmediateAbort(); @@ -79,20 +96,18 @@ class CAssert : public CSError virtual bool GetErrorMessage(lptstr lpszError, uint uiMaxError ) const override; }; -#ifdef _WIN32 +#ifdef WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS // Catch and get details on the system exceptions. - class CWinException : public CSError + class CWinStructuredException : public CSError { public: static const char *m_sClassName; const size_t m_pAddress; - CWinException(uint uCode, size_t pAddress); - virtual ~CWinException(); - private: - CWinException& operator=(const CWinException& other); + CWinStructuredException(uint uCode, size_t pAddress); + virtual ~CWinStructuredException(); + CWinStructuredException& operator=(const CWinStructuredException& other) = delete; - public: virtual bool GetErrorMessage(lptstr lpszError, uint nMaxError) const override; }; #endif diff --git a/src/common/CExpression.cpp b/src/common/CExpression.cpp index 208a57e58..8a530cb15 100644 --- a/src/common/CExpression.cpp +++ b/src/common/CExpression.cpp @@ -125,7 +125,7 @@ int StrncpyCharBytesWritten(int iBytesToWrite, size_t uiBufSize, bool fPrintErro return (uiBufSize > 1) ? int(uiBufSize - 1) : 0; // Bytes written, excluding the string terminator. } -llong power(llong base, llong level) +static llong cexpression_power(llong base, llong level) { double rc = pow((double)base, (double)level); return (llong)rc; @@ -1057,7 +1057,7 @@ llong CExpression::GetValMath( llong llVal, lpctstr & pExpr ) llValSecond = GetVal(pExpr); if (llVal < 0) { - llVal = power(llVal, llValSecond); + llVal = cexpression_power(llVal, llValSecond); 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. @@ -1065,7 +1065,7 @@ llong CExpression::GetValMath( llong llVal, lpctstr & pExpr ) g_Log.EventError("Power of zero with zero or negative exponent is undefined.\n"); break; } - llVal = power(llVal, llValSecond); + llVal = cexpression_power(llVal, llValSecond); break; } @@ -1230,18 +1230,18 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSube } // Helper lambda functions for the next section. - auto findLastClosingBracket = [](lptstr pExpr) -> lptstr + auto findLastClosingBracket = [](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, // 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'); + ASSERT(*pExpr_ != '\0'); lptstr pExprFinder; - const size_t uiExprLength = strlen(pExpr); - const lptstr pComment = Str_FindSubstring(pExpr, "//", uiExprLength, 2); + const size_t uiExprLength = strlen(pExpr_); + const lptstr pComment = Str_FindSubstring(pExpr_, "//", uiExprLength, 2); if (nullptr == pComment) { - pExprFinder = pExpr + uiExprLength - 1; + pExprFinder = pExpr_ + uiExprLength - 1; // Now pExprFinder is at the end of the string } else { @@ -1256,28 +1256,28 @@ int CExpression::GetConditionalSubexpressions(lptstr& pExpr, SubexprData(&psSube --pExprFinder; else break; - } while (pExprFinder > pExpr); + } while (pExprFinder > pExpr_); return (*pExprFinder == ')') ? pExprFinder : nullptr; }; - auto skipBracketedSubexpression = [](lptstr pExpr) -> lptstr + auto skipBracketedSubexpression = [](lptstr pExpr_) -> lptstr { - ASSERT(*pExpr == '('); - tchar ch; + ASSERT(*pExpr_ == '('); + tchar ch_; uint uiOpenedCurlyBrackets = 1; while (uiOpenedCurlyBrackets != 0) // i'm interested only to the outermost range, not eventual sub-sub-sub-blah ranges { - ch = *(++pExpr); - if (ch == '(') + ch_ = *(++pExpr_); + if (ch_ == '(') ++uiOpenedCurlyBrackets; - else if (ch == ')') + else if (ch_ == ')') --uiOpenedCurlyBrackets; - else if (ch == '\0') + else if (ch_ == '\0') return nullptr; // Error } if (uiOpenedCurlyBrackets != 0) return nullptr; // Error - return pExpr; + return pExpr_; }; diff --git a/src/common/CFloatMath.cpp b/src/common/CFloatMath.cpp index 1cfd8afe1..8b0238ce6 100644 --- a/src/common/CFloatMath.cpp +++ b/src/common/CFloatMath.cpp @@ -19,7 +19,7 @@ CSString CFloatMath::FloatMath(lpctstr& Expr) } -short int _iReentrant_Count = 0; +static thread_local short int _iReentrant_Count = 0; realtype CFloatMath::MakeFloatMath( lpctstr & Expr ) { diff --git a/src/common/CLog.cpp b/src/common/CLog.cpp index 4e90575ae..364dadae5 100644 --- a/src/common/CLog.cpp +++ b/src/common/CLog.cpp @@ -75,7 +75,6 @@ int CEventLog::EventCustom(ConsoleTextColor iColor, dword dwMask, lpctstr pszFor return iret; } -#ifdef _DEBUG int CEventLog::EventEvent(lpctstr pszFormat, ...) noexcept { va_list vargs; @@ -84,7 +83,6 @@ int CEventLog::EventEvent(lpctstr pszFormat, ...) noexcept va_end(vargs); return iret; } -#endif //_DEBUG //------- @@ -265,7 +263,7 @@ int CLog::EventStr( dword dwMask, lpctstr pszMsg, ConsoleTextColor iLogColor) no } // Get the script context. (if there is one) - tchar szScriptContext[ _MAX_PATH + 16 ]; + tchar szScriptContext[ SPHERE_MAX_PATH + 16 ]; if ( !(dwMask & LOGM_NOCONTEXT) && m_pScriptContext ) { CScriptLineContext LineContext = m_pScriptContext->GetContext(); @@ -360,7 +358,7 @@ int CLog::EventStr( dword dwMask, lpctstr pszMsg, ConsoleTextColor iLogColor) no CSTime CLog::sm_prevCatchTick; -void _cdecl CLog::CatchEvent( const CSError * pErr, lpctstr pszCatchContext, ... ) +void CLog::CatchEvent( const CSError * pErr, lpctstr pszCatchContext, ... ) { CSTime timeCurrent = CSTime::GetCurrentTime(); if ( sm_prevCatchTick.GetTime() == timeCurrent.GetTime() ) // prevent message floods. @@ -394,7 +392,7 @@ void _cdecl CLog::CatchEvent( const CSError * pErr, lpctstr pszCatchContext, ... va_start(vargs, pszCatchContext); uiLen += vsnprintf(szMsg + uiLen, sizeof(szMsg) - uiLen, pszCatchContext, vargs); - uiLen += snprintf (szMsg + uiLen, sizeof(szMsg) - uiLen, "\n"); + /*uiLen += */ snprintf (szMsg + uiLen, sizeof(szMsg) - uiLen, "\n"); EventStr(eSeverity, szMsg); va_end(vargs); @@ -407,7 +405,7 @@ void _cdecl CLog::CatchEvent( const CSError * pErr, lpctstr pszCatchContext, ... sm_prevCatchTick = timeCurrent; } -void _cdecl CLog::CatchStdException(const std::exception * pExc, lpctstr pszCatchContext, ...) +void CLog::CatchStdException(const std::exception * pExc, lpctstr pszCatchContext, ...) { tchar szMsg[512]; diff --git a/src/common/CLog.h b/src/common/CLog.h index f644e22fe..27a100d74 100644 --- a/src/common/CLog.h +++ b/src/common/CLog.h @@ -9,7 +9,10 @@ #include "sphere_library/CSFileText.h" #include "sphere_library/CSTime.h" #include "../sphere/ConsoleInterface.h" -#include + +namespace std { +class exception; // Forward declaration +} // ----------------------------- // CEventLog @@ -63,14 +66,12 @@ class CEventLog int VEvent(dword dwMask, lpctstr pszFormat, ConsoleTextColor iColor, va_list args) noexcept; public: - int _cdecl Event( dword dwMask, lpctstr pszFormat, ... ) noexcept __printfargs(3,4); - int _cdecl EventDebug(lpctstr pszFormat, ...) noexcept __printfargs(2,3); - int _cdecl EventError(lpctstr pszFormat, ...) noexcept __printfargs(2,3); - int _cdecl EventWarn(lpctstr pszFormat, ...) noexcept __printfargs(2,3); - int _cdecl EventCustom(ConsoleTextColor iColor, dword dwMask, lpctstr pszFormat, ...) noexcept __printfargs(4,5); -#ifdef _DEBUG - int _cdecl EventEvent( lpctstr pszFormat, ... ) noexcept __printfargs(2,3); -#endif //_DEBUG + int Event( dword dwMask, lpctstr pszFormat, ... ) noexcept SPHERE_PRINTFARGS(3,4); + int EventDebug(lpctstr pszFormat, ...) noexcept SPHERE_PRINTFARGS(2,3); + int EventError(lpctstr pszFormat, ...) noexcept SPHERE_PRINTFARGS(2,3); + int EventWarn(lpctstr pszFormat, ...) noexcept SPHERE_PRINTFARGS(2,3); + int EventCustom(ConsoleTextColor iColor, dword dwMask, lpctstr pszFormat, ...) noexcept SPHERE_PRINTFARGS(4,5); + int EventEvent( lpctstr pszFormat, ... ) noexcept SPHERE_PRINTFARGS(2,3); CEventLog(); virtual ~CEventLog(); @@ -118,8 +119,8 @@ public: bool OpenLog(lpctstr pszName = nullptr); bool IsLogged( dword dwMask ) const; virtual int EventStr( dword dwMask, lpctstr pszMsg, ConsoleTextColor iLogColor = CTCOL_DEFAULT ) noexcept final; // final: for now, it doesn't have any other virtual methods - void _cdecl CatchEvent( const CSError * pErr, lpctstr pszCatchContext, ... ) __printfargs(3,4); - void _cdecl CatchStdException( const std::exception * pExc, lpctstr pszCatchContext, ... ) __printfargs(3,4); + void CatchEvent( const CSError * pErr, lpctstr pszCatchContext, ... ) SPHERE_PRINTFARGS(3,4); + void CatchStdException( const std::exception * pExc, lpctstr pszCatchContext, ... ) SPHERE_PRINTFARGS(3,4); public: CLog(); diff --git a/src/common/CRect.cpp b/src/common/CRect.cpp index c25aea5a0..589fe09ac 100644 --- a/src/common/CRect.cpp +++ b/src/common/CRect.cpp @@ -1,7 +1,5 @@ #include "../game/uo_files/CUOMapList.h" #include "../game/CSectorList.h" -#include "../game/CServer.h" -#include "../game/CWorldMap.h" #include "CLog.h" #include "CRect.h" diff --git a/src/common/CScript.cpp b/src/common/CScript.cpp index 681b00ff9..c7a4da969 100644 --- a/src/common/CScript.cpp +++ b/src/common/CScript.cpp @@ -9,11 +9,16 @@ #include "common.h" +/* + * TODO: + * make CScriptKey m_pszKey and m_pszVal private. + * make setter and getters methods that will automatically update the buffer lengths, held in a separate value. +*/ + /////////////////////////////////////////////////////////////// // -CScriptKey - bool CScriptKey::IsKey( lpctstr pszName ) const { ASSERT(m_pszKey); @@ -369,7 +374,7 @@ bool CScriptKeyAlloc::ParseKey( lpctstr ptcKey, lpctstr pszVal ) if (ptcKey != m_pszKey) { // Invalid key, or not yet inited. - strcpy(m_pszKey, ptcKey); + Str_CopyLimitNull(m_pszKey, ptcKey, m_Mem.GetDataLength()); } m_pszArg = m_pszKey + lenkey; @@ -485,9 +490,9 @@ bool CScript::_Open( lpctstr ptcFilename, uint uiFlags ) lpctstr ptcExt = GetFilesExt(ptcFilename); if ( !ptcExt ) { - tchar ptcTemp[_MAX_PATH]; - Str_CopyLimit(ptcTemp, ptcFilename, _MAX_PATH-4); // -4 beause of SPHERE_SCRIPT, which is = ".scp" - strcat(ptcTemp, SPHERE_SCRIPT); + tchar ptcTemp[SPHERE_MAX_PATH]; + const size_t uiCopied = Str_CopyLimit(ptcTemp, ptcFilename, sizeof(ptcTemp) - SPHERE_SCRIPT_EXT_LEN); + Str_ConcatLimitNull(ptcTemp, SPHERE_SCRIPT_EXT, sizeof(ptcTemp) - uiCopied); _SetFilePath(ptcTemp); uiFlags |= OF_TEXT; } @@ -836,7 +841,7 @@ CScriptLineContext CScript::GetContext() const return LineContext; } -bool _cdecl CScript::WriteSection( lpctstr ptcSection, ... ) +bool CScript::WriteSection( lpctstr ptcSection, ... ) { ADDTOCALLSTACK_DEBUG("CScript::WriteSection"); // Write out the section header. @@ -928,7 +933,7 @@ bool CScript::WriteKeyStr(lpctstr ptcKey, lpctstr ptcVal) return true; } -//void _cdecl CScript::WriteKeyFormat( lpctstr ptcKey, lpctstr pszVal, ... ) +//void CScript::WriteKeyFormat( lpctstr ptcKey, lpctstr pszVal, ... ) //{ // ADDTOCALLSTACK("CScript::WriteKeyFormat"); // tchar *pszTemp = Str_GetTemp(); @@ -940,7 +945,7 @@ bool CScript::WriteKeyStr(lpctstr ptcKey, lpctstr ptcVal) //} static thread_local tchar ptcWriteKeyBuf[SCRIPT_MAX_LINE_LEN]; -void _cdecl CScript::WriteKeyFormat(lpctstr ptcKey, lpctstr pszVal, ...) +void CScript::WriteKeyFormat(lpctstr ptcKey, lpctstr pszVal, ...) { ADDTOCALLSTACK_DEBUG("CScript::WriteKeyFormat"); va_list vargs; diff --git a/src/common/CScript.h b/src/common/CScript.h index 855f87fe5..bb21601a0 100644 --- a/src/common/CScript.h +++ b/src/common/CScript.h @@ -31,11 +31,13 @@ class CScriptKey // Args passed with the key. bool HasArgs() const; - tchar * GetArgRaw() const; // Not need to parse at all. - tchar * GetArgStr( bool * fQuoted ); // this could be a quoted string ? - inline tchar * GetArgStr() { - return GetArgStr(nullptr); - } + + // Not need to parse at all. + tchar * GetArgRaw() const; + + // this could be a quoted string ? + tchar * GetArgStr(bool * fQuoted = nullptr); + char GetArgCVal(); uchar GetArgUCVal(); @@ -62,7 +64,8 @@ class CScriptKey dword GetArgFlag( dword dwStart, dword dwMask ); int64 GetArgLLFlag( uint64 iStart, uint64 iMask ); -public: +// This class is meant only to be inherited. +protected: CScriptKey(); CScriptKey( tchar * ptcKey, tchar * ptcArg ); ~CScriptKey() = default; @@ -156,8 +159,8 @@ public: CScriptLineContext GetContext() const; bool ReadKeyParse(); // Write stuff out to a script file. - bool _cdecl WriteSection(lpctstr pszSection, ...) __printfargs(2,3); - void _cdecl WriteKeyFormat(lpctstr ptcKey, lpctstr pszFormat, ...) __printfargs(3,4); + bool WriteSection(lpctstr pszSection, ...) SPHERE_PRINTFARGS(2,3); + void WriteKeyFormat(lpctstr ptcKey, lpctstr pszFormat, ...) SPHERE_PRINTFARGS(3,4); bool WriteKeySingle(lptstr ptcKey); bool WriteKeyStr(lpctstr ptcKey, lpctstr ptcVal); diff --git a/src/common/CScriptObj.cpp b/src/common/CScriptObj.cpp index 684109e4d..73c5d2575 100644 --- a/src/common/CScriptObj.cpp +++ b/src/common/CScriptObj.cpp @@ -496,7 +496,7 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc if (pRef == nullptr) { // Invalid ref: just return 0, it isn't an error. - sVal.FormatVal(0); + sVal.SetValFalse(); return true; } @@ -516,7 +516,7 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc if (pRef == nullptr) // good command but bad reference. { - sVal.FormatVal(0); + sVal.SetValFalse(); return false; } return pRef->r_WriteVal( ptcKey, sVal, pSrc ); @@ -667,7 +667,7 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc if ( pVar ) sVal = pVar->GetValStr(); else if ( fZero ) - sVal = "0"; + sVal.SetValFalse(); } return true; case SSC_RESDEF0: @@ -679,7 +679,7 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc if ( pVar ) sVal = pVar->GetValStr(); else if ( fZero ) - sVal = "0"; + sVal.SetValFalse(); } return true; case SSC_DEFMSG: diff --git a/src/common/CScriptTriggerArgs.cpp b/src/common/CScriptTriggerArgs.cpp index 698e1269b..f967d1d32 100644 --- a/src/common/CScriptTriggerArgs.cpp +++ b/src/common/CScriptTriggerArgs.cpp @@ -543,7 +543,7 @@ bool CScriptTriggerArgs::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsol if ( pObj ) sVal.FormatHex(pObj->GetUID()); else - sVal.FormatVal(0); + sVal.SetValFalse(); } break; case AGC_S: diff --git a/src/common/CTextConsole.cpp b/src/common/CTextConsole.cpp index b98c301e6..89fdbbf8b 100644 --- a/src/common/CTextConsole.cpp +++ b/src/common/CTextConsole.cpp @@ -139,7 +139,7 @@ void CTextConsole::VSysMessage( lpctstr pszFormat, va_list args ) const SysMessage(tsTemp.buffer()); } -void _cdecl CTextConsole::SysMessagef( lpctstr pszFormat, ... ) const +void CTextConsole::SysMessagef( lpctstr pszFormat, ... ) const { va_list vargs; va_start( vargs, pszFormat ); diff --git a/src/common/CTextConsole.h b/src/common/CTextConsole.h index 5429f4f85..4bd15f897 100644 --- a/src/common/CTextConsole.h +++ b/src/common/CTextConsole.h @@ -41,7 +41,7 @@ class CTextConsole virtual void SysMessage( lpctstr pszMessage ) const = 0; // Feed back message. void VSysMessage( lpctstr pszFormat, va_list args ) const; - void _cdecl SysMessagef( lpctstr pszFormat, ... ) const __printfargs(2,3); + void SysMessagef( lpctstr pszFormat, ... ) const SPHERE_PRINTFARGS(2,3); public: CTextConsole() { }; diff --git a/src/common/CUOClientVersion.cpp b/src/common/CUOClientVersion.cpp index 07113de19..56be65445 100644 --- a/src/common/CUOClientVersion.cpp +++ b/src/common/CUOClientVersion.cpp @@ -1,7 +1,5 @@ #include "CUOClientVersion.h" -#include "CExpression.h" #include "CLog.h" -#include "sphereproto.h" #include #include #include diff --git a/src/common/CUOInstall.cpp b/src/common/CUOInstall.cpp index 2240ced2c..f09727661 100644 --- a/src/common/CUOInstall.cpp +++ b/src/common/CUOInstall.cpp @@ -58,7 +58,7 @@ bool CUOInstall::FindInstall() return false; } - tchar szValue[ _MAX_PATH ]; + tchar szValue[ SPHERE_MAX_PATH ]; DWORD lSize = sizeof( szValue ); DWORD dwType = REG_SZ; lRet = RegQueryValueEx(hKey, "ExePath", nullptr, &dwType, (byte*)szValue, &lSize); diff --git a/src/common/CVarDefMap.cpp b/src/common/CVarDefMap.cpp index 3e99fae28..09d2b452b 100644 --- a/src/common/CVarDefMap.cpp +++ b/src/common/CVarDefMap.cpp @@ -2,7 +2,6 @@ #include "../common/CLog.h" #include "../game/CServer.h" #include "../game/CServerConfig.h" -#include "CException.h" #include "CExpression.h" #include "CScript.h" #include "CTextConsole.h" diff --git a/src/common/ListDefContMap.cpp b/src/common/ListDefContMap.cpp index 8d0d7c68b..7d092eafb 100644 --- a/src/common/ListDefContMap.cpp +++ b/src/common/ListDefContMap.cpp @@ -2,7 +2,6 @@ #include "../common/sphere_library/sstringobjs.h" #include "../common/CLog.h" #include "../sphere/threads.h" -#include "../game/CServerConfig.h" #include "CExpression.h" #include "CScript.h" #include "CTextConsole.h" @@ -269,7 +268,7 @@ bool CListDefCont::AddElementStr(lpctstr ptcKey) if ( (m_listElements.size() + 1) >= INTPTR_MAX ) // overflow? is it even useful? return false; - REMOVE_QUOTES( ptcKey ); + REMOVE_QUOTES( ptcKey ); m_listElements.emplace_back( new CListDefContStr(m_Key.GetBuffer(), ptcKey) ); @@ -774,14 +773,14 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) Str_Parse(ppCmds[0], &(ppCmds[1]), "." ); CListDefCont* pListBase = GetKey(ppCmds[0]); - lpctstr ptcArg = s.GetArgRaw(); + lpctstr ptcArg = s.GetArgRaw(); // LIST. if ( ppCmds[1] && *(ppCmds[1]) ) { Str_Parse(ppCmds[1], &(ppCmds[2]), "." ); - if ( !IsSimpleNumberString(ppCmds[1]) ) + if ( !IsStrNumeric(ppCmds[1]) ) { // LIST.. // Am i calling a valid operation? @@ -806,7 +805,7 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) m_Container.insert(pListBase); } - if ( IsSimpleNumberString(ptcArg) ) + if ( IsStrNumeric(ptcArg) ) return pListBase->AddElementNum(Exp_Get64Val(ptcArg)); else return pListBase->AddElementStr(ptcArg); @@ -830,22 +829,22 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) m_Container.insert(pListBase); } - tchar* ppCmd[2]; - ppCmd[0] = const_cast(ptcArg); - while ( Str_Parse( ppCmd[0], &(ppCmd[1]), "," )) + tchar* ppArgs[2]; + ppArgs[0] = const_cast(ptcArg); + while ( Str_Parse( ppArgs[0], &(ppArgs[1]), "," )) { - if ( IsSimpleNumberString(ppCmd[0]) ) - pListBase->AddElementNum(Exp_Get64Val(ppCmd[0])); + if ( IsStrNumeric(ppArgs[0]) ) + pListBase->AddElementNum(Exp_Get64Val(ppArgs[0])); else - pListBase->AddElementStr(ppCmd[0]); - ppCmd[0] = ppCmd[1]; + pListBase->AddElementStr(ppArgs[0]); + ppArgs[0] = ppArgs[1]; } //insert last element - if ( IsSimpleNumberString(ppCmd[0]) ) - return pListBase->AddElementNum(Exp_Get64Val(ppCmd[0])); + if ( IsStrNumeric(ppArgs[0]) ) + return pListBase->AddElementNum(Exp_Get64Val(ppArgs[0])); else - return pListBase->AddElementStr(ppCmd[0]); + return pListBase->AddElementStr(ppArgs[0]); } else if ( !strnicmp(ppCmds[1], "sort", 4) ) { @@ -889,7 +888,7 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) else if ( !strnicmp(ppCmds[2], "insert", 6) && ptcArg && *ptcArg ) { // Inserts at the nth index of LIST.xxx - const bool fIsNum = ( IsSimpleNumberString(ptcArg) ); + const bool fIsNum = ( IsStrNumeric(ptcArg) ); if ( nIndex >= pListBase->GetCount() ) { @@ -918,7 +917,7 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) if ( !pListElem ) return false; - if ( IsSimpleNumberString(ptcArg) ) + if ( IsStrNumeric(ptcArg) ) return pListBase->SetNumAt(nIndex, Exp_Get64Val(ptcArg)); else return pListBase->SetStrAt(nIndex, ptcArg); @@ -933,7 +932,7 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) pListBase = new CListDefCont(ppCmds[0]); m_Container.insert(pListBase); - if ( IsSimpleNumberString(ptcArg) ) + if ( IsStrNumeric(ptcArg) ) return pListBase->AddElementNum(Exp_Get64Val(ptcArg)); else return pListBase->AddElementStr(ptcArg); @@ -953,7 +952,7 @@ bool CListDefMap::r_LoadVal( lpctstr ptcKey, CScript & s ) m_Container.insert(pListBase); } - if ( IsSimpleNumberString(ptcArg) ) + if ( IsStrNumeric(ptcArg) ) return pListBase->AddElementNum(Exp_Get64Val(ptcArg)); else return pListBase->AddElementStr(ptcArg); diff --git a/src/common/ListDefContMap.h b/src/common/ListDefContMap.h index db86bc11e..9df957509 100644 --- a/src/common/ListDefContMap.h +++ b/src/common/ListDefContMap.h @@ -214,9 +214,9 @@ class CListDefMap void ClearKeys(lpctstr mask = nullptr); void DeleteKey( lpctstr key ); - bool r_LoadVal( lpctstr ptcKey, CScript & s ); - bool r_Write( CTextConsole *pSrc, lpctstr pszString, CSString& strVal ); - void r_WriteSave( CScript& s ); + bool r_LoadVal( lpctstr ptcKey, CScript & s ) NONVIRTUAL; + bool r_Write( CTextConsole *pSrc, lpctstr pszString, CSString& strVal ) NONVIRTUAL; + void r_WriteSave( CScript& s ) NONVIRTUAL; }; #endif // _INC_LISTDEFCONTMAP_H diff --git a/src/common/common.h b/src/common/common.h index 65b68040d..1b20d9a8b 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -12,7 +12,8 @@ #define SPHERE_FILE "sphere" // file name prefix #define SPHERE_TITLE "SphereServer" -#define SPHERE_SCRIPT ".scp" +#define SPHERE_SCRIPT_EXT ".scp" +#define SPHERE_SCRIPT_EXT_LEN 4 #define SCRIPT_MAX_LINE_LEN 4096 // default size. #define SCRIPT_MAX_SECTION_LEN 128 @@ -49,6 +50,21 @@ /* Coding helpers */ +// On Windows, Clang with MSVC runtime defines _MSC_VER! (But also __clang__). +#if !defined(_MSC_VER) || defined(__clang__) +# define NON_MSVC_COMPILER 1 +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +# define MSVC_COMPILER 1 +#endif + +// On Windows, Clang can use MinGW or MSVC backend. +#ifdef _MSC_VER +# define MSVC_RUNTIME +#endif + + // Strings #define STRINGIFY_IMPL_(x) #x #define STRINGIFY(x) STRINGIFY_IMPL_(x) @@ -124,6 +140,27 @@ constexpr T sign(const T n) noexcept #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! +template +[[nodiscard]] +constexpr T saturating_sub(T a, T b) noexcept { + // Saturating subtraction. + + // Ensure T is an arithmetic type + static_assert(std::is_arithmetic_v, "T must be an arithmetic type"); + + // For unsigned types + if constexpr (std::is_unsigned_v) + return (a > b) ? a - b : 0; + // For signed types + else + { + if (b > 0 && a < std::numeric_limits::min() + b) + return std::numeric_limits::min(); // Saturate to minimum + return a - b; + } +} + +#define SATURATING_SUB_SELF(var, val) var = saturating_sub(var, val) /* End of arithmetic code */ @@ -131,8 +168,9 @@ constexpr T sign(const T n) noexcept /* Compiler/c++ language helpers */ // 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 -consteval T ensure_comptime(T&& val_) noexcept { +consteval T as_consteval(T&& val_) noexcept { return val_; } @@ -172,10 +210,20 @@ constexpr void UnreferencedParameter(T const&) noexcept { // Function specifier, like noexcept. Use this to make us know that the function code was checked and we know it can throw an exception. #define CANTHROW noexcept(false) +// To be used only as an helper marker, since there are functions with similar names intended to have different signatures and/or not be virtual. +// This means that we do NOT have forgotten to add the "virtual" qualifier, simply this method isn't virtual. +#define NONVIRTUAL + // Cpp attributes #define FALLTHROUGH [[fallthrough]] #define NODISCARD [[nodiscard]] +#if defined(__GNUC__) || defined(__clang__) +# define RETURNS_NOTNULL [[gnu::returns_nonnull]] +#else +# define RETURNS_NOTNULL +#endif + #ifdef _DEBUG #define NOEXCEPT_NODEBUG #else @@ -192,20 +240,21 @@ constexpr void UnreferencedParameter(T const&) noexcept { // b = 1-based index of arguments // (note: add 1 to index for non-static class methods because 'this' argument // is inserted in position 1) -#ifdef _MSC_VER - #define __printfargs(a,b) +#ifdef MSVC_COMPILER + #define SPHERE_PRINTFARGS(a,b) #else - #ifdef _WIN32 - #define __printfargs(a,b) __attribute__ ((format(gnu_printf, a, b))) + #ifdef __MINGW32__ + // Clang doesn't have a way to switch from gnu or ms style printf arguments. It just depends on the runtime used. + #define SPHERE_PRINTFARGS(a,b) __attribute__ ((format(gnu_printf, a, b))) #else - #define __printfargs(a,b) __attribute__ ((format(printf, a, b))) + #define SPHERE_PRINTFARGS(a,b) __attribute__ ((format(printf, a, b))) #endif #endif /* Sanitizers utilities */ -#if defined(_MSC_VER) +#if defined(MSVC_COMPILER) #if defined(__SANITIZE_ADDRESS__) || defined(ADDRESS_SANITIZER) #define NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) diff --git a/src/common/crashdump/crashdump.cpp b/src/common/crashdump/crashdump.cpp index 469a3c8dd..3beb4ca5a 100644 --- a/src/common/crashdump/crashdump.cpp +++ b/src/common/crashdump/crashdump.cpp @@ -1,4 +1,4 @@ -#if defined(_WIN32) && !defined(_DEBUG) && !defined(_NO_CRASHDUMP) +#ifdef WINDOWS_SHOULD_EMIT_CRASH_DUMP #include "crashdump.h" @@ -45,32 +45,32 @@ void CrashDump::Disable() } } -void CrashDump::StartCrashDump( DWORD processID, DWORD threadID, struct _EXCEPTION_POINTERS* pExceptionData ) +void CrashDump::StartCrashDump( DWORD processID, DWORD threadID, struct _EXCEPTION_POINTERS* pExceptionData ) { HANDLE process = nullptr, dumpFile = nullptr; - + if (!processID || !threadID || !pExceptionData) return; process = GetCurrentProcess(); if (process == nullptr) return; - + char buf[128]; SYSTEMTIME st; GetSystemTime(&st); snprintf(buf, sizeof(buf), "sphereCrash_%02i%02i-%02i%02i%02i%04i.dmp", st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); - dumpFile = CreateFile(buf, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0); + dumpFile = CreateFile(buf, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, 0, nullptr); if (dumpFile == INVALID_HANDLE_VALUE) return; - + MINIDUMP_EXCEPTION_INFORMATION eInfo; eInfo.ThreadId = threadID; eInfo.ExceptionPointers = pExceptionData; eInfo.ClientPointers = true; - + m_tDumpFunction(process, processID, dumpFile, MiniDumpWithDataSegs, &eInfo, nullptr, nullptr); - + CloseHandle(dumpFile); return; } diff --git a/src/common/crashdump/crashdump.h b/src/common/crashdump/crashdump.h index 0a8387722..401727ee7 100644 --- a/src/common/crashdump/crashdump.h +++ b/src/common/crashdump/crashdump.h @@ -1,8 +1,10 @@ -#if !defined(_INC_CRASHDUMP_H) && defined(_WIN32) && !defined(_DEBUG) && !defined(_NO_CRASHDUMP) +#ifdef WINDOWS_SHOULD_EMIT_CRASH_DUMP #define _INC_CRASHDUMP_H #include +// TODO: enable creation of the crash dump also for mingw, since it can manage windows structured exceptions. + #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN // include just windows.h without the other winapi headers, we'll add them manually when needed #endif diff --git a/src/common/crypto/CBCrypt.cpp b/src/common/crypto/CBCrypt.cpp index e917d422c..ef39b8cca 100644 --- a/src/common/crypto/CBCrypt.cpp +++ b/src/common/crypto/CBCrypt.cpp @@ -61,7 +61,7 @@ static int timing_safe_strcmp(const char *str1, const char *str2) * nonzero otherwise. * */ -int bcrypt_gensalt(const char* prefix, int factor, char salt[BCRYPT_HASHSIZE]) +static int bcrypt_gensalt(const char* prefix, int factor, char salt[BCRYPT_HASHSIZE]) { #define RANDBYTES (16) //char input[RANDBYTES]; @@ -94,7 +94,7 @@ int bcrypt_gensalt(const char* prefix, int factor, char salt[BCRYPT_HASHSIZE]) * The return value is zero if the password could be hashed and nonzero * otherwise. */ -int bcrypt_hashpw(const char *passwd, const char salt[BCRYPT_HASHSIZE], char hash[BCRYPT_HASHSIZE]) +static int bcrypt_hashpw(const char *passwd, const char salt[BCRYPT_HASHSIZE], char hash[BCRYPT_HASHSIZE]) { char *aux; aux = crypt_rn(passwd, salt, hash, BCRYPT_HASHSIZE); @@ -110,7 +110,7 @@ int bcrypt_hashpw(const char *passwd, const char salt[BCRYPT_HASHSIZE], char has * passwords don't match. * */ -int bcrypt_checkpw(const char *passwd, const char hash[BCRYPT_HASHSIZE]) +static int bcrypt_checkpw(const char *passwd, const char hash[BCRYPT_HASHSIZE]) { int ret; char outhash[BCRYPT_HASHSIZE]; diff --git a/src/common/datatypes.h b/src/common/datatypes.h index c7125776e..16531cc31 100644 --- a/src/common/datatypes.h +++ b/src/common/datatypes.h @@ -104,7 +104,7 @@ typedef uint64_t uint64; #else // _WIN32 - #ifdef _MSC_VER // already defined on MinGW + #ifdef _MSC_VER // already defined on MinGW #include typedef SSIZE_T ssize_t; #endif // _MSC_VER @@ -115,6 +115,7 @@ typedef uint64_t uint64; #define PRIxSIZE_T "zx" #define PRIdSSIZE_T "zd" #else + // If using MSVC runtime and not GNU/MinGW. #define PRIuSIZE_T "Iu" #define PRIxSIZE_T "Ix" #define PRIdSSIZE_T "Id" diff --git a/src/common/os_unix.h b/src/common/os_unix.h index 684fba9ad..92eee09b3 100644 --- a/src/common/os_unix.h +++ b/src/common/os_unix.h @@ -6,21 +6,36 @@ #ifndef _INC_OS_UNIX_H #define _INC_OS_UNIX_H +//#include // PATH_MAX #include // usleep #include // toupper, tolower //#define SLASH_PATH "/" -#ifndef _MAX_PATH // stdlib.h ? - #define _MAX_PATH 260 // max. length of full pathname + +// Max. length of full pathname + +/* +#if defined(PATH_MAX) // limits.h +# if PATH_MAX <= 0 +# define SPHERE_MAX_PATH 260 +# elif PATH_MAX > 1024 +# define SPHERE_MAX_PATH 1024 // Define a "big enough" max value, just to avoid the need of enormous char buffers. +# else +# define SPHERE_MAX_PATH PATH_MAX +# endif +#else +// If we want to keep compatibility with legacy Windows max path length. +# define SPHERE_MAX_PATH 260 #endif +*/ +// TODO: enable longer MAX_PATHs on both Linux and Windows, also checking that increasing that limit doesn't cause any buffer overflow. +#define SPHERE_MAX_PATH 260 -#define _cdecl -#define __cdecl -#define FAR +//#define SPHERE_CDECL __attribute__((cdecl)) +#define SPHERE_CDECL #define E_FAIL 0x80004005 // exception code -#define HANDLE dword /* cross-platform functions macros */ @@ -31,17 +46,14 @@ #define HIWORD(l) ((word)((dword)(l) >> 16)) #define LOBYTE(w) ((byte)((dword)(w) & 0xff)) #define HIBYTE(w) ((byte)((dword)(w) >> 8)) -#define IsBadReadPtr( p, len ) ((p) == nullptr) -#define IsBadStringPtr( p, len ) ((p) == nullptr) -#define Sleep(mSec) usleep(mSec*1000) // arg is microseconds = 1/1000000 -#define SleepEx(mSec, unused) usleep(mSec*1000) // arg is microseconds = 1/1000000 #ifdef _BSD int getTimezone(); - #define _timezone getTimezone() + #define TIMEZONE getTimezone() #else - #define _timezone timezone + #define TIMEZONE timezone #endif + /* */ /* file handling definitions */ @@ -54,24 +66,18 @@ /* thread-specific definitions */ #define STDFUNC_GETPID getpid #define CRITICAL_SECTION pthread_mutex_t -#define Sleep(mSec) usleep(mSec*1000) // arg is microseconds = 1/1000000 -#define SleepEx(mSec, unused) usleep(mSec*1000) // arg is microseconds = 1/1000000 -/* */ - -#define ERROR_SUCCESS 0 -#define HKEY_LOCAL_MACHINE (( HKEY ) 0x80000002 ) +/* */ +#define SLEEP(mSec) usleep(mSec*1000) // arg is microseconds = 1/1000000 /* No portable UNIX/LINUX equiv to this. */ - -// The others, not inlined, are in common.cpp -inline void _strupr( tchar * pszStr ) +inline void _strupr( tchar * pszStr ) noexcept { for ( ; pszStr[0] != '\0'; ++pszStr ) *pszStr = toupper( *pszStr ); } -inline void _strlwr( tchar * pszStr ) +inline void _strlwr( tchar * pszStr ) noexcept { for ( ; pszStr[0] != '\0'; ++pszStr ) *pszStr = tolower( *pszStr ); diff --git a/src/common/os_windows.h b/src/common/os_windows.h index 9d8b08f31..c2c9f91cf 100644 --- a/src/common/os_windows.h +++ b/src/common/os_windows.h @@ -6,6 +6,17 @@ #ifndef _INC_OS_WINDOWS_H #define _INC_OS_WINDOWS_H +// Windows keeps this reduced limit for compatibility with older API calls and FAT32 path length... +// this limit includes the total length of the path, such as directories, subdirectories, and the filename, +// and it is constrained to 259 characters for the path itself, with the 260th character being reserved for the null terminator. +// On newer versions, Even though long paths are supported (via path prefix or application manifest - if Vista+), +// _MAX_PATH is not updated to reflect the new maximum path length (which can now be up to 32,767 characters). +// The long path feature requires the usage of the wide-character WinAPI calls and the \\?\ prefix. +#ifdef _MAX_PATH +# define SPHERE_MAX_PATH _MAX_PATH +#else +# define SPHERE_MAX_PATH 260 +#endif // _WIN32_WINNT version constants /* @@ -27,7 +38,6 @@ #define _WIN32_WINNT 0x0501 // By default we target Windows XP #endif - #undef FD_SETSIZE #define FD_SETSIZE 1024 // for max of n users ! default = 64 (FD: file descriptor) @@ -48,17 +58,20 @@ #define NOTEXTMETRIC #define NOWH -# ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) // Workaround to a possible VS compiler bug: instead of complaining if a macro expands to a "defined" macro, // it complains if a define macro contains the words "defined" in its name... -# pragma warning(push) -# pragma warning(disable: 5105) -# endif -# include -# ifdef _MSC_VER -# pragma warning(pop) -# endif +# pragma warning(push) +# pragma warning(disable: 5105) +#endif + +#include +#if defined(_MSC_VER) && !defined(__clang__) +# pragma warning(pop) +#endif + +#define SPHERE_CDECL __cdecl /* file handling definitions */ #define STDFUNC_FILENO(_x_) _get_osfhandle(_fileno(_x_)) @@ -76,16 +89,17 @@ #define STRICT // strict conversion of handles and pointers. #endif -#ifndef _MSC_VER // No Microsoft compiler - #define _cdecl __cdecl - +#ifdef __MINGW32__ // No Microsoft compiler // Not defined for mingw. #define LSTATUS int - typedef void (__cdecl *_invalid_parameter_handler)(const wchar_t *,const wchar_t *,const wchar_t *,unsigned int,uintptr_t); + typedef void (SPHERE_CDECL *_invalid_parameter_handler)(const wchar_t *,const wchar_t *,const wchar_t *,unsigned int,uintptr_t); // Stuctured exception handling windows api not implemented on mingw. #define __except(P) catch(int) #endif // _MSC_VER +#define SLEEP Sleep +#define TIMEZONE _timezone + const OSVERSIONINFO * Sphere_GetOSInfo() noexcept; diff --git a/src/common/resource/CResourceHolder.cpp b/src/common/resource/CResourceHolder.cpp index 621628f1a..ea3322cfa 100644 --- a/src/common/resource/CResourceHolder.cpp +++ b/src/common/resource/CResourceHolder.cpp @@ -104,13 +104,13 @@ CResourceScript * CResourceHolder::AddResourceFile( lpctstr pszName ) ASSERT(pszName != nullptr); // Is this really just a dir name ? - if (strlen(pszName) >= _MAX_PATH) + if (strlen(pszName) >= SPHERE_MAX_PATH) throw CSError(LOGL_ERROR, 0, "Filename too long!"); - tchar szName[_MAX_PATH]; + tchar szName[SPHERE_MAX_PATH]; Str_CopyLimitNull(szName, pszName, sizeof(szName)); - tchar szTitle[_MAX_PATH]; + tchar szTitle[SPHERE_MAX_PATH]; lpctstr ptcTitle = CScript::GetFilesTitle(szName); ASSERT_ALWAYS(strlen(ptcTitle) < sizeof(szTitle)); Str_CopyLimitNull(szTitle, ptcTitle, sizeof(szTitle)); @@ -125,8 +125,8 @@ CResourceScript * CResourceHolder::AddResourceFile( lpctstr pszName ) if ( pszExt == nullptr ) { // No file extension provided, so append .scp to the filename - Str_ConcatLimitNull( szName, SPHERE_SCRIPT, sizeof(szName) ); - Str_ConcatLimitNull( szTitle, SPHERE_SCRIPT, sizeof(szTitle) ); + 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"))) @@ -159,7 +159,7 @@ void CResourceHolder::AddResourceDir( lpctstr pszDirName ) if ( pszDirName[0] == '\0' ) return; - CSString sFilePath = CSFile::GetMergedFileName( pszDirName, "*" SPHERE_SCRIPT ); + CSString sFilePath = CSFile::GetMergedFileName( pszDirName, "*" SPHERE_SCRIPT_EXT ); CSFileList filelist; int iRet = filelist.ReadDir( sFilePath, false ); diff --git a/src/common/resource/CResourceHolder.h b/src/common/resource/CResourceHolder.h index a5c4b40d5..f4e5f8b33 100644 --- a/src/common/resource/CResourceHolder.h +++ b/src/common/resource/CResourceHolder.h @@ -71,11 +71,4 @@ class CResourceHolder : public CScriptObj CResourceHolder& operator=(const CResourceHolder& other) = delete; }; -inline lpctstr CResourceHolder::GetResourceBlockName( RES_TYPE restype ) // static -{ - if ( restype < 0 || restype >= RES_QTY ) - restype = RES_UNKNOWN; - return( sm_szResourceBlocks[restype] ); -} - #endif // _INC_CResourceHolder_H diff --git a/src/common/resource/CResourceRef.cpp b/src/common/resource/CResourceRef.cpp index 500a427ee..1697734d2 100644 --- a/src/common/resource/CResourceRef.cpp +++ b/src/common/resource/CResourceRef.cpp @@ -84,7 +84,6 @@ bool CResourceRefArray::r_LoadVal( CScript & s, RES_TYPE restype ) int iArgCount = Str_ParseCmds( pszCmd, ppBlocks, ARRAY_COUNT(ppBlocks)); for ( int i = 0; i < iArgCount; ++i ) { - std::shared_ptr pResourceDefRef; CResourceLink* pResourceLink = nullptr; pszCmd = ppBlocks[i]; @@ -128,7 +127,7 @@ bool CResourceRefArray::r_LoadVal( CScript & s, RES_TYPE restype ) if (pResourceLink == nullptr) { fRet = false; - DEBUG_ERR(("Unknown '%s' Resource '%s'\n", CResourceHolder::GetResourceBlockName(restype), pszCmd)); + g_Log.EventError("Unknown '%s' Resource '%s'\n", CResourceHolder::GetResourceBlockName(restype), pszCmd); } } @@ -198,6 +197,13 @@ size_t CResourceRefArray::FindResourceName( RES_TYPE restype, lpctstr ptcKey ) c return FindResourceID(pResourceLink->GetResourceID()); } +lpctstr CResourceHolder::GetResourceBlockName( RES_TYPE restype ) // static +{ + if ( restype < 0 || restype >= RES_QTY ) + restype = RES_UNKNOWN; + return sm_szResourceBlocks[restype]; +} + void CResourceRefArray::r_Write( CScript & s, lpctstr ptcKey ) const { ADDTOCALLSTACK_DEBUG("CResourceRefArray::r_Write"); diff --git a/src/common/resource/sections/CDialogDef.cpp b/src/common/resource/sections/CDialogDef.cpp index f59e75719..ae31f1abb 100644 --- a/src/common/resource/sections/CDialogDef.cpp +++ b/src/common/resource/sections/CDialogDef.cpp @@ -144,25 +144,25 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t lptstr ptcArgs = s.GetArgStr(); //g_Log.EventDebug("Dialog index %d, KEY %s ARG %s.\n", index, ptcKey, ptcArgs); - const auto _SkipAll = [](lptstr& ptcArgs) noexcept -> void + const auto _SkipAll = [](lptstr& ptcArgs_) noexcept -> void { - SKIP_SEPARATORS(ptcArgs); - GETNONWHITESPACE(ptcArgs); + SKIP_SEPARATORS(ptcArgs_); + GETNONWHITESPACE(ptcArgs_); }; - const auto _CalcRelative = [](lptstr& ptcArgs, int &iCoordBase) -> int + const auto _CalcRelative = [](lptstr& ptcArgs_, int &iCoordBase_) -> int { int c; - if ( *ptcArgs == '-' && IsSpace(ptcArgs[1])) - c = iCoordBase, ++ptcArgs; - else if ( *ptcArgs == '+' ) - c = iCoordBase + Exp_GetSingle( ++ptcArgs ); - else if ( *ptcArgs == '-' ) - c = iCoordBase - Exp_GetSingle( ++ptcArgs ); - else if ( *ptcArgs == '*' ) - iCoordBase = c = iCoordBase + Exp_GetSingle( ++ptcArgs ); + if ( *ptcArgs_ == '-' && IsSpace(ptcArgs_[1])) + c = iCoordBase_, ++ptcArgs_; + else if ( *ptcArgs_ == '+' ) + c = iCoordBase_ + Exp_GetSingle( ++ptcArgs_ ); + else if ( *ptcArgs_ == '-' ) + c = iCoordBase_ - Exp_GetSingle( ++ptcArgs_ ); + else if ( *ptcArgs_ == '*' ) + iCoordBase_ = c = iCoordBase_ + Exp_GetSingle( ++ptcArgs_ ); else - c = Exp_GetSingle( ptcArgs ); + c = Exp_GetSingle( ptcArgs_ ); return c; }; diff --git a/src/common/resource/sections/CSkillDef.cpp b/src/common/resource/sections/CSkillDef.cpp index e64bebc22..621a75f53 100644 --- a/src/common/resource/sections/CSkillDef.cpp +++ b/src/common/resource/sections/CSkillDef.cpp @@ -191,10 +191,10 @@ bool CSkillDef::r_LoadVal( CScript &s ) m_vcDelay.Load( s.GetArgStr()); break; case SKC_FLAGS: - m_dwFlags = s.GetArgVal(); + m_dwFlags = s.GetArgDWVal(); break; case SKC_GROUP: - m_dwGroup = s.GetArgVal(); + m_dwGroup = s.GetArgDWVal(); break; case SKC_EFFECT: m_vcEffect.Load( s.GetArgStr()); @@ -246,4 +246,4 @@ bool CSkillDef::r_LoadVal( CScript &s ) EXC_ADD_SCRIPT; EXC_DEBUG_END; return false; -} \ No newline at end of file +} diff --git a/src/common/resource/sections/CWebPageDef.cpp b/src/common/resource/sections/CWebPageDef.cpp index 9816ad0ef..518f17875 100644 --- a/src/common/resource/sections/CWebPageDef.cpp +++ b/src/common/resource/sections/CWebPageDef.cpp @@ -390,8 +390,8 @@ void CWebPageDef::WebPageLog() lpctstr pszExt = FileRead.GetFileExt(); - tchar szName[ _MAX_PATH ]; - Str_CopyLimitNull( szName, m_sDstFilePath, _MAX_PATH); + tchar szName[ SPHERE_MAX_PATH ]; + Str_CopyLimitNull( szName, m_sDstFilePath, SPHERE_MAX_PATH); szName[ m_sDstFilePath.GetLength() - strlen(pszExt) ] = '\0'; CSTime datetime = CSTime::GetCurrentTime(); @@ -780,7 +780,7 @@ void CWebPageDef::ServPage( CClient * pClient, tchar * pszPage, CSTime * pdateIf ADDTOCALLSTACK("CWebPageDef::ServPage"); // make sure this is a valid format for the request. - tchar szPageName[_MAX_PATH]; + tchar szPageName[SPHERE_MAX_PATH]; Str_GetBare( szPageName, pszPage, sizeof(szPageName), "!\"#$%&()*,:;<=>?[]^{|}-+'`" ); int iError = 404; diff --git a/src/common/sphere_library/CSFile.cpp b/src/common/sphere_library/CSFile.cpp index 27788a0a4..b652df0ec 100644 --- a/src/common/sphere_library/CSFile.cpp +++ b/src/common/sphere_library/CSFile.cpp @@ -434,18 +434,21 @@ CSString CSFile::GetMergedFileName( lpctstr pszBase, lpctstr pszName ) // static ADDTOCALLSTACK("CSFile::GetMergedFileName"); // Merge path and file name. - tchar ptcFilePath[ _MAX_PATH ]; + tchar ptcFilePath[ SPHERE_MAX_PATH ]; size_t len = 0; if ( pszBase && pszBase[0] ) { - len = Str_CopyLimitNull( ptcFilePath, pszBase, sizeof(ptcFilePath) - 1); // eventually, leave space for the (back)slash - if (len && ptcFilePath[len - 1] != '\\' && ptcFilePath[len - 1] != '/') + len = Str_CopyLimitNull(ptcFilePath, pszBase, sizeof(ptcFilePath) - 1); // eventually, leave space for the (back)slash + if ((len > 1) && (ptcFilePath[len - 1] != '\\') && (ptcFilePath[len - 1] != '/')) { + // Append the slash/backslash, overwriting the string terminator. #ifdef _WIN32 - strcat(ptcFilePath, "\\"); + ptcFilePath[len] = '\\'; #else - strcat(ptcFilePath, "/"); + ptcFilePath[len] = '/'; #endif + // Add back the string terminator. We're sure we won't overflow since we passed sizeof(ptcFilePath) - 1. + ptcFilePath[len + 1] = '\0'; } } else diff --git a/src/common/sphere_library/CSFileList.cpp b/src/common/sphere_library/CSFileList.cpp index bf3c46457..997540b25 100644 --- a/src/common/sphere_library/CSFileList.cpp +++ b/src/common/sphere_library/CSFileList.cpp @@ -56,7 +56,7 @@ int CSFileList::ReadDir( lpctstr pszFileDir, bool bShowError ) { ADDTOCALLSTACK("CSFileList::ReadDir"); // NOTE: It seems NOT to like the trailing \ alone - tchar szFileDir[_MAX_PATH]; + tchar szFileDir[SPHERE_MAX_PATH]; size_t len = Str_CopyLen(szFileDir, pszFileDir); #ifdef _WIN32 if ( len > 0 ) @@ -73,7 +73,7 @@ int CSFileList::ReadDir( lpctstr pszFileDir, bool bShowError ) if ( lFind == -1 ) #else - char szFilename[_MAX_PATH]; + char szFilename[SPHERE_MAX_PATH]; // Need to strip out the *.scp part for ( size_t i = len; i > 0; --i ) { @@ -110,15 +110,15 @@ int CSFileList::ReadDir( lpctstr pszFileDir, bool bShowError ) if ( fileinfo->d_name[0] == '.' ) continue; - const int ret = snprintf(szFilename, _MAX_PATH, "%s%s", szFileDir, fileinfo->d_name); - szFilename[_MAX_PATH - 1] = '\0'; - if ((ret < 0) || (ret > _MAX_PATH - 1)) + const int ret = snprintf(szFilename, SPHERE_MAX_PATH, "%s%s", szFileDir, fileinfo->d_name); + szFilename[SPHERE_MAX_PATH - 1] = '\0'; + if ((ret < 0) || (ret > SPHERE_MAX_PATH - 1)) { g_Log.EventError("Unable to concatenate the path (too long). Current file '%s'.\n", fileinfo->d_name); break; } len = strlen(szFilename); - if ( len > 4 && !strcmpi(&szFilename[len - 4], SPHERE_SCRIPT) ) + if ( (len > SPHERE_SCRIPT_EXT_LEN) && !strcmpi(&szFilename[len - SPHERE_SCRIPT_EXT_LEN], SPHERE_SCRIPT_EXT) ) AddTail(fileinfo->d_name); #endif } diff --git a/src/common/sphere_library/CSFileText.cpp b/src/common/sphere_library/CSFileText.cpp index 686437c9f..adbfb8be4 100644 --- a/src/common/sphere_library/CSFileText.cpp +++ b/src/common/sphere_library/CSFileText.cpp @@ -155,7 +155,7 @@ bool CSFileText::IsEOF() const } -int _cdecl CSFileText::_Printf(lpctstr pFormat, ...) +int CSFileText::_Printf(lpctstr pFormat, ...) { ADDTOCALLSTACK("CSFileText::_Printf"); ASSERT(pFormat); @@ -167,7 +167,7 @@ int _cdecl CSFileText::_Printf(lpctstr pFormat, ...) return iRet; } -int _cdecl CSFileText::Printf( lpctstr pFormat, ... ) +int CSFileText::Printf( lpctstr pFormat, ... ) { ADDTOCALLSTACK("CSFileText::Printf"); ASSERT(pFormat); diff --git a/src/common/sphere_library/CSFileText.h b/src/common/sphere_library/CSFileText.h index f8912cad7..8727491b4 100644 --- a/src/common/sphere_library/CSFileText.h +++ b/src/common/sphere_library/CSFileText.h @@ -84,8 +84,8 @@ public: virtual bool IsEOF() const; * @param ... argument list. * @return total chars of the output. */ -protected: int _cdecl _Printf( lpctstr pFormat, ... ) __printfargs(2,3); -public: int _cdecl Printf(lpctstr pFormat, ...) __printfargs(2, 3); +protected: int _Printf( lpctstr pFormat, ... ) SPHERE_PRINTFARGS(2,3); +public: int Printf(lpctstr pFormat, ...) SPHERE_PRINTFARGS(2, 3); /** * @brief Reads data from the file. * @param pBuffer buffer where store the readed data. diff --git a/src/common/sphere_library/CSObjList.cpp b/src/common/sphere_library/CSObjList.cpp index 35fafb63d..a66cad392 100644 --- a/src/common/sphere_library/CSObjList.cpp +++ b/src/common/sphere_library/CSObjList.cpp @@ -56,18 +56,26 @@ CSObjListRec * CSObjList::GetContentAt( size_t index ) const void CSObjList::ClearContainer() { // delete all entries. + bool fSuccess = false; EXC_TRY("Deleting objects scheduled for deletion"); for (;;) // iterate the list. { CSObjListRec * pRec = GetContainerHead(); - if ( pRec == nullptr ) - break; + if ( pRec == nullptr ) { + fSuccess = true; + break; + } ASSERT( pRec->GetParent() == this ); delete pRec; } EXC_CATCH; - m_uiCount = 0; + if (fSuccess) { + ASSERT(m_uiCount == 0); + } + else { + m_uiCount = 0; + } m_pHead = nullptr; m_pTail = nullptr; } diff --git a/src/common/sphere_library/CSString.cpp b/src/common/sphere_library/CSString.cpp index 3e96295a9..a0cc3b665 100644 --- a/src/common/sphere_library/CSString.cpp +++ b/src/common/sphere_library/CSString.cpp @@ -103,9 +103,9 @@ bool CSString::IsValid() const noexcept int CSString::Resize(int iNewLength, bool fPreciseSize) { - if (iNewLength >= m_iMaxLength) + const bool fValid = IsValid(); + if ((iNewLength >= m_iMaxLength) || !fValid) { - const bool fValid = IsValid(); #ifdef DEBUG_STRINGS gMemAmount -= m_iMaxLength; #endif @@ -131,9 +131,9 @@ int CSString::Resize(int iNewLength, bool fPreciseSize) { const int iMinLength = 1 + minimum(iNewLength, m_iLength); Str_CopyLimitNull(pNewData, m_pchData, iMinLength); - delete[] m_pchData; } - pNewData[m_iLength] = '\0'; + if (fValid) + delete[] m_pchData; m_pchData = pNewData; } ASSERT(m_pchData); @@ -142,6 +142,16 @@ int CSString::Resize(int iNewLength, bool fPreciseSize) return m_iLength; } +void CSString::SetValFalse() +{ + Copy("0"); +} + +void CSString::SetValTrue() +{ + Copy("1"); +} + // CSString:: Element access @@ -251,7 +261,7 @@ CSString& CSString::operator=(CSString&& s) noexcept // CSString:: Formatting -void _cdecl CSString::Format(lpctstr pStr, ...) +void CSString::Format(lpctstr pStr, ...) { va_list vargs; va_start(vargs, pStr); @@ -398,6 +408,65 @@ void CSString::FormatU64Val(uint64 uiVal) // CSString:: String operations +tchar CSString::GetAt(int nIndex) const +{ + ASSERT(nIndex >= 0); + ASSERT(nIndex <= m_iLength); // Allow to get the null char. + return m_pchData[nIndex]; +} + +tchar& CSString::ReferenceAt(int nIndex) +{ + ASSERT(nIndex >= 0); + ASSERT(nIndex < m_iLength); + return m_pchData[nIndex]; +} + +void CSString::MakeUpper() noexcept +{ + _strupr(m_pchData); +} + +void CSString::MakeLower() noexcept +{ + _strlwr(m_pchData); +} + +void CSString::Reverse() noexcept +{ + Str_Reverse(m_pchData); +} + +int CSString::Compare(lpctstr pStr) const noexcept +{ + return strcmp(m_pchData, pStr); +} + +int CSString::CompareNoCase(lpctstr pStr) const noexcept +{ + return strcmpi(m_pchData, pStr); +} + +int CSString::indexOf(tchar c) noexcept +{ + return indexOf(c, 0); +} + +int CSString::indexOf(const CSString& str) noexcept +{ + return indexOf(str, 0); +} + +int CSString::lastIndexOf(tchar c) noexcept +{ + return lastIndexOf(c, 0); +} + +int CSString::lastIndexOf(const CSString& str) noexcept +{ + return lastIndexOf(str, 0); +} + int CSString::indexOf(tchar c, int offset) noexcept { if ((offset < 0) || !IsValid()) diff --git a/src/common/sphere_library/CSString.h b/src/common/sphere_library/CSString.h index d68e70329..26aedb428 100644 --- a/src/common/sphere_library/CSString.h +++ b/src/common/sphere_library/CSString.h @@ -6,12 +6,12 @@ #ifndef _INC_CSSTRING_H #define _INC_CSSTRING_H +#include "../common.h" #ifdef __MINGW32__ #include #endif // __MINGW32__ #include // needed for va_list -#include "sstring.h" /** * @brief Custom String implementation. @@ -195,7 +195,7 @@ class CSString * @return character in position nIndex. */ [[nodiscard]] - inline tchar GetAt(int nIndex) const; + tchar GetAt(int nIndex) const; /** * @brief Gets the reference to character a specified position (0 based). @@ -203,7 +203,7 @@ class CSString * @return reference to character in position nIndex. */ [[nodiscard]] - inline tchar& ReferenceAt(int nIndex); + tchar& ReferenceAt(int nIndex); /** * @brief Puts a character in a specified position (0 based). @@ -214,6 +214,16 @@ class CSString */ void SetAt(int nIndex, tchar ch); + /** + * @brief Sets the string to an integer false value (0), usually handy for scripts return values. + */ + void SetValFalse(); + + /** + * @brief Sets the string to an integer true value (1), usually handy for scripts return values. + */ + void SetValTrue(); + ///@} /** @name Modifiers: @@ -273,17 +283,17 @@ class CSString /** * @brief Changes the capitalization of CSString to upper. */ - inline void MakeUpper() noexcept; + void MakeUpper() noexcept; /** * @brief Changes the capitalization of CSString to lower. */ - inline void MakeLower() noexcept; + void MakeLower() noexcept; /** * @brief Reverses the CSString. */ - inline void Reverse() noexcept; + void Reverse() noexcept; ///@} @@ -297,7 +307,7 @@ class CSString * @param pStr formatted string. * @param ... list of values. */ - void _cdecl Format(lpctstr pStr, ...) __printfargs(2, 3); + void Format(lpctstr pStr, ...) SPHERE_PRINTFARGS(2, 3); /** * @brief Join a formated string (printf like) with values and copy into this. @@ -492,7 +502,7 @@ class CSString * @return <0 if the first character that not match has lower value in CSString than in pStr. 0 if the contents of both are equal. >0 if the first character that does not match has greater value in CSString than pStr. */ [[nodiscard]] - inline int Compare(lpctstr pStr) const noexcept; + int Compare(lpctstr pStr) const noexcept; /** * @brief Compares the CSString to string pStr (case insensitive) (_strcmpi wrapper). @@ -505,7 +515,7 @@ class CSString * @return <0 if the first character that not match has lower value in CSString than in pStr. 0 if the contents of both are equal. >0 if the first character that does not match has greater value in CSString than pStr. */ [[nodiscard]] - inline int CompareNoCase(lpctstr pStr) const noexcept; + int CompareNoCase(lpctstr pStr) const noexcept; /** * @brief Gets the internal pointer. @@ -523,7 +533,7 @@ class CSString * @return position of the character in CSString if any, -1 otherwise. */ [[nodiscard]] - inline int indexOf(tchar c) noexcept; + int indexOf(tchar c) noexcept; /** * @brief Look for the first occurence of c in CSString from a position. @@ -540,7 +550,7 @@ class CSString * @return position of the substring in CSString if any, -1 otherwise. */ [[nodiscard]] - inline int indexOf(const CSString& str) noexcept; + int indexOf(const CSString& str) noexcept; /** * @brief Look for the first occurence of a substring in CSString from a position. @@ -557,7 +567,7 @@ class CSString * @return position of the character in CSString if any, -1 otherwise. */ [[nodiscard]] - inline int lastIndexOf(tchar c) noexcept; + int lastIndexOf(tchar c) noexcept; /** * @brief Look for the last occurence of c in CSString from a position to the end. @@ -574,7 +584,7 @@ class CSString * @return position of the substring in CSString if any, -1 otherwise. */ [[nodiscard]] - inline int lastIndexOf(const CSString& str) noexcept; + int lastIndexOf(const CSString& str) noexcept; /** * @brief Look for the last occurence of a substring in CSString from a position to the end. @@ -605,7 +615,12 @@ CSString::CSString(CSString&& s) noexcept : *this = std::move(s); // Call the move assignment operator } -CSString CSString::EmptyNew() +CSString::operator lpctstr() const noexcept +{ + return GetBuffer(); +} + +CSString CSString::EmptyNew() // static { return CSString(false); } @@ -625,90 +640,27 @@ int CSString::GetCapacity() const noexcept return m_iMaxLength; } -tchar CSString::operator[](int nIndex) const -{ - return GetAt(nIndex); -} - -tchar& CSString::operator[](int nIndex) -{ - return ReferenceAt(nIndex); -} - -tchar CSString::GetAt(int nIndex) const -{ - ASSERT(nIndex >= 0); - ASSERT(nIndex <= m_iLength); // Allow to get the null char. - return m_pchData[nIndex]; -} - -tchar& CSString::ReferenceAt(int nIndex) -{ - ASSERT(nIndex >= 0); - ASSERT(nIndex < m_iLength); - return m_pchData[nIndex]; -} - -void CSString::MakeUpper() noexcept -{ - _strupr(m_pchData); -} - -void CSString::MakeLower() noexcept -{ - _strlwr(m_pchData); -} - -void CSString::Reverse() noexcept -{ - Str_Reverse(m_pchData); -} - -CSString::operator lpctstr() const noexcept -{ - return GetBuffer(); -} - -int CSString::Compare(lpctstr pStr) const noexcept -{ - return strcmp(m_pchData, pStr); -} - -int CSString::CompareNoCase(lpctstr pStr) const noexcept -{ - return strcmpi(m_pchData, pStr); -} - lpctstr CSString::GetBuffer() const noexcept { - return m_pchData; + return m_pchData; } /* lptstr CSString::GetBuffer() noexcept { - return m_pchData; + return m_pchData; } */ -int CSString::indexOf(tchar c) noexcept -{ - return indexOf(c, 0); -} - -int CSString::indexOf(const CSString& str) noexcept +tchar CSString::operator[](int nIndex) const { - return indexOf(str, 0); + return GetAt(nIndex); } -int CSString::lastIndexOf(tchar c) noexcept +tchar& CSString::operator[](int nIndex) { - return lastIndexOf(c, 0); + return ReferenceAt(nIndex); } -int CSString::lastIndexOf(const CSString& str) noexcept -{ - return lastIndexOf(str, 0); -} #endif // _INC_CSSTRING_H diff --git a/src/common/sphere_library/CSTime.cpp b/src/common/sphere_library/CSTime.cpp index bc1ee97d4..7985f29a4 100644 --- a/src/common/sphere_library/CSTime.cpp +++ b/src/common/sphere_library/CSTime.cpp @@ -5,7 +5,6 @@ // #include "../../sphere/threads.h" -#include "../CLog.h" #include "sstring.h" #include "CSTime.h" #include @@ -26,7 +25,7 @@ // We don't have GetSupportedTickCount on Windows versions previous to Vista/Windows Server 2008. We need to check for overflows // (which occurs every 49.7 days of continuous running of the server, if measured with GetTickCount, every 7 years // with GetSupportedTickCount) manually every time we compare two values. -# if _MSC_VER +# if MSVC_COMPILER # pragma warning(push) # pragma warning(disable: 28159) # endif @@ -35,7 +34,7 @@ # else static inline llong GetSupportedTickCount() noexcept { return (llong)GetTickCount64(); } # endif -# if _MSC_VER +# if MSVC_COMPILER # pragma warning(pop) # endif #endif @@ -173,7 +172,7 @@ static std::tm safe_localtime(const time_t t) noexcept #if defined(__unix__) || defined(__APPLE__) || defined(_POSIX_VERSION) localtime_r(&t, &atm); -#elif defined(_MSC_VER) +#elif defined(MSVC_RUNTIME) || defined(__MINGW32__) localtime_s(&atm, &t); #elif defined(__STDC_LIB_EXT1__) localtime_s(&t, &atm); @@ -221,7 +220,7 @@ static std::tm safe_gmtime(const time_t t) noexcept #if defined(__unix__) || defined(__APPLE__) || defined(_POSIX_VERSION) gmtime_r(&t, &atm); -#elif defined(_MSC_VER) +#elif defined(MSVC_RUNTIME) || defined(__MINGW32__) gmtime_s(&atm, &t); #elif defined(__STDC_LIB_EXT1__) gmtime_s(&t, &atm); @@ -256,8 +255,8 @@ std::tm CSTime::GetLocalTmPlain() const noexcept #define maxTimeBufferSize 128 #endif -#if defined(_WIN32) && defined (_MSC_VER) -static void __cdecl invalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, uint line, uintptr_t pReserved) +#if defined(_WIN32) && defined (MSVC_COMPILER) +static void SPHERE_CDECL invalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, uint line, uintptr_t pReserved) { // bad format has been specified UnreferencedParameter(expression); @@ -276,12 +275,12 @@ static void FormatDateTime(tchar * pszTemp, lpctstr pszFormat, const struct tm * ASSERT(ptmTemp != nullptr); #ifdef _WIN32 -#ifdef _MSC_VER +#ifdef MSVC_COMPILER // on windows we need to set the invalid parameter handler, or else the program will terminate when a bad format is encountered _invalid_parameter_handler newHandler, oldHandler; newHandler = static_cast<_invalid_parameter_handler>(invalidParameterHandler); oldHandler = _set_invalid_parameter_handler(newHandler); -#endif // _MSC_VER +#endif // MSVC_COMPILER try { #endif // _WIN32 @@ -298,10 +297,10 @@ static void FormatDateTime(tchar * pszTemp, lpctstr pszFormat, const struct tm * pszTemp[0] = '\0'; } -#ifdef _MSC_VER +#ifdef MSVC_COMPILER // restore previous parameter handler _set_invalid_parameter_handler(oldHandler); -#endif // _MSC_VER +#endif // MSVC_COMPILER #endif // _WIN32 } diff --git a/src/common/sphere_library/CSWindow.cpp b/src/common/sphere_library/CSWindow.cpp index 09c0da251..2b1dca8dd 100644 --- a/src/common/sphere_library/CSWindow.cpp +++ b/src/common/sphere_library/CSWindow.cpp @@ -252,7 +252,7 @@ void CWinApp::InitInstance(LPCTSTR pszAppName, HINSTANCE hInstance, LPTSTR lpszC m_hInstance = hInstance; m_lpCmdLine = lpszCmdLine; - char szFileName[_MAX_PATH]; + char szFileName[SPHERE_MAX_PATH]; if (! GetModuleFileName(m_hInstance, szFileName, sizeof(szFileName))) return; m_pszExeName = szFileName; diff --git a/src/common/sphere_library/sptr.h b/src/common/sphere_library/sptr.h index 66bc169bc..ce9b396cc 100644 --- a/src/common/sphere_library/sptr.h +++ b/src/common/sphere_library/sptr.h @@ -1,14 +1,21 @@ #ifndef _INC_SPTR_H #define _INC_SPTR_H -#ifndef _MSC_VER +// On Windows, Clang with MSVC runtime defines _MSC_VER! (But also __clang__). +#ifndef NON_MSVC_COMPILER +# if !defined(_MSC_VER) || defined(__clang__) +# define NON_MSVC_COMPILER 1 +# endif +#endif + +#if NON_MSVC_COMPILER # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif #include -#ifndef _MSC_VER +#if NON_MSVC_COMPILER # pragma GCC diagnostic pop #endif diff --git a/src/common/sphere_library/sresetevents.cpp b/src/common/sphere_library/sresetevents.cpp index aaa245093..bc4d875c3 100644 --- a/src/common/sphere_library/sresetevents.cpp +++ b/src/common/sphere_library/sresetevents.cpp @@ -39,7 +39,11 @@ void AutoResetEvent::wait(uint timeout) // 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; } diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index 6311a1930..1c9953f30 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -4,7 +4,7 @@ #include "../CExpression.h" -#if defined(_MSC_VER) +#ifdef MSVC_COMPILER #include #pragma warning( push ) #pragma warning ( disable : ALL_CODE_ANALYSIS_WARNINGS ) @@ -22,7 +22,7 @@ #include -#ifdef _MSC_VER +#ifdef MSVC_COMPILER #pragma warning( pop ) #else #pragma GCC diagnostic pop @@ -482,7 +482,7 @@ size_t strlen_mb(const char* ptr) size_t Str_LengthUTF8(const char* strInUTF8MB) noexcept { size_t len; // number of characters in the string -#ifdef _MSC_VER +#ifdef MSVC_RUNTIME mbstowcs_s(&len, nullptr, 0, strInUTF8MB, 0); // includes null terminator len -= 1; #else @@ -1261,7 +1261,7 @@ MATCH_TYPE Str_Match(lpctstr pPattern, lpctstr pText) noexcept return MATCH_VALID; } -#ifdef _MSC_VER +#ifdef MSVC_COMPILER // /GL + /LTCG flags inline in linking phase this function, but probably in a wrong way, so that // something gets corrupted on the memory and an exception is generated later #pragma auto_inline(off) @@ -1409,7 +1409,7 @@ bool Str_Parse(tchar * pLine, tchar ** ppArg, lpctstr pszSep) noexcept return true; } -#ifdef _MSC_VER +#ifdef MSVC_COMPILER #pragma auto_inline(on) #endif @@ -1761,7 +1761,7 @@ size_t UTF8MBSTR::ConvertStringToUTF8(lpctstr strIn, char*& strOutUTF8MB) noexce strOutUTF8MB = new char[len + 1](); #if defined(_WIN32) && defined(UNICODE) -#ifdef _MSC_VER +#ifdef MSVC_RUNTIME size_t aux = 0; wcstombs_s(&aux, strOutUTF8MB, len + 1, strIn, len); #else @@ -1783,7 +1783,7 @@ size_t UTF8MBSTR::ConvertUTF8ToString(const char* strInUTF8MB, lptstr& strOut) n #if defined(_WIN32) && defined(UNICODE) // tchar is wchar_t -#ifdef _MSC_VER +#ifdef MSVC_RUNTIME size_t aux = 0; mbstowcs_s(&aux, strInUTF8MB, len + 1, strInUTF8MB, len); #else diff --git a/src/common/sphere_library/sstringobjs.cpp b/src/common/sphere_library/sstringobjs.cpp index 951b88923..765aeb009 100644 --- a/src/common/sphere_library/sstringobjs.cpp +++ b/src/common/sphere_library/sstringobjs.cpp @@ -34,7 +34,7 @@ static tchar* getUnsafeStringBuffer() noexcept tchar* Str_GetTemp() noexcept { - IThread *pThreadState = ThreadHolder::get().current(); + AbstractThread *pThreadState = ThreadHolder::get().current(); if (pThreadState) return static_cast(pThreadState)->allocateBuffer(); return getUnsafeStringBuffer(); diff --git a/src/common/sphere_library/stypecast.cpp b/src/common/sphere_library/stypecast.cpp new file mode 100644 index 000000000..f0e0c3de4 --- /dev/null +++ b/src/common/sphere_library/stypecast.cpp @@ -0,0 +1,11 @@ +#include "../CLog.h" +#include "stypecast.h" + +namespace detail_stypecast { + +void LogEventWarnWrapper(const char* warn_str) +{ + g_Log.EventWarn(warn_str); +} + +} diff --git a/src/common/sphere_library/stypecast.h b/src/common/sphere_library/stypecast.h index d450be93b..d4143ec2a 100644 --- a/src/common/sphere_library/stypecast.h +++ b/src/common/sphere_library/stypecast.h @@ -5,6 +5,16 @@ #include #include +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)) + + // Helper template to work on enum types. // Primary template for non-enum types, T will simply be itself @@ -29,19 +39,19 @@ using underlying_or_self_t = typename underlying_or_self::type; // Use this as a double check, to be sure at compile time that the two variables have the same size and sign. template [[nodiscard]] -constexpr Tout n_alias_cast(const Tin a) noexcept +constexpr Tout n_alias_cast(const Tin source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable is not an arithmetictype."); static_assert(std::is_arithmetic_v, "Output variable is not an arithmetic 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."); - return static_cast(a); + return static_cast(source_val); } template [[nodiscard]] -constexpr Tout enum_alias_cast(const Tin a) noexcept +constexpr Tout enum_alias_cast(const Tin source_val) noexcept { static_assert(std::is_enum_v || std::is_enum_v, "Nor input nor output variables are an enum."); static_assert(std::is_arithmetic_v || std::is_enum_v, "Input variable is not a numeric type."); @@ -67,55 +77,7 @@ constexpr Tout enum_alias_cast(const Tin a) noexcept "Casting unsigned to signed."); */ - return static_cast(a); -} - - -/* Unsigned (and size_t) to signed, clamping. */ - -[[nodiscard]] constexpr -int8 i8_from_u8_clamping(const uint8 a) noexcept -{ - return (a > (uint8_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int8_t)a; -} - -[[nodiscard]] constexpr -int16 i16_from_u16_clamping(const uint16 a) noexcept -{ - return (a > (uint16_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int16_t)a; -} - -[[nodiscard]] constexpr -int32 i32_from_u32_clamping(const uint32 a) noexcept -{ - return (a > (uint32_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int32_t)a; -} - -[[nodiscard]] constexpr -int64 i64_from_u64_clamping(const uint64 a) noexcept -{ - return (a > (uint64_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int64_t)a; -} - -[[nodiscard]] constexpr -int32 i32_from_usize_clamping(const size_t a) noexcept -{ - return (a > (size_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int32_t)a; -} - -[[nodiscard]] constexpr -int64 i64_from_usize_clamping(const size_t a) noexcept -{ - return (a > (size_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int64_t)a; -} - -[[nodiscard]] constexpr -uint32 u32_from_usize_clamping(const size_t a) noexcept -{ - if constexpr (sizeof(size_t) == 8) - return (a > (size_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (uint32_t)a; - else - return a; + return static_cast(source_val); } @@ -131,7 +93,7 @@ uint32 u32_from_usize_clamping(const size_t a) noexcept // Promote to the corresponding 32 bits numeric type a smaller numeric variable. template [[nodiscard]] -constexpr auto n_promote_n32(const T a) noexcept +constexpr auto n_promote_n32(const T source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable has not a arithmetic type."); static_assert(std::is_integral_v || (std::is_floating_point_v && std::is_signed_v), "Unsigned floating point numbers are unsupported by the language standard"); @@ -139,25 +101,25 @@ constexpr auto n_promote_n32(const T a) noexcept if constexpr (std::is_signed_v) { if constexpr (std::is_floating_point_v) - return static_cast(a); - return static_cast(a); + return static_cast(source_val); + return static_cast(source_val); } else - return static_cast(a); + return static_cast(source_val); } template [[nodiscard]] -constexpr auto enum_promote_n32(const T a) noexcept +constexpr auto enum_promote_n32(const T source_val) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); - return n_promote_n32(static_cast>(a)); + return n_promote_n32(static_cast>(source_val)); } // Promote to the corresponding 64 bits numeric type a smaller numeric variable. template [[nodiscard]] -constexpr auto n_promote_n64(const T a) noexcept +constexpr auto n_promote_n64(const T source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable has not a arithmetic type."); static_assert(std::is_integral_v || (std::is_floating_point_v && std::is_signed_v), "Unsigned floating point numbers are unsupported by the language standard"); @@ -165,25 +127,25 @@ constexpr auto n_promote_n64(const T a) noexcept if constexpr (std::is_signed_v) { if constexpr (std::is_floating_point_v) - return static_cast(a); - return static_cast(a); + return static_cast(source_val); + return static_cast(source_val); } else - return static_cast(a); + return static_cast(source_val); } template [[nodiscard]] -constexpr auto enum_promote_n64(const T a) noexcept +constexpr auto enum_promote_n64(const T source_val) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); - return n_promote_n64(static_cast>(a)); + return n_promote_n64(static_cast>(source_val)); } // Narrow a 64 bits number to a 32 bits number, discarding any upper exceeding bytes. template [[nodiscard]] -constexpr auto n64_narrow_n32(const T a) noexcept +constexpr auto n64_narrow_n32(const T source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable has not a arithmetic type."); static_assert(std::is_integral_v || (std::is_floating_point_v && std::is_signed_v), "Unsigned floating point numbers are unsupported by the language standard"); @@ -194,40 +156,55 @@ constexpr auto n64_narrow_n32(const T a) noexcept if constexpr (std::is_signed_v) { if constexpr (std::is_floating_point_v) - return static_cast(a & umask); - return static_cast(a & umask); + return static_cast(source_val & umask); + return static_cast(source_val & umask); } else - return static_cast(a & umask); + return static_cast(source_val & umask); } -// Narrow a 64 bits number to a 32 bits number and ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow. +// Narrow a 64 bits number to a 32 bits number. +// 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 a) + auto n64_narrow_n32_checked(const T source_val, bool should_assert) { - if constexpr (std::is_signed_v) { - ASSERT(a <= std::numeric_limits::max()); + if (should_assert) + { + if constexpr (std::is_signed_v) { + ASSERT(source_val <= std::numeric_limits::max()); + } + else { + ASSERT(source_val <= std::numeric_limits::max()); + } } - else { - ASSERT(a <= std::numeric_limits::max()); + else + { + if constexpr (std::is_signed_v) { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 to 32 bits signed integer will overflow.\n"); + } + else { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 to 32 bits unsigned integer will overflow.\n"); + } } - return n64_narrow_n32(a); + return n64_narrow_n32(source_val); } template [[nodiscard]] -constexpr auto enum64_narrow_n32_checked(const T a) noexcept +constexpr auto enum64_narrow_n32_checked(const T source_val, bool should_assert) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); - return n64_narrow_n32_checked(static_cast>(a)); + return n64_narrow_n32_checked(static_cast>(source_val, should_assert)); } - // Narrow a 64 bits number to a 16 bits number, discarding any upper exceeding bytes. template [[nodiscard]] -constexpr auto n64_narrow_n16(const T a) noexcept +constexpr auto n64_narrow_n16(const T source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable has not a arithmetic type."); static_assert(std::is_integral_v || (std::is_floating_point_v && std::is_signed_v), "Unsigned floating point numbers are unsupported by the language standard"); @@ -238,39 +215,56 @@ constexpr auto n64_narrow_n16(const T a) noexcept if constexpr (std::is_signed_v) { if constexpr (std::is_floating_point_v) - return static_cast(a & umask); - return static_cast(a & umask); + return static_cast(source_val & umask); + return static_cast(source_val & umask); } else - return static_cast(a & umask); + return static_cast(source_val & umask); } -// Narrow a 64 bits number to a 16 bits number and ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow. +// Narrow a 64 bits number to a 16 bits number. +// 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 a) + auto n64_narrow_n16_checked(const T source_val, bool should_assert) { - if constexpr (std::is_signed_v) { - ASSERT(a <= std::numeric_limits::max()); + if (should_assert) + { + if constexpr (std::is_signed_v) { + ASSERT(source_val <= std::numeric_limits::max()); + } + else { + ASSERT(source_val <= std::numeric_limits::max()); + } } - else { - ASSERT(a <= std::numeric_limits::max()); + else + { + if constexpr (std::is_signed_v) { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 to 16 bits signed integer will overflow.\n"); + } + else { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 to 16 bits unsigned integer will overflow.\n"); + } } - return n64_narrow_n16(a); + return n64_narrow_n16(source_val); } + template [[nodiscard]] -constexpr auto enum64_narrow_n16_checked(const T a) noexcept +constexpr auto enum64_narrow_n16_checked(const T source_val, bool should_assert) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); - return n64_narrow_n16_checked(static_cast>(a)); + return n64_narrow_n16_checked(static_cast>(source_val, should_assert)); } -// Narrow a 64 bits number to a 8 bits number, discarding any upper exceeding bytes. +// Narrow a 64 bits number to a 8 a number, discarding any upper exceeding bytes. template [[nodiscard]] -constexpr auto n64_narrow_n8(const T a) noexcept +constexpr auto n64_narrow_n8(const T source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable has not a arithmetic type"); static_assert(std::is_floating_point_v == false, "Corresponding 8-bit floating point type does not exist?"); @@ -280,37 +274,53 @@ constexpr auto n64_narrow_n8(const T a) noexcept // Since the narrowing can be implementation specific, here we decide that we take only the lower 8 bytes and discard the upper ones. constexpr uint64 umask = 0x0000'0000'0000'00FF; if constexpr (std::is_signed_v) - return static_cast(a & umask); + return static_cast(source_val & umask); else - return static_cast(a & umask); + return static_cast(source_val & umask); } -// Narrow a 64 bits number to a 8 bits number and ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow. +// Narrow a 64 bits number to a 8 bits number. +// 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 a) + auto n64_narrow_n8_checked(const T source_val, bool should_assert) { - if constexpr (std::is_signed_v) { - ASSERT(a <= std::numeric_limits::max()); + if (should_assert) + { + if constexpr (std::is_signed_v) { + ASSERT(source_val <= std::numeric_limits::max()); + } + else { + ASSERT(source_val <= std::numeric_limits::max()); + } } - else { - ASSERT(a <= std::numeric_limits::max()); + else + { + if constexpr (std::is_signed_v) { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 to 8 bits signed integer will overflow.\n"); + } + else { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 64 to 8 bits unsigned integer will overflow.\n"); + } } - return n64_narrow_n8(a); + return n64_narrow_n8(source_val); } template [[nodiscard]] -constexpr auto enum64_narrow_n8_checked(const T a) noexcept +constexpr auto enum64_narrow_n8_checked(const T source_val, bool should_assert) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); - return n64_narrow_n8_checked(static_cast>(a)); + return n64_narrow_n8_checked(static_cast>(source_val, should_assert)); } // Narrow a 32 bits number to a 16 bits number, discarding any upper exceeding bytes. template [[nodiscard]] -constexpr auto n32_narrow_n16(const T a) noexcept +constexpr auto n32_narrow_n16(const T source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable has not a arithmetic type."); static_assert(std::is_integral_v || (std::is_floating_point_v && std::is_signed_v), "Unsigned floating point numbers are unsupported by the language standard"); @@ -321,39 +331,55 @@ constexpr auto n32_narrow_n16(const T a) noexcept if constexpr (std::is_signed_v) { if constexpr (std::is_floating_point_v) - return static_cast(a & umask); - return static_cast(a & umask); + return static_cast(source_val & umask); + return static_cast(source_val & umask); } else - return static_cast(a & umask); + return static_cast(source_val & umask); } -// Narrow a 32 bits number to a 16 bits number and ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow. +// Narrow a 32 bits number to a 16 bits number. +// 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 a) + auto n32_narrow_n16_checked(const T source_val, bool should_assert) { - if constexpr (std::is_signed_v) { - ASSERT(a <= std::numeric_limits::max()); + if (should_assert) + { + if constexpr (std::is_signed_v) { + ASSERT(source_val <= std::numeric_limits::max()); + } + else { + ASSERT(source_val <= std::numeric_limits::max()); + } } - else { - ASSERT(a <= std::numeric_limits::max()); + else + { + if constexpr (std::is_signed_v) { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 32 to 16 bits signed integer will overflow.\n"); + } + else { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 32 to 16 bits unsigned integer will overflow.\n"); + } } - return n32_narrow_n16(a); + return n32_narrow_n16(source_val); } template [[nodiscard]] -constexpr auto enum32_narrow_n16_checked(const T a) noexcept +constexpr auto enum32_narrow_n16_checked(const T source_val, bool should_assert) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); - return n32_narrow_n16_checked(static_cast>(a)); + return n32_narrow_n16_checked(static_cast>(source_val, should_assert)); } // Narrow a 32 bits number to a 8 bits number, discarding any upper exceeding bytes. template [[nodiscard]] -constexpr auto n32_narrow_n8(const T a) noexcept +constexpr auto n32_narrow_n8(const T source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable has not a arithmetic type."); static_assert(sizeof(T) == 4, "Input variable is not a 32 bit number."); @@ -361,37 +387,53 @@ constexpr auto n32_narrow_n8(const T a) noexcept // Since the narrowing can be implementation specific, here we decide that we take only the lower 16 bytes and discard the upper ones. constexpr uint32 umask = 0x0000'00FF; if constexpr (std::is_signed_v) - return static_cast (a & umask); + return static_cast (source_val & umask); else - return static_cast(a & umask); + return static_cast(source_val & umask); } -// Narrow a 32 bits number to an 8 bits number and ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow. +// Narrow a 32 bits number to a 16 bits number. +// 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 a) + auto n32_narrow_n8_checked(const T source_val, bool should_assert) { - if constexpr (std::is_signed_v) { - ASSERT(a <= std::numeric_limits::max()); + if (should_assert) + { + if constexpr (std::is_signed_v) { + ASSERT(source_val <= std::numeric_limits::max()); + } + else { + ASSERT(source_val <= std::numeric_limits::max()); + } } - else { - ASSERT(a <= std::numeric_limits::max()); + else + { + if constexpr (std::is_signed_v) { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 32 to 8 bits signed integer will overflow.\n"); + } + else { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 32 to 8 bits unsigned integer will overflow.\n"); + } } - return n32_narrow_n8(a); + return n32_narrow_n8(source_val); } template [[nodiscard]] -constexpr auto enum32_narrow_n8_checked(const T a) noexcept +constexpr auto enum32_narrow_n8_checked(const T source_val, bool should_assert) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); - return n32_narrow_n8_checked(static_cast>(a)); + return n32_narrow_n8_checked(static_cast>(source_val, should_assert)); } // Narrow a 16 bits number to an 8 bits number, discarding any upper exceeding bytes. template [[nodiscard]] -constexpr auto n16_narrow_n8(const T a) noexcept +constexpr auto n16_narrow_n8(const T source_val) noexcept { static_assert(std::is_arithmetic_v, "Input variable has not a arithmetic type."); static_assert(std::is_integral_v, "Only integral types are supported by this function."); @@ -400,48 +442,64 @@ constexpr auto n16_narrow_n8(const T a) noexcept // Since the narrowing can be implementation specific, here we decide that we take only the lower 16 bytes and discard the upper ones. constexpr uint16 umask = 0x00FF; if constexpr (std::is_signed_v) - return static_cast(a & umask); + return static_cast(source_val & umask); else - return static_cast(a & umask); + return static_cast(source_val & umask); } -// Narrow a 16 bits number to an 8 bits number and ASSERT (because you're reasonably sure but not absolutely certain) that it won't overflow. +// Narrow a 32 bits number to a 16 bits number. +// 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 a) + auto n16_narrow_n8_checked(const T source_val, bool should_assert) { - if constexpr (std::is_signed_v) { - ASSERT(a <= std::numeric_limits::max()); + if (should_assert) + { + if constexpr (std::is_signed_v) { + ASSERT(source_val <= std::numeric_limits::max()); + } + else { + ASSERT(source_val <= std::numeric_limits::max()); + } } - else { - ASSERT(a <= std::numeric_limits::max()); + else + { + if constexpr (std::is_signed_v) { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 16 to 8 bits signed integer will overflow.\n"); + } + else { + if (source_val > std::numeric_limits::max()) + detail_stypecast::LogEventWarnWrapper("[Internal] Narrowing conversion from 16 to 8 bits unsigned integer will overflow.\n"); + } } - return n16_narrow_n8(a); + return n16_narrow_n8(source_val); } template [[nodiscard]] -constexpr auto enum16_narrow_n8_checked(const T a) noexcept +constexpr auto enum16_narrow_n8_checked(const T source_val, bool should_assert) noexcept { static_assert(std::is_enum_v, "Input variable is not an enum type."); - return n16_narrow_n8_checked(static_cast>(a)); + return n16_narrow_n8_checked(static_cast>(source_val, should_assert)); } // If size_t is bigger than a 32 bits number, narrow it to a 32 bits number discarding any upper exceeding bytes, otherwise plain return the same value.. [[nodiscard]] -constexpr uint32 usize_narrow_u32(const size_t a) noexcept +constexpr uint32 usize_narrow_u32(const size_t source_val) noexcept { // This doesn't work because n64_narrow_n32 static_asserts will be evaluated and fail on 32 bits compilation. /* if constexpr (sizeof(size_t) == 8) - return n64_narrow_n32(a); + return n64_narrow_n32(source_val); else return a; */ #if SIZE_MAX == UINT64_MAX - return n64_narrow_n32(a); + return n64_narrow_n32(source_val); #elif SIZE_MAX == UINT32_MAX - return a; + return source_val; #else # error "size_t is neither 8 nor 4 bytes?" #endif @@ -449,129 +507,247 @@ constexpr uint32 usize_narrow_u32(const size_t a) 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 a) +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); +} + + +/* Unsigned (and size_t) to signed, clamping. */ + +[[nodiscard]] constexpr + int8 i8_from_u8_clamping(const uint8 source_val) noexcept +{ + return (source_val > (uint8_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int8_t)source_val; +} + +[[nodiscard]] constexpr + int16 i16_from_u16_clamping(const uint16 source_val) noexcept +{ + return (source_val > (uint16_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int16_t)source_val; +} + +[[nodiscard]] constexpr + int32 i32_from_u32_clamping(const uint32 source_val) noexcept { - ASSERT(a <= std::numeric_limits::max()); - return usize_narrow_u32(a); + return (source_val > (uint32_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int32_t)source_val; +} + +[[nodiscard]] constexpr + int64 i64_from_u64_clamping(const uint64 source_val) noexcept +{ + return (source_val > (uint64_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int64_t)source_val; +} + +[[nodiscard]] constexpr + int32 i32_from_usize_clamping(const size_t source_val) noexcept +{ + return (source_val > (size_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int32_t)source_val; +} + +[[nodiscard]] constexpr + int64 i64_from_usize_clamping(const size_t source_val) noexcept +{ + return (source_val > (size_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (int64_t)source_val; +} + +[[nodiscard]] constexpr + uint32 u32_from_usize_clamping(const size_t source_val) noexcept +{ + if constexpr (sizeof(size_t) == 8) + return (source_val > (size_t)std::numeric_limits::max()) ? std::numeric_limits::max() : (uint32_t)source_val; + else + return source_val; } /* Unsigned (and size_t) to signed, checked for overflows. */ -// Convert an 8 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 -int8 i8_from_u8_checked(const uint8 a) // not clamping/capping + int8 i8_from_u8_checked(const uint8 source_val, bool should_assert) // not clamping/capping { - ASSERT(a <= (uint8_t)std::numeric_limits::max()); - return static_cast(a); + 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); } -template int8 i8_from_u8_checked(T) = delete; // disable implicit type conversion for the input argument +template int8 i8_from_u8_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument -// Convert a 16 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 -int16 i16_from_u16_checked(const uint16 a) // not clamping/capping + int16 i8_from_u16_checked(const uint16 source_val, bool should_assert) // not clamping/capping { - ASSERT(a <= (uint16_t)std::numeric_limits::max()); - return static_cast(a); + 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); } -template int16 i16_from_u16_checked(T) = delete; // disable implicit type conversion for the input argument +template int16 i8_from_u16_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument [[nodiscard]] inline -int16 i16_from_u32_checked(const uint32 a) // not clamping/capping + int16 i16_from_u16_checked(const uint16 source_val, bool should_assert) // not clamping/capping { - ASSERT(a <= (uint32_t)std::numeric_limits::max()); - return static_cast(a); + 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); } -template int16 i16_from_u32_checked(T) = delete; // disable implicit type conversion for the input argument +template int16 i16_from_u16_checked(T, bool) = delete; // disable implicit type conversion for the inpuT source_valrgument [[nodiscard]] inline -int16 i16_from_u64_checked(const uint64 a) // not clamping/capping + int16 i16_from_u32_checked(const uint32 source_val, bool should_assert) // not clamping/capping { - ASSERT(a <= (uint64_t)std::numeric_limits::max()); - return static_cast(a); + 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); } -template int16 i16_from_u64_checked(T) = delete; // disable implicit type conversion for the input argument +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); +} +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 a) // not clamping/capping + int32 i32_from_u32_checked(const uint32 source_val, bool should_assert) // not clamping/capping { - ASSERT(a <= (uint32_t)std::numeric_limits::max()); - return static_cast(a); + 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); } -template int32 i32_from_u32_checked(T) = delete; // disable implicit type conversion for the input argument +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 a) // not clamping/capping + int32 i32_from_u64_checked(const uint64 source_val, bool should_assert) // not clamping/capping { - ASSERT(a <= (uint64_t)std::numeric_limits::max()); - return static_cast(a); + 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); } -template int32 i32_from_u64_checked(T) = delete; // disable implicit type conversion for the input argument +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 a) // not clamping/capping + int64 i64_from_u64_checked(const uint64 source_val, bool should_assert) // not clamping/capping { - ASSERT(a <= (uint64_t)std::numeric_limits::max()); - return static_cast(a); + 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); } -template int64 i64_from_u64_checked(T) = delete; // disable implicit type conversion for the input argument +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 a) // not clamping/capping + int8 i8_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping { #if SIZE_MAX == UINT64_MAX - return n64_narrow_n8_checked(a); + return n64_narrow_n8_checked(source_val, should_assert); #elif SIZE_MAX == UINT32_MAX - return n32_narrow_n8_checked(a); + return n32_narrow_n8_checked(source_val, should_assert); #else # error "size_t is neither 8 nor 4 bytes?" #endif } -template int8 i8_from_usize_checked(T) = delete; // disable implicit type conversion for the input argument +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 a) // not clamping/capping + int16 i16_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping { #if SIZE_MAX == UINT64_MAX - return n64_narrow_n16_checked(a); + return n64_narrow_n16_checked(source_val, should_assert); #elif SIZE_MAX == UINT32_MAX - return n32_narrow_n16_checked(a); + return n32_narrow_n16_checked(source_val, should_assert); #else # error "size_t is neither 8 nor 4 bytes?" #endif } -template int16 i16_from_usize_checked(T) = delete; // disable implicit type conversion for the input argument +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 a) // not clamping/capping + int32 i32_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping { #if SIZE_MAX == UINT64_MAX - return i32_from_u32_clamping(n64_narrow_n32_checked(a)); + return i32_from_u32_clamping(n64_narrow_n32_checked(source_val, should_assert)); #elif SIZE_MAX == UINT32_MAX - return i32_from_u32_checked(n_alias_cast(a)); + return i32_from_u32_checked(n_alias_cast(source_val), should_assert); #else # error "size_t is neither 8 nor 4 bytes?" #endif } -template int32 i32_from_usize_checked(T) = delete; // disable implicit type conversion for the input argument +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 a) // not clamping/capping + int64 i64_from_usize_checked(const size_t source_val, bool should_assert) // not clamping/capping { #if SIZE_MAX == UINT64_MAX - return i64_from_u64_checked(n_alias_cast(a)); + return i64_from_u64_checked(n_alias_cast(source_val), should_assert); #elif SIZE_MAX == UINT32_MAX - return static_cast(a); // For sure it will fit + (void)should_assert; + return static_cast(source_val); // For sure it will fit #else # error "size_t is neither 8 nor 4 bytes?" #endif } -template int64 i64_from_usize_checked(T) = delete; // disable implicit type conversion for the input argument +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 f2fa7078e..2414c39e5 100644 --- a/src/common/sqlite/SQLite.cpp +++ b/src/common/sqlite/SQLite.cpp @@ -702,10 +702,10 @@ SQLiteTablePtr::SQLiteTablePtr( SQLiteTable * pTable ) m_pTable = pTable; } -SQLiteTablePtr::SQLiteTablePtr( const SQLiteTablePtr & cTablePtr ) +SQLiteTablePtr::SQLiteTablePtr( SQLiteTablePtr & cTablePtr ) { m_pTable=cTablePtr.m_pTable; - ((SQLiteTablePtr *)&cTablePtr)->m_pTable=nullptr; + cTablePtr.m_pTable=nullptr; } SQLiteTablePtr::~SQLiteTablePtr() @@ -714,12 +714,12 @@ SQLiteTablePtr::~SQLiteTablePtr() delete m_pTable; } -void SQLiteTablePtr::operator =( const SQLiteTablePtr & cTablePtr ) +void SQLiteTablePtr::operator =( SQLiteTablePtr & cTablePtr ) { if (m_pTable) delete m_pTable; m_pTable=cTablePtr.m_pTable; - ((SQLiteTablePtr *)&cTablePtr)->m_pTable=nullptr; + cTablePtr.m_pTable=nullptr; } SQLiteTable * SQLiteTablePtr::Detach() diff --git a/src/common/sqlite/SQLite.h b/src/common/sqlite/SQLite.h index e06b1a910..5386bba5f 100644 --- a/src/common/sqlite/SQLite.h +++ b/src/common/sqlite/SQLite.h @@ -181,7 +181,8 @@ class SQLiteTablePtr // If you have a previous table connected to this class, // you do not have to worry, // it will commit suicide before eating the new table. - SQLiteTablePtr( const SQLiteTablePtr & cTablePtr ); + SQLiteTablePtr( SQLiteTablePtr & cTablePtr ); + SQLiteTablePtr( const SQLiteTablePtr & cTablePtr ) = delete; // Destructor... virtual ~SQLiteTablePtr(); @@ -191,7 +192,8 @@ class SQLiteTablePtr // If you have a previous table connected to this class, // you do not have to worry, // it will commit suicide before eating the new table. - void operator =(const SQLiteTablePtr & cTablePtr); + void operator =(SQLiteTablePtr & cTablePtr); + void operator =(const SQLiteTablePtr & cTablePtr) = delete; // Functor operator, will de-reference the m_pTable member. // WARNING: Use with care! Check for non-null m_pTable first! diff --git a/src/game/CBase.cpp b/src/game/CBase.cpp index cd0311d11..52773a54e 100644 --- a/src/game/CBase.cpp +++ b/src/game/CBase.cpp @@ -235,9 +235,9 @@ bool CBaseBaseDef::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * p tchar *pszTmp = Str_GetTemp(); m_BaseResources.WriteKeys( pszTmp, Str_TempLength(), index, fQtyOnly, fKeyOnly ); if ( fQtyOnly && pszTmp[0] == '\0' ) - strcpy( pszTmp, "0" ); - - sVal = pszTmp; + sVal.SetValFalse(); // '0' + else + sVal = pszTmp; } } else diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index fdec42ab5..cecbdb93a 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -1132,10 +1132,10 @@ bool CObjBase::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc, if ( pChar ) sVal.FormatVal(bCanSee ? pChar->CanSee(pObj) : pChar->CanSeeLOS(pt, nullptr, pChar->GetVisualRange(), flags)); else - sVal.FormatVal(0); + sVal.SetValFalse(); } else if ( !pChar ) // no char -> no see - sVal.FormatVal(0); + sVal.SetValFalse(); else // standart way src TO current object sVal.FormatVal(bCanSee ? pChar->CanSee(this) : pChar->CanSeeLOS(this, (word)(flags))); } @@ -1199,7 +1199,7 @@ bool CObjBase::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc, GETNONWHITESPACE( ptcKey ); CClient * pThisClient = pSrc->GetChar() ? ( pSrc->GetChar()->IsClientActive() ? pSrc->GetChar()->GetClientActive() : nullptr ) : nullptr; - sVal.FormatVal(0); + sVal.SetValFalse(); if ( pThisClient ) { diff --git a/src/game/CObjBaseTemplate.h b/src/game/CObjBaseTemplate.h index af8a2ec44..adb470413 100644 --- a/src/game/CObjBaseTemplate.h +++ b/src/game/CObjBaseTemplate.h @@ -71,10 +71,12 @@ class CObjBaseTemplate : public CSObjContRec virtual int IsWeird() const; // Parent objects - virtual const CObjBaseTemplate* GetTopLevelObj() const { + [[nodiscard]] RETURNS_NOTNULL + virtual const CObjBaseTemplate* GetTopLevelObj() const { return this; } - virtual CObjBaseTemplate* GetTopLevelObj() { + [[nodiscard]] RETURNS_NOTNULL + virtual CObjBaseTemplate* GetTopLevelObj() { return this; } diff --git a/src/game/CSector.cpp b/src/game/CSector.cpp index bf6402cfe..c931c7715 100644 --- a/src/game/CSector.cpp +++ b/src/game/CSector.cpp @@ -284,7 +284,7 @@ bool CSector::r_LoadVal( CScript &s ) SetWeatherChance( false, s.HasArgs() ? s.GetArgVal() : -1 ); return true; case SC_FLAGS: - m_dwFlags = s.GetArgVal(); + m_dwFlags = s.GetArgDWVal(); return true; case SC_LIGHT: if ( g_Cfg.m_fAllowLightOverride ) diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index 354749a43..007c9890d 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -35,6 +35,303 @@ #include +//****************************************************** + +static void dword_q_sort(dword *numbers, dword left, dword right) +{ + dword pivot, l_hold, r_hold; + + l_hold = left; + r_hold = right; + pivot = numbers[left]; + while (left < right) + { + while ((numbers[right] >= pivot) && (left < right)) right--; + if (left != right) + { + numbers[left] = numbers[right]; + left++; + } + while ((numbers[left] <= pivot) && (left < right)) left++; + if (left != right) + { + numbers[right] = numbers[left]; + right--; + } + } + numbers[left] = pivot; + pivot = left; + left = l_hold; + right = r_hold; + if (left < pivot) + dword_q_sort(numbers, left, pivot-1); + if (right > pivot) + dword_q_sort(numbers, pivot+1, right); +} + +static void defragSphere(char *path) +{ + ASSERT(path != nullptr); + + constexpr size_t mb10 = 10*1024*1024; + constexpr size_t mb5 = 5*1024*1024; + + CSFileText inf; + CSFile ouf; + char file_buf[1024]; + char path_buf[SPHERE_MAX_PATH], path_buf_2[SPHERE_MAX_PATH]; + char *str_ptr = nullptr, *str_ptr_2 = nullptr; + + g_Log.Event(LOGM_INIT, "Defragmentation (UID alteration) of " SPHERE_TITLE " saves.\n" + "Use it on your risk and if you know what you are doing since it can possibly harm your server.\n" + "The process can take up to several hours depending on the CPU you have.\n" + "After finished, you will have your '" SPHERE_FILE "*.scp' files converted and saved as '" SPHERE_FILE "*.scp.new'.\n"); + + // UID_O_INDEX_MASK ? + constexpr dword MAX_UID = 40'000'000U; // limit to 40mln of objects, takes 40mln*4bytes ~= 160mb + + dword dwIdxUID = 0; + dword* puids = (dword*)calloc(MAX_UID, sizeof(dword)); + for ( uint i = 0; i < 3; ++i ) + { + Str_CopyLimitNull(path_buf, path, sizeof(path_buf)); + if ( i == 0 ) strcat(path_buf, SPHERE_FILE "statics" SPHERE_SCRIPT_EXT); + else if ( i == 1 ) strcat(path_buf, SPHERE_FILE "world" SPHERE_SCRIPT_EXT); + else strcat(path_buf, SPHERE_FILE "chars" SPHERE_SCRIPT_EXT); + + g_Log.Event(LOGM_INIT, "Reading current UIDs: %s\n", path_buf); + if ( !inf.Open(path_buf, OF_READ|OF_TEXT|OF_DEFAULTMODE) ) + { + g_Log.Event(LOGM_INIT, "Cannot open file for reading. Skipped!\n"); + continue; + } + size_t uiBytesRead = 0, uiTotalMb = 0; + while ((dwIdxUID < MAX_UID) && !feof(inf._pStream)) + { + fgets(file_buf, sizeof(file_buf), inf._pStream); + uiBytesRead += strlen(file_buf); + if ( uiBytesRead > mb10 ) + { + uiBytesRead -= mb10; + uiTotalMb += 10; + g_Log.Event(LOGM_INIT, "Total read %" PRIuSIZE_T " Mb\n", uiTotalMb); + } + if (( file_buf[0] == 'S' ) && ( strstr(file_buf, "SERIAL=") == file_buf )) + { + str_ptr = file_buf + 7; + str_ptr_2 = str_ptr; + while (*str_ptr_2 && (*str_ptr_2 != '\r') && (*str_ptr_2 != '\n')) + { + ++str_ptr_2; + } + *str_ptr_2 = 0; + + // prepare new uid + *(str_ptr-1) = '0'; + *str_ptr = 'x'; + --str_ptr; + puids[dwIdxUID++] = strtoul(str_ptr, &str_ptr_2, 16); + } + } + inf.Close(); + } + const dword dwTotalUIDs = dwIdxUID; + g_Log.Event(LOGM_INIT, "Totally having %" PRIu32 " unique objects (UIDs), latest: 0%x\n", dwTotalUIDs, puids[dwTotalUIDs-1]); + + g_Log.Event(LOGM_INIT, "Quick-Sorting the UIDs array...\n"); + dword_q_sort(puids, 0, dwTotalUIDs -1); + + for ( uint i = 0; i < 5; ++i ) + { + Str_CopyLimitNull(path_buf, path, sizeof(path_buf)); + if ( !i ) strcat(path_buf, SPHERE_FILE "accu.scp"); + else if ( i == 1 ) strcat(path_buf, SPHERE_FILE "chars" SPHERE_SCRIPT_EXT); + else if ( i == 2 ) strcat(path_buf, SPHERE_FILE "data" SPHERE_SCRIPT_EXT); + else if ( i == 3 ) strcat(path_buf, SPHERE_FILE "world" SPHERE_SCRIPT_EXT); + else if ( i == 4 ) strcat(path_buf, SPHERE_FILE "statics" SPHERE_SCRIPT_EXT); + g_Log.Event(LOGM_INIT, "Updating UID-s in %s to %s.new\n", path_buf, path_buf); + if ( !inf.Open(path_buf, OF_READ|OF_TEXT|OF_DEFAULTMODE) ) + { + g_Log.Event(LOGM_INIT, "Cannot open file for reading. Skipped!\n"); + continue; + } + Str_ConcatLimitNull(path_buf, ".new", sizeof(path_buf)); + if ( !ouf.Open(path_buf, OF_WRITE|OF_CREATE|OF_DEFAULTMODE) ) + { + g_Log.Event(LOGM_INIT, "Cannot open file for writing. Skipped!\n"); + continue; + } + + size_t uiBytesRead = 0, uiTotalMb = 0; + while ( inf.ReadString(file_buf, sizeof(file_buf)) ) + { + dwIdxUID = (dword)strlen(file_buf); + if (dwIdxUID > (ARRAY_COUNT(file_buf) - 3)) + dwIdxUID = ARRAY_COUNT(file_buf) - 3; + + file_buf[dwIdxUID] = file_buf[dwIdxUID +1] = file_buf[dwIdxUID +2] = 0; // just to be sure to be in line always + // NOTE: it is much faster than to use memcpy to clear before reading + bool fSpecial = false; + uiBytesRead += dwIdxUID; + if ( uiBytesRead > mb5 ) + { + uiBytesRead -= mb5; + uiTotalMb += 5; + g_Log.Event(LOGM_INIT, "Total processed %" PRIuSIZE_T " Mb\n", uiTotalMb); + } + str_ptr = file_buf; + + // Note 28-Jun-2004 + // mounts seems having ACTARG1 > 0x30000000. The actual UID is ACTARG1-0x30000000. The + // new also should be new+0x30000000. need investigation if this can help making mounts + // not to disappear after the defrag + if (( file_buf[0] == 'A' ) && ( strstr(file_buf, "ACTARG1=0") == file_buf )) // ACTARG1= + str_ptr += 8; + else if (( file_buf[0] == 'C' ) && ( strstr(file_buf, "CONT=0") == file_buf )) // CONT= + str_ptr += 5; + else if (( file_buf[0] == 'C' ) && ( strstr(file_buf, "CHARUID=0") == file_buf )) // CHARUID= + str_ptr += 8; + else if (( file_buf[0] == 'L' ) && ( strstr(file_buf, "LASTCHARUID=0") == file_buf )) // LASTCHARUID= + str_ptr += 12; + else if (( file_buf[0] == 'L' ) && ( strstr(file_buf, "LINK=0") == file_buf )) // LINK= + str_ptr += 5; + else if (( file_buf[0] == 'M' ) && ( strstr(file_buf, "MEMBER=0") == file_buf )) // MEMBER= + { + str_ptr += 7; + fSpecial = true; + } + else if (( file_buf[0] == 'M' ) && ( strstr(file_buf, "MORE1=0") == file_buf )) // MORE1= + str_ptr += 6; + else if (( file_buf[0] == 'M' ) && ( strstr(file_buf, "MORE2=0") == file_buf )) // MORE2= + str_ptr += 6; + else if (( file_buf[0] == 'S' ) && ( strstr(file_buf, "SERIAL=0") == file_buf )) // SERIAL= + str_ptr += 7; + else if ((( file_buf[0] == 'T' ) && ( strstr(file_buf, "TAG.") == file_buf )) || // TAG.= + (( file_buf[0] == 'R' ) && ( strstr(file_buf, "REGION.TAG") == file_buf ))) + { + while ( *str_ptr && ( *str_ptr != '=' )) + ++str_ptr; + ++str_ptr; + } + else if (( i == 2 ) && strchr(file_buf, '=')) // spheredata.scp - plain VARs + { + while ( *str_ptr && ( *str_ptr != '=' )) + ++str_ptr; + ++str_ptr; + } + else + str_ptr = nullptr; + + // UIDs are always hex, so prefixed by 0 + if ( str_ptr && ( *str_ptr != '0' )) + str_ptr = nullptr; + + // here we got potentialy UID-contained variable + // check if it really is only UID-like var containing + if ( str_ptr ) + { + str_ptr_2 = str_ptr; + while ( *str_ptr_2 && + ((( *str_ptr_2 >= '0' ) && ( *str_ptr_2 <= '9' )) || + (( *str_ptr_2 >= 'a' ) && ( *str_ptr_2 <= 'f' ))) ) + ++str_ptr_2; + if ( !fSpecial ) + { + if ( *str_ptr_2 && ( *str_ptr_2 != '\r' ) && ( *str_ptr_2 != '\n' )) // some more text in line + str_ptr = nullptr; + } + } + + // here we definitely know that this is very uid-like + if ( str_ptr ) + { + char c, c1, c2; + c = *str_ptr_2; + + *str_ptr_2 = 0; + // here in p we have the current value of the line. + // check if it is a valid UID + + // prepare converting 0.. to 0x.. + c1 = *(str_ptr-1); + c2 = *str_ptr; + *(str_ptr-1) = '0'; + *str_ptr = 'x'; + --str_ptr; + dwIdxUID = strtoul(str_ptr, &str_ptr_2, 16); + ++str_ptr; + *(str_ptr-1) = c1; + *str_ptr = c2; + // Note 28-Jun-2004 + // The search algourytm is very simple and fast. But maybe integrate some other, at least /2 algorythm + // since has amount/2 tryes at worst chance to get the item and never scans the whole array + // It should improve speed since defragmenting 150Mb saves takes ~2:30 on 2.0Mhz CPU + { + dword dStep = dwTotalUIDs /2; + dword d = dStep; + for (;;) + { + dStep /= 2; + + if ( puids[d] == dwIdxUID) + { + dwIdxUID = d | (puids[d]&0xF0000000); // do not forget attach item and special flags like 04.. + break; + } + else + { + if (puids[d] < dwIdxUID) + d += dStep; + else + d -= dStep; + } + + if ( dStep == 1 ) + { + dwIdxUID = 0xFFFFFFFFL; + break; // did not find the UID + } + } + } + + // Search for this uid in the table + /* for ( d = 0; d < dTotalUIDs; d++ ) + { + if ( !uids[d] ) // end of array + { + uid = 0xFFFFFFFFL; + break; + } + else if ( uids[d] == uid ) + { + uid = d | (uids[d]&0xF0000000); // do not forget attach item and special flags like 04.. + break; + } + }*/ + + // replace UID by the new one since it has been found + *str_ptr_2 = c; + if (dwIdxUID != 0xFFFFFFFFL ) + { + *str_ptr = 0; + ASSERT(strlen(str_ptr_2) < sizeof(path_buf)); + Str_CopyLimitNull(path_buf, str_ptr_2, sizeof(path_buf)); // here we don't need anymore the old values of path_buf, so i can reuse it here + snprintf(path_buf_2, sizeof(path_buf_2), "0%" PRIx32, dwIdxUID); + strcat(file_buf, path_buf_2); + strcat(file_buf, path_buf); + } + } + // output the resulting line + ouf.Write(file_buf, (int)strlen(file_buf)); + } + inf.Close(); + ouf.Close(); + } + + free(puids); + g_Log.Event(LOGM_INIT, "Defragmentation complete.\n"); +} + //////////////////////////////////////////////////////////////////////////////////////// // -CServer @@ -678,11 +975,16 @@ bool CServer::OnConsoleCmd( CSString & sText, CTextConsole * pSrc ) size_t iThreadCount = ThreadHolder::get().getActiveThreads(); for ( size_t iThreads = 0; iThreads < iThreadCount; ++iThreads ) { - IThread * thrCurrent = ThreadHolder::get().getThreadAt(iThreads); + AbstractThread * thrCurrent = ThreadHolder::get().getThreadAt(iThreads); if (thrCurrent != nullptr) { - pSrc->SysMessagef("%" PRIuSIZE_T " - Id: %" PRIu64 ", Priority: %d, Name: %s.\n", - (iThreads + 1), (uint64)thrCurrent->getId(), thrCurrent->getPriority(), thrCurrent->getName()); + pSrc->SysMessagef( + "%" PRIuSIZE_T " - Id: %" PRIu64 ", Priority: %d, Name: %s.\n", + (iThreads + 1), + (uint64)thrCurrent->getId(), + enum_alias_cast(thrCurrent->getPriority()), + thrCurrent->getName() + ); } } } break; @@ -814,7 +1116,7 @@ bool CServer::OnConsoleCmd( CSString & sText, CTextConsole * pSrc ) if ( !strnicmp(pszText, "strip tng", 9) || !strnicmp(pszText, "tngstrip", 8)) { Str_CopyLimitNull(z, dirname, Str_TempLength()); - Str_ConcatLimitNull(z, "sphere_strip_tng" SPHERE_SCRIPT, Str_TempLength()); + Str_ConcatLimitNull(z, "sphere_strip_tng" SPHERE_SCRIPT_EXT, Str_TempLength()); if (pSrc != this) { pSrc->SysMessagef("StripFile is %s.\n", z); @@ -896,7 +1198,7 @@ bool CServer::OnConsoleCmd( CSString & sText, CTextConsole * pSrc ) else if ( !strnicmp(pszText, "strip axis", 10) || !strnicmp(pszText, "strip", 5) ) { Str_CopyLimitNull(z, dirname, Str_TempLength()); - Str_ConcatLimitNull(z, "sphere_strip_axis" SPHERE_SCRIPT, Str_TempLength()); + Str_ConcatLimitNull(z, "sphere_strip_axis" SPHERE_SCRIPT_EXT, Str_TempLength()); if (pSrc != this) { pSrc->SysMessagef("StripFile is %s.\n", z); @@ -1065,7 +1367,7 @@ void CServer::ProfileDump( CTextConsole * pSrc, bool bDump ) size_t uiThreadCount = ThreadHolder::get().getActiveThreads(); for ( size_t iThreads = 0; iThreads < uiThreadCount; ++iThreads) { - IThread* thrCurrent = ThreadHolder::get().getThreadAt(iThreads); + AbstractThread* thrCurrent = ThreadHolder::get().getThreadAt(iThreads); if (thrCurrent == nullptr) continue; @@ -2059,7 +2361,7 @@ bool CServer::CommandLine( int argc, tchar * argv[] ) } } continue; -#if defined(_WIN32) && !defined(_DEBUG) && !defined(_NO_CRASHDUMP) +#if defined(_WIN32) && !defined(_DEBUG) && defined(WINDOWS_GENERATE_CRASHDUMP) case 'E': CrashDump::Enable(); if (CrashDump::IsEnabled()) @@ -2192,7 +2494,7 @@ bool CServer::SocketsInit() // Initialize sockets // What are we listing our port as to the world. // Tell the admin what we know. - tchar szName[ _MAX_PATH ]; + tchar szName[ SPHERE_MAX_PATH ]; struct hostent * pHost = nullptr; int iRet = gethostname(szName, sizeof(szName)); @@ -2202,7 +2504,7 @@ bool CServer::SocketsInit() // Initialize sockets { pHost = gethostbyname(szName); if ( pHost && pHost->h_addr && pHost->h_name && pHost->h_name[0] ) - Str_CopyLimitNull(szName, pHost->h_name, _MAX_PATH); + Str_CopyLimitNull(szName, pHost->h_name, SPHERE_MAX_PATH); } g_Log.Event( LOGM_INIT, "Server started on hostname '%s'\n", szName); @@ -2238,7 +2540,8 @@ void CServer::_OnTick() EXC_TRY("Tick"); #ifndef _WIN32 - if (g_UnixTerminal.isReady()) + // This happens on T_Main, and not on T_UnixTerm. + if (g_UnixTerminal.isReady()) { tchar c = g_UnixTerminal.read(); if ( OnConsoleKey(m_sConsoleText, c, false) == 2 ) diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index b5a56b9e5..6beadd6bd 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -317,7 +317,7 @@ CServerConfig::CServerConfig() // Networking _uiNetworkThreads = 0; // if there aren't the ini settings, by default we'll not use additional network threads - _uiNetworkThreadPriority= IThread::Disabled; + _iNetworkThreadPriority = enum_alias_cast(ThreadPriority::Disabled); m_fUseAsyncNetwork = 0; m_iNetMaxPings = 15; m_iNetHistoryTTLSeconds = 300; @@ -644,7 +644,7 @@ enum RC_TYPE RC_MYSQLTICKS, // m_fMySqlTicks RC_MYSQLUSER, // m_sMySqlUser RC_NETTTL, // m_iNetHistoryTTL - RC_NETWORKTHREADPRIORITY, // _uiNetworkThreadPriority + RC_NETWORKTHREADPRIORITY, // _iNetworkThreadPriority RC_NETWORKTHREADS, // _uiNetworkThreads RC_NORESROBE, RC_NOTOTIMEOUT, @@ -741,7 +741,7 @@ enum RC_TYPE // TODO: use offsetof by cstddef. Though, it requires the class/struct to be a POD type, so we need to encapsulate the values in a separate struct. // This hack does happen because this class hasn't virtual methods? Or simply because the compiler is so smart and protects us from ourselves? -#ifdef __GNUC__ +#if NON_MSVC_COMPILER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif @@ -937,7 +937,7 @@ const CAssocReg CServerConfig::sm_szLoadKeys[RC_QTY + 1] { "MYSQLTICKS", { ELEM_BOOL, static_castOFFSETOF(CServerConfig,m_fMySqlTicks) }}, { "MYSQLUSER", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sMySqlUser) }}, { "NETTTL", { ELEM_INT, static_castOFFSETOF(CServerConfig, m_iNetHistoryTTLSeconds) }}, - { "NETWORKTHREADPRIORITY", { ELEM_MASK_INT,static_castOFFSETOF(CServerConfig,_uiNetworkThreadPriority)}}, + { "NETWORKTHREADPRIORITY", { ELEM_MASK_INT,static_castOFFSETOF(CServerConfig,_iNetworkThreadPriority)}}, { "NETWORKTHREADS", { ELEM_MASK_INT,static_castOFFSETOF(CServerConfig,_uiNetworkThreads) }}, { "NORESROBE", { ELEM_BOOL, static_castOFFSETOF(CServerConfig,m_fNoResRobe) }}, { "NOTOTIMEOUT", { ELEM_INT, static_castOFFSETOF(CServerConfig,m_iNotoTimeout) }}, @@ -1030,7 +1030,7 @@ const CAssocReg CServerConfig::sm_szLoadKeys[RC_QTY + 1] { nullptr, { ELEM_VOID, 0, }} }; -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif @@ -1483,15 +1483,15 @@ bool CServerConfig::r_LoadVal( CScript &s ) case RC_NETWORKTHREADPRIORITY: { - int priority = s.GetArgVal(); - if (priority < 1) - priority = IThread::Normal; - else if (priority > 4) - priority = IThread::RealTime; + int priority = s.GetArgVal(); + if (priority < 1) + priority = enum_alias_cast(ThreadPriority::Normal); + else if (priority > 4) + priority = enum_alias_cast(ThreadPriority::RealTime); else - priority = IThread::Low + (IThread::Priority)priority; + priority = enum_alias_cast(ThreadPriority::Low) + priority; - _uiNetworkThreadPriority = priority; + _iNetworkThreadPriority = priority; } break; case RC_WALKBUFFER: @@ -1632,7 +1632,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * if ( !strnicmp(ptcKey, "MAP(", 4) ) { ptcKey += 4; - sVal.FormatVal(0); + sVal.SetValFalse(); // Parse the arguments after the round brackets tchar * pszArgsNext; @@ -1691,7 +1691,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * lpctstr pszCmd = ptcKey + 8; int iNumber = Exp_GetVal(pszCmd); SKIP_SEPARATORS(pszCmd); - sVal.FormatVal(0); + sVal.SetValFalse(); if (!*pszCmd) { @@ -1738,7 +1738,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * ptcKey = ptcKey + 3; int iMapNumber = Exp_GetVal(ptcKey); SKIP_SEPARATORS(ptcKey); - sVal.FormatVal(0); + sVal.SetValFalse(); if ( g_MapList.IsMapSupported(iMapNumber) ) { @@ -1784,7 +1784,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * size_t iNumber = Exp_GetVal(pszCmd); SKIP_SEPARATORS(pszCmd); - sVal.FormatVal(0); + sVal.SetValFalse(); for (size_t i = 0; i < g_World.m_Multis.size(); ++i) { @@ -1832,7 +1832,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * int iNumber = Exp_GetVal(pszCmd); SKIP_SEPARATORS(pszCmd); - sVal.FormatVal(0); + sVal.SetValFalse(); if (iNumber < 0 || iNumber >= (int) m_Functions.size()) //invalid index can potentially crash the server, this check is strongly needed { @@ -1879,7 +1879,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * size_t iNumber = Exp_GetVal(pszCmd); SKIP_SEPARATORS(pszCmd); - sVal.FormatVal(0); + sVal.SetValFalse(); for ( size_t i = 0; i < g_World.m_Stones.size(); ++i ) { @@ -1914,7 +1914,7 @@ bool CServerConfig::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * if (cli_num >= g_Serv.StatGet( SERV_STAT_CLIENTS )) return false; - sVal.FormatVal(0); + sVal.SetValFalse(); ClientIterator it; for (CClient* pClient = it.next(); pClient != nullptr; pClient = it.next()) { @@ -3052,7 +3052,9 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) } rid = CResourceID( (dword)pVarNum->GetValNum(), 0 ); - restype = rid.GetResType(); + + // This value won't be read, since we return anyways once in this branch. + //restype = rid.GetResType(); CResourceDef * pRes = nullptr; size_t index = m_ResHash.FindKey( rid ); @@ -4683,13 +4685,13 @@ bool CServerConfig::Load( bool fResync ) // Now load the *TABLES.SCP file. if ( ! fResync ) { - if ( ! OpenResourceFind( m_scpTables, SPHERE_FILE "tables" SPHERE_SCRIPT )) + if ( ! OpenResourceFind( m_scpTables, SPHERE_FILE "tables" SPHERE_SCRIPT_EXT )) { - g_Log.Event( LOGL_FATAL|LOGM_INIT, "Error opening table definitions file (" SPHERE_FILE "tables" SPHERE_SCRIPT ")...\n" ); + g_Log.Event( LOGL_FATAL|LOGM_INIT, "Error opening table definitions file (" SPHERE_FILE "tables" SPHERE_SCRIPT_EXT ")...\n" ); return false; } - g_Log.Event(LOGL_EVENT|LOGM_INIT, "Loading table definitions file (" SPHERE_FILE "tables" SPHERE_SCRIPT ")...\n"); + g_Log.Event(LOGL_EVENT|LOGM_INIT, "Loading table definitions file (" SPHERE_FILE "tables" SPHERE_SCRIPT_EXT ")...\n"); LoadResourcesOpen(&m_scpTables); m_scpTables.Close(); } @@ -4989,7 +4991,7 @@ bool CServerConfig::DumpUnscriptedItems( CTextConsole * pSrc, lpctstr pszFilenam return false; if ( pszFilename == nullptr || pszFilename[0] == '\0' ) - pszFilename = "unscripted_items" SPHERE_SCRIPT; + pszFilename = "unscripted_items" SPHERE_SCRIPT_EXT; else if ( strlen( pszFilename ) <= 4 ) return false; diff --git a/src/game/CServerConfig.h b/src/game/CServerConfig.h index 615bb6e99..f47025fc2 100644 --- a/src/game/CServerConfig.h +++ b/src/game/CServerConfig.h @@ -569,7 +569,7 @@ extern class CServerConfig : public CResourceHolder // network settings uint _uiNetworkThreads; // number of network threads to create - uint _uiNetworkThreadPriority; // priority of network threads + int _iNetworkThreadPriority; // priority of network threads int m_fUseAsyncNetwork; // 0=normal send, 1=async send, 2=async send for 4.0.0+ only int m_iNetMaxPings; // max pings before blocking an ip int m_iNetHistoryTTLSeconds; // time to remember an ip diff --git a/src/game/CServerDef.cpp b/src/game/CServerDef.cpp index 232c7a691..ec84ff743 100644 --- a/src/game/CServerDef.cpp +++ b/src/game/CServerDef.cpp @@ -32,13 +32,13 @@ // PSAPI external definitions typedef intptr_t (WINAPI *pfnGetProcessMemoryInfo)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD); - HMODULE m_hmPsapiDll = nullptr; - pfnGetProcessMemoryInfo m_GetProcessMemoryInfo = nullptr; - PROCESS_MEMORY_COUNTERS pcnt; + static HMODULE m_hmPsapiDll = nullptr; + static pfnGetProcessMemoryInfo m_GetProcessMemoryInfo = nullptr; + static PROCESS_MEMORY_COUNTERS pcnt; #else // (Unix) #include #endif - bool m_fPmemory = true; // process memory information is available? + static bool sm_fHasMemoryInfo = true; // process memory information is available? ////////////////////////////////////////////////////////////////////// // -CServerDef @@ -54,7 +54,7 @@ CServerDef::CServerDef( lpctstr pszName, CSocketAddressIP dwIP ) : _iTimeCreate = CWorldGameTime::GetCurrentTime().GetTimeRaw(); // Set default time zone from UTC - m_TimeZone = (char)( _timezone / (60 * 60) ); // Greenwich mean time. + m_TimeZone = (char)( TIMEZONE / (60 * 60) ); // Greenwich mean time. m_eAccApp = ACCAPP_Unspecified; } @@ -68,7 +68,7 @@ size_t CServerDef::StatGet(SERV_STAT_TYPE i) const if ( i == SERV_STAT_MEM ) // memory information { d = 0; - if ( m_fPmemory ) + if ( sm_fHasMemoryInfo ) { #ifdef _WIN32 if ( !m_hmPsapiDll ) // try to load psapi.dll if not loaded yet @@ -77,17 +77,17 @@ size_t CServerDef::StatGet(SERV_STAT_TYPE i) const m_hmPsapiDll = LoadLibrary(TEXT("psapi.dll")); if (m_hmPsapiDll == nullptr) { - m_fPmemory = false; + sm_fHasMemoryInfo = false; g_Log.EventError(("Unable to load process information PSAPI.DLL library. Memory information will be not available.\n")); } else { -#ifndef _MSC_VER +#ifdef NON_MSVC_COMPILER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" #endif m_GetProcessMemoryInfo = reinterpret_cast(::GetProcAddress(m_hmPsapiDll,"GetProcessMemoryInfo")); -#ifndef _MSC_VER +#if NON_MSVC_COMPILER #pragma GCC diagnostic pop #endif } @@ -147,7 +147,7 @@ size_t CServerDef::StatGet(SERV_STAT_TYPE i) const if ( !d ) { g_Log.EventError(("Unable to load process information from getrusage() and procfs. Memory information will be not available.\n")); - m_fPmemory = false; + sm_fHasMemoryInfo = false; } #endif } diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index 1d09fa9e3..df69a4ad9 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -1,15 +1,15 @@ #include "../common/CException.h" #include "../common/CExpression.h" +#include "../common/CLog.h" #include "../common/sphereversion.h" #include "../network/CClientIterator.h" #include "../network/CNetworkManager.h" #include "../sphere/ProfileTask.h" -#include "../common/CLog.h" #include "chars/CChar.h" #include "clients/CClient.h" #include "clients/CGMPage.h" #include "items/CItemMulti.h" -#include "items/CItemStone.h" +#include "items/CItemStone.h" // Needed to use CItemmStone methods, the unique_ptr was forward declared in the header #include "CServer.h" #include "CScriptProfiler.h" #include "CSector.h" @@ -24,7 +24,7 @@ #include -lpctstr GetReasonForGarbageCode(int iCode = -1) +static lpctstr GetReasonForGarbageCode(int iCode = -1) noexcept { lpctstr pStr; switch ( iCode ) @@ -194,7 +194,7 @@ lpctstr GetReasonForGarbageCode(int iCode = -1) return pStr; } -void ReportGarbageCollection(CObjBase * pObj, int iResultCode) +static void ReportGarbageCollection(CObjBase * pObj, int iResultCode) { ASSERT(pObj != nullptr); @@ -725,7 +725,7 @@ void CWorld::GetBackupName( CSString & sArchive, lpctstr pszBaseDir, tchar chTyp pszBaseDir, iGroup, iCount&0x07, chType, - SPHERE_SCRIPT ); + SPHERE_SCRIPT_EXT ); } bool CWorld::OpenScriptBackup( CScript & s, lpctstr pszBaseDir, lpctstr pszBaseName, int iSaveCount ) // static @@ -741,7 +741,7 @@ bool CWorld::OpenScriptBackup( CScript & s, lpctstr pszBaseDir, lpctstr pszBaseN // rename previous save to archive name. CSString sSaveName; - sSaveName.Format( "%s" SPHERE_FILE "%s%s", pszBaseDir, pszBaseName, SPHERE_SCRIPT ); + sSaveName.Format( "%s" SPHERE_FILE "%s%s", pszBaseDir, pszBaseName, SPHERE_SCRIPT_EXT ); if ( iSaveCount > 0 ) { if ( ::rename(sSaveName, sArchive) ) @@ -967,7 +967,7 @@ bool CWorld::SaveForce() // Save world state else pCurBlock = save_msgs[5]; - fSave = SaveStage(); + fSave = SaveStage(); if ( !(_iSaveStage & 0x7F) ) { g_Serv.PrintPercent( _iSaveStage, (ssize_t)iSectorsQty + 3 ); @@ -1098,7 +1098,7 @@ bool CWorld::CheckAvailableSpaceForSave(bool fStatics) auto CalcPrevSavesSize = [=, &fSizeErr, &uiPreviousSaveSize](lpctstr ptcSaveName) -> void { struct stat st; - CSString strSaveFile = g_Cfg.m_sWorldBaseDir + SPHERE_FILE + ptcSaveName + SPHERE_SCRIPT; + CSString strSaveFile = g_Cfg.m_sWorldBaseDir + SPHERE_FILE + ptcSaveName + SPHERE_SCRIPT_EXT; if (!stat(strSaveFile.GetBuffer(), &st)) { const ullong uiCurSavefileSize = (ullong)st.st_size; @@ -1357,19 +1357,19 @@ bool CWorld::LoadWorld() // Load world from script // NOTE: WE MUST Sync these files ! CHAR and WORLD !!! CSString sStaticsName; - sStaticsName.Format("%s" SPHERE_FILE "statics" SPHERE_SCRIPT, static_cast(g_Cfg.m_sWorldBaseDir)); + sStaticsName.Format("%s" SPHERE_FILE "statics" SPHERE_SCRIPT_EXT, static_cast(g_Cfg.m_sWorldBaseDir)); CSString sWorldName; - sWorldName.Format("%s" SPHERE_FILE "world" SPHERE_SCRIPT, static_cast(g_Cfg.m_sWorldBaseDir)); + sWorldName.Format("%s" SPHERE_FILE "world" SPHERE_SCRIPT_EXT, static_cast(g_Cfg.m_sWorldBaseDir)); CSString sMultisName; - sMultisName.Format("%s" SPHERE_FILE "multis" SPHERE_SCRIPT, static_cast(g_Cfg.m_sWorldBaseDir)); + sMultisName.Format("%s" SPHERE_FILE "multis" SPHERE_SCRIPT_EXT, static_cast(g_Cfg.m_sWorldBaseDir)); CSString sCharsName; - sCharsName.Format("%s" SPHERE_FILE "chars" SPHERE_SCRIPT, static_cast(g_Cfg.m_sWorldBaseDir)); + sCharsName.Format("%s" SPHERE_FILE "chars" SPHERE_SCRIPT_EXT, static_cast(g_Cfg.m_sWorldBaseDir)); CSString sDataName; - sDataName.Format("%s" SPHERE_FILE "data" SPHERE_SCRIPT, static_cast(g_Cfg.m_sWorldBaseDir)); + sDataName.Format("%s" SPHERE_FILE "data" SPHERE_SCRIPT_EXT, static_cast(g_Cfg.m_sWorldBaseDir)); int iPrevSaveCount = m_iSaveCountID; for (;;) diff --git a/src/game/CWorldCache.cpp b/src/game/CWorldCache.cpp index a5f3459b4..6873ee560 100644 --- a/src/game/CWorldCache.cpp +++ b/src/game/CWorldCache.cpp @@ -3,6 +3,7 @@ #include "uo_files/CUOMapList.h" #include "CWorldCache.h" + CWorldCache::CWorldCache() { _iTimeLastMapBlockCacheCheck = 0; @@ -11,8 +12,7 @@ CWorldCache::CWorldCache() _mapBlocks[i].reset(); } - -static int _GetMapBlocksCount(int iMap) +static int _GetMapBlocksCount(int iMap) noexcept { const int iXBlocks = g_MapList.GetMapSizeX(iMap) / UO_BLOCK_SIZE; const int iYBlocks = g_MapList.GetMapSizeY(iMap) / UO_BLOCK_SIZE; diff --git a/src/game/CWorldComm.cpp b/src/game/CWorldComm.cpp index d7d3f56a8..78713a5ca 100644 --- a/src/game/CWorldComm.cpp +++ b/src/game/CWorldComm.cpp @@ -235,7 +235,7 @@ void CWorldComm::Broadcast(lpctstr pMsg) // static Speak( nullptr, pMsg, HUE_TEXT_DEF, TALKMODE_BROADCAST, FONT_BOLD ); } -void __cdecl CWorldComm::Broadcastf(lpctstr pMsg, ...) // static +void CWorldComm::Broadcastf(lpctstr pMsg, ...) // static { // System broadcast in bold text ADDTOCALLSTACK("CWorldComm::Broadcastf"); diff --git a/src/game/CWorldComm.h b/src/game/CWorldComm.h index 065d101b7..6a365c54b 100644 --- a/src/game/CWorldComm.h +++ b/src/game/CWorldComm.h @@ -24,7 +24,7 @@ class CWorldComm static void SpeakUNICODE(const CObjBaseTemplate* pSrc, const nachar* pText, HUE_TYPE wHue, TALKMODE_TYPE mode, FONT_TYPE font, CLanguageID lang); static void Broadcast(lpctstr pMsg); - static void __cdecl Broadcastf(lpctstr pMsg, ...) __printfargs(1, 2); + static void Broadcastf(lpctstr pMsg, ...) SPHERE_PRINTFARGS(1, 2); }; diff --git a/src/game/CWorldImport.cpp b/src/game/CWorldImport.cpp index 693d4c9d4..98ee09c38 100644 --- a/src/game/CWorldImport.cpp +++ b/src/game/CWorldImport.cpp @@ -816,7 +816,7 @@ bool CWorld::Import( lpctstr pszFilename, const CChar * pSrc, word wModeFlags, i fImport.m_pszArg1 = pszArg1; fImport.m_pszArg2 = pszArg2; - if ( ! strcmpi( pszFilename + (iLen - 4), ".WSC" )) + if ( ! strcmpi( pszFilename + (iLen - 4), ".WSC" )) { if ( ! fImport.ImportWSC(s, wModeFlags, dx, dy )) return false; @@ -844,7 +844,7 @@ bool CWorld::DumpAreas( CTextConsole * pSrc, lpctstr pszFilename ) return false; if ( pszFilename == nullptr || *pszFilename == '\0' ) - pszFilename = "map_all" SPHERE_SCRIPT; + pszFilename = "map_all" SPHERE_SCRIPT_EXT; else if ( strlen( pszFilename ) <= 4 ) return false; diff --git a/src/game/CWorldSearch.h b/src/game/CWorldSearch.h index 7a598c83b..c8da39010 100644 --- a/src/game/CWorldSearch.h +++ b/src/game/CWorldSearch.h @@ -5,7 +5,7 @@ #include "../common/CRect.h" /* -#ifdef _MSC_VER +#ifdef MSVC_COMPILER # pragma warning(push) # pragma warning(disable : 4244) # pragma warning(disable : 4267) @@ -14,7 +14,7 @@ #include -#ifdef _MSC_VER +#ifdef MSVC_COMPILER # pragma warning(pop) #endif */ diff --git a/src/game/CWorldTicker.cpp b/src/game/CWorldTicker.cpp index 31c6af85c..659c4e4cc 100644 --- a/src/game/CWorldTicker.cpp +++ b/src/game/CWorldTicker.cpp @@ -167,7 +167,6 @@ void CWorldTicker::_InsertCharTicking(const int64 iTickNext, CChar* pChar) std::unique_lock lock(_mCharTickList.MT_CMUTEX); #endif - _mCharTickList.emplace(iTickNext, pChar); pChar->_iTimePeriodicTick = iTickNext; } @@ -228,7 +227,18 @@ void CWorldTicker::AddCharTicking(CChar* pChar, bool fNeedsLock) } if (iTickNext == iTickOld) + { +/* +#ifdef _DEBUG + auto it = std::find_if(_mCharTickList.begin(), _mCharTickList.end(), + [pChar](const std::pair& elem) { + return elem.second == pChar; + }); + DEBUG_ASSERT(it == _mCharTickList.end()); +#endif +*/ return; + } //if (iTickNext < CWorldGameTime::GetCurrentTime().GetTimeRaw()) // We do that to get them tick as sooner as possible // return; diff --git a/src/game/CWorldTicker.h b/src/game/CWorldTicker.h index 31d84a808..22729e3b2 100644 --- a/src/game/CWorldTicker.h +++ b/src/game/CWorldTicker.h @@ -39,14 +39,14 @@ /* End of phmap.h inclusion */ /* Include btree.h */ -#ifdef __GNUC__ +#if NON_MSVC_COMPILER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif #include -#ifdef __GNUC__ +#if NON_MSVC_COMPILER #pragma GCC diagnostic pop #endif /* End of btree.h inclusion */ diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index 5c90e79a1..c1b95127a 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -1592,7 +1592,7 @@ void CChar::SetID( CREID_TYPE id ) CCharBase * pCharDef = CCharBase::FindCharBase(id); if ( pCharDef == nullptr ) { - if ( (id != (CREID_TYPE)-1) && (id != CREID_INVALID) ) + if ( id != CREID_INVALID ) DEBUG_ERR(("Setting invalid char ID 0%x\n", id)); id = (CREID_TYPE)(g_Cfg.ResourceGetIndexType(RES_CHARDEF, "DEFAULTCHAR")); @@ -1643,7 +1643,7 @@ void CChar::SetID( CREID_TYPE id ) if ( pHand ) GetPackSafe()->ContentAdd(pHand); } - UpdateMode(nullptr, true); + UpdateMode(true, nullptr); } @@ -2356,13 +2356,25 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo if (strlen(ptcKey) == 11) { + // This returns the total current follower slots. sVal.FormatSVal(GetCurFollowers()); return true; } - sVal.FormatVal(0); + sVal.SetValFalse(); ptcKey += 11; - if (*ptcKey != '.' || m_followers.empty()) + if (*ptcKey != '.') + return false; + ptcKey += 1; + + if (!strnicmp(ptcKey, "CHARCOUNT", 9)) + { + sVal.FormatSTVal(m_followers.size()); + return true; + } + + // Access objects by ID. + if (m_followers.empty()) return false; const uint uiIndex = Exp_GetUVal(ptcKey); @@ -2373,6 +2385,12 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo if (!pCharArg) return false; + if (*ptcKey == '\0') + { + sVal.SetValTrue(); + return true; + } + if (pCharArg->r_WriteVal(ptcKey, sVal, pSrc, fNoCallParent, fNoCallChildren)) return true; @@ -2392,7 +2410,7 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo return true; } - sVal.FormatVal(0); + sVal.SetValFalse(); ptcKey += 8; if ( *ptcKey == '.' ) @@ -2514,7 +2532,7 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo return true; } - sVal.FormatVal(0); + sVal.SetValFalse(); ptcKey += 8; if ( *ptcKey == '.' ) @@ -2619,7 +2637,7 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo --i; } - sVal = "0"; + sVal.SetValFalse(); delete[] pszFameAt0; return true; } @@ -2715,7 +2733,7 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo --i; } - sVal = "0"; + sVal.SetValFalse(); delete[] pszKarmaAt0; return true; } @@ -2801,12 +2819,12 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo CResourceQtyArray Resources; if ( Resources.Load(ptcKey) > 0 && SkillResourceTest( &Resources ) ) { - sVal.FormatVal(1); + sVal.SetValTrue(); return true; } } } - sVal.FormatVal(0); + sVal.SetValFalse(); return true; case CHC_CANMOVE: { @@ -2829,7 +2847,7 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo if ( pItem ) sVal.FormatHex(pItem->m_itFigurine.m_UID); else - sVal.FormatVal(0); + sVal.SetValFalse(); return true; } case CHC_MOVE: @@ -2875,7 +2893,7 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo if ( m_pPlayer != nullptr ) sVal = ( m_pParty != nullptr ) ? "1" : "0"; else - sVal = "0"; + sVal.SetValFalse(); return true; case CHC_ISMYPET: if (!m_pNPC) @@ -2893,7 +2911,7 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo sVal = IsDisconnected() ? "0" : "1"; return true; } - sVal = "0"; + sVal.SetValFalse(); return true; case CHC_ISSTUCK: { @@ -3509,7 +3527,7 @@ bool CChar::r_LoadVal( CScript & s ) if (!s.HasArgs()) return false; - if (!strnicmp(ptcKey, "DELETE", 6) || !strnicmp(ptcKey, "DEL", 3)) + if (!strnicmp(ptcKey, "DELUID", 6)) { if (m_followers.empty()) return false; @@ -3534,6 +3552,28 @@ bool CChar::r_LoadVal( CScript & s ) return true; } + if (!strnicmp(ptcKey, "DELINDEX", 6)) + { + if (m_followers.empty()) + return false; + + const uint uiIndex = s.GetArgUVal(); + if (uiIndex >= m_followers.size()) + return false; + + CChar *pChar = m_followers[uiIndex].uid.CharFind(); + m_followers.erase(m_followers.begin() + uiIndex); + + if (!pChar) + { + g_Log.EventWarn("Follower with index %u is an invalid char! (Owner: 0%" PRIx32 ".)\n", uiIndex, GetUID().GetObjUID()); + return true; + } + + pChar->NPC_PetClearOwners(); + return true; + } + if (!strnicmp(ptcKey, "ADD", 3)) { int64 piCmd[2]; @@ -3789,7 +3829,6 @@ bool CChar::r_LoadVal( CScript & s ) DIR_TYPE dir = static_cast(s.GetArgVal()); if (dir <= DIR_INVALID || dir >= DIR_QTY) dir = DIR_SE; - m_dirFace = dir; UpdateDir( dir ); } break; @@ -3817,8 +3856,16 @@ bool CChar::r_LoadVal( CScript & s ) break; } // Don't modify STATF_SAVEPARITY, STATF_PET, STATF_SPAWNED here - _uiStatFlag = (_uiStatFlag & (STATF_SAVEPARITY | STATF_PET | STATF_SPAWNED)) | (s.GetArgLLVal() & ~(STATF_SAVEPARITY | STATF_PET | STATF_SPAWNED)); - NotoSave_Update(); + static constexpr auto uiFlagsNoChange = (STATF_SAVEPARITY | STATF_PET | STATF_SPAWNED); + static constexpr auto uiFlagsRequireFullUpdate = (STATF_STONE | STATF_HIDDEN | STATF_DEAD); + const auto uiCurFlags = _uiStatFlag; + const auto uiNewFlags = s.GetArgULLVal(); + _uiStatFlag = (_uiStatFlag & uiFlagsNoChange) | (uiNewFlags & ~uiFlagsNoChange); + if (uiCurFlags != uiNewFlags) + { + const bool fDoFullUpdate = (uiCurFlags & uiFlagsRequireFullUpdate) != (uiNewFlags & uiFlagsRequireFullUpdate); + NotoSave_Update(fDoFullUpdate); + } break; } case CHC_FONT: @@ -3971,7 +4018,7 @@ bool CChar::r_LoadVal( CScript & s ) StatFlag_Mod(STATF_STONE,fSet); if ( fChange ) { - UpdateMode(nullptr, true); + UpdateMode(true, nullptr); if ( IsClientActive() ) m_pClient->addCharMove(this); } @@ -4164,10 +4211,14 @@ void CChar::r_Write( CScript & s ) s.WriteKeyVal("OFAME", GetFame() ); int iVal; - if ((iVal = Stat_GetMod(STAT_FOOD)) != 0) - s.WriteKeyVal("MODFOOD", iVal); + //if ((iVal = Stat_GetMod(STAT_FOOD)) != 0) + // s.WriteKeyVal("MODFOOD", iVal); if ((iVal = Stat_GetBase(STAT_FOOD)) != Char_GetDef()->m_MaxFood) s.WriteKeyVal("OFOOD", iVal); + if ((iVal = Stat_GetMaxMod(STAT_FOOD)) != 0) + s.WriteKeyVal("MODMAXFOOD", iVal); + if ((iVal = Stat_GetMax(STAT_FOOD)) != 0) + s.WriteKeyVal("MAXFOOD", iVal); s.WriteKeyVal("FOOD", Stat_GetVal(STAT_FOOD)); static constexpr lpctstr _ptcKeyModStat[STAT_BASE_QTY] = @@ -4183,7 +4234,7 @@ void CChar::r_Write( CScript & s ) "ODEX" }; - for (int j = 0; j < STAT_BASE_QTY; ++j) + for (int j = 0; j < STAT_BASE_QTY; ++j) { // this is VERY important, saving the MOD first if ((iVal = Stat_GetMod((STAT_TYPE)j)) != 0) @@ -4593,7 +4644,7 @@ bool CChar::r_Verb( CScript &s, CTextConsole * pSrc ) // Execute command from sc if ( pSrc ) { _uiStatFlag = s.GetArgLLFlag( _uiStatFlag, STATF_INSUBSTANTIAL ); - UpdateMode(nullptr, true); + UpdateMode(true, nullptr); if ( IsStatFlag(STATF_INSUBSTANTIAL) ) { if ( IsClientActive() ) @@ -4880,7 +4931,7 @@ bool CChar::r_Verb( CScript &s, CTextConsole * pSrc ) // Execute command from sc if ( ! IsPlayableCharacter()) return false; SetHue( GetHue() ^ HUE_UNDERWEAR /*, false, pSrc*/ ); //call @Dye on underwear? - UpdateMode(); + UpdateMode(true, nullptr); break; case CHV_UNEQUIP: // uid return ItemBounce( CUID::ItemFindFromUID(s.GetArgVal()) ); @@ -4996,7 +5047,7 @@ bool CChar::OnTriggerSpeech( bool bIsPet, lpctstr pszText, CChar * pSrc, TALKMOD } // Gaining exp -uint Calc_ExpGet_Exp(uint level) +static uint Calc_ExpGet_Exp(uint level) { if (level <= 1) return 0; @@ -5016,7 +5067,7 @@ uint Calc_ExpGet_Exp(uint level) } // Increasing level -uint Calc_ExpGet_Level(uint exp) +static uint Calc_ExpGet_Level(uint exp) { if (g_Cfg.m_iLevelNextAt < 1) //Must do this check in case ini's LevelNextAt is not set or server will freeze because exp will never decrease in the while. { diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index 20a4bd212..35a5edff1 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -495,8 +495,10 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; bool IsVerticalSpace( const CPointMap& ptDest, bool fForceMount = false ) const; public: - virtual CObjBaseTemplate* GetTopLevelObj() override; - virtual const CObjBaseTemplate* GetTopLevelObj() const override; + [[nodiscard]] RETURNS_NOTNULL + virtual CObjBaseTemplate* GetTopLevelObj() override; + [[nodiscard]] RETURNS_NOTNULL + virtual const CObjBaseTemplate* GetTopLevelObj() const override; bool IsSwimming() const; bool MoveToRegion(CRegionWorld* pNewArea, bool fAllowReject); @@ -555,7 +557,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; ANIM_TYPE GenerateAnimate(ANIM_TYPE action, bool fTranslate = true, bool fBackward = false, byte iFrameDelay = 0, byte iAnimLen = 7); bool UpdateAnimate(ANIM_TYPE action, bool fTranslate = true, bool fBackward = false, byte iFrameDelay = 0, byte iAnimLen = 7); - void UpdateMode( CClient * pExcludeClient = nullptr, bool fFull= false ); + void UpdateMode( bool fFull, CClient * pExcludeClient = nullptr); void UpdateSpeedMode(); void UpdateVisualRange(); void UpdateMove( const CPointMap & ptOld, CClient * pClientExclude = nullptr, bool bFull = false ); @@ -821,8 +823,10 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; /** * @brief Clearing notoriety and update myself so everyone checks my noto again. + * + * @param fCharFullUpdate Should it do a full character entity update instead of a partial one? */ - void NotoSave_Update(); + void NotoSave_Update(bool fCharFullUpdate = false); /** * @brief Deleting myself and sending data again for given char. @@ -1019,7 +1023,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; bool Spell_CastDone(); virtual bool OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, CItem * pSourceItem, bool fReflecting = false, int64 iDuration = 0) override; bool Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bool fFailMsg, bool fCheckAntiMagic = true ); - CChar* Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE uiCreature); + CChar* Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE uiCreature, std::optional iFollowerSlotsOverride); int64 GetSpellDuration( SPELL_TYPE spell, int iSkillLevel, CChar * pCharSrc = nullptr ); // in tenths of second // Memories about objects in the world. ------------------- @@ -1177,16 +1181,28 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept; virtual void Update(const CClient* pClientExclude = nullptr) override; virtual void Flip() override; + + void EatAnim(CItem* pItem, ushort uiQty); + bool Reveal( uint64 iFlags = 0 ); + void Jail( CTextConsole * pSrc, bool fSet, int iCell ); bool SetPoison( int iSkill, int iHits, CChar * pCharSrc ); bool SetPoisonCure( bool fExtra ); bool CheckCorpseCrime( CItemCorpse *pCorpse, bool fLooting, bool fTest ); CItemCorpse * FindMyCorpse( bool fIgnoreLOS = false, int iRadius = 2) const; CItemCorpse * MakeCorpse( bool fFrontFall ); - bool RaiseCorpse( CItemCorpse * pCorpse ); - bool Death(); - bool Reveal( uint64 iFlags = 0 ); - void Jail( CTextConsole * pSrc, bool fSet, int iCell ); - void EatAnim(CItem* pItem, ushort uiQty); + bool RaiseCorpse( CItemCorpse * pCorpse ); + + enum class DeathRequestResult + { + Success, + SuccessAndDelete, + AlreadyDead, + Aborted, + AbortedNoLog + }; + + DeathRequestResult Death(); + /** * @Brief I'm calling guards (Player speech) * diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 897a1e55d..b4c427f1a 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -2428,7 +2428,7 @@ bool CChar::UpdateAnimate(ANIM_TYPE action, bool fTranslate, bool fBackward , by // If character status has been changed // (Polymorph, war mode or hide), resend him -void CChar::UpdateMode( CClient * pExcludeClient, bool fFull ) +void CChar::UpdateMode( bool fFull, CClient * pExcludeClient ) { ADDTOCALLSTACK("CChar::UpdateMode"); @@ -3524,7 +3524,7 @@ bool CChar::Reveal( uint64 iFlags ) return false; m_StepStealth = 0; - UpdateMode(nullptr, true); + UpdateMode(true, nullptr); SysMessageDefault(DEFMSG_HIDING_REVEALED); return true; } @@ -4222,8 +4222,6 @@ bool CChar::SetPoison( int iSkill, int iHits, CChar * pCharSrc ) } } - - CClient *pClient = GetClientActive(); if ( pClient && IsSetOF(OF_Buffs) ) { @@ -4253,7 +4251,7 @@ void CChar::Wake() RaiseCorpse(pCorpse); StatFlag_Clear(STATF_SLEEPING); - UpdateMode(); + UpdateMode(false, nullptr); } // Sleep @@ -4277,7 +4275,7 @@ void CChar::SleepStart( bool fFrontFall ) SetID(_iPrev_id); StatFlag_Set(STATF_SLEEPING); StatFlag_Clear(STATF_HIDDEN); - UpdateMode(); + UpdateMode(false, nullptr); } // We died, calling @Death, removing trade windows. @@ -4285,20 +4283,20 @@ void CChar::SleepStart( bool fFrontFall ) // Cleaning myself (dispel, cure, dismounting ...). // Creating the corpse ( MakeCorpse() ). // Removing myself from view, generating Death packets. -// RETURN: -// true = successfully died -// false = something went wrong? i'm an NPC, just delete (excepting BONDED ones). -bool CChar::Death() +CChar::DeathRequestResult CChar::Death() { ADDTOCALLSTACK("CChar::Death"); - if ( IsStatFlag(STATF_DEAD|STATF_INVUL) ) - return true; + if ( IsStatFlag(STATF_DEAD) ) + return DeathRequestResult::AlreadyDead; + + if ( IsStatFlag(STATF_INVUL) ) + return DeathRequestResult::AbortedNoLog; if ( IsTrigUsed(TRIGGER_DEATH) ) { if ( OnTrigger(CTRIG_Death, this) == TRIGRET_RET_TRUE ) - return true; + return DeathRequestResult::Aborted; } //Dismount now. Later is may be too late and cause problems if ( m_pNPC ) @@ -4353,7 +4351,8 @@ bool CChar::Death() iKillStrLen += snprintf( pszKillStr + iKillStrLen, Str_TempLength() - iKillStrLen, "%s%c'%s'.", - iKillers ? ", " : "", (pKiller->m_pPlayer) ? 'P':'N', pKiller->GetNameWithoutIncognito() ); + iKillers ? ", " : "", + (pKiller->m_pPlayer) ? 'P':'N', pKiller->GetNameWithoutIncognito() ); ++iKillers; } @@ -4361,7 +4360,7 @@ bool CChar::Death() // Record the kill event for posterity if ( !iKillers ) - iKillStrLen += snprintf( pszKillStr + iKillStrLen, Str_TempLength() - iKillStrLen, "accident." ); + /*iKillStrLen +=*/ snprintf( pszKillStr + iKillStrLen, Str_TempLength() - iKillStrLen, "accident." ); if ( m_pPlayer ) g_Log.Event( LOGL_EVENT|LOGM_KILLS, "%s\n", pszKillStr ); if ( m_pParty ) @@ -4385,7 +4384,7 @@ bool CChar::Death() } // Create the corpse item - bool fFrontFall = g_Rand.GetVal(2); + bool fFrontFall = g_Rand.GetValFast(2); CItemCorpse * pCorpse = MakeCorpse(fFrontFall); if ( pCorpse ) { @@ -4405,15 +4404,15 @@ bool CChar::Death() if ( m_pNPC->m_bonded ) { m_CanMask |= CAN_C_GHOST; - UpdateMode(nullptr, true); - return true; + UpdateMode(true, nullptr); + return DeathRequestResult::Success; } if ( pCorpse ) pCorpse->m_uidLink.InitUID(); NPC_PetClearOwners(); - return false; // delete the NPC + return DeathRequestResult::SuccessAndDelete; // delete the NPC } if ( m_pPlayer ) @@ -4495,7 +4494,7 @@ bool CChar::Death() } } - return true; + return DeathRequestResult::Success; } // Check if we are held in place. @@ -5685,7 +5684,7 @@ void CChar::OnTickStatusUpdate() if ( m_fStatusUpdate & SU_UPDATE_MODE ) { - UpdateMode(); + UpdateMode(false, nullptr); m_fStatusUpdate &= ~SU_UPDATE_MODE; } @@ -5916,8 +5915,26 @@ bool CChar::OnTickPeriodic() */ if (!IsStatFlag(STATF_DEAD) && (Stat_GetVal(STAT_STR) <= 0)) { - EXC_SET_BLOCK("death"); - return Death(); + EXC_SET_BLOCK("death?"); + const DeathRequestResult deathRes = Death(); + if ((deathRes != DeathRequestResult::Aborted) && (deathRes != DeathRequestResult::AbortedNoLog)) + { + if (deathRes == DeathRequestResult::AlreadyDead) + goto do_status_update; + if (deathRes == DeathRequestResult::SuccessAndDelete) + return false; + return true; + } + else + { +//#ifdef _DEBUG + if (deathRes != DeathRequestResult::AbortedNoLog) + { + g_Log.EventEvent("Aborted char '%s' (0x%" PRIx32 " ) death.\n", GetName(), GetUID().GetObjUID()); + } +//#endif + ; // Then, fall through. + } } // Stats regeneration @@ -5935,18 +5952,21 @@ bool CChar::OnTickPeriodic() if (IsClientActive()) { CClient* pClient = GetClientActive(); + // Players have a silly "always run" flag that gets stuck on. if ( (iTimeCur - pClient->m_timeLastEventWalk) > (2 * MSECS_PER_TENTH) ) { StatFlag_Clear(STATF_FLY); } - // Show returning anim for thowing weapons after throw it - if ((pClient->m_timeLastSkillThrowing > 0) && ((iTimeCur - pClient->m_timeLastSkillThrowing) > (2 * MSECS_PER_TENTH))) - { - pClient->m_timeLastSkillThrowing = 0; - if (pClient->m_pSkillThrowingTarg->IsValidUID()) - Effect(EFFECT_BOLT, pClient->m_SkillThrowingAnimID, pClient->m_pSkillThrowingTarg, 18, 1, false, pClient->m_SkillThrowingAnimHue, pClient->m_SkillThrowingAnimRender); - } + + // Show returning anim for thowing weapons after throw it + if ((pClient->m_timeLastSkillThrowing > 0) && ((iTimeCur - pClient->m_timeLastSkillThrowing) > (2 * MSECS_PER_TENTH))) + { + pClient->m_timeLastSkillThrowing = 0; + if (pClient->m_pSkillThrowingTarg->IsValidUID()) + Effect(EFFECT_BOLT, pClient->m_SkillThrowingAnimID, pClient->m_pSkillThrowingTarg, 18, 1, false, pClient->m_SkillThrowingAnimHue, pClient->m_SkillThrowingAnimRender); + } + // Check targeting timeout, if set if ((pClient->m_Targ_Timeout > 0) && ((iTimeCur - pClient->m_Targ_Timeout) > 0) ) { @@ -5961,6 +5981,7 @@ bool CChar::OnTickPeriodic() CheckLocationEffects(true); } +do_status_update: EXC_SET_BLOCK("update stats"); OnTickStatusUpdate(); EXC_CATCH; diff --git a/src/game/chars/CCharNPC.cpp b/src/game/chars/CCharNPC.cpp index d6cb5a6ac..8f855ef37 100644 --- a/src/game/chars/CCharNPC.cpp +++ b/src/game/chars/CCharNPC.cpp @@ -206,12 +206,12 @@ bool CCharNPC::r_WriteVal( CChar * pChar, lpctstr ptcKey, CSString & sVal ) default: if ( FindTableHeadSorted( ptcKey, CCharPlayer::sm_szLoadKeys, CPC_QTY ) >= 0 ) { - sVal = "0"; + sVal.SetValFalse(); return true; } if ( FindTableSorted( ptcKey, CClient::sm_szLoadKeys, CC_QTY ) >= 0 ) { - sVal = "0"; + sVal.SetValFalse(); return true; } return(false ); diff --git a/src/game/chars/CCharNotoriety.cpp b/src/game/chars/CCharNotoriety.cpp index e5d561103..c3880442b 100644 --- a/src/game/chars/CCharNotoriety.cpp +++ b/src/game/chars/CCharNotoriety.cpp @@ -712,13 +712,13 @@ void CChar::NotoSave_Clear() m_notoSaves.clear(); } -void CChar::NotoSave_Update() +void CChar::NotoSave_Update(bool fCharFullUpdate) { //ADDTOCALLSTACK_DEBUG("CChar::NotoSave_Update"); EXC_TRY("NotoSave_Update"); NotoSave_Clear(); - UpdateMode(); + UpdateMode(fCharFullUpdate, nullptr); UpdatePropertyFlag(); EXC_CATCH; diff --git a/src/game/chars/CCharPlayer.cpp b/src/game/chars/CCharPlayer.cpp index dad515de2..5fa0e5ba6 100644 --- a/src/game/chars/CCharPlayer.cpp +++ b/src/game/chars/CCharPlayer.cpp @@ -198,7 +198,7 @@ bool CCharPlayer::r_WriteVal( CChar * pChar, lpctstr ptcKey, CSString & sVal ) if ( pMyGuild ) sVal.FormatHex((dword)pMyGuild->GetUID()); else - sVal.FormatVal(0); + sVal.SetValFalse(); return true; } else if ( *ptcKey == '.' ) @@ -335,7 +335,7 @@ bool CCharPlayer::r_WriteVal( CChar * pChar, lpctstr ptcKey, CSString & sVal ) default: if ( FindTableSorted( ptcKey, CCharNPC::sm_szLoadKeys, CNC_QTY ) >= 0 ) { - sVal = "0"; + sVal.SetValFalse(); return true; } return false; diff --git a/src/game/chars/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index 5721611b1..fe11e036f 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -2438,7 +2438,7 @@ int CChar::Skill_Hiding( SKTRIG_TYPE stage ) ObjMessage(g_Cfg.GetDefaultMsg(DEFMSG_HIDING_SUCCESS), this); StatFlag_Set(STATF_HIDDEN); Reveal(STATF_INVISIBLE); // clear previous invisibility spell effect (this will not reveal the char because STATF_HIDDEN still set) - UpdateMode(nullptr, true); + UpdateMode(true, nullptr); if ( IsClientActive() ) { GetClientActive()->removeBuff( BI_HIDDEN ); diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index f96cbefad..d68bf7da3 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -701,7 +701,7 @@ void CChar::Spell_Effect_Remove(CItem * pSpell) case LAYER_SPELL_Paralyze: StatFlag_Clear(STATF_FREEZE); - UpdateMode(); // immediately tell the client that now he's able to move (without this, it will be able to move only on next tick update) + UpdateMode(false, nullptr); // immediately tell the client that now he's able to move (without this, it will be able to move only on next tick update) if (pClient) pClient->removeBuff(BI_PARALYZE); return; @@ -1183,7 +1183,7 @@ void CChar::Spell_Effect_Add( CItem * pSpell ) StatFlag_Set(STATF_INVISIBLE); Reveal(STATF_HIDDEN); // clear previous Hiding skill effect (this will not reveal the char because STATF_Invisibility still set) UpdateModeFlag(); - UpdateMode(nullptr, true); + UpdateMode(true, nullptr); if (pClient && IsSetOF(OF_Buffs)) { pClient->removeBuff(BI_INVISIBILITY); @@ -1192,7 +1192,7 @@ void CChar::Spell_Effect_Add( CItem * pSpell ) return; case LAYER_SPELL_Paralyze: StatFlag_Set(STATF_FREEZE); - UpdateMode(); + UpdateMode(false, nullptr); if (pClient && IsSetOF(OF_Buffs)) { pClient->removeBuff(BI_PARALYZE); @@ -2498,7 +2498,7 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo return true; } -CChar * CChar::Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE uiCreature) +CChar * CChar::Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE uiCreature, std::optional iFollowerSlotsOverride) { ADDTOCALLSTACK("CChar::Spell_CanSummon"); //Create the NPC and check if we can actually place it in the world, but do not place it yet. @@ -2618,7 +2618,7 @@ CChar * CChar::Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE u if (IsSetOF(OF_PetSlots)) { - short iFollowerSlots = pChar->GetFollowerSlots(); + short iFollowerSlots = iFollowerSlotsOverride.has_value() ? iFollowerSlotsOverride.value() : pChar->GetFollowerSlots(); if (!FollowersUpdate(pChar, iFollowerSlots, true)) { SysMessageDefault(DEFMSG_PETSLOTS_TRY_SUMMON); @@ -2841,22 +2841,11 @@ bool CChar::Spell_CastDone() CObjBase * pObjSrc = m_Act_Prv_UID.ObjFind(); CChar* pSummon = nullptr; - ITEMID_TYPE iT1 = ITEMID_NOTHING; - ITEMID_TYPE iT2 = ITEMID_NOTHING; - CREID_TYPE iC1 = CREID_INVALID; - HUE_TYPE iColor = HUE_DEFAULT; - - uint fieldWidth = 0; - uint fieldGauge = 0; - uint areaRadius = 0; - SPELL_TYPE spell = m_atMagery.m_iSpell; const CSpellDef * pSpellDef = g_Cfg.GetSpellDef(spell); if (pSpellDef == nullptr) return false; - const bool fIsSpellField = pSpellDef->IsSpellType(SPELLFLAG_FIELD); - int iSkill, iDifficulty; if (!pSpellDef->GetPrimarySkill(&iSkill, &iDifficulty)) return false; @@ -2881,28 +2870,52 @@ bool CChar::Spell_CastDone() if ( (iSkill == SKILL_MYSTICISM) && (g_Cfg.m_iRacialFlags & RACIALF_GARG_MYSTICINSIGHT) && (iSkillLevel < 300) && IsGargoyle() ) iSkillLevel = 300; // Racial trait (Mystic Insight). Gargoyles always have a minimum of 30.0 Mysticism. - CScriptTriggerArgs Args(spell, iSkillLevel, pObjSrc); - Args.m_VarsLocal.SetNum("fieldWidth", 0); - Args.m_VarsLocal.SetNum("fieldGauge", 0); - Args.m_VarsLocal.SetNum("areaRadius", 0); - Args.m_VarsLocal.SetNum("duration", GetSpellDuration(spell, iSkillLevel, this), true); // tenths of second + const bool fIsSpellArea = pSpellDef->IsSpellType(SPELLFLAG_AREA); + const bool fIsSpellField = pSpellDef->IsSpellType(SPELLFLAG_FIELD); + const bool fIsSpellSummon = pSpellDef->IsSpellType(SPELLFLAG_SUMMON); + + HUE_TYPE uiColor = HUE_DEFAULT; + ITEMID_TYPE uiCreatedItemID_1 = ITEMID_NOTHING; + ITEMID_TYPE uiCreatedItemID_2 = ITEMID_NOTHING; + CREID_TYPE uiSummonedCreatureID = CREID_INVALID; + short iFollowerSlotsOverride = -1; + + uint uiFieldWidth = 0; + uint uiFieldGauge = 0; + uint uiAreaRadius = 0; + + CScriptTriggerArgs Args(spell, iSkillLevel, pObjSrc); + Args.m_VarsLocal.SetNum("Duration", GetSpellDuration(spell, iSkillLevel, this), true); // tenths of second + + if (fIsSpellArea) + { + Args.m_VarsLocal.SetNum("AreaRadius", 0); + } if (fIsSpellField) { + Args.m_VarsLocal.SetNum("FieldWidth", 0); + Args.m_VarsLocal.SetNum("FieldGauge", 0); + switch (spell) // Only setting ids and locals for field spells { - case SPELL_Wall_of_Stone: iT1 = ITEMID_STONE_WALL; iT2 = ITEMID_STONE_WALL; break; - case SPELL_Fire_Field: iT1 = ITEMID_FX_FIRE_F_EW; iT2 = ITEMID_FX_FIRE_F_NS; break; - case SPELL_Poison_Field: iT1 = ITEMID_FX_POISON_F_EW; iT2 = ITEMID_FX_POISON_F_NS; break; - case SPELL_Paralyze_Field: iT1 = ITEMID_FX_PARA_F_EW; iT2 = ITEMID_FX_PARA_F_NS; break; - case SPELL_Energy_Field: iT1 = ITEMID_FX_ENERGY_F_EW; iT2 = ITEMID_FX_ENERGY_F_NS; break; + case SPELL_Wall_of_Stone: uiCreatedItemID_1 = ITEMID_STONE_WALL; uiCreatedItemID_2 = ITEMID_STONE_WALL; break; + case SPELL_Fire_Field: uiCreatedItemID_1 = ITEMID_FX_FIRE_F_EW; uiCreatedItemID_2 = ITEMID_FX_FIRE_F_NS; break; + case SPELL_Poison_Field: uiCreatedItemID_1 = ITEMID_FX_POISON_F_EW; uiCreatedItemID_2 = ITEMID_FX_POISON_F_NS; break; + case SPELL_Paralyze_Field: uiCreatedItemID_1 = ITEMID_FX_PARA_F_EW; uiCreatedItemID_2 = ITEMID_FX_PARA_F_NS; break; + case SPELL_Energy_Field: uiCreatedItemID_1 = ITEMID_FX_ENERGY_F_EW; uiCreatedItemID_2 = ITEMID_FX_ENERGY_F_NS; break; default: break; } - Args.m_VarsLocal.SetNum("CreateObject1", iT1, false); - Args.m_VarsLocal.SetNum("CreateObject2", iT2, false); + Args.m_VarsLocal.SetNum("CreateObject1", uiCreatedItemID_1, false); + Args.m_VarsLocal.SetNum("CreateObject2", uiCreatedItemID_2, false); } + if (fIsSpellSummon) + { + Args.m_VarsLocal.SetNum("FollowerSlotsOverride", iFollowerSlotsOverride); + } + if (IsTrigUsed(TRIGGER_SPELLSUCCESS)) { if (OnTrigger(CTRIG_SpellSuccess, this, &Args) == TRIGRET_RET_TRUE) @@ -2925,58 +2938,67 @@ bool CChar::Spell_CastDone() //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"))); - fieldWidth = (uint)Args.m_VarsLocal.GetKeyNum("fieldWidth"); - fieldGauge = (uint)Args.m_VarsLocal.GetKeyNum("fieldGauge"); + uiFieldWidth = (uint)Args.m_VarsLocal.GetKeyNum("FieldWidth"); + uiFieldGauge = (uint)Args.m_VarsLocal.GetKeyNum("FieldGauge"); } - iC1 = (CREID_TYPE)(Args.m_VarsLocal.GetKeyNum("CreateObject1") & 0xFFFF); - areaRadius = (uint)Args.m_VarsLocal.GetKeyNum("areaRadius"); - int iDuration = (int)(Args.m_VarsLocal.GetKeyNum("duration")); - iDuration = maximum(0, iDuration); - iColor = (HUE_TYPE)(Args.m_VarsLocal.GetKeyNum("EffectColor")); + 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")); - if (pSpellDef->IsSpellType(SPELLFLAG_SUMMON)) + if (fIsSpellSummon) { + iFollowerSlotsOverride = n64_narrow_n16(Args.m_VarsLocal.GetKeyNum("FollowerSlotsOverride")); + if (!pSpellDef->IsSpellType(SPELLFLAG_TARG_OBJ | SPELLFLAG_TARG_XYZ)) m_Act_p = GetTopPoint(); - pSummon = Spell_Summon_Try(spell, m_Act_p, iC1); + std::optional iMaybeOverride; + if (iFollowerSlotsOverride != -1) + iMaybeOverride = iFollowerSlotsOverride; + pSummon = Spell_Summon_Try(spell, m_Act_p, uiSummonedCreatureID, iMaybeOverride); if (!pSummon) { return false; } } + // Consume the reagents/mana/scroll/charge if (!Spell_CanCast(spell, false, pObjSrc, true)) + { + if (pSummon) + pSummon->Delete(true); return false; + } if (pSpellDef->IsSpellType(SPELLFLAG_SCRIPTED)) { - if (pSpellDef->IsSpellType(SPELLFLAG_SUMMON)) + if (fIsSpellSummon) { Spell_Summon_Place(pSummon, m_Act_p, iDuration); } else if (fIsSpellField) { - if (iT1 && iT2) + if (uiCreatedItemID_1 && uiCreatedItemID_2) { - if (!fieldWidth) - fieldWidth = 3; - if (!fieldGauge) - fieldGauge = 1; + if (!uiFieldWidth) + uiFieldWidth = 3; + if (!uiFieldGauge) + uiFieldGauge = 1; - Spell_Field(m_Act_p, iT1, iT2, fieldWidth, fieldGauge, iSkillLevel, this, it1test, it2test, iDuration, iColor); + Spell_Field(m_Act_p, uiCreatedItemID_1, uiCreatedItemID_2, uiFieldWidth, uiFieldGauge, iSkillLevel, this, it1test, it2test, iDuration, uiColor); } } - else if (pSpellDef->IsSpellType(SPELLFLAG_AREA)) + else if (fIsSpellArea) { - if (!areaRadius) - areaRadius = 4; + if (!uiAreaRadius) + uiAreaRadius = 4; if (!pSpellDef->IsSpellType(SPELLFLAG_TARG_OBJ | SPELLFLAG_TARG_XYZ)) - Spell_Area(GetTopPoint(), areaRadius, iSkillLevel, iDuration); + Spell_Area(GetTopPoint(), uiAreaRadius, iSkillLevel, iDuration); else - Spell_Area(m_Act_p, areaRadius, iSkillLevel, iDuration); + Spell_Area(m_Act_p, uiAreaRadius, iSkillLevel, iDuration); } else if (pSpellDef->IsSpellType(SPELLFLAG_POLY)) return false; @@ -2988,45 +3010,45 @@ bool CChar::Spell_CastDone() } else if (fIsSpellField) { - if (!fieldWidth) - fieldWidth = 3; - if (!fieldGauge) - fieldGauge = 1; + if (!uiFieldWidth) + uiFieldWidth = 3; + if (!uiFieldGauge) + uiFieldGauge = 1; - Spell_Field(m_Act_p, iT1, iT2, fieldWidth, fieldGauge, iSkillLevel, this, it1test, it2test, iDuration, iColor); + Spell_Field(m_Act_p, uiCreatedItemID_1, uiCreatedItemID_2, uiFieldWidth, uiFieldGauge, iSkillLevel, this, it1test, it2test, iDuration, uiColor); } - else if (pSpellDef->IsSpellType(SPELLFLAG_AREA)) + else if (fIsSpellArea) { - if (!areaRadius) + if (!uiAreaRadius) { switch (spell) { - case SPELL_Arch_Cure: areaRadius = 2; break; - case SPELL_Arch_Prot: areaRadius = 3; break; - case SPELL_Mass_Curse: areaRadius = 2; break; - case SPELL_Reveal: areaRadius = 1 + (iSkillLevel / 200); break; - case SPELL_Chain_Lightning: areaRadius = 2; break; - case SPELL_Mass_Dispel: areaRadius = 8; break; - case SPELL_Meteor_Swarm: areaRadius = 2; break; - case SPELL_Earthquake: areaRadius = 1 + (iSkillLevel / 150); break; - case SPELL_Poison_Strike: areaRadius = 2; break; - case SPELL_Wither: areaRadius = 4; break; - default: areaRadius = 4; break; + case SPELL_Arch_Cure: uiAreaRadius = 2; break; + case SPELL_Arch_Prot: uiAreaRadius = 3; break; + case SPELL_Mass_Curse: uiAreaRadius = 2; break; + case SPELL_Reveal: uiAreaRadius = 1 + (iSkillLevel / 200); break; + case SPELL_Chain_Lightning: uiAreaRadius = 2; break; + case SPELL_Mass_Dispel: uiAreaRadius = 8; break; + case SPELL_Meteor_Swarm: uiAreaRadius = 2; break; + case SPELL_Earthquake: uiAreaRadius = 1 + (iSkillLevel / 150); break; + case SPELL_Poison_Strike: uiAreaRadius = 2; break; + case SPELL_Wither: uiAreaRadius = 4; break; + default: uiAreaRadius = 4; break; } } if (!pSpellDef->IsSpellType(SPELLFLAG_TARG_OBJ | SPELLFLAG_TARG_XYZ)) - Spell_Area(GetTopPoint(), areaRadius, iSkillLevel, iDuration); + Spell_Area(GetTopPoint(), uiAreaRadius, iSkillLevel, iDuration); else - Spell_Area(m_Act_p, areaRadius, iSkillLevel, iDuration); + Spell_Area(m_Act_p, uiAreaRadius, iSkillLevel, iDuration); } - else if (pSpellDef->IsSpellType(SPELLFLAG_SUMMON)) + else if (fIsSpellSummon) { Spell_Summon_Place(pSummon, m_Act_p, iDuration); } else { - iT1 = it1test; // Set iT1 to it1test here because spell_field() needed both values to be passed. + uiCreatedItemID_1 = it1test; // Set iT1 to it1test here because spell_field() needed both values to be passed. switch (spell) { @@ -3035,7 +3057,7 @@ bool CChar::Spell_CastDone() case SPELL_Create_Food: { const CResourceID ridFoodDefault = g_Cfg.ResourceGetIDType(RES_ITEMDEF, "DEFFOOD"); - const ITEMID_TYPE idFood = ((iT1 > ITEMID_NOTHING) ? iT1 : (ITEMID_TYPE)(ridFoodDefault.GetResIndex())); + const ITEMID_TYPE idFood = ((uiCreatedItemID_1 > ITEMID_NOTHING) ? uiCreatedItemID_1 : (ITEMID_TYPE)(ridFoodDefault.GetResIndex())); CItem *pItem = CItem::CreateScript(idFood, this); ASSERT(pItem); if (pSpellDef->IsSpellType(SPELLFLAG_TARG_OBJ|SPELLFLAG_TARG_XYZ)) @@ -3112,12 +3134,12 @@ bool CChar::Spell_CastDone() case SPELL_Flame_Strike: { // Display spell. - if (!iT1) - iT1 = ITEMID_FX_FLAMESTRIKE; + if (!uiCreatedItemID_1) + uiCreatedItemID_1 = ITEMID_FX_FLAMESTRIKE; if (pObj == nullptr) { - EffectLocation(EFFECT_XYZ, iT1, nullptr, &m_Act_p, 20, 30); + EffectLocation(EFFECT_XYZ, uiCreatedItemID_1, nullptr, &m_Act_p, 20, 30); } else { diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index 2dd9cfbdd..32c20165e 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -486,13 +486,14 @@ int CChar::GetStatPercent(STAT_TYPE i) const return IMulDiv(Stat_GetVal(i), 100, maxval); } - +[[nodiscard]] RETURNS_NOTNULL const CObjBaseTemplate* CChar::GetTopLevelObj() const { // Get the object that has a location in the world. (Ground level) return this; } +[[nodiscard]] RETURNS_NOTNULL CObjBaseTemplate* CChar::GetTopLevelObj() { // Get the object that has a location in the world. (Ground level) @@ -692,7 +693,7 @@ byte CChar::GetModeFlag( const CClient *pViewer ) const 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 + mode |= CHARMODE_INVIS; //When sending CHARMODE_INVIS state to client, your character anim are grey return mode; } diff --git a/src/game/chars/CCharUse.cpp b/src/game/chars/CCharUse.cpp index 8b9599e2f..56023d9f1 100644 --- a/src/game/chars/CCharUse.cpp +++ b/src/game/chars/CCharUse.cpp @@ -1125,9 +1125,9 @@ CChar * CChar::Use_Figurine( CItem * pItem, bool fCheckFollowerSlots ) std::optional iFollowerSlots; const bool fShouldCheckFollowerSlots = (fCheckFollowerSlots && IsSetOF(OF_PetSlots)); - auto _CheckFollowerSlots = [this](short iFollowerSlots) -> bool + auto _CheckFollowerSlots = [this](short iFollowerSlots_) -> bool { - if ( !FollowersUpdate(this, iFollowerSlots, true) ) + if ( !FollowersUpdate(this, iFollowerSlots_, true) ) { SysMessageDefault(DEFMSG_PETSLOTS_TRY_CONTROL); return false; diff --git a/src/game/clients/CAccount.cpp b/src/game/clients/CAccount.cpp index a7a82c695..4d4eca1b0 100644 --- a/src/game/clients/CAccount.cpp +++ b/src/game/clients/CAccount.cpp @@ -73,7 +73,7 @@ bool CAccounts::Account_LoadAll( bool fChanges, bool fClearChanges ) char *z = Str_GetTemp(); pszBaseDir = g_Cfg.m_sAcctBaseDir.IsEmpty() ? g_Cfg.m_sWorldBaseDir : g_Cfg.m_sAcctBaseDir; - pszBaseName = ( fChanges ) ? (SPHERE_FILE "acct" SPHERE_SCRIPT) : (SPHERE_FILE "accu" SPHERE_SCRIPT); + pszBaseName = ( fChanges ) ? (SPHERE_FILE "acct" SPHERE_SCRIPT_EXT) : (SPHERE_FILE "accu" SPHERE_SCRIPT_EXT); Str_CopyLimitNull(z, pszBaseDir, Str_TempLength()); Str_ConcatLimitNull(z, pszBaseName, Str_TempLength()); @@ -104,7 +104,7 @@ bool CAccounts::Account_LoadAll( bool fChanges, bool fClearChanges ) if (!s.Open(nullptr, OF_WRITE | OF_TEXT | OF_DEFAULTMODE)) return false; - s.WriteString( "// Accounts are periodically moved to the " SPHERE_FILE "accu" SPHERE_SCRIPT " file.\n" + s.WriteString( "// Accounts are periodically moved to the " SPHERE_FILE "accu" SPHERE_SCRIPT_EXT " file.\n" "// All account changes should be made here.\n" "// Use the /ACCOUNT UPDATE command to force accounts to update.\n" ); @@ -142,7 +142,7 @@ bool CAccounts::Account_SaveAll() s.Printf("// " SPHERE_TITLE " %s accounts file\n" "// NOTE: This file cannot be edited while the server is running.\n" - "// Any file changes must be made to " SPHERE_FILE "accu" SPHERE_SCRIPT ". This is read in at save time.\n", + "// Any file changes must be made to " SPHERE_FILE "accu" SPHERE_SCRIPT_EXT ". This is read in at save time.\n", g_Serv.GetName()); for ( size_t i = 0; i < m_Accounts.size(); ++i ) diff --git a/src/game/clients/CClientDialog.cpp b/src/game/clients/CClientDialog.cpp index d75c49988..a7fd691ed 100644 --- a/src/game/clients/CClientDialog.cpp +++ b/src/game/clients/CClientDialog.cpp @@ -125,7 +125,9 @@ bool CClient::addGumpDialogProps( const CUID& uid ) addSkillWindow((SKILL_TYPE)(g_Cfg.m_iMaxSkill), true); tchar *pszMsg = Str_GetTemp(); - strcpy(pszMsg, pObj->IsItem() ? "D_ITEMPROP1" : "D_CHARPROP1" ); + Str_CopyLimitNull(pszMsg, + (pObj->IsItem() ? "D_ITEMPROP1" : "D_CHARPROP1"), + Str_TempLength()); CResourceID rid = g_Cfg.ResourceGetIDType(RES_DIALOG, pszMsg); if ( ! rid.IsValidUID()) diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index 5764f0635..c4ecc42ff 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -1032,7 +1032,7 @@ void CClient::Event_CombatMode( bool fWar ) // Only for switching to combat mode } addPlayerWarMode(); - m_pChar->UpdateMode( this, m_pChar->IsStatFlag( STATF_DEAD )); + m_pChar->UpdateMode(m_pChar->IsStatFlag(STATF_DEAD), this); } bool CClient::Event_Command(lpctstr pszCommand, TALKMODE_TYPE mode) @@ -2236,7 +2236,7 @@ bool CDialogResponseArgs::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConso ptcKey += 6; SKIP_SEPARATORS(ptcKey); - size_t iQty = m_CheckArray.size(); + const size_t iQty = m_CheckArray.size(); if ( ptcKey[0] == '\0' ) { sVal.FormatSTVal(iQty); @@ -2254,17 +2254,17 @@ bool CDialogResponseArgs::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConso return true; } - dword dwNum = Exp_GetDWSingle( ptcKey ); + const dword dwNum = Exp_GetDWSingle( ptcKey ); SKIP_SEPARATORS(ptcKey); for ( uint i = 0; i < iQty; ++i ) { if ( dwNum == m_CheckArray[i] ) { - sVal = "1"; + sVal.SetValTrue(); return true; } } - sVal = "0"; + sVal.SetValFalse(); return true; } if ( ! strnicmp( ptcKey, "ARGTXT", 6 )) @@ -2272,14 +2272,14 @@ bool CDialogResponseArgs::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConso ptcKey += 6; SKIP_SEPARATORS(ptcKey); - size_t iQty = m_TextArray.size(); + const size_t iQty = m_TextArray.size(); if ( ptcKey[0] == '\0' ) { sVal.FormatSTVal(iQty); return true; } - dword dwNum = Exp_GetDWSingle( ptcKey ); + const dword dwNum = Exp_GetDWSingle( ptcKey ); SKIP_SEPARATORS(ptcKey); for ( uint i = 0; i < iQty; ++i ) diff --git a/src/game/clients/CClientLog.cpp b/src/game/clients/CClientLog.cpp index f3a84daf8..784ee801d 100644 --- a/src/game/clients/CClientLog.cpp +++ b/src/game/clients/CClientLog.cpp @@ -748,7 +748,7 @@ bool CClient::OnRxWebPageRequest( byte * pRequest, size_t uiLen ) tchar * ppRequest[4]; int iQtyArgs = Str_ParseCmds(ppLines[0], ppRequest, ARRAY_COUNT(ppRequest), " "); - if (( iQtyArgs < 2 ) || ( strlen(ppRequest[1]) >= _MAX_PATH )) + if (( iQtyArgs < 2 ) || ( strlen(ppRequest[1]) >= SPHERE_MAX_PATH )) return false; if ( strchr(ppRequest[1], '\r') || strchr(ppRequest[1], 0x0c) ) @@ -829,7 +829,7 @@ bool CClient::OnRxWebPageRequest( byte * pRequest, size_t uiLen ) // Host: localhost:2593\r\n // \r\n - tchar szPageName[_MAX_PATH]; + tchar szPageName[SPHERE_MAX_PATH]; if ( !Str_GetBare( szPageName, Str_TrimWhitespace(ppRequest[1]), sizeof(szPageName), "!\"#$%&()*,:;<=>?[]^{|}-+'`" ) ) return false; diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index 978b5affe..d2e6f6611 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -2722,7 +2722,7 @@ void CClient::SendPacket( tchar * ptcKey ) while ( *ptcKey ) { if ( packet->getLength() > SCRIPT_MAX_LINE_LEN - 4 ) - { // we won't get here because this lenght is enforced in all scripts + { // we won't get here because this length is enforced in all scripts DEBUG_ERR(("SENDPACKET too big.\n")); delete packet; @@ -2734,7 +2734,7 @@ void CClient::SendPacket( tchar * ptcKey ) if ( toupper(*ptcKey) == 'D' ) { ++ptcKey; - dword iVal = Exp_GetVal(ptcKey); + dword iVal = Exp_GetDWVal(ptcKey); packet->writeInt32(iVal); } diff --git a/src/game/clients/CClientTooltip.cpp b/src/game/clients/CClientTooltip.cpp index 036865271..5f72b3ffa 100644 --- a/src/game/clients/CClientTooltip.cpp +++ b/src/game/clients/CClientTooltip.cpp @@ -21,7 +21,7 @@ CClientTooltip::CClientTooltip(dword dwClilocID, int64 iArgs) snprintf(m_args, MAX_TOOLTIP_LEN - 1, "%" PRId64, iArgs); } -void __cdecl CClientTooltip::FormatArgs(lpctstr format, ...) +void CClientTooltip::FormatArgs(lpctstr format, ...) { va_list vargs; va_start( vargs, format ); diff --git a/src/game/clients/CClientTooltip.h b/src/game/clients/CClientTooltip.h index 350e02ada..0375961ab 100644 --- a/src/game/clients/CClientTooltip.h +++ b/src/game/clients/CClientTooltip.h @@ -28,7 +28,7 @@ class CClientTooltip CClientTooltip& operator=(const CClientTooltip& other) = delete; public: - void __cdecl FormatArgs(lpctstr format, ...) __printfargs(2,3); + void FormatArgs(lpctstr format, ...) SPHERE_PRINTFARGS(2,3); }; diff --git a/src/game/clients/CParty.cpp b/src/game/clients/CParty.cpp index 0a4dfdf91..047eeece0 100644 --- a/src/game/clients/CParty.cpp +++ b/src/game/clients/CParty.cpp @@ -614,7 +614,7 @@ bool CPartyDef::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole *pSrc, { if ( pRef == nullptr ) // good command but bad link. { - sVal = "0"; + sVal.SetValFalse(); return true; } if ( ptcKey[0] == '\0' ) // we where just testing the ref. @@ -623,7 +623,7 @@ bool CPartyDef::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole *pSrc, if ( pObj ) sVal.FormatHex((dword)pObj->GetUID()); else - sVal.FormatVal(1); + sVal.SetValTrue(); return true; } return pRef->r_WriteVal(ptcKey, sVal, pSrc); diff --git a/src/game/components/CCChampion.cpp b/src/game/components/CCChampion.cpp index b3b7cf164..98266c28a 100644 --- a/src/game/components/CCChampion.cpp +++ b/src/game/components/CCChampion.cpp @@ -959,7 +959,7 @@ bool CCChampion::r_WriteVal(lpctstr ptcKey, CSString& sVal, CTextConsole* pSrc) } else { - sVal.FormatVal(0); + sVal.SetValFalse(); } break; } diff --git a/src/game/components/CCMultiMovable.cpp b/src/game/components/CCMultiMovable.cpp index 53666a36b..95c8efc01 100644 --- a/src/game/components/CCMultiMovable.cpp +++ b/src/game/components/CCMultiMovable.cpp @@ -1317,7 +1317,7 @@ bool CCMultiMovable::r_WriteVal(lpctstr ptcKey, CSString & sVal, CTextConsole * if (pItemThis->m_itShip.m_Pilot.IsValidUID()) sVal.FormatHex(pItemThis->m_itShip.m_Pilot); else - sVal.FormatVal(0); + sVal.SetValFalse(); } break; diff --git a/src/game/components/CCSpawn.cpp b/src/game/components/CCSpawn.cpp index 7abcfe81a..12ddc0cda 100644 --- a/src/game/components/CCSpawn.cpp +++ b/src/game/components/CCSpawn.cpp @@ -158,12 +158,12 @@ const CResourceDef* CCSpawn::_FixDef() if (pItem->IsType(IT_SPAWN_CHAR)) { - auto _TryChar = [this](CREID_TYPE idChar) -> const CResourceDef* + auto _TryChar = [this](CREID_TYPE idChar_) -> const CResourceDef* { - const CResourceDef* pResDef = CCharBase::FindCharBase(idChar); - if (pResDef) - _idSpawn = CResourceIDBase(RES_CHARDEF, idChar); - return pResDef; + const CResourceDef* pResDef_ = CCharBase::FindCharBase(idChar_); + if (pResDef_) + _idSpawn = CResourceIDBase(RES_CHARDEF, idChar_); + return pResDef_; }; CREID_TYPE idChar = (CREID_TYPE)iIndex; @@ -204,12 +204,12 @@ const CResourceDef* CCSpawn::_FixDef() } else if (pItem->IsType(IT_SPAWN_ITEM)) { - auto _TryItem = [this](ITEMID_TYPE idItem) -> const CResourceDef* + auto _TryItem = [this](ITEMID_TYPE idItem_) -> const CResourceDef* { - const CResourceDef* pResDef = CItemBase::FindItemBase(idItem); - if (pResDef) - _idSpawn = CResourceIDBase(RES_ITEMDEF, idItem); - return pResDef; + const CResourceDef* pResDef_ = CItemBase::FindItemBase(idItem_); + if (pResDef_) + _idSpawn = CResourceIDBase(RES_ITEMDEF, idItem_); + return pResDef_; }; ITEMID_TYPE idItem = (ITEMID_TYPE)iIndex; @@ -865,7 +865,7 @@ bool CCSpawn::r_WriteVal(lpctstr ptcKey, CSString & sVal, CTextConsole *pSrc) } else { - sVal.FormatVal(0); + sVal.SetValFalse(); } return true; } diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index e4696be88..fba785497 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -367,7 +367,7 @@ CItem * CItem::CreateBase( ITEMID_TYPE id, IT_TYPE type ) // static ASSERT(pItem); pItem->SetType(type, false); - if (idErrorMsg && idErrorMsg != (ITEMID_TYPE)-1) + if (idErrorMsg && idErrorMsg != (ITEMID_TYPE)-1) DEBUG_ERR(("CreateBase invalid item ID=0%" PRIx32 ", defaulting to ID=0%" PRIx32 ". Created UID=0%" PRIx32 "\n", idErrorMsg, id, (dword)pItem->GetUID())); return pItem; @@ -1953,9 +1953,17 @@ lpctstr CItem::GetNameFull( bool fIdentified ) const // Who is it stolen from ? const CChar * pChar = m_uidLink.CharFind(); if ( pChar ) - len += snprintf(pTemp + len, Str_TempLength() - len, " (%s %s)", g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_STOLEN_FROM ), pChar->GetName()); - else - len += snprintf(pTemp + len, Str_TempLength() - len, " (%s)", g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_STOLEN ) ); + { + /*len +=*/ snprintf(pTemp + len, Str_TempLength() - len, + " (%s %s)", + g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_STOLEN_FROM ), pChar->GetName()); + } + else + { + /*len +=*/ snprintf(pTemp + len, Str_TempLength() - len, + " (%s)", + g_Cfg.GetDefaultMsg( DEFMSG_ITEMTITLE_STOLEN ) ); + } } return pTemp; @@ -2805,7 +2813,7 @@ bool CItem::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo if ( id != GetDispID() ) sVal.FormatHex( id ); else - sVal.FormatVal(0); + sVal.SetValFalse(); } break; case IC_HEIGHT: @@ -4097,6 +4105,7 @@ CItem* CItem::GetTopContainer() return (pItem == this) ? nullptr : pItem; } +[[nodiscard]] RETURNS_NOTNULL const CObjBaseTemplate* CItem::GetTopLevelObj() const { // recursively get the item that is at "top" level. @@ -4108,6 +4117,7 @@ const CObjBaseTemplate* CItem::GetTopLevelObj() const return pObj->GetTopLevelObj(); } +[[nodiscard]] RETURNS_NOTNULL CObjBaseTemplate* CItem::GetTopLevelObj() { // recursively get the item that is at "top" level. @@ -5060,7 +5070,8 @@ lpctstr CItem::Use_SpyGlass( CChar * pUser ) const WEATHER_TYPE wtWeather = ptCoords.GetSector()->GetWeather(); byte iLight = ptCoords.GetSector()->GetLight(); CSString sSearch; - tchar *pResult = Str_GetTemp(); + tchar *pResult = Str_GetTemp(); + size_t uiResultStrAvailableLen = Str_TempLength(); // Weather bonus double rWeatherSight = wtWeather == WEATHER_RAIN ? (0.25 * bSight) : 0.0; @@ -5118,7 +5129,7 @@ lpctstr CItem::Use_SpyGlass( CChar * pUser ) const sSearch = g_Cfg.GetDefaultMsg(DEFMSG_USE_SPYGLASS_WEATHER); else sSearch = g_Cfg.GetDefaultMsg(DEFMSG_USE_SPYGLASS_NO_LAND); - Str_CopyLimitNull( pResult, sSearch, Str_TempLength() ); + SATURATING_SUB_SELF(uiResultStrAvailableLen, Str_CopyLimitNull( pResult, sSearch, uiResultStrAvailableLen )); break; } @@ -5182,7 +5193,7 @@ lpctstr CItem::Use_SpyGlass( CChar * pUser ) const sSearch.Format(g_Cfg.GetDefaultMsg(DEFMSG_SHIP_SEEN_SHIP_SINGLE), pBoatSighted->GetName(), CPointBase::sm_szDirs[dir]); else sSearch.Format(g_Cfg.GetDefaultMsg(DEFMSG_SHIP_SEEN_SHIP_MANY), CPointBase::sm_szDirs[dir]); - strcat( pResult, sSearch); + SATURATING_SUB_SELF(uiResultStrAvailableLen, Str_ConcatLimitNull(pResult, sSearch, uiResultStrAvailableLen)); } if (iItemSighted) // Report item sightings, also boats beyond the boat visibility range in the radar screen @@ -5203,8 +5214,8 @@ lpctstr CItem::Use_SpyGlass( CChar * pUser ) const else sSearch.Format(g_Cfg.GetDefaultMsg(DEFMSG_SHIP_SEEN_SPECIAL_DIR), pItemSighted->GetNameFull(false), static_cast(CPointBase::sm_szDirs[ dir ])); } - strcat( pResult, sSearch); - } + SATURATING_SUB_SELF(uiResultStrAvailableLen, Str_ConcatLimitNull(pResult, sSearch, uiResultStrAvailableLen)); + } // Check for creatures Area->RestartSearch(); @@ -5238,8 +5249,8 @@ lpctstr CItem::Use_SpyGlass( CChar * pUser ) const sSearch.Format(g_Cfg.GetDefaultMsg(DEFMSG_SHIP_SEEN_CREAT_SINGLE), CPointBase::sm_szDirs[dir] ); else sSearch.Format(g_Cfg.GetDefaultMsg(DEFMSG_SHIP_SEEN_CREAT_MANY), CPointBase::sm_szDirs[dir] ); - strcat( pResult, sSearch); - } + Str_ConcatLimitNull(pResult, sSearch, uiResultStrAvailableLen); + } return pResult; } @@ -5553,7 +5564,7 @@ bool CItem::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, spell = (SPELL_TYPE)(Args.m_iN1); iSkillLevel = (int)(Args.m_iN2); pSpellDef = g_Cfg.GetSpellDef( spell ); - + ASSERT(pSpellDef); if ( IsType(IT_WAND) ) // try to recharge the wand. { diff --git a/src/game/items/CItem.h b/src/game/items/CItem.h index 1ee537082..7377fdc5a 100644 --- a/src/game/items/CItem.h +++ b/src/game/items/CItem.h @@ -770,8 +770,10 @@ protected: virtual void _SetTimeout(int64 iMsecs) override final; bool MoveToCheck(const CPointMap & pt, CChar * pCharMover = nullptr ); virtual bool MoveNearObj( const CObjBaseTemplate *pItem, ushort uiSteps = 0 ) override; - virtual CObjBaseTemplate* GetTopLevelObj() override; - virtual const CObjBaseTemplate* GetTopLevelObj() const override; + [[nodiscard]] RETURNS_NOTNULL + virtual CObjBaseTemplate* GetTopLevelObj() override; + [[nodiscard]] RETURNS_NOTNULL + virtual const CObjBaseTemplate* GetTopLevelObj() const override; CObjBase * GetContainer() const noexcept; CItem * GetTopContainer(); diff --git a/src/game/items/CItemBase.cpp b/src/game/items/CItemBase.cpp index ad6fc1cf6..1d888e94a 100644 --- a/src/game/items/CItemBase.cpp +++ b/src/game/items/CItemBase.cpp @@ -1364,7 +1364,7 @@ bool CItemBase::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc return false; if ( !CCPropsItemWeapon::CanSubscribe(this)) - sVal.FormatVal(0); + sVal.SetValFalse(); else sVal.FormatVal( m_layer == LAYER_HAND2 ); break; diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index e4b8c7b87..01cf07eca 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -2690,7 +2690,7 @@ bool CItemMulti::r_WriteVal(lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc break; } } - sVal.FormatVal(0); + sVal.SetValFalse(); break; } case SHL_REGION: diff --git a/src/game/items/CItemMultiCustom.cpp b/src/game/items/CItemMultiCustom.cpp index 22e84d1d2..3cf453ec2 100644 --- a/src/game/items/CItemMultiCustom.cpp +++ b/src/game/items/CItemMultiCustom.cpp @@ -1894,7 +1894,7 @@ uint8 CItemMultiCustom::GetPlane(const CMultiComponent * pComponent) int8 CItemMultiCustom::GetPlaneZ(uint8 plane) { - return n32_narrow_n8_checked(7 + ((plane - 1) * 20)); + return n32_narrow_n8_checked(7 + ((plane - 1) * 20), true); } bool CItemMultiCustom::IsValidItem(ITEMID_TYPE id, CClient * pClientSrc, bool fMulti) diff --git a/src/game/items/CItemShip.cpp b/src/game/items/CItemShip.cpp index 620a02209..db9bea489 100644 --- a/src/game/items/CItemShip.cpp +++ b/src/game/items/CItemShip.cpp @@ -158,7 +158,7 @@ bool CItemShip::r_WriteVal(lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, if (pItemHold) sVal.FormatHex(pItemHold->GetUID()); else - sVal.FormatVal(0); + sVal.SetValFalse(); } break; case IMCS_PLANK: @@ -174,7 +174,7 @@ bool CItemShip::r_WriteVal(lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, if (pTiller) sVal.FormatHex(pTiller->GetUID()); else - sVal.FormatVal(0); + sVal.SetValFalse(); } break; default: diff --git a/src/game/items/CItemStone.cpp b/src/game/items/CItemStone.cpp index 506f95078..3a51112bf 100644 --- a/src/game/items/CItemStone.cpp +++ b/src/game/items/CItemStone.cpp @@ -492,7 +492,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr SKIP_SEPARATORS(pszCmd); CStoneMember * pMember = static_cast (GetContainerHead()); - sVal.FormatVal(0); + sVal.SetValFalse(); for ( int i = 0 ; pMember != nullptr; pMember = pMember->GetNext() ) { @@ -515,7 +515,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr else if ( !strnicmp("memberfromuid.", ptcKey, 14) ) { lpctstr pszCmd = ptcKey + 14; - sVal.FormatVal(0); + sVal.SetValFalse(); if ( !pszCmd[0] ) return true; @@ -580,7 +580,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr SKIP_SEPARATORS(pszCmd); CStoneMember * pMember = static_cast (GetContainerHead()); - sVal.FormatVal(0); + sVal.SetValFalse(); for ( int i = 0 ; pMember != nullptr; pMember = pMember->GetNext() ) { @@ -603,7 +603,7 @@ bool CItemStone::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSr else if ( !strnicmp("guildfromuid.", ptcKey, 13) ) { lpctstr pszCmd = ptcKey + 13; - sVal.FormatVal(0); + sVal.SetValFalse(); if ( !pszCmd[0] ) return true; diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 350d633ae..6f7fdeb5a 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -71,12 +71,8 @@ GlobalInitializer::GlobalInitializer() //--- Exception handling - // Set exception catcher? -#if defined(_WIN32) && defined(_MSC_VER) && !defined(_NIGHTLYBUILD) - // We don't need an exception translator for the Debug build, since that build would, generally, be used with a debugger. - // We don't want that for Release build either because, in order to call _set_se_translator, we should set the /EHa - // compiler flag, which slows down code a bit. - SetExceptionTranslator(); +#ifdef WINDOWS_SPHERE_SHOULD_HANDLE_STRUCTURED_EXCEPTIONS + SetWindowsStructuredExceptionTranslator(); #endif // Set function to handle the invalid case where a pure virtual function is called. @@ -164,9 +160,8 @@ CSStringList g_AutoComplete; // auto-complete list CScriptProfiler g_profiler; // script profiler CUOMapList g_MapList; // global maps information -MainThread g_Main; -extern PingServer g_PingServer; -extern CDataBaseAsyncHelper g_asyncHdb; +static MainThread g_Main; +static PingServer g_PingServer; @@ -174,7 +169,7 @@ extern CDataBaseAsyncHelper g_asyncHdb; // Main server loop MainThread::MainThread() - : AbstractSphereThread("T_Main", IThread::RealTime) + : AbstractSphereThread("T_Main", ThreadPriority::RealTime) { m_profile.EnableProfile(PROFILE_NETWORK_RX); m_profile.EnableProfile(PROFILE_CLIENTS); @@ -193,7 +188,6 @@ MainThread::MainThread() void MainThread::onStart() { AbstractSphereThread::onStart(); - SetExceptionTranslator(); } void MainThread::tick() @@ -213,16 +207,16 @@ bool MainThread::shouldExit() noexcept static bool WritePidFile(int iMode = 0) { - lpctstr file = SPHERE_FILE ".pid"; + lpctstr fileName = SPHERE_FILE ".pid"; FILE* pidFile; if (iMode == 1) // delete { - return (STDFUNC_UNLINK(file) == 0); + return (STDFUNC_UNLINK(fileName) == 0); } else if (iMode == 2) // check for .pid file { - pidFile = fopen(file, "r"); + pidFile = fopen(fileName, "r"); if (pidFile) { g_Log.Event(LOGM_INIT, SPHERE_FILE ".pid already exists. Secondary launch or unclean shutdown?\n"); @@ -232,7 +226,7 @@ static bool WritePidFile(int iMode = 0) } else { - pidFile = fopen(file, "w"); + pidFile = fopen(fileName, "w"); if (pidFile) { pid_t spherepid = STDFUNC_GETPID(); @@ -450,7 +444,7 @@ static void Sphere_MainMonitorLoop() if ( g_Serv.GetExitFlag() ) break; - Sleep(1000); + SLEEP(1000); } EXC_SET_BLOCK("Checks"); @@ -469,304 +463,6 @@ static void Sphere_MainMonitorLoop() } -//****************************************************** - -static void dword_q_sort(dword *numbers, dword left, dword right) -{ - dword pivot, l_hold, r_hold; - - l_hold = left; - r_hold = right; - pivot = numbers[left]; - while (left < right) - { - while ((numbers[right] >= pivot) && (left < right)) right--; - if (left != right) - { - numbers[left] = numbers[right]; - left++; - } - while ((numbers[left] <= pivot) && (left < right)) left++; - if (left != right) - { - numbers[right] = numbers[left]; - right--; - } - } - numbers[left] = pivot; - pivot = left; - left = l_hold; - right = r_hold; - if (left < pivot) - dword_q_sort(numbers, left, pivot-1); - if (right > pivot) - dword_q_sort(numbers, pivot+1, right); -} - -void defragSphere(char *path) -{ - ASSERT(path != nullptr); - - constexpr size_t mb10 = 10*1024*1024; - constexpr size_t mb5 = 5*1024*1024; - - CSFileText inf; - CSFile ouf; - char file_buf[1024]; - char path_buf[_MAX_PATH], path_buf_2[_MAX_PATH]; - char *str_ptr = nullptr, *str_ptr_2 = nullptr; - - g_Log.Event(LOGM_INIT, "Defragmentation (UID alteration) of " SPHERE_TITLE " saves.\n" - "Use it on your risk and if you know what you are doing since it can possibly harm your server.\n" - "The process can take up to several hours depending on the CPU you have.\n" - "After finished, you will have your '" SPHERE_FILE "*.scp' files converted and saved as '" SPHERE_FILE "*.scp.new'.\n"); - - // UID_O_INDEX_MASK ? - constexpr dword MAX_UID = 40'000'000U; // limit to 40mln of objects, takes 40mln*4bytes ~= 160mb - - dword dwIdxUID = 0; - dword* puids = (dword*)calloc(MAX_UID, sizeof(dword)); - for ( uint i = 0; i < 3; ++i ) - { - Str_CopyLimitNull(path_buf, path, sizeof(path_buf)); - if ( i == 0 ) strcat(path_buf, SPHERE_FILE "statics" SPHERE_SCRIPT); - else if ( i == 1 ) strcat(path_buf, SPHERE_FILE "world" SPHERE_SCRIPT); - else strcat(path_buf, SPHERE_FILE "chars" SPHERE_SCRIPT); - - g_Log.Event(LOGM_INIT, "Reading current UIDs: %s\n", path_buf); - if ( !inf.Open(path_buf, OF_READ|OF_TEXT|OF_DEFAULTMODE) ) - { - g_Log.Event(LOGM_INIT, "Cannot open file for reading. Skipped!\n"); - continue; - } - size_t uiBytesRead = 0, uiTotalMb = 0; - while ((dwIdxUID < MAX_UID) && !feof(inf._pStream)) - { - fgets(file_buf, sizeof(file_buf), inf._pStream); - uiBytesRead += strlen(file_buf); - if ( uiBytesRead > mb10 ) - { - uiBytesRead -= mb10; - uiTotalMb += 10; - g_Log.Event(LOGM_INIT, "Total read %" PRIuSIZE_T " Mb\n", uiTotalMb); - } - if (( file_buf[0] == 'S' ) && ( strstr(file_buf, "SERIAL=") == file_buf )) - { - str_ptr = file_buf + 7; - str_ptr_2 = str_ptr; - while (*str_ptr_2 && (*str_ptr_2 != '\r') && (*str_ptr_2 != '\n')) - { - ++str_ptr_2; - } - *str_ptr_2 = 0; - - // prepare new uid - *(str_ptr-1) = '0'; - *str_ptr = 'x'; - --str_ptr; - puids[dwIdxUID++] = strtoul(str_ptr, &str_ptr_2, 16); - } - } - inf.Close(); - } - const dword dwTotalUIDs = dwIdxUID; - g_Log.Event(LOGM_INIT, "Totally having %" PRIu32 " unique objects (UIDs), latest: 0%x\n", dwTotalUIDs, puids[dwTotalUIDs-1]); - - g_Log.Event(LOGM_INIT, "Quick-Sorting the UIDs array...\n"); - dword_q_sort(puids, 0, dwTotalUIDs -1); - - for ( uint i = 0; i < 5; ++i ) - { - Str_CopyLimitNull(path_buf, path, sizeof(path_buf)); - if ( !i ) strcat(path_buf, SPHERE_FILE "accu.scp"); - else if ( i == 1 ) strcat(path_buf, SPHERE_FILE "chars" SPHERE_SCRIPT); - else if ( i == 2 ) strcat(path_buf, SPHERE_FILE "data" SPHERE_SCRIPT); - else if ( i == 3 ) strcat(path_buf, SPHERE_FILE "world" SPHERE_SCRIPT); - else if ( i == 4 ) strcat(path_buf, SPHERE_FILE "statics" SPHERE_SCRIPT); - g_Log.Event(LOGM_INIT, "Updating UID-s in %s to %s.new\n", path_buf, path_buf); - if ( !inf.Open(path_buf, OF_READ|OF_TEXT|OF_DEFAULTMODE) ) - { - g_Log.Event(LOGM_INIT, "Cannot open file for reading. Skipped!\n"); - continue; - } - Str_ConcatLimitNull(path_buf, ".new", sizeof(path_buf)); - if ( !ouf.Open(path_buf, OF_WRITE|OF_CREATE|OF_DEFAULTMODE) ) - { - g_Log.Event(LOGM_INIT, "Cannot open file for writing. Skipped!\n"); - continue; - } - - size_t uiBytesRead = 0, uiTotalMb = 0; - while ( inf.ReadString(file_buf, sizeof(file_buf)) ) - { - dwIdxUID = (dword)strlen(file_buf); - if (dwIdxUID > (ARRAY_COUNT(file_buf) - 3)) - dwIdxUID = ARRAY_COUNT(file_buf) - 3; - - file_buf[dwIdxUID] = file_buf[dwIdxUID +1] = file_buf[dwIdxUID +2] = 0; // just to be sure to be in line always - // NOTE: it is much faster than to use memcpy to clear before reading - bool fSpecial = false; - uiBytesRead += dwIdxUID; - if ( uiBytesRead > mb5 ) - { - uiBytesRead -= mb5; - uiTotalMb += 5; - g_Log.Event(LOGM_INIT, "Total processed %" PRIuSIZE_T " Mb\n", uiTotalMb); - } - str_ptr = file_buf; - - // Note 28-Jun-2004 - // mounts seems having ACTARG1 > 0x30000000. The actual UID is ACTARG1-0x30000000. The - // new also should be new+0x30000000. need investigation if this can help making mounts - // not to disappear after the defrag - if (( file_buf[0] == 'A' ) && ( strstr(file_buf, "ACTARG1=0") == file_buf )) // ACTARG1= - str_ptr += 8; - else if (( file_buf[0] == 'C' ) && ( strstr(file_buf, "CONT=0") == file_buf )) // CONT= - str_ptr += 5; - else if (( file_buf[0] == 'C' ) && ( strstr(file_buf, "CHARUID=0") == file_buf )) // CHARUID= - str_ptr += 8; - else if (( file_buf[0] == 'L' ) && ( strstr(file_buf, "LASTCHARUID=0") == file_buf )) // LASTCHARUID= - str_ptr += 12; - else if (( file_buf[0] == 'L' ) && ( strstr(file_buf, "LINK=0") == file_buf )) // LINK= - str_ptr += 5; - else if (( file_buf[0] == 'M' ) && ( strstr(file_buf, "MEMBER=0") == file_buf )) // MEMBER= - { - str_ptr += 7; - fSpecial = true; - } - else if (( file_buf[0] == 'M' ) && ( strstr(file_buf, "MORE1=0") == file_buf )) // MORE1= - str_ptr += 6; - else if (( file_buf[0] == 'M' ) && ( strstr(file_buf, "MORE2=0") == file_buf )) // MORE2= - str_ptr += 6; - else if (( file_buf[0] == 'S' ) && ( strstr(file_buf, "SERIAL=0") == file_buf )) // SERIAL= - str_ptr += 7; - else if ((( file_buf[0] == 'T' ) && ( strstr(file_buf, "TAG.") == file_buf )) || // TAG.= - (( file_buf[0] == 'R' ) && ( strstr(file_buf, "REGION.TAG") == file_buf ))) - { - while ( *str_ptr && ( *str_ptr != '=' )) - ++str_ptr; - ++str_ptr; - } - else if (( i == 2 ) && strchr(file_buf, '=')) // spheredata.scp - plain VARs - { - while ( *str_ptr && ( *str_ptr != '=' )) - ++str_ptr; - ++str_ptr; - } - else - str_ptr = nullptr; - - // UIDs are always hex, so prefixed by 0 - if ( str_ptr && ( *str_ptr != '0' )) - str_ptr = nullptr; - - // here we got potentialy UID-contained variable - // check if it really is only UID-like var containing - if ( str_ptr ) - { - str_ptr_2 = str_ptr; - while ( *str_ptr_2 && - ((( *str_ptr_2 >= '0' ) && ( *str_ptr_2 <= '9' )) || - (( *str_ptr_2 >= 'a' ) && ( *str_ptr_2 <= 'f' ))) ) - ++str_ptr_2; - if ( !fSpecial ) - { - if ( *str_ptr_2 && ( *str_ptr_2 != '\r' ) && ( *str_ptr_2 != '\n' )) // some more text in line - str_ptr = nullptr; - } - } - - // here we definitely know that this is very uid-like - if ( str_ptr ) - { - char c, c1, c2; - c = *str_ptr_2; - - *str_ptr_2 = 0; - // here in p we have the current value of the line. - // check if it is a valid UID - - // prepare converting 0.. to 0x.. - c1 = *(str_ptr-1); - c2 = *str_ptr; - *(str_ptr-1) = '0'; - *str_ptr = 'x'; - --str_ptr; - dwIdxUID = strtoul(str_ptr, &str_ptr_2, 16); - ++str_ptr; - *(str_ptr-1) = c1; - *str_ptr = c2; - // Note 28-Jun-2004 - // The search algourytm is very simple and fast. But maybe integrate some other, at least /2 algorythm - // since has amount/2 tryes at worst chance to get the item and never scans the whole array - // It should improve speed since defragmenting 150Mb saves takes ~2:30 on 2.0Mhz CPU - { - dword dStep = dwTotalUIDs /2; - dword d = dStep; - for (;;) - { - dStep /= 2; - - if ( puids[d] == dwIdxUID) - { - dwIdxUID = d | (puids[d]&0xF0000000); // do not forget attach item and special flags like 04.. - break; - } - else - { - if (puids[d] < dwIdxUID) - d += dStep; - else - d -= dStep; - } - - if ( dStep == 1 ) - { - dwIdxUID = 0xFFFFFFFFL; - break; // did not find the UID - } - } - } - - // Search for this uid in the table -/* for ( d = 0; d < dTotalUIDs; d++ ) - { - if ( !uids[d] ) // end of array - { - uid = 0xFFFFFFFFL; - break; - } - else if ( uids[d] == uid ) - { - uid = d | (uids[d]&0xF0000000); // do not forget attach item and special flags like 04.. - break; - } - }*/ - - // replace UID by the new one since it has been found - *str_ptr_2 = c; - if (dwIdxUID != 0xFFFFFFFFL ) - { - *str_ptr = 0; - ASSERT(strlen(str_ptr_2) < sizeof(path_buf)); - Str_CopyLimitNull(path_buf, str_ptr_2, sizeof(path_buf)); // here we don't need anymore the old values of path_buf, so i can reuse it here - snprintf(path_buf_2, sizeof(path_buf_2), "0%" PRIx32, dwIdxUID); - strcat(file_buf, path_buf_2); - strcat(file_buf, path_buf); - } - } - // output the resulting line - ouf.Write(file_buf, (int)strlen(file_buf)); - } - inf.Close(); - ouf.Close(); - } - - free(puids); - g_Log.Event(LOGM_INIT, "Defragmentation complete.\n"); -} - - void atexit_handler() { ThreadHolder::get().markThreadsClosing(); @@ -776,13 +472,13 @@ void atexit_handler() #ifdef _WIN32 int Sphere_MainEntryPoint( int argc, char *argv[] ) #else -int _cdecl main( int argc, char * argv[] ) +int main( int argc, char * argv[] ) #endif { static constexpr lpctstr m_sClassName = "main"; EXC_TRY("MAIN"); - const int atexit_handler_result = std::atexit(atexit_handler); // Handler will be called + const int atexit_handler_result = std::atexit(atexit_handler); // Handler will be called if (atexit_handler_result != 0) { g_Log.Event(LOGL_CRIT, "atexit handler registration failed.\n"); @@ -791,14 +487,14 @@ int _cdecl main( int argc, char * argv[] ) { // Ensure i have this to have context for ADDTOCALLSTACK and other operations. - const IThread* curthread = ThreadHolder::get().current(); + const AbstractThread* curthread = ThreadHolder::get().current(); ASSERT(curthread != nullptr); ASSERT(dynamic_cast(curthread)); (void)curthread; } #ifndef _WIN32 - IThread::setThreadName("T_SphereStartup"); + AbstractThread::setThreadName("T_SphereStartup"); g_UnixTerminal.start(); @@ -830,7 +526,7 @@ int _cdecl main( int argc, char * argv[] ) if (fShouldCoreRunInSeparateThread) { g_Main.start(); // Starts another thread to do all the work (it does Sphere_OnTick()) - IThread::setThreadName("T_Monitor"); + AbstractThread::setThreadName("T_Monitor"); Sphere_MainMonitorLoop(); // Use this thread to monitor if the others are stuck } else @@ -851,7 +547,7 @@ int _cdecl main( int argc, char * argv[] ) { while (g_NTWindow.isActive()) { - Sleep (100); + SLEEP(100); } } #endif diff --git a/src/game/triggers.cpp b/src/game/triggers.cpp index ffa50a500..8dd92fbcf 100644 --- a/src/game/triggers.cpp +++ b/src/game/triggers.cpp @@ -21,7 +21,7 @@ struct TRIGGER_T_ID //{ // int m_used; //}; -std::vector g_triggers_id; +static std::vector sm_vTriggersId; bool IsTrigUsed(E_TRIGGERS id) @@ -29,7 +29,7 @@ bool IsTrigUsed(E_TRIGGERS id) if ( g_Serv.IsLoading() == true) return false; - return (( (uint)id < g_triggers_id.size() ) && g_triggers_id[id].m_used ); + return (( (uint)id < sm_vTriggersId.size() ) && sm_vTriggersId[id].m_used ); } bool IsTrigUsed(const char *name) @@ -47,12 +47,12 @@ bool IsTrigUsed(const char *name) void TriglistInit() { TRIGGER_T_ID trig{}; - g_triggers_id.clear(); + sm_vTriggersId.clear(); #define ADD(_a_) \ snprintf(trig.m_name, TRIGGER_NAME_MAX_LEN, "@%s", #_a_); \ trig.m_used = 0; \ - g_triggers_id.push_back(trig); + sm_vTriggersId.push_back(trig); #include "../tables/triggers.tbl" #undef ADD @@ -60,7 +60,7 @@ void TriglistInit() void TriglistClear() { - for ( auto it = g_triggers_id.begin(), end = g_triggers_id.end(); it != end; ++it ) + for ( auto it = sm_vTriggersId.begin(), end = sm_vTriggersId.end(); it != end; ++it ) { it->m_used = 0; } @@ -68,13 +68,13 @@ void TriglistClear() void TriglistAdd(E_TRIGGERS id) { - if (g_triggers_id.size() ) - ++ g_triggers_id[id].m_used; + if (sm_vTriggersId.size() ) + ++ sm_vTriggersId[id].m_used; } void TriglistAdd(const char *name) { - for ( auto it = g_triggers_id.begin(), end = g_triggers_id.end(); it != end; ++it ) + for ( auto it = sm_vTriggersId.begin(), end = sm_vTriggersId.end(); it != end; ++it ) { if ( !strcmpi(it->m_name, name) ) { @@ -87,7 +87,7 @@ void TriglistAdd(const char *name) void Triglist(int &total, int &used) { total = used = 0; - for ( auto it = g_triggers_id.cbegin(), end = g_triggers_id.cend(); it != end; ++it ) + for ( auto it = sm_vTriggersId.cbegin(), end = sm_vTriggersId.cend(); it != end; ++it ) { ++total; if ( it->m_used ) @@ -97,7 +97,7 @@ void Triglist(int &total, int &used) void TriglistPrint() { - for ( auto it = g_triggers_id.cbegin(), end = g_triggers_id.cend(); it != end; ++it ) + for ( auto it = sm_vTriggersId.cbegin(), end = sm_vTriggersId.cend(); it != end; ++it ) { if (it->m_used) { diff --git a/src/game/uo_files/CUOHuesRec.h b/src/game/uo_files/CUOHuesRec.h index 711fa565c..904cf4558 100644 --- a/src/game/uo_files/CUOHuesRec.h +++ b/src/game/uo_files/CUOHuesRec.h @@ -9,7 +9,7 @@ #include "../../common/common.h" // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -31,7 +31,7 @@ struct CUOHuesRec // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/game/uo_files/CUOIndexRec.h b/src/game/uo_files/CUOIndexRec.h index 2ce39725b..8cd663ae9 100644 --- a/src/game/uo_files/CUOIndexRec.h +++ b/src/game/uo_files/CUOIndexRec.h @@ -10,7 +10,7 @@ // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -53,7 +53,7 @@ struct CUOIndexRec // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/game/uo_files/CUOItemTypeRec.h b/src/game/uo_files/CUOItemTypeRec.h index 21d4febae..f8c843560 100644 --- a/src/game/uo_files/CUOItemTypeRec.h +++ b/src/game/uo_files/CUOItemTypeRec.h @@ -9,7 +9,7 @@ #include "../../common/common.h" // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -56,7 +56,7 @@ struct CUOItemTypeRec_HS // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/game/uo_files/CUOMapBlock.h b/src/game/uo_files/CUOMapBlock.h index 9bc9e9075..6774473b1 100644 --- a/src/game/uo_files/CUOMapBlock.h +++ b/src/game/uo_files/CUOMapBlock.h @@ -10,7 +10,7 @@ #include "uofiles_macros.h" // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -32,7 +32,7 @@ struct CUOMapBlock // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/game/uo_files/CUOMapMeter.h b/src/game/uo_files/CUOMapMeter.h index 94350d817..46d664ec5 100644 --- a/src/game/uo_files/CUOMapMeter.h +++ b/src/game/uo_files/CUOMapMeter.h @@ -9,7 +9,7 @@ #include "../../common/common.h" // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -32,7 +32,7 @@ struct CUOMapMeter // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/game/uo_files/CUOMultiItemRec.h b/src/game/uo_files/CUOMultiItemRec.h index 421da7c98..da45d75be 100644 --- a/src/game/uo_files/CUOMultiItemRec.h +++ b/src/game/uo_files/CUOMultiItemRec.h @@ -10,7 +10,7 @@ #include "uofiles_enums_itemid.h" // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -50,7 +50,7 @@ struct CUOMultiItemRec_HS // (Multi.mul, High Seas+) // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/game/uo_files/CUOStaticItemRec.h b/src/game/uo_files/CUOStaticItemRec.h index 69776f73f..86925e1ee 100644 --- a/src/game/uo_files/CUOStaticItemRec.h +++ b/src/game/uo_files/CUOStaticItemRec.h @@ -10,7 +10,7 @@ #include "uofiles_enums_itemid.h" // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -39,7 +39,7 @@ struct CUOStaticItemRec // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/game/uo_files/CUOTerrainTypeRec.h b/src/game/uo_files/CUOTerrainTypeRec.h index 8e3413bad..e97b19354 100644 --- a/src/game/uo_files/CUOTerrainTypeRec.h +++ b/src/game/uo_files/CUOTerrainTypeRec.h @@ -9,7 +9,7 @@ #include "../../common/common.h" // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -51,7 +51,7 @@ struct CUOTerrainTypeRec_HS // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/game/uo_files/CUOVersionBlock.h b/src/game/uo_files/CUOVersionBlock.h index 09b2ac2e6..e50e75e4e 100644 --- a/src/game/uo_files/CUOVersionBlock.h +++ b/src/game/uo_files/CUOVersionBlock.h @@ -13,7 +13,7 @@ // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -59,7 +59,7 @@ struct CUOVersionBlock // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#ifdef MSVC_COMPILER #pragma pack() #else #undef PACK_NEEDED diff --git a/src/network/CClientIterator.cpp b/src/network/CClientIterator.cpp index ee861577b..93e9e4d25 100644 --- a/src/network/CClientIterator.cpp +++ b/src/network/CClientIterator.cpp @@ -8,6 +8,24 @@ ClientIterator::ClientIterator(const CNetworkManager* network) { m_network = (network == nullptr ? &g_NetworkManager : network); m_nextClient = static_cast (m_network->m_clients.GetContainerHead()); + +#ifdef _DEBUG + const uint uiCnt = (uint)g_NetworkManager.m_clients.GetContentCount(); + //g_Log.EventWarn("Client count: %u.\n", uiCnt); + uint uiCntReal = 0; + if (CClient *pCliLoop = m_nextClient) + { + //g_Log.EventEvent("Start counting.\n"); + do ++ uiCntReal; + while ((pCliLoop = pCliLoop->GetNext()) != nullptr); + } + //g_Log.EventWarn("Actual clients in list: %u.\n", uiCntReal); + if (uiCnt != uiCntReal) + { + g_Log.EventWarn("Client count mismatch!.\n"); + EXC_NOTIFY_DEBUGGER; + } +#endif } ClientIterator::~ClientIterator() diff --git a/src/network/CNetState.cpp b/src/network/CNetState.cpp index 0ca402c38..eec21d7fb 100644 --- a/src/network/CNetState.cpp +++ b/src/network/CNetState.cpp @@ -54,8 +54,8 @@ void CNetState::clear(void) { g_Serv.StatDec(SERV_STAT_CLIENTS); + static constexpr LOG_TYPE logFlags = enum_alias_cast(LOGM_NOCONTEXT | LOGM_CLIENTS_LOG | LOGL_EVENT); const CONNECT_TYPE connectionType = m_client->GetConnectType(); - const LOG_TYPE logFlags = LOG_TYPE(LOGM_NOCONTEXT | LOGM_CLIENTS_LOG | LOGL_EVENT); const size_t uiClients = g_Serv.StatGet(SERV_STAT_CLIENTS); const lpctstr ptcAddress = m_peerAddress.GetAddrStr(); if (connectionType == CONNECT_LOGIN) @@ -242,7 +242,7 @@ void CNetState::markReadClosed(void) volatile DEBUGNETWORK(("%x:Client being closed by read-thread\n", m_id)); m_isReadClosed = true; - if (m_parent != nullptr && m_parent->getPriority() == IThread::Disabled) + if (m_parent != nullptr && m_parent->getPriority() == ThreadPriority::Disabled) m_parent->awaken(); } diff --git a/src/network/CNetworkInput.cpp b/src/network/CNetworkInput.cpp index 23380e34d..70f050b37 100644 --- a/src/network/CNetworkInput.cpp +++ b/src/network/CNetworkInput.cpp @@ -54,7 +54,7 @@ bool CNetworkInput::processInput() { // if the thread does not receive ticks, we must perform a quick select to see if we should // wake up the thread - if (m_thread->isActive() && m_thread->getPriority() == IThread::Disabled) + if (m_thread->isActive() && m_thread->getPriority() == ThreadPriority::Disabled) { fd_set fds; if (checkForData(fds)) diff --git a/src/network/CNetworkManager.cpp b/src/network/CNetworkManager.cpp index d38e00fa9..ca558b289 100644 --- a/src/network/CNetworkManager.cpp +++ b/src/network/CNetworkManager.cpp @@ -391,7 +391,7 @@ void CNetworkManager::start(void) if (ntCount > 1) { // If we have more than one thread (this hasn't sense... at this point isThreaded should be == true) - char name[IThread::m_nameMaxLength]; + char name[AbstractThread::m_nameMaxLength]; snprintf(name, sizeof(name), "T_Net #%u", (uint)pThread->getId()); pThread->overwriteInternalThreadName(name); } @@ -426,10 +426,11 @@ void CNetworkManager::tick(void) if (!pClient || (pClient->GetConnectType() == CONNECT_UNK)) { const int64 iTimeSinceConnectionMs = iCurSysTimeMs - state->m_iConnectionTimeMs; - if (iTimeSinceConnectionMs > g_Cfg._iTimeoutIncompleteConnectionMs) + const int64 iTimeoutIncompleteConnectionMs = g_Cfg._iTimeoutIncompleteConnectionMs; + if (iTimeSinceConnectionMs > iTimeoutIncompleteConnectionMs) { EXC_SET_BLOCK("mark closed for timeout"); - g_Log.Event(LOGM_CLIENTS_LOG | LOGL_EVENT, + g_Log.Event(LOGM_CLIENTS_LOG | LOGL_WARN, // LOGM_NOCONTEXT "%x:Force closing connection from IP %s. Reason: timed out before completing login.\n", state->id(), state->m_peerAddress.GetAddrStr()); //state->markReadClosed(); @@ -453,7 +454,7 @@ void CNetworkManager::tick(void) // sent from CNetworkOutput::QueuePacketTransaction can be ignored // the safest solution to this is to send additional signals from here CNetworkThread* thread = state->getParentThread(); - if (thread != nullptr && state->hasPendingData() && thread->getPriority() == IThread::Disabled) + if (thread != nullptr && state->hasPendingData() && thread->getPriority() == ThreadPriority::Disabled) thread->awaken(); #endif continue; diff --git a/src/network/CNetworkOutput.cpp b/src/network/CNetworkOutput.cpp index 3abf84731..445c24df1 100644 --- a/src/network/CNetworkOutput.cpp +++ b/src/network/CNetworkOutput.cpp @@ -16,7 +16,7 @@ * void SendCompleted_Winsock Winsock event handler for when async operation completes * ***************************************************************************/ -void CALLBACK SendCompleted_Winsock(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags) +static void CALLBACK SendCompleted_Winsock(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags) { UnreferencedParameter(dwFlags); ADDTOCALLSTACK("SendCompleted_Winsock"); @@ -126,7 +126,7 @@ bool CNetworkOutput::processOutput() if (packetsSent > 0) { // notify thread there could be more to process - if (m_thread->getPriority() == IThread::Disabled) + if (m_thread->getPriority() == ThreadPriority::Disabled) m_thread->awaken(); } @@ -173,7 +173,7 @@ size_t CNetworkOutput::flush(CNetState* state) // when this isn't the active thread, all we can do is raise a request to flush this // client later state->markFlush(true); - if (m_thread->getPriority() == IThread::Disabled) + if (m_thread->getPriority() == ThreadPriority::Disabled) m_thread->awaken(); return 0; @@ -647,6 +647,6 @@ void CNetworkOutput::QueuePacketTransaction(PacketTransaction* transaction) // notify thread CNetworkThread* thread = state->getParentThread(); - if (thread != nullptr && thread->getPriority() == IThread::Disabled) + if (thread != nullptr && thread->getPriority() == ThreadPriority::Disabled) thread->awaken(); } diff --git a/src/network/CNetworkThread.cpp b/src/network/CNetworkThread.cpp index bedb35de3..0ebb6db11 100644 --- a/src/network/CNetworkThread.cpp +++ b/src/network/CNetworkThread.cpp @@ -12,13 +12,13 @@ static const char* GenerateNetworkThreadName(size_t id) { char* name = Str_GetTemp(); - snprintf(name, IThread::m_nameMaxLength, "T_Net #%" PRIuSIZE_T, id); + snprintf(name, AbstractThread::m_nameMaxLength, "T_Net #%" PRIuSIZE_T, id); return name; } CNetworkThread::CNetworkThread(CNetworkManager* manager, size_t id) - : AbstractSphereThread(GenerateNetworkThreadName(id), IThread::Disabled), + : AbstractSphereThread(GenerateNetworkThreadName(id), ThreadPriority::Disabled), m_manager(manager), m_id(id), _iTimeLastStateDataCheck(0) { } @@ -31,7 +31,7 @@ void CNetworkThread::assignNetworkState(CNetState* state) { ADDTOCALLSTACK("CNetworkThread::assignNetworkState"); m_assignQueue.push(state); - if (getPriority() == IThread::Disabled) + if (getPriority() == ThreadPriority::Disabled) awaken(); } @@ -113,8 +113,8 @@ void CNetworkThread::tick(void) if (m_states.empty()) { // we haven't been assigned any clients, so go idle for now - if (getPriority() != IThread::Disabled) - setPriority(IThread::Low); + if (getPriority() != ThreadPriority::Disabled) + setPriority(ThreadPriority::Low); return; } @@ -122,7 +122,7 @@ void CNetworkThread::tick(void) processOutput(); // we're active, take priority - setPriority(static_cast(g_Cfg._uiNetworkThreadPriority)); + setPriority(static_cast(g_Cfg._iNetworkThreadPriority)); static constexpr int64 kiStateDataCheckPeriod = 10 * 1000; // 10 seconds, expressed in milliseconds const int64 iTimeCur = CSTime::GetMonotonicSysTimeMilli(); diff --git a/src/network/CSocket.cpp b/src/network/CSocket.cpp index c99c40b05..9014f36bc 100644 --- a/src/network/CSocket.cpp +++ b/src/network/CSocket.cpp @@ -433,12 +433,12 @@ CSocketAddress CSocket::GetPeerName( ) const int CSocket::SetSockOpt( int nOptionName, const void * optval, int optlen, int nLevel ) const { // level = SOL_SOCKET and IPPROTO_TCP. - return( setsockopt( m_hSocket, nLevel, nOptionName, reinterpret_cast(optval), optlen )); + return( setsockopt( m_hSocket, nLevel, nOptionName, reinterpret_cast(optval), optlen )); } int CSocket::GetSockOpt( int nOptionName, void * optval, int * poptlen, int nLevel ) const { - return( getsockopt( m_hSocket, nLevel, nOptionName, reinterpret_cast(optval), reinterpret_cast(poptlen))); + return( getsockopt( m_hSocket, nLevel, nOptionName, reinterpret_cast(optval), reinterpret_cast(poptlen))); } #ifdef _WIN32 @@ -457,7 +457,7 @@ int CSocket::GetSockOpt( int nOptionName, void * optval, int * poptlen, int nLev { // TO BE CALLED IN CClient destructor !!! CancelIo(reinterpret_cast(m_hSocket)); - SleepEx(1, TRUE); + SleepEx(1, TRUE); } #else diff --git a/src/network/PingServer.cpp b/src/network/PingServer.cpp index 63a4dd4ad..2627c1227 100644 --- a/src/network/PingServer.cpp +++ b/src/network/PingServer.cpp @@ -3,10 +3,10 @@ #include "PingServer.h" -PingServer g_PingServer; +static PingServer s_PingServer; // run the thread in RealTime as we need pings to be responded to ASAP -PingServer::PingServer() : AbstractSphereThread("PingServer", IThread::RealTime) +PingServer::PingServer() : AbstractSphereThread("PingServer", ThreadPriority::RealTime) { m_profile.EnableProfile(PROFILE_NETWORK_RX); m_profile.EnableProfile(PROFILE_NETWORK_TX); diff --git a/src/network/PingServer.h b/src/network/PingServer.h index 9be1d819a..6e28717ac 100644 --- a/src/network/PingServer.h +++ b/src/network/PingServer.h @@ -23,9 +23,8 @@ class PingServer : public AbstractSphereThread PingServer(void); virtual ~PingServer(void); -private: - PingServer(const PingServer& copy); - PingServer& operator=(const PingServer& other); + PingServer(const PingServer& copy) = delete; + PingServer& operator=(const PingServer& other) = delete; public: virtual void onStart() override; diff --git a/src/network/linuxev.cpp b/src/network/linuxev.cpp index 122d748ac..c946fcfe5 100644 --- a/src/network/linuxev.cpp +++ b/src/network/linuxev.cpp @@ -63,7 +63,7 @@ static void socketslave_cb(struct ev_loop *loop, struct ev_io *watcher, int reve static constexpr double kLoopCollectNetworkInputSeconds = 0.002; static constexpr double kLoopWaitBeforeNextCycleSeconds = 0.008; -LinuxEv::LinuxEv(void) : AbstractSphereThread("T_NetLoopOut", IThread::High) +LinuxEv::LinuxEv(void) : AbstractSphereThread("T_NetLoopOut", ThreadPriority::High) //, m_watchMainsock{} { // Right now, we use libev to send asynchronously packets to clients. diff --git a/src/network/net_datatypes.h b/src/network/net_datatypes.h index e6633453c..04a5cd44b 100644 --- a/src/network/net_datatypes.h +++ b/src/network/net_datatypes.h @@ -18,7 +18,7 @@ // All these structures must be byte packed. -#if defined(_WIN32) && defined(_MSC_VER) +#if defined(MSVC_COMPILER) // Microsoft dependant pragma #pragma pack(1) #define PACK_NEEDED @@ -81,7 +81,7 @@ struct ndword // Turn off structure packing. -#if defined(_WIN32) && defined(_MSC_VER) +#if defined(_WIN32) && defined(MSVC_COMPILER) #pragma pack() #endif diff --git a/src/network/packet.cpp b/src/network/packet.cpp index 10adbb25a..f524c263f 100644 --- a/src/network/packet.cpp +++ b/src/network/packet.cpp @@ -815,12 +815,12 @@ void Packet::readStringASCII(wchar* buffer, uint length, bool includeNull) char* bufferReal = new char[(size_t)length + 1](); readStringASCII(bufferReal, length, includeNull); -#ifdef _MSC_VER +# ifdef MSVC_RUNTIME size_t aux; mbstowcs_s(&aux, buffer, length + 1, bufferReal, length); -#else +# else mbstowcs(buffer, bufferReal, length); -#endif +# endif delete[] bufferReal; #else diff --git a/src/network/send.cpp b/src/network/send.cpp index b70bc8527..50856ed42 100644 --- a/src/network/send.cpp +++ b/src/network/send.cpp @@ -134,7 +134,7 @@ PacketObjectStatus::PacketObjectStatus(const CClient* target, CObjBase* object) const CNetState * state = target->GetNetState(); const CChar *character = target->GetChar(); - CChar *objectChar = object->IsChar() ? static_cast(object) : nullptr; + CChar *objectChar = object->IsChar() ? static_cast(object) : nullptr; bool fCanRename = false; byte version = 0; @@ -199,7 +199,7 @@ PacketObjectStatus::PacketObjectStatus(const CClient* target, CObjBase* object) writeInt16(iHitsMax); // Max hit points writeBool(fCanRename); writeByte(version); - if (state->isClientEnhanced() && objectChar && objectChar->IsPlayableCharacter()) + if (state->isClientEnhanced() && objectChar && objectChar->IsClientType() /*objectChar->IsPlayableCharacter()*/) { // The Enhanced Client wants the char race and other things when showing paperdolls (otherwise the interface throws an "unnoticeable" internal error) WriteVersionSpecific(target, objectChar, version); @@ -211,7 +211,7 @@ PacketObjectStatus::PacketObjectStatus(const CClient* target, CObjBase* object) void PacketObjectStatus::WriteVersionSpecific(const CClient* target, CChar* other, byte version) { - bool fElemental = IsSetCombatFlags(COMBAT_ELEMENTAL_ENGINE); + const bool fElemental = IsSetCombatFlags(COMBAT_ELEMENTAL_ENGINE); const CCharBase * otherDefinition = other->Char_GetDef(); const CCPropsChar* pCCPChar = other->GetComponentProps(); const CCPropsChar* pBaseCCPChar = otherDefinition->GetComponentProps(); diff --git a/src/resources/SphereSvr.rc b/src/resources/SphereSvr.rc index 7d99d47b0..4d087d7d9 100644 --- a/src/resources/SphereSvr.rc +++ b/src/resources/SphereSvr.rc @@ -13,14 +13,14 @@ # define WIN32_LEAN_AND_MEAN // include just windows.h without the other winapi headers, we'll add them manually when needed # endif -# ifdef _MSC_VER +# if defined(_MSC_VER) && !defined(__clang__) // Workaround to a possible VS compiler bug: instead of complaining if a macro expands to a "defined" macro, // it complains if a define macro contains the words "defined" in its name... # pragma warning(push) # pragma warning(disable: 5105) # endif # include -# ifdef _MSC_VER +# if defined(_MSC_VER) && !defined(__clang__) # pragma warning(pop) # endif @@ -89,12 +89,12 @@ IDR_MAINFRAME ICON "spheresvr.ico" // #ifdef APSTUDIO_INVOKED -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN "win_resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN "\r\n" "\0" diff --git a/src/resources/resource.h b/src/resources/resource.h deleted file mode 100644 index b406120f2..000000000 --- a/src/resources/resource.h +++ /dev/null @@ -1,43 +0,0 @@ -//{{NO_DEPENDENCIES}} -// File di inclusione generato con Microsoft Visual C++. -// Utilizzato da SphereSvr.rc -// -#define IDC_STATIC 0 -#define IDR_MAINFRAME 100 -#define IDR_ABOUT_BOX 101 -#define IDM_POP_TRAY 102 -#define IDM_POP_LOG 103 -#define IDC_STAT_STATS 201 -#define IDC_STAT_CLIENTS 202 -#define IDC_O_LOG_LEVEL 250 -#define IDC_O_LOG_CLIENTS 251 -#define IDC_O_LOG_GM_CMDS 252 -#define IDC_O_LOG_GM_PAGES 253 -#define IDC_O_LOG_SPEAK 254 -#define IDM_STATUS 528 -#define IDM_RESTORE 544 -#define IDM_MINIMIZE 560 -#define IDM_EDIT_COPY 580 -#define IDM_EXIT 582 -#define IDC_ABOUT_VERSION 1117 -#define IDC_ABOUT_COMPILER 1118 -#define IDC_ABOUT_MENASOFT_LINK 1119 -#define IDC_ABOUT_SPHERE_LINK 1120 -#define IDC_SETUP_PORT 4020 -#define IDC_SETUP_SCRIPTS 4021 -#define IDC_SETUP_SAVE 4022 -#define IDC_SETUP_LOG 4023 -#define IDC_SETUP_NAME 4024 -#define IDM_RESYNC_PAUSE 32784 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_3D_CONTROLS 1 -#define _APS_NEXT_RESOURCE_VALUE 161 -#define _APS_NEXT_COMMAND_VALUE 32785 -#define _APS_NEXT_CONTROL_VALUE 4026 -#define _APS_NEXT_SYMED_VALUE 210 -#endif -#endif diff --git a/src/sphere/ProfileData.cpp b/src/sphere/ProfileData.cpp index 9789a3334..f57a17358 100644 --- a/src/sphere/ProfileData.cpp +++ b/src/sphere/ProfileData.cpp @@ -1,5 +1,5 @@ -#include "../common/sphere_library/CSTime.h" +//#include "../common/sphere_library/CSTime.h" #include "../game/CScriptProfiler.h" #include "ProfileData.h" #include "threads.h" diff --git a/src/sphere/ProfileTask.cpp b/src/sphere/ProfileTask.cpp index 500a837cf..9ff76d6b0 100644 --- a/src/sphere/ProfileTask.cpp +++ b/src/sphere/ProfileTask.cpp @@ -21,7 +21,7 @@ ProfileTask::ProfileTask(PROFILE_TYPE id) : if (th.closing()) return; - IThread* icontext = th.current(); + AbstractThread* icontext = th.current(); if (icontext == nullptr) { // Thread was deleted, manually or by app closing signal. diff --git a/src/sphere/UnixTerminal.cpp b/src/sphere/UnixTerminal.cpp index e3e961b13..f11449411 100644 --- a/src/sphere/UnixTerminal.cpp +++ b/src/sphere/UnixTerminal.cpp @@ -11,7 +11,7 @@ #endif -UnixTerminal::UnixTerminal() : AbstractSphereThread("T_UnixTerm", IThread::Highest), +UnixTerminal::UnixTerminal() : AbstractSphereThread("T_UnixTerm", ThreadPriority::Highest), #ifdef _USECURSES m_window(nullptr), #else diff --git a/src/sphere/asyncdb.cpp b/src/sphere/asyncdb.cpp index cff073390..d2685c9ad 100644 --- a/src/sphere/asyncdb.cpp +++ b/src/sphere/asyncdb.cpp @@ -6,7 +6,7 @@ CDataBaseAsyncHelper g_asyncHdb; -CDataBaseAsyncHelper::CDataBaseAsyncHelper(void) : AbstractSphereThread("AsyncDatabaseHelper", IThread::Low) +CDataBaseAsyncHelper::CDataBaseAsyncHelper(void) : AbstractSphereThread("AsyncDatabaseHelper", ThreadPriority::Low) { } diff --git a/src/sphere/asyncdb.h b/src/sphere/asyncdb.h index aede0ba29..2bc3018c0 100644 --- a/src/sphere/asyncdb.h +++ b/src/sphere/asyncdb.h @@ -39,4 +39,7 @@ class CDataBaseAsyncHelper : public AbstractSphereThread void addQuery(bool isQuery, lpctstr sFunction, lpctstr sQuery); }; +extern CDataBaseAsyncHelper g_asyncHdb; + + #endif // _INC_ASYNCDB_H diff --git a/src/sphere/ntservice.cpp b/src/sphere/ntservice.cpp index 88857c81c..f1b643d02 100644 --- a/src/sphere/ntservice.cpp +++ b/src/sphere/ntservice.cpp @@ -252,7 +252,7 @@ void CNTService::ServiceStop() // PURPOSE: Installs the service on the local machine void CNTService::CmdInstallService() { - char szPath[_MAX_PATH * 2]; + char szPath[SPHERE_MAX_PATH * 2]; char szErr[256]; ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, "Installing Service."); @@ -408,7 +408,7 @@ void CNTService::CmdRemoveService() while ( QueryServiceStatus(schService, &m_sStatus) ) // wait the service to stop { if ( m_sStatus.dwCurrentState == SERVICE_STOP_PENDING ) - Sleep(1000); + SLEEP(1000); else break; } @@ -450,14 +450,14 @@ void CNTService::CmdMainStart() // FUNCTION: main() // ///////////////////////////////////////////////////////////////////////////////////// -#ifdef _MSC_VER +#ifdef MSVC_COMPILER int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) #else int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) #endif { UnreferencedParameter(hPrevInstance); - IThread::setThreadName("T_SphereStartup"); + AbstractThread::setThreadName("T_SphereStartup"); TCHAR *argv[32]; argv[0] = nullptr; diff --git a/src/sphere/ntservice.h b/src/sphere/ntservice.h index a064e41ab..124ffa518 100644 --- a/src/sphere/ntservice.h +++ b/src/sphere/ntservice.h @@ -8,14 +8,14 @@ #ifdef _WIN32 +#include "../common/common.h" -#ifndef _MSC_VER +#ifdef NON_MSVC_COMPILER #include #else #include // exception handling info. -#endif // _MSC_VER +#endif // NON_MSVC_COMPILER -#include "../common/common.h" extern class CNTService diff --git a/src/sphere/ntwindow.cpp b/src/sphere/ntwindow.cpp index 5b21fbb47..d5804aa4a 100644 --- a/src/sphere/ntwindow.cpp +++ b/src/sphere/ntwindow.cpp @@ -24,7 +24,7 @@ #define IDT_ONTICK 1 -CNTApp theApp; +static CNTApp theApp; //************************************ // -CAboutDlg @@ -100,7 +100,7 @@ void CNTWindow::CStatusDlg::FillStats() size_t iThreadCount = ThreadHolder::get().getActiveThreads(); for ( size_t iThreads = 0; iThreads < iThreadCount; ++iThreads) { - IThread* thrCurrent = ThreadHolder::get().getThreadAt(iThreads); + AbstractThread* thrCurrent = ThreadHolder::get().getThreadAt(iThreads); if (thrCurrent == nullptr) continue; @@ -163,9 +163,11 @@ BOOL CNTWindow::CStatusDlg::DefDialogProc( UINT message, WPARAM wParam, LPARAM l return false; } -CNTWindow::CNTWindow() : AbstractSphereThread("T_ConsoleWindow", IThread::Highest), +CNTWindow::CNTWindow() : AbstractSphereThread("T_ConsoleWindow", ThreadPriority::Highest), _NTWInitParams{}, m_zCommands {{}} { + _fKeepAliveAtShutdown = true; + m_iLogTextLen = 0; m_fLogScrollLock = false; m_dwColorNew = RGB( 0xaf,0xaf,0xaf ); @@ -274,14 +276,14 @@ void CNTWindow::List_AddSingle(COLORREF color, LPCTSTR ptcText) if ( iNewLen > iMaxTextLen ) { - const int iCut = iNewLen - iMaxTextLen; + const int iCut = iNewLen - iMaxTextLen; m_wndLog.SetSel( 0, iCut ); // These SetRedraw FALSE/TRUE calls will make the log panel scroll much faster when spamming text, but // it will generate some drawing artifact //m_wndLog.SetRedraw(FALSE); - m_wndLog.ReplaceSel( "" ); + m_wndLog.ReplaceSel( "" ); } else if (NTWindow_CanScroll()) theApp.m_wndMain.m_wndLog.ScrollLine(); @@ -318,7 +320,7 @@ void CNTWindow::List_AddGroup(std::deque>&& msgs) { iTotalTextLen += co->GetTextString().GetLength(); } - + const int iNewLen = m_iLogTextLen + iTotalTextLen; if (iNewLen > iMaxTextLen) @@ -746,7 +748,7 @@ LRESULT CNTWindow::OnNotify( int idCtrl, NMHDR * pnmh ) break; // use dclick to open the corresponding script file - TCHAR * pos = strstr(zTemp, SPHERE_SCRIPT); + TCHAR * pos = strstr(zTemp, SPHERE_SCRIPT_EXT); if ( pos != nullptr ) { // use two formats of file names: @@ -782,7 +784,7 @@ LRESULT CNTWindow::OnNotify( int idCtrl, NMHDR * pnmh ) // since certain files aren't listed, handle these separately if (filePath == nullptr) { - if ( strstr(SPHERE_FILE "tables" SPHERE_SCRIPT, start) ) + if ( strstr(SPHERE_FILE "tables" SPHERE_SCRIPT_EXT, start) ) { TCHAR * z = Str_GetTemp(); strcpy(z, g_Cfg.m_sSCPBaseDir); @@ -1025,7 +1027,7 @@ bool CNTWindow::NTWindow_OnTick( int iWaitmSec ) iWaitmSec = 0; } } - + // Give the windows message loops a tick. for (;;) @@ -1157,7 +1159,7 @@ bool CNTWindow::NTWindow_OnTick( int iWaitmSec ) } else if ( !*pszCurSel ) // or there is still no selection { - curmatch = firstmatch; + curmatch = firstmatch; } else // need to find for the next record { diff --git a/src/sphere/ntwindow.h b/src/sphere/ntwindow.h index 42d7730e0..2c4b75d18 100644 --- a/src/sphere/ntwindow.h +++ b/src/sphere/ntwindow.h @@ -23,10 +23,10 @@ extern struct CNTWindow : public AbstractSphereThread, public CSWindow, public C int nCmdShow; } _NTWInitParams; - virtual void onStart(); - virtual void terminate(bool ended); + virtual void onStart() override; + virtual void terminate(bool ended) override; virtual bool shouldExit() noexcept override; - virtual void tick(); + virtual void tick() override; bool NTWindow_Init(HINSTANCE hInstance, LPTSTR lpCmdLinel, int nCmdShow); void NTWindow_ExitServer(); @@ -41,7 +41,7 @@ extern struct CNTWindow : public AbstractSphereThread, public CSWindow, public C std::shared_mutex _mutexWindowTitle; void exitActions(); - + public: class CAboutDlg : public CDialogBase // CNTWindow::CAboutDlg { diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index 201514c2a..aacad6db1 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -54,73 +54,6 @@ struct TemporaryStringThreadSafeStateHolder } }; -IThread::IThread() noexcept : - m_threadSystemId(0), m_threadHolderId(-1) - { }; - -// This is needed to prevent weak-vtables warning (puts the vtable in this translation unit instead of every translation unit) -IThread::~IThread() noexcept = default; - -#ifdef _WIN32 -#pragma pack(push, 8) -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; - LPCSTR szName; - DWORD dwThreadID; - DWORD dwFlags; -} THREADNAME_INFO; -#pragma pack(pop) - -static constexpr DWORD MS_VC_EXCEPTION = 0x406D1388; -#endif - -void IThread::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 - Str_CopyLimitNull(name_trimmed, name, m_nameMaxLength); - -#if defined(_WIN32) - #if defined(_MSC_VER) // TODO: support thread naming when compiling with compilers other than Microsoft's - // Windows uses THREADNAME_INFO structure to set thread name - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = name_trimmed; - info.dwThreadID = (DWORD)(-1); - 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__) // Mac - pthread_setname_np(name_trimmed); -#elif !defined(_BSD) // Linux - prctl(PR_SET_NAME, name_trimmed, 0, 0, 0); -#elif defined(__FreeBSD__) || defined(__OpenBSD__) - pthread_set_name_np(getCurrentThreadId(), name_trimmed); -#elif defined(__NetBSD__) - pthread_setname_np(getCurrentThreadId(), "%s", name_trimmed); -#endif - - auto athr = static_cast(ThreadHolder::get().current()); - ASSERT(athr); - - 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); - - athr->overwriteInternalThreadName(name_trimmed); -} - /** * ThreadHolder @@ -143,30 +76,29 @@ bool ThreadHolder::closing() noexcept return ret; } -IThread* ThreadHolder::current() noexcept +AbstractThread* ThreadHolder::current() 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. - IThread* retval = nullptr; + AbstractThread* retval = nullptr; RETRY_SHARED_LOCK_FOR_TASK(m_mutex, lock, retval, - ([this, &lock]() -> IThread* + ([this, &lock]() -> AbstractThread* { - if (m_closingThreads) - [[unlikely]] - { - //STDERR_LOG("Closing?\n"); - return nullptr; - } - - const threadid_t tid = IThread::getCurrentThreadSystemId(); + const threadid_t tid = AbstractThread::getCurrentThreadSystemId(); if (m_spherethreadpairs_systemid_ptr.empty()) [[unlikely]] { - auto thread = static_cast(DummySphereThread::getInstance()); + if (m_closingThreads) [[unlikely]] + { + //STDERR_LOG("Closing?\n"); + return nullptr; + } + + auto thread = static_cast(DummySphereThread::getInstance()); if (!thread) [[unlikely]] { @@ -189,6 +121,7 @@ IThread* ThreadHolder::current() noexcept break; } } + if (!found) [[unlikely]] { @@ -199,7 +132,7 @@ IThread* ThreadHolder::current() noexcept RaiseImmediateAbort(); } - auto thread = static_cast(found->second); + auto thread = static_cast(found->second); ASSERT(thread->m_threadHolderId != -1); SphereThreadData *tdata = &(m_threads[thread->m_threadHolderId]); @@ -210,6 +143,22 @@ IThread* ThreadHolder::current() noexcept return nullptr; } + if (m_closingThreads) [[unlikely]] + { + auto spherethread = dynamic_cast(thread); + if (!spherethread) + { + // Should never happen. + RaiseImmediateAbort(); + } + + 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()) ); @@ -219,7 +168,7 @@ IThread* ThreadHolder::current() noexcept return retval; } -void ThreadHolder::push(IThread *thread) noexcept +void ThreadHolder::push(AbstractThread *thread) noexcept { bool fExceptionThrown = false; try @@ -227,9 +176,10 @@ void ThreadHolder::push(IThread *thread) noexcept auto sphere_thread = dynamic_cast(thread); if (!sphere_thread) { - //throw CSError(LOGL_FATAL, 0, "IThread not being an AbstractSphereThread?"); - STDERR_LOG("IThread not being an AbstractSphereThread?"); - fExceptionThrown = true; + //throw CSError(LOGL_FATAL, 0, "AbstractThread not being an AbstractSphereThread?"); + STDERR_LOG("AbstractThread not being an AbstractSphereThread?"); + //fExceptionThrown = true; + goto soft_throw; } std::unique_lock lock(m_mutex); @@ -274,10 +224,12 @@ void ThreadHolder::push(IThread *thread) noexcept if (fExceptionThrown) { +soft_throw: // Should never happen. RaiseImmediateAbort(); } +#ifdef _DEBUG if (dynamic_cast(thread)) { // Too early in the init process to use the console... @@ -290,25 +242,10 @@ void ThreadHolder::push(IThread *thread) noexcept "Registered thread '%s' with ThreadHolder ID %d.\n", thread->getName(), thread->m_threadHolderId); } +#endif } -/* -SphereThreadData* ThreadHolder::findThreadData(IThread* thread) noexcept -{ - // If checking for current thread, use another escamotage to retrieve it... - - // This should always run guarded by a MUTEX! - SimpleThreadLock lock(m_mutex); - for (size_t i = 0; i < m_threadCount; ++i) - { - if (m_threads[i].m_ptr == thread) - return &(m_threads[i]); - } - return nullptr; -} -*/ - -void ThreadHolder::remove(IThread *thread) CANTHROW +void ThreadHolder::remove(AbstractThread *thread) CANTHROW { if (!thread) throw CSError(LOGL_FATAL, 0, "thread == nullptr"); @@ -346,16 +283,19 @@ void ThreadHolder::markThreadsClosing() CANTHROW for (auto& thread_data : m_threads) { auto sphere_thread = static_cast(thread_data.m_ptr); + if (sphere_thread->_fKeepAliveAtShutdown) + continue; + sphere_thread->_fIsClosing = true; thread_data.m_closed = true; } } -IThread * ThreadHolder::getThreadAt(size_t at) noexcept +AbstractThread * ThreadHolder::getThreadAt(size_t at) noexcept { - IThread* retval = nullptr; + AbstractThread* retval = nullptr; RETRY_SHARED_LOCK_FOR_TASK(m_mutex, lock, retval, - ([this, at]() -> IThread* + ([this, at]() -> AbstractThread* { // MSVC: warning C5101: use of preprocessor directive in function-like macro argument list is undefined behavior. //#ifdef _DEBUG @@ -367,7 +307,7 @@ IThread * ThreadHolder::getThreadAt(size_t at) noexcept } //#endif - if ( at > getActiveThreads() ) + if ( at > getActiveThreads() ) [[unlikely]] { return nullptr; @@ -396,7 +336,9 @@ IThread * ThreadHolder::getThreadAt(size_t at) noexcept */ int AbstractThread::m_threadsAvailable = 0; -AbstractThread::AbstractThread(const char *name, IThread::Priority priority) +AbstractThread::AbstractThread(const char *name, ThreadPriority priority) : + m_threadSystemId(0), m_threadHolderId(-1), + _fKeepAliveAtShutdown(false), _fIsClosing(false) { if( AbstractThread::m_threadsAvailable == 0 ) { @@ -411,7 +353,6 @@ AbstractThread::AbstractThread(const char *name, IThread::Priority priority) } m_threadSystemId = 0; Str_CopyLimitNull(m_name, name, sizeof(m_name)); - m_handle = 0; m_hangCheck = 0; _thread_selfTerminateAfterThisTick = true; m_terminateRequested = true; @@ -453,14 +394,16 @@ void AbstractThread::start() pthread_attr_t threadAttr; pthread_attr_init(&threadAttr); pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); - int result = pthread_create( &m_handle, &threadAttr, &runner, this ); + spherethread_t threadHandle{}; + int result = pthread_create( &threadHandle, &threadAttr, &runner, this ); pthread_attr_destroy(&threadAttr); if (result != 0) { - m_handle = 0; + m_handle = std::nullopt; throw CSError(LOGL_FATAL, 0, "Unable to spawn a new thread"); } + m_handle = threadHandle; #endif m_terminateEvent.reset(); @@ -479,10 +422,10 @@ void AbstractThread::terminate(bool ended) if (wasCurrentThread == false) { #ifdef _WIN32 - TerminateThread(m_handle, 0); - CloseHandle(m_handle); + TerminateThread(m_handle.value(), 0); + CloseHandle(m_handle.value()); #else - pthread_cancel(m_handle); // IBM say it so + pthread_cancel(m_handle.value()); // IBM say it so #endif } } @@ -490,7 +433,7 @@ void AbstractThread::terminate(bool ended) // Common things ThreadHolder::get().remove(this); m_threadSystemId = 0; - m_handle = 0; + m_handle = std::nullopt; // let everyone know we have been terminated m_terminateEvent.set(); @@ -623,7 +566,7 @@ SPHERE_THREADENTRY_RETNTYPE AbstractThread::runner(void *callerThread) bool AbstractThread::isActive() const { - return (m_handle != 0); + return m_handle.has_value(); } void AbstractThread::waitForClose() @@ -659,7 +602,7 @@ bool AbstractThread::isCurrentThread() const noexcept #ifdef _WIN32 return (getId() == ::GetCurrentThreadId()); #else - return pthread_equal(m_handle,pthread_self()); + return m_handle.has_value() && pthread_equal(m_handle.value(), pthread_self()); #endif } @@ -709,56 +652,119 @@ void AbstractThread::onStart() ThreadHolder::get().push(this); - if (isActive()) // This thread has actually been spawned and the code is executing on a different thread + if (isActive()) // This thread has actually been spawned and the code is executing on a different thread setThreadName(getName()); +#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); +#endif } -void AbstractThread::setPriority(IThread::Priority pri) +void AbstractThread::setPriority(ThreadPriority pri) { - ASSERT(((pri >= IThread::Idle) && (pri <= IThread::RealTime)) || (pri == IThread::Disabled)); + 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 IThread::Idle: + case ThreadPriority::Idle: m_tickPeriod = 1000; break; - case IThread::Low: + case ThreadPriority::Low: m_tickPeriod = 200; break; - case IThread::Normal: + case ThreadPriority::Normal: m_tickPeriod = 100; break; - case IThread::High: + case ThreadPriority::High: m_tickPeriod = 50; break; - case IThread::Highest: + case ThreadPriority::Highest: m_tickPeriod = 5; break; - case IThread::RealTime: + case ThreadPriority::RealTime: m_tickPeriod = 0; break; - case IThread::Disabled: + case ThreadPriority::Disabled: m_tickPeriod = AutoResetEvent::_kiInfinite; break; - }\ + } } bool AbstractThread::shouldExit() noexcept { - return m_terminateRequested || _thread_selfTerminateAfterThisTick; + return closing() || m_terminateRequested || _thread_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 + 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 +#pragma pack(push, 8) + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; + LPCSTR szName; + DWORD dwThreadID; + 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; + + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#endif // MSVC_COMPILER +#elif defined(__APPLE__) // Mac + pthread_setname_np(name_trimmed); +#elif !defined(_BSD) // Linux + prctl(PR_SET_NAME, name_trimmed, 0, 0, 0); +#elif defined(__FreeBSD__) || defined(__OpenBSD__) + pthread_set_name_np(getCurrentThreadId(), name_trimmed); +#elif defined(__NetBSD__) + pthread_setname_np(getCurrentThreadId(), "%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); } + /* * AbstractSphereThread */ -AbstractSphereThread::AbstractSphereThread(const char *name, Priority priority) - : AbstractThread(name, priority), _fIsClosing(false) +AbstractSphereThread::AbstractSphereThread(const char *name, ThreadPriority priority) + : AbstractThread(name, priority) { #ifdef THREAD_TRACK_CALLSTACK m_stackPos = -1; @@ -778,7 +784,6 @@ AbstractSphereThread::~AbstractSphereThread() _fIsClosing = true; } - char *AbstractSphereThread::allocateBuffer() noexcept { auto* tsholder = TemporaryStringThreadSafeStateHolder::get(); @@ -798,6 +803,7 @@ char *AbstractSphereThread::allocateBuffer() noexcept return buffer; } +static TemporaryStringThreadSafeStateHolder::TemporaryStringStorage * getThreadRawStringBuffer() { @@ -949,7 +955,7 @@ void AbstractSphereThread::printStackTrace() noexcept DummySphereThread *DummySphereThread::_instance = nullptr; DummySphereThread::DummySphereThread() - : AbstractSphereThread("dummy", IThread::Normal) + : AbstractSphereThread("dummy", ThreadPriority::Normal) { } @@ -989,7 +995,7 @@ StackDebugInformation::StackDebugInformation(const char *name) noexcept if (th.closing()) [[unlikely]] return; - IThread *icontext = th.current(); + AbstractThread *icontext = th.current(); if (icontext == nullptr) [[unlikely]] { @@ -1021,14 +1027,14 @@ StackDebugInformation::~StackDebugInformation() noexcept void StackDebugInformation::printStackTrace() noexcept // static { - IThread* pThreadState = ThreadHolder::get().current(); + AbstractThread* pThreadState = ThreadHolder::get().current(); if (pThreadState) static_cast(pThreadState)->printStackTrace(); } void StackDebugInformation::freezeCallStack(bool freeze) noexcept // static { - IThread* pThreadState = ThreadHolder::get().current(); + AbstractThread* pThreadState = ThreadHolder::get().current(); if (pThreadState) static_cast(pThreadState)->freezeCallStack(freeze); } diff --git a/src/sphere/threads.h b/src/sphere/threads.h index f4ea58293..2b72d6c4b 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -31,11 +31,12 @@ 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__ +# ifdef __APPLE__ typedef uint64_t threadid_t; -#else +# else typedef pthread_t threadid_t; #endif @@ -43,10 +44,10 @@ #define SPHERE_THREADENTRY_CALLTYPE #endif -class IThread; +class AbstractThread; // stores a value unique to each thread, intended to hold -// a pointer (e.g. the current IThread instance) +// a pointer (e.g. the current AbstractThread instance) template class TlsValue { @@ -130,131 +131,118 @@ T TlsValue::get() const #endif } +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 +}; -// Interface for threads. Almost always should be used instead of any implementing classes -class IThread +// Thread base implementation, without Sphere "extensions". +class AbstractThread { -public: // TODO: lazy - threadid_t m_threadSystemId; - int m_threadHolderId; + friend class ThreadHolder; + static int m_threadsAvailable; public: - enum Priority - { - Idle, // tick 1000ms - Low, // tick 200ms - Normal, // tick 100ms - High, // tick 50ms - Highest, // tick 5ms - RealTime, // tick almost instantly - Disabled = 0xFF // tick never - }; - - IThread() noexcept; - virtual ~IThread() noexcept; - - virtual threadid_t getId() const = 0; - virtual const char *getName() const = 0; - - virtual bool isActive() const = 0; - virtual bool checkStuck() = 0; - - virtual void start() = 0; - virtual void terminate(bool ended) = 0; - virtual void waitForClose() = 0; - - virtual void setPriority(Priority) = 0; - virtual Priority getPriority() const = 0; - - 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; -#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); - } - - static constexpr uint m_nameMaxLength = 16; // Unix support a max 16 bytes thread name. - static void setThreadName(const char* name); + static constexpr uint m_nameMaxLength = 16; // Unix support a max 16 bytes thread name. protected: - virtual bool shouldExit() noexcept = 0; -}; - + threadid_t m_threadSystemId; + int m_threadHolderId; -// Thread implementation. See IThread for list of available methods. -class AbstractThread : public IThread -{ - friend class ThreadHolder; + bool _fKeepAliveAtShutdown; + volatile std::atomic_bool _thread_selfTerminateAfterThisTick; + volatile std::atomic_bool _fIsClosing; +private: + volatile std::atomic_bool m_terminateRequested; + char m_name[30]; -protected: - bool _thread_selfTerminateAfterThisTick; + // 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; -private: - char m_name[30]; - static int m_threadsAvailable; - spherethread_t m_handle; uint m_hangCheck; - Priority m_priority; + ThreadPriority m_priority; uint m_tickPeriod; AutoResetEvent m_sleepEvent; - - volatile std::atomic_bool m_terminateRequested; ManualResetEvent m_terminateEvent; public: - AbstractThread(const char *name, Priority priority = IThread::Normal); + AbstractThread(const char *name, ThreadPriority priority = ThreadPriority::Normal); virtual ~AbstractThread(); AbstractThread(const AbstractThread& copy) = delete; AbstractThread& operator=(const AbstractThread& other) = delete; public: - virtual threadid_t getId() const noexcept override { return m_threadSystemId; } - virtual const char *getName() const noexcept override { return m_name; } + threadid_t getId() const noexcept { return m_threadSystemId; } + virtual const char *getName() const noexcept { return m_name; } - virtual bool isActive() const override; - virtual bool checkStuck() override; + virtual bool isActive() const; + virtual bool checkStuck(); - virtual void start() override; - virtual void terminate(bool ended) override; - virtual void waitForClose() override; + virtual void start(); + virtual void terminate(bool ended); + virtual void waitForClose(); void awaken(); - virtual void setPriority(Priority pri) override; - virtual Priority getPriority() const override { return m_priority; } + void setPriority(ThreadPriority pri); + ThreadPriority getPriority() const { return m_priority; } void overwriteInternalThreadName(const char* name) noexcept; bool isCurrentThread() const noexcept; -protected: - virtual void tick() = 0; - // NOTE: this should not be too long-lasted function, so no world loading, etc here!!! - virtual void onStart(); - virtual bool shouldExit() noexcept override; + protected: + virtual void tick() = 0; -private: - void run(); - static SPHERE_THREADENTRY_RETNTYPE SPHERE_THREADENTRY_CALLTYPE runner(void *callerThread); + // NOTE: this should not be too long-lasted function, so no world loading, etc here!!! + virtual void onStart(); + virtual bool shouldExit() noexcept; + + private: + void run(); + static SPHERE_THREADENTRY_RETNTYPE SPHERE_THREADENTRY_CALLTYPE runner(void *callerThread); + + public: + static void setThreadName(const char* name); + + bool closing() 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; +#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); + } }; @@ -263,7 +251,6 @@ class AbstractSphereThread : public AbstractThread { friend class ThreadHolder; - bool _fIsClosing; #ifdef THREAD_TRACK_CALLSTACK struct STACK_INFO_REC { @@ -277,7 +264,7 @@ class AbstractSphereThread : public AbstractThread #endif public: - AbstractSphereThread(const char *name, Priority priority = IThread::Normal); + AbstractSphereThread(const char *name, ThreadPriority priority = ThreadPriority::Normal); virtual ~AbstractSphereThread(); AbstractSphereThread(const AbstractSphereThread& copy) = delete; @@ -291,9 +278,6 @@ class AbstractSphereThread : public AbstractThread void getStringBuffer(TemporaryString &string) noexcept; void exceptionCaught(); - bool closing() noexcept { - return _fIsClosing; - } #ifdef THREAD_TRACK_CALLSTACK inline void freezeCallStack(bool freeze) noexcept @@ -335,8 +319,9 @@ class ThreadHolder { friend class AbstractThread; - struct SphereThreadData { - IThread *m_ptr; + struct SphereThreadData + { + AbstractThread *m_ptr; bool m_closed; }; using spherethreadlist_t = std::vector; @@ -356,7 +341,7 @@ class ThreadHolder friend void Sphere_ExitServer(void); void markThreadsClosing() CANTHROW; - //SphereThreadData* findThreadData(IThread* thread) noexcept; + //SphereThreadData* findThreadData(AbstractThread* thread) noexcept; public: static constexpr lpctstr m_sClassName = "ThreadHolder"; @@ -364,14 +349,14 @@ class ThreadHolder static ThreadHolder& get() noexcept; bool closing() noexcept; - // returns current working thread or DummySphereThread * if no IThread threads are running - IThread *current() 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(IThread *thread) noexcept; + void push(AbstractThread *thread) noexcept; // removes a thread from the list. Sould NOT be called, internal usage - void remove(IThread *thread) CANTHROW; + void remove(AbstractThread *thread) CANTHROW; // returns thread at i pos - IThread * getThreadAt(size_t at) noexcept; + AbstractThread * getThreadAt(size_t at) noexcept; // returns number of running threads. Sould NOT be called, unit tests usage inline size_t getActiveThreads() noexcept { return m_threadCount; }