diff --git a/cmake/toolchains/Windows-MSVC.cmake b/cmake/toolchains/Windows-MSVC.cmake index 29b032cb1..df29d25d6 100644 --- a/cmake/toolchains/Windows-MSVC.cmake +++ b/cmake/toolchains/Windows-MSVC.cmake @@ -116,9 +116,9 @@ function (toolchain_exe_stuff) target_compile_options(spheresvr PRIVATE ${cxx_compiler_flags_common} - $<$: $,/MT,/MD> /EHsc /Oy /GL /GA /Gw /Gy /GF /GR- $,/O1 /Zi,/O2>> - $<$: $,/MT,/MD> /EHa /Oy /GL /GA /Gw /Gy /GF $,/O1 /Zi,/O2>> - $<$: $,/MTd,/MDd> /EHsc /Oy- /MDd /ob1 /Od $,/Zi,/ZI>> #/Gs + $<$: $,/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- /MDd /ob1 /Od /Gs $,/Zi,/ZI>> # ASan (and compilation for ARM arch) doesn't support edit and continue option (ZI) ) diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index 4c5feb64b..70f06ba74 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -270,6 +270,7 @@ src/common/sphere_library/sstring.cpp src/common/sphere_library/sstring.h src/common/sphere_library/sstringobjs.cpp src/common/sphere_library/sstringobjs.h +src/common/sphere_library/stypecast.h ) SOURCE_GROUP (common\\sphere_library FILES ${spherelibrary_SRCS}) diff --git a/src/common/CCacheableScriptFile.cpp b/src/common/CCacheableScriptFile.cpp index 25f50267a..be1f53ed0 100644 --- a/src/common/CCacheableScriptFile.cpp +++ b/src/common/CCacheableScriptFile.cpp @@ -99,9 +99,34 @@ bool CCacheableScriptFile::_Open(lpctstr ptcFilename, uint uiModeFlags) _fileContent = new std::vector; _fileContent->reserve(iFileLength / 25); + auto _SkipOneEndline = [](lpctstr &str_end) -> size_t // return how much i have moved past + { + bool fDoneN = false, fDoneR = false; + lpctstr before = str_end; + while (true) + { + const char ch = *str_end; + if (ch == '\n' && !fDoneN) + { + fDoneN = true; + ++str_end; + } + else if (ch == '\r' && !fDoneR) + { + fDoneR = true; + ++str_end; + } + else + { + break; + } + } + return str_end - before; + }; + const char *fileCursor = fileContentCopy.get(); size_t uiFileCursorRemainingLegth = iFileLength; - ssize_t iStrLen; + ssize_t iStrLen = 0; for (;; fileCursor += (size_t)iStrLen, uiFileCursorRemainingLegth -= (size_t)iStrLen) { if (uiFileCursorRemainingLegth == 0) @@ -109,11 +134,16 @@ bool CCacheableScriptFile::_Open(lpctstr ptcFilename, uint uiModeFlags) iStrLen = sGetLine_StaticBuf(fileCursor, minimum(uiFileCursorRemainingLegth, SCRIPT_MAX_LINE_LEN)); if (iStrLen < 0) - break; + break; if (iStrLen < 1 /*|| (fileCursor[iStrLen] != '\n') It can also be a '\0' value, but it might not be necessary to check for either of the two...*/) { - ++ iStrLen; // Skip \n + lpctstr str_end = fileCursor; + size_t uiSkip = _SkipOneEndline(str_end); + ASSERT(uiSkip > 0); + //iStrLen = (ssize_t)std::max((size_t)1, uiSkip); + iStrLen = (ssize_t)uiSkip; + _fileContent->emplace_back(); continue; } @@ -128,24 +158,14 @@ bool CCacheableScriptFile::_Open(lpctstr ptcFilename, uint uiModeFlags) const lpctstr str_start = (fUTF ? &fileCursor[3] : fileCursor); size_t len_to_copy = (size_t)iStrLen - (fUTF ? 3 : 0); - while (len_to_copy > 0) - { - const int ch = str_start[len_to_copy - 1]; - if (ch == '\n' || ch == '\r') - { - // Do not copy that, but skip it. - len_to_copy -= 1; - iStrLen += 1; - } - else { - break; - } - } + ASSERT(len_to_copy > 0); + + // go past the end of this substring, there should be the delimiter/newline/etc, just skip it. + lpctstr str_end = &str_start[len_to_copy]; + _SkipOneEndline(str_end); + _fileContent->emplace_back(str_start, len_to_copy); + iStrLen += (str_end - len_to_copy - str_start); - if (len_to_copy == 0) - _fileContent->emplace_back(); - else - _fileContent->emplace_back(str_start, len_to_copy); fFirstLine = false; fUTF = false; } // closes while diff --git a/src/common/CPointBase.cpp b/src/common/CPointBase.cpp index 5f09b249c..377aafaac 100644 --- a/src/common/CPointBase.cpp +++ b/src/common/CPointBase.cpp @@ -905,6 +905,7 @@ int CPointBase::Read( tchar * pszVal ) CPointBase ptTest; ptTest.m_z = 0; ptTest.m_map = 0; + bool fError = false; tchar * ppVal[4]; int iArgs = Str_ParseCmds( pszVal, ppVal, ARRAY_COUNT( ppVal ), " ,\t" ); @@ -914,7 +915,14 @@ int CPointBase::Read( tchar * pszVal ) case 4: // m_map if ( IsDigit(ppVal[3][0])) { - ptTest.m_map = (uchar)(Str_ToUI(ppVal[3])); + const std::optional from = Str_ToU8(ppVal[3]); + if (!from.has_value()) + { + fError = true; + break; + } + + ptTest.m_map = from.value(); if ( !g_MapList.IsMapSupported(ptTest.m_map) ) { g_Log.EventError("Unsupported map #%d specified. Auto-fixing that to 0.\n", ptTest.m_map); @@ -925,26 +933,48 @@ int CPointBase::Read( tchar * pszVal ) case 3: // m_z if (IsDigit(ppVal[2][0]) || ppVal[2][0] == '-') { - ptTest.m_z = (char)(Str_ToI(ppVal[2])); + const std::optional from = Str_ToI8(ppVal[2]); + if (!from.has_value()) + { + fError = true; + break; + } + + ptTest.m_z = from.value(); } FALLTHROUGH; case 2: if (IsDigit(ppVal[1][0])) { - ptTest.m_y = (short)(Str_ToI(ppVal[1])); + const std::optional from = Str_ToI16(ppVal[1]); + if (!from.has_value()) + { + fError = true; + break; + } + + ptTest.m_y = from.value(); } FALLTHROUGH; case 1: if (IsDigit(ppVal[0][0])) { - ptTest.m_x = (short)(Str_ToI(ppVal[0])); + std::optional from = Str_ToI16(ppVal[0]); + if (!from.has_value()) + { + fError = true; + break; + } + + ptTest.m_x = from.value(); } FALLTHROUGH; case 0: break; } - if (!ptTest.IsValidPoint()) + fError |= !ptTest.IsValidPoint(); + if (fError) { InitPoint(); return 0; diff --git a/src/common/CScriptObj.cpp b/src/common/CScriptObj.cpp index d418ebc78..0836fdc77 100644 --- a/src/common/CScriptObj.cpp +++ b/src/common/CScriptObj.cpp @@ -1103,9 +1103,20 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc int iQty = Str_ParseCmds(const_cast(ptcKey), ppCmd, ARRAY_COUNT(ppCmd), ", "); if ( iQty < 3 ) return false; - int iPrefixCode = Str_ToI(ppCmd[0]); - int iCost = Str_ToI(ppCmd[1]); - CSString sHash = CBCrypt::HashBCrypt(ppCmd[2], iPrefixCode, maximum(4,minimum(31,iCost))); + + std::optional iconv; + + iconv = Str_ToI(ppCmd[0]); + if (!iconv.has_value()) + return false; + int iPrefixCode = *iconv; + + iconv = Str_ToI(ppCmd[1]); + if (!iconv.has_value()) + return false; + int iCost = *iconv; + + CSString sHash(CBCrypt::HashBCrypt(ppCmd[2], iPrefixCode, maximum(4,minimum(31,iCost)))); sVal.Format("%s", sHash.GetBuffer()); } return true; @@ -1115,6 +1126,7 @@ bool CScriptObj::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc int iQty = Str_ParseCmds(const_cast(ptcKey), ppCmd, ARRAY_COUNT(ppCmd), ", "); if ( iQty < 2 ) return false; + bool fValidated = CBCrypt::ValidateBCrypt(ppCmd[0], ppCmd[1]); sVal.FormatVal((int)fValidated); } return true; @@ -1897,7 +1909,7 @@ int CScriptObj::ParseScriptText(tchar * ptcResponse, CTextConsole * pSrc, int iF pContext->_fParseScriptText_Brackets = false; tchar* ptcRecurseParse = ptcResponse + i; - const size_t iLen = ParseScriptText(ptcRecurseParse, pSrc, 4, pArgs); + const int iLen = ParseScriptText(ptcRecurseParse, pSrc, 4, pArgs); pContext->_fParseScriptText_Brackets = true; -- pContext->_iParseScriptText_Reentrant; @@ -2136,7 +2148,11 @@ bool CScriptObj::Execute_FullTrigger(CScript& s, CTextConsole* pSrc, CScriptTrig CScriptObj* pRef = this; if (iArgQty == 2) { - CChar* pCharFound = CUID::CharFindFromUID(Str_ToI(piCmd[1])); + std::optional iconv = Str_ToU(piCmd[1]); + if (!iconv.has_value()) + return false; + + CChar* pCharFound = CUID::CharFindFromUID(*iconv); if (pCharFound) pRef = pCharFound; } diff --git a/src/common/CUOClientVersion.cpp b/src/common/CUOClientVersion.cpp index 3d393c1e3..07113de19 100644 --- a/src/common/CUOClientVersion.cpp +++ b/src/common/CUOClientVersion.cpp @@ -1,5 +1,6 @@ #include "CUOClientVersion.h" #include "CExpression.h" +#include "CLog.h" #include "sphereproto.h" #include #include @@ -18,11 +19,13 @@ uint CUOClientVersion::GetLegacyVersionNumber() const noexcept uint factor_revision = 100; uint factor_minor = 100'00; uint factor_major = 100'00'00; - if (m_revision > 100) { + if (m_revision > 100) + { factor_minor *= 10; factor_major *= 10; } - if (m_minor > 100) { + if (m_minor > 100) + { factor_major *= 10; } @@ -75,7 +78,7 @@ CUOClientVersion::CUOClientVersion(dword uiClientVersionNumber) noexcept : m_major = uiClientVersionNumber / 100'000'00; m_minor = (uiClientVersionNumber / 1000'00) % 100; m_revision = (uiClientVersionNumber / 100 ) % 1000; - + } else if (uiClientVersionNumber > 10'00'000'00) { @@ -120,13 +123,16 @@ CUOClientVersion::CUOClientVersion(lpctstr ptcVersion) noexcept : // Ranges algorithms not yet supported by Apple Clang... // const ptrdiff_t count = std::ranges::count(std::string_view(ptcVersion), '.'); const auto svVersion = std::string_view(ptcVersion); - const auto count = std::count(svVersion.cbegin(), svVersion.cend(), '_'); + const auto count = std::count(svVersion.cbegin(), svVersion.cend(), '.'); if (count == 2) ApplyVersionFromStringOldFormat(ptcVersion); else if (count == 3) - ApplyVersionFromStringOldFormat(ptcVersion); + ApplyVersionFromStringNewFormat(ptcVersion); else - ASSERT(false); // Malformed string? + { + // Malformed string? + g_Log.Event(LOGL_CRIT|LOGM_CLIENTS_LOG|LOGM_NOCONTEXT, "Received malformed client version string?.\n"); + } } @@ -167,7 +173,7 @@ void CUOClientVersion::ApplyVersionFromStringOldFormat(lpctstr ptcVersion) noexc // Get version of old clients, which report the client version as ASCII string (eg: '5.0.2b') byte uiLetter = 0; - size_t uiMax = strlen(ptcVersion); + const size_t uiMax = strnlen(ptcVersion, 20); for (size_t i = 0; i < uiMax; ++i) { if (IsAlpha(ptcVersion[i])) @@ -195,7 +201,7 @@ void CUOClientVersion::ApplyVersionFromStringNewFormat(lpctstr ptcVersion) noexc { // Get version of newer clients, which use only 4 numbers separated by dots (example: 6.0.1.1) - std::string_view sw(ptcVersion); + const std::string_view sw(ptcVersion); const size_t dot1 = sw.find_first_of('.', 0); const size_t dot2 = sw.find_first_of('.', dot1 + 1); const size_t dot3 = sw.find_first_of('.', dot2 + 1); @@ -210,10 +216,10 @@ void CUOClientVersion::ApplyVersionFromStringNewFormat(lpctstr ptcVersion) noexc uint val1, val2, val3, val4; bool ok = true; - ok = ok && svtonum(sw1, val1); - ok = ok && svtonum(sw2, val2); - ok = ok && svtonum(sw3, val3); - ok = ok && svtonum(sw4, val4); + ok = ok && sv_to_num(sw1, &val1); + ok = ok && sv_to_num(sw2, &val2); + ok = ok && sv_to_num(sw3, &val3); + ok = ok && sv_to_num(sw4, &val4); if (!ok) return; diff --git a/src/common/basic_threading.h b/src/common/basic_threading.h index 5f1d802ca..d5c7193fc 100644 --- a/src/common/basic_threading.h +++ b/src/common/basic_threading.h @@ -50,15 +50,15 @@ #if MT_ENGINES == 1 //_DEBUG -#define MT_ENGINE_SHARED_LOCK_SET MT_SHARED_LOCK_SET -#define MT_ENGINE_SHARED_LOCK MT_SHARED_LOCK -#define MT_ENGINE_SHARED_UNLOCK MT_SHARED_UNLOCK -#define MT_ENGINE_UNIQUE_LOCK_SET MT_UNIQUE_LOCK_SET -#define MT_ENGINE_UNIQUE_UNLOCK MT_UNIQUE_UNLOCK - -#define MT_ENGINE_RETURN(x) MT_RETURN(x) -#define MT_ENGINE_SHARED_LOCK_RETURN(x) MT_SHARED_LOCK_RETURN(x) -#define MT_ENGINE_UNIQUE_LOCK_RETURN(x) MT_UNIQUE_LOCK_RETURN(x) + #define MT_ENGINE_SHARED_LOCK_SET MT_SHARED_LOCK_SET + #define MT_ENGINE_SHARED_LOCK MT_SHARED_LOCK + #define MT_ENGINE_SHARED_UNLOCK MT_SHARED_UNLOCK + #define MT_ENGINE_UNIQUE_LOCK_SET MT_UNIQUE_LOCK_SET + #define MT_ENGINE_UNIQUE_UNLOCK MT_UNIQUE_UNLOCK + + #define MT_ENGINE_RETURN(x) MT_RETURN(x) + #define MT_ENGINE_SHARED_LOCK_RETURN(x) MT_SHARED_LOCK_RETURN(x) + #define MT_ENGINE_UNIQUE_LOCK_RETURN(x) MT_UNIQUE_LOCK_RETURN(x) #else #define MT_ENGINE_SHARED_LOCK_SET (void)0 diff --git a/src/common/common.cpp b/src/common/common.cpp index d88a4361b..d7b937b8b 100644 --- a/src/common/common.cpp +++ b/src/common/common.cpp @@ -7,7 +7,8 @@ extern "C" { - void globalstartsymbol() {} // put this here as just the starting offset. + // Put this here as just the starting offset. Needed by Windows crash dump. + void globalstartsymbol() {} } #ifndef _WIN32 @@ -16,7 +17,7 @@ extern "C" #include #include #include - int getTimezone() + int getTimezone() noexcept { tm tp; memset(&tp, 0x00, sizeof(tp)); @@ -27,7 +28,7 @@ extern "C" #else // _WIN32 - const OSVERSIONINFO * Sphere_GetOSInfo() + const OSVERSIONINFO * Sphere_GetOSInfo() noexcept { // NEVER return nullptr ! static OSVERSIONINFO g_osInfo; @@ -47,14 +48,16 @@ extern "C" #endif // !_WIN32 -CLanguageID::CLanguageID(int iDefault) : +// TODO: move those in a separate cpp file. + +CLanguageID::CLanguageID(int iDefault) noexcept : m_codes{} { UnreferencedParameter(iDefault); ASSERT(iDefault == 0); } -void CLanguageID::GetStrDef(tchar* pszLang) +void CLanguageID::GetStrDef(tchar* pszLang) noexcept { if (!IsDef()) { @@ -67,7 +70,7 @@ void CLanguageID::GetStrDef(tchar* pszLang) } } -void CLanguageID::GetStr(tchar* pszLang) const +void CLanguageID::GetStr(tchar* pszLang) const noexcept { memcpy(pszLang, m_codes, 3); pszLang[3] = '\0'; @@ -80,7 +83,7 @@ lpctstr CLanguageID::GetStr() const return pszTmp; } -bool CLanguageID::Set(lpctstr pszLang) +bool CLanguageID::Set(lpctstr pszLang) noexcept { // needs not be terminated! if (pszLang != nullptr) diff --git a/src/common/common.h b/src/common/common.h index 36312c656..a9f037a06 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -20,7 +20,6 @@ // C abs function has different in/out types the std:: ones in cmath. It's defined in stdlib.h. #include #include // for smart pointers -#include #include "assertion.h" #include "basic_threading.h" @@ -32,6 +31,8 @@ #include "os_unix.h" #endif +#include "sphere_library/stypecast.h" + // Strings #define _STRINGIFY_AUX(x) #x @@ -61,7 +62,7 @@ */ #undef UNREFERENCED_PARAMETER template -inline void UnreferencedParameter(T const&) noexcept { +inline constexpr void UnreferencedParameter(T const&) noexcept { ; } @@ -130,192 +131,44 @@ inline void UnreferencedParameter(T const&) noexcept { #define MAKEDWORD(low, high) ((dword)(((word)low) | (((dword)((word)high)) << 16))) -//-- Explicitly promote to a larger type or narrow to a smaller type, instead of inattentive casts. - -// Promote to the corresponding 32 bits numeric type a smaller numeric variable. -template -auto i_promote32(const T a) noexcept -{ - static_assert(std::is_arithmetic_v, "Input variable is not an 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"); - static_assert(sizeof(T) < 4, "Input variable is not smaller than a 32 bit number."); - if constexpr (std::is_signed_v) - { - if constexpr (std::is_floating_point_v) - return static_cast(a); - return static_cast(a); - } - else - return static_cast(a); -} - -// Promote to the corresponding 64 bits numeric type a smaller numeric variable. -template -auto i_promote64(const T a) noexcept -{ - static_assert(std::is_arithmetic_v, "Input variable is not an 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"); - static_assert(sizeof(T) < 8, "Input variable is not smaller than a 64 bit number."); - if constexpr (std::is_signed_v) - { - if constexpr (std::is_floating_point_v) - return static_cast(a); - return static_cast(a); - } - else - return static_cast(a); -} - -template -auto i_narrow32(const T a) noexcept -{ - static_assert(std::is_arithmetic_v, "Input variable is not an 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"); - static_assert(sizeof(T) >= 4, "Input variable is smaller than a 32 bit number."); - - // Since the narrowing can be implementation specific, here we decide that we take only the lower 32 bytes and discard the upper ones. - constexpr uint64 umask = 0x0000'0000'FFFF'FFFF; - if constexpr (std::is_signed_v) - { - if constexpr (std::is_floating_point_v) - return static_cast(static_cast(a) & umask); - return static_cast(static_cast(a) & umask); - } - else - return static_cast(static_cast(a) & umask); -} - -template -auto i_narrow16(const T a) noexcept -{ - static_assert(std::is_arithmetic_v, "Input variable is not an 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"); - static_assert(sizeof(T) >= 2, "Input variable is smaller than a 16 bit number."); - - // Since the narrowing can be implementation specific, here we decide that we take only the lower 32 bytes and discard the upper ones. - constexpr uint64 umask = 0x0000'0000'FFFF'FFFF; - if constexpr (std::is_signed_v) - { - if constexpr (std::is_floating_point_v) - return static_cast(static_cast(a) & umask); - return static_cast(static_cast(a) & umask); - } - else - return static_cast(static_cast(a) & umask); -} - -template -auto i64_narrow32(const T a) noexcept -{ - static_assert(std::is_arithmetic_v, "Input variable is not an 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"); - static_assert(sizeof(T) == 8, "Input variable is not a 64 bit number."); - - // Since the narrowing can be implementation specific, here we decide that we take only the lower 32 bytes and discard the upper ones. - constexpr uint64 umask = 0x0000'0000'FFFF'FFFF; - if constexpr (std::is_signed_v) - { - if constexpr (std::is_floating_point_v) - return static_cast(a & umask); - return static_cast(a & umask); - } - else - return static_cast(a & umask); -} - -template -auto i64_narrow16(const T a) noexcept -{ - static_assert(std::is_arithmetic_v, "Input variable is not an 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"); - static_assert(sizeof(T) == 8, "Input variable is not a 64 bit number."); - - // Since the narrowing can be implementation specific, here we decide that we take only the lower 32 bytes and discard the upper ones. - constexpr uint64 umask = 0x0000'0000'0000'FFFF; - if constexpr (std::is_signed_v) - { - if constexpr (std::is_floating_point_v) - return static_cast(a & umask); - return static_cast(a & umask); - } - else - return static_cast(a & umask); -} - -template -auto i32_narrow16(const T a) noexcept -{ - static_assert(std::is_arithmetic_v, "Input variable is not an 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"); - static_assert(sizeof(T) == 4, "Input variable is not a 32 bit number."); - - // 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'FFFF; - if constexpr (std::is_signed_v) - { - if constexpr (std::is_floating_point_v) - return static_cast(a & umask); - return static_cast(a & umask); - } - else - return static_cast(a & umask); -} - -template -auto i16_narrow8(const T a) noexcept -{ - static_assert(std::is_arithmetic_v, "Input variable is not an arithmetic type."); - static_assert(std::is_integral_v, "Only integral types are supported by this function."); - static_assert(sizeof(T) == 2, "Input variable is not a 16 bit number."); - - // 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); - } - else - return static_cast(a & umask); -} - - //#define IMulDiv(a,b,c) (((((int)(a)*(int)(b)) + (int)(c / 2)) / (int)(c)) - (IsNegative((int)(a)*(int)(b)))) -inline int IMulDiv(const int a, const int b, const int c) noexcept +inline constexpr int IMulDiv(const int a, const int b, const int c) noexcept { const int ab = a*b; return ((ab + (c/2)) / c) - IsNegative(ab); } -inline uint UIMulDiv(const uint a, const uint b, const uint c) noexcept +inline constexpr uint UIMulDiv(const uint a, const uint b, const uint c) noexcept { const int ab = a * b; return ((ab + (c / 2)) / c) - IsNegative(ab); } //#define IMulDivLL(a,b,c) (((((llong)(a)*(llong)(b)) + (llong)(c / 2)) / (llong)(c)) - (IsNegative((llong)(a)*(llong)(b)))) -inline llong IMulDivLL(const llong a, const llong b, const llong c) noexcept +inline constexpr llong IMulDivLL(const llong a, const llong b, const llong c) noexcept { const llong ab = a*b; return ((ab + (c/2)) / c) - IsNegative(ab); } -inline realtype IMulDivRT(const realtype a, const realtype b, const realtype c) noexcept +inline constexpr realtype IMulDivRT(const realtype a, const realtype b, const realtype c) noexcept { const realtype ab = a*b; return ((ab + (c/2)) / c) - IsNegative(ab); } //#define IMulDivDown(a,b,c) (((a)*(b))/(c)) -inline int IMulDivDown(const int a, const int b, const int c) noexcept +inline constexpr int IMulDivDown(const int a, const int b, const int c) noexcept { return (a*b)/c; } -inline llong IMulDivDownLL(const llong a, const llong b, const llong c) noexcept +inline constexpr llong IMulDivDownLL(const llong a, const llong b, const llong c) noexcept { return (a*b)/c; } //#define sign(n) (((n) < 0) ? -1 : (((n) > 0) ? 1 : 0)) -template inline T sign(const T n) noexcept +template +inline constexpr T sign(const T n) noexcept { static_assert(std::is_arithmetic::value, "Invalid data type."); return ( (n < 0) ? -1 : ((n > 0) ? 1 : 0) ); diff --git a/src/common/os_windows.h b/src/common/os_windows.h index ee7990582..c564ef059 100644 --- a/src/common/os_windows.h +++ b/src/common/os_windows.h @@ -77,7 +77,7 @@ #define __except(P) catch(int) #endif // _MSC_VER -const OSVERSIONINFO * Sphere_GetOSInfo(); +const OSVERSIONINFO * Sphere_GetOSInfo() noexcept; #endif // _INC_OS_WINDOWS_H diff --git a/src/common/resource/CResourceDef.cpp b/src/common/resource/CResourceDef.cpp index 15ff928a1..23fa784a4 100644 --- a/src/common/resource/CResourceDef.cpp +++ b/src/common/resource/CResourceDef.cpp @@ -139,7 +139,7 @@ bool CResourceDef::MakeResourceName() // Is this is subsequent key with a number? Get the highest (plus one) if ( IsStrNumericDec( ptcKey ) ) { - size_t uiVarThis = Str_ToUI( ptcKey ); + size_t uiVarThis = Str_ToU( ptcKey ); if ( uiVarThis >= uiVar ) uiVar = uiVarThis + 1; } diff --git a/src/common/resource/sections/CDialogDef.cpp b/src/common/resource/sections/CDialogDef.cpp index ce4a091c3..b0a4f90db 100644 --- a/src/common/resource/sections/CDialogDef.cpp +++ b/src/common/resource/sections/CDialogDef.cpp @@ -139,7 +139,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t // The first part of the key is GUMPCTL_TYPE lpctstr ptcKey = s.GetKey(); - int index = FindTableSorted( ptcKey, sm_szLoadKeys, ARRAY_COUNT(sm_szLoadKeys)-1 ); + const int index = FindTableSorted( ptcKey, sm_szLoadKeys, ARRAY_COUNT(sm_szLoadKeys)-1 ); if ( index < 0 ) { const size_t uiFunctionIndex = r_GetFunctionIndex(ptcKey); @@ -151,13 +151,13 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t if ( r_Call(uiFunctionIndex, pSrc, &Args, &sVal) ) return true; } + if (!m_pObj) return CResourceLink::r_Verb(s, pSrc); return m_pObj->r_Verb(s, pSrc); } lpctstr pszArgs = s.GetArgStr(); - switch( index ) { case GUMPCTL_PAGE: @@ -177,6 +177,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "page %d", iNewPage ); return true; } + case GUMPCTL_BUTTON: // 7 = X,Y,Down gump,Up gump,pressable(1/0),page,id case GUMPCTL_BUTTONTILEART: // 11 = X,Y,Down gump,Up gump,pressable(1/0),page,id,tileart,hue,X,Y { @@ -210,6 +211,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t return true; } + case GUMPCTL_GUMPPIC: { GET_RELATIVE( x, m_iOriginX ); @@ -220,6 +222,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "gumppic %d %d %d%s%s", x, y, id, *pszArgs ? " hue=" : "", *pszArgs ? pszArgs : "" ); return true; } + case GUMPCTL_GUMPPICTILED: { GET_RELATIVE( x, m_iOriginX ); @@ -231,6 +234,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "gumppictiled %d %d %d %d %d", x, y, sX, sY, id ); return true; } + case GUMPCTL_PICINPIC: { GET_RELATIVE(x, m_iOriginX); @@ -244,6 +248,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format("picinpic %d %d %d %d %d %d %d", x, y, id, w, h, sX, sY); return true; } + case GUMPCTL_RESIZEPIC: { GET_RELATIVE( x, m_iOriginX ); @@ -255,6 +260,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "resizepic %d %d %d %d %d", x, y, id, sX, sY ); return true; } + case GUMPCTL_TILEPIC: case GUMPCTL_TILEPICHUE: { @@ -271,6 +277,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t return true; } + case GUMPCTL_DTEXT: { GET_RELATIVE( x, m_iOriginX ); @@ -284,6 +291,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "text %d %d %d %u", x, y, hue, iText ); return true; } + case GUMPCTL_DCROPPEDTEXT: { GET_RELATIVE( x, m_iOriginX ); @@ -298,6 +306,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "croppedtext %d %d %d %d %d %u", x, y, w, h, hue, iText ); return true; } + case GUMPCTL_DHTMLGUMP: { GET_RELATIVE( x, m_iOriginX ); @@ -312,6 +321,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "htmlgump %d %d %d %d %u %d %d", x, y, w, h, iText, bck, options ); return true; } + case GUMPCTL_DTEXTENTRY: { GET_RELATIVE( x, m_iOriginX ); @@ -326,6 +336,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "textentry %d %d %d %d %d %d %u", x, y, w, h, hue, id, iText ); return true; } + case GUMPCTL_DTEXTENTRYLIMITED: { GET_RELATIVE( x, m_iOriginX ); @@ -341,6 +352,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "textentrylimited %d %d %d %d %d %d %u %d", x, y, w, h, hue, id, iText, charLimit ); return true; } + case GUMPCTL_CHECKBOX: { GET_RELATIVE( x, m_iOriginX ); @@ -353,6 +365,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "checkbox %d %d %d %d %d %d", x, y, down, up, state, id ); return true; } + case GUMPCTL_RADIO: { GET_RELATIVE( x, m_iOriginX ); @@ -365,6 +378,18 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "radio %d %d %d %d %d %d", x, y, down, up, state, id ); return true; } + + case GUMPCTL_TOOLTIP: + { + // The client expects this to be sent as a string in decimal notation. + std::optional conv = Str_ToU(pszArgs); + if (!conv.has_value()) + return false; + + m_sControls.emplace_back(false).Format( "tooltip %u", conv.value() ); + return true; + } + case GUMPCTL_CHECKERTRANS: { GET_RELATIVE( x, m_iOriginX ); @@ -375,6 +400,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "checkertrans %d %d %d %d", x, y, width, height ); return true; } + case GUMPCTL_DORIGIN: { // GET_RELATIVE( x, m_iOriginX ); @@ -400,9 +426,11 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t return true; } + case GUMPCTL_NODISPOSE: m_fNoDispose = true; break; + case GUMPCTL_CROPPEDTEXT: case GUMPCTL_TEXT: case GUMPCTL_TEXTENTRY: @@ -427,6 +455,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format( "xmfhtmlgumpcolor %d %d %d %d %d %d %d%s%s", x, y, sX, sY, cliloc, hasBack, canScroll, *pszArgs ? " " : "", *pszArgs ? pszArgs : "" ); return true; } + case GUMPCTL_XMFHTMLTOK: // 9 = x y width height has_background has_scrollbar color cliloc_id @args { GET_RELATIVE(x, m_iOriginX); @@ -442,6 +471,7 @@ bool CDialogDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on t m_sControls.emplace_back(false).Format("xmfhtmltok %d %d %d %d %d %d %d %d %s", x, y, sX, sY, hasBack, canScroll, color, cliloc, *pszArgs ? pszArgs : ""); return true; } + default: break; } @@ -536,6 +566,7 @@ bool CDialogDef::GumpSetup( int iPage, CClient * pClient, CObjBase * pObjSrc, lp const auto trigRet = OnTriggerRunVal( s, TRIGRUN_SECTION_TRUE, pClient->GetChar(), &Args ); m_sText.shrink_to_fit(); m_sControls.shrink_to_fit(); + if ( trigRet == TRIGRET_RET_TRUE ) return false; return true; diff --git a/src/common/sphere_library/CSString.h b/src/common/sphere_library/CSString.h index ecadf8b94..d68e70329 100644 --- a/src/common/sphere_library/CSString.h +++ b/src/common/sphere_library/CSString.h @@ -130,14 +130,14 @@ class CSString * @brief Check the length of the CSString. * @return true if length is 0, false otherwise. */ - NODISCARD + [[nodiscard]] inline bool IsEmpty() const noexcept; /** * @brief Check if there is data allocated and if has zero length. * @return false if no data or zero length, true otherwise. */ - NODISCARD + [[nodiscard]] bool IsValid() const noexcept; /** @@ -155,14 +155,14 @@ class CSString * @brief Get the length of the held string. * @return the length of the CSString. */ - NODISCARD + [[nodiscard]] inline int GetLength() const noexcept; /** * @brief Get the length of the internal buffer, which can be greater than the held string length. * @return the length of the CSString. */ - NODISCARD + [[nodiscard]] inline int GetCapacity() const noexcept; ///@} @@ -177,7 +177,7 @@ class CSString * @param nIndex position of the character. * @return character in position nIndex. */ - NODISCARD + [[nodiscard]] inline tchar operator[](int nIndex) const; /** @@ -186,7 +186,7 @@ class CSString * @param nIndex position of the character. * @return reference to character in position nIndex. */ - NODISCARD + [[nodiscard]] inline tchar& operator[](int nIndex); /** @@ -194,7 +194,7 @@ class CSString * @param nIndex position of the character. * @return character in position nIndex. */ - NODISCARD + [[nodiscard]] inline tchar GetAt(int nIndex) const; /** @@ -202,7 +202,7 @@ class CSString * @param nIndex position of the character. * @return reference to character in position nIndex. */ - NODISCARD + [[nodiscard]] inline tchar& ReferenceAt(int nIndex); /** @@ -478,7 +478,7 @@ class CSString * @brief cast as const lpcstr. * @return internal data pointer. */ - NODISCARD + [[nodiscard]] inline operator lpctstr() const noexcept; /** @@ -491,7 +491,7 @@ class CSString * @param pStr string to compare. * @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 + [[nodiscard]] inline int Compare(lpctstr pStr) const noexcept; /** @@ -504,14 +504,14 @@ class CSString * @param pStr string to compare. * @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 + [[nodiscard]] inline int CompareNoCase(lpctstr pStr) const noexcept; /** * @brief Gets the internal pointer. * @return Pointer to internal data. */ - NODISCARD + [[nodiscard]] inline lpctstr GetBuffer() const noexcept; // Provide only a read-only buffer: if we modify it we'll break the internal length counter, other than possibly write past the end of the string (the buffer is small). @@ -522,7 +522,7 @@ class CSString * @param c character to look for. * @return position of the character in CSString if any, -1 otherwise. */ - NODISCARD + [[nodiscard]] inline int indexOf(tchar c) noexcept; /** @@ -531,7 +531,7 @@ class CSString * @param offset position from start the search. * @return position of the character in CSString if any, -1 otherwise. */ - NODISCARD + [[nodiscard]] int indexOf(tchar c, int offset) noexcept; /** @@ -539,7 +539,7 @@ class CSString * @param str substring to look for. * @return position of the substring in CSString if any, -1 otherwise. */ - NODISCARD + [[nodiscard]] inline int indexOf(const CSString& str) noexcept; /** @@ -548,7 +548,7 @@ class CSString * @param offset position from start the search. * @return position of the substring in CSString if any, -1 otherwise. */ - NODISCARD + [[nodiscard]] int indexOf(const CSString& str, int offset) noexcept; /** @@ -556,7 +556,7 @@ class CSString * @param c character to look for. * @return position of the character in CSString if any, -1 otherwise. */ - NODISCARD + [[nodiscard]] inline int lastIndexOf(tchar c) noexcept; /** @@ -565,7 +565,7 @@ class CSString * @param from position where stop the search. * @return position of the character in CSString if any, -1 otherwise. */ - NODISCARD + [[nodiscard]] int lastIndexOf(tchar c, int from) noexcept; /** @@ -573,7 +573,7 @@ class CSString * @param str substring to look for. * @return position of the substring in CSString if any, -1 otherwise. */ - NODISCARD + [[nodiscard]] inline int lastIndexOf(const CSString& str) noexcept; /** @@ -582,7 +582,7 @@ class CSString * @param from position where stop the search. * @return position of the substring in CSString if any, -1 otherwise. */ - NODISCARD + [[nodiscard]] int lastIndexOf(const CSString& str, int from) noexcept; ///@} diff --git a/src/common/sphere_library/CSTime.cpp b/src/common/sphere_library/CSTime.cpp index 9ad60d751..54c6dc423 100644 --- a/src/common/sphere_library/CSTime.cpp +++ b/src/common/sphere_library/CSTime.cpp @@ -5,6 +5,7 @@ // #include +#include #include "CSTime.h" #include "CSString.h" #include "../../common/CLog.h" @@ -13,7 +14,7 @@ // Windows epoch is January 1, 1601 (start of Gregorian calendar cycle) // Unix epoch is January 1, 1970 (adjustment in "ticks" 100 nanosecond) -#define UNIX_TICKS_PER_SECOND 10000000 //a tick is 100ns +//#define UNIX_TICKS_PER_SECOND 10000000 //a tick is 100ns #if _WIN32 //# define UNIX_TIME_START 0x019DB1DED53E8000LL // January 1, 1970 (start of Unix epoch) in "ticks" //# define WINDOWS_UNIX_EPOCH_OFFSET 11644473600 // (number of seconds between January 1, 1601 and January 1, 1970). @@ -142,6 +143,12 @@ CSTime CSTime::GetCurrentTime() noexcept // static return CSTime(::time(nullptr)); } +/* +CSTime::CSTime( struct tm atm ) noexcept +{ + m_time = mktime(&atm); +} +*/ CSTime::CSTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST) noexcept @@ -157,24 +164,69 @@ CSTime::CSTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, m_time = mktime(&atm); } -CSTime::CSTime( struct tm atm ) noexcept +static std::tm safe_localtime(const time_t t) noexcept { - m_time = mktime(&atm); + std::tm atm {}; + + // Standard C localtime is not thread-safe. We need alternatives. + // https://stackoverflow.com/questions/38034033/c-localtime-this-function-or-variable-may-be-unsafe + +#if defined(__unix__) + localtime_r(&t, &atm); +#elif defined(_MSC_VER) + localtime_s(&atm, &t); +#elif defined(__STDC_LIB_EXT1__) + localtime_s(&t, &atm); +#else + + // To be tested! C++20 way. + // https://stackoverflow.com/questions/61190884/current-time-and-date-in-c20-days + static_assert(false, "Untested yet. If needed, test it and if it's fine delete this static_assert"); + + using namespace std::chrono; + const auto current_zoned_time = zoned_time{current_zone(), system_clock::from_time_t(t)}; + const auto local_time_point = current_zoned_time.get_local_time(); + //const auto local_duration = local_time_point.time_since_epoch(); + + // Get a local time_point with days precision + const auto ld = floor(local_time_point); + + // Convert local days-precision time_point to a local {y, m, d} calendar + const year_month_day ymd{ld}; + + // Split time since local midnight into {h, m, s, subseconds} + const hh_mm_ss hms{local_time_point - ld}; + + // get_info(): Returns a std::chrono::time_zone::transition_info object, which provides information about whether DST is currently active. + const bool is_dst = (current_zoned_time.get_info().save != std::chrono::seconds{0}); + + atm.tm_sec = (int)hms.seconds().count(); + atm.tm_min = (int)hms.minutes().count(); + atm.tm_hour = (int)hms.hours().count(); + atm.tm_mday = (int)(uint)ymd.day(); + atm.tm_mon = (int)(uint)ymd.month() - 1; + atm.tm_year = (int)ymd.year() - 1900; + atm.tm_isdst= (int)is_dst; +#endif + + return atm; } -struct tm* CSTime::GetLocalTm(struct tm* ptm) const noexcept +static std::tm safe_localtime_unoffset(std::tm atm) noexcept { - if (ptm != nullptr) - { - struct tm* ptmTemp = localtime(&m_time); - if (ptmTemp == nullptr) - return nullptr; // indicates the m_time was not initialized! + atm.tm_year += 1900; + atm.tm_mon += 1; + return atm; +} - *ptm = *ptmTemp; - return ptm; - } - else - return localtime(&m_time); +std::tm CSTime::GetLocalTm() const noexcept +{ + return safe_localtime(m_time); +} + +std::tm CSTime::GetLocalTmPlain() const noexcept +{ + return safe_localtime_unoffset(safe_localtime(m_time)); } //////////////////////////////////////////////////////////////////////////// @@ -297,19 +349,19 @@ bool CSTime::Read(tchar *pszVal) return true; } -CSTime::CSTime() noexcept +CSTime::CSTime() noexcept : + m_time(0) { - m_time = 0; } -CSTime::CSTime(time_t time) noexcept +CSTime::CSTime(time_t time) noexcept : + m_time(time) { - m_time = time; } -CSTime::CSTime(const CSTime& timeSrc) noexcept +CSTime::CSTime(const CSTime& timeSrc) noexcept : + m_time(timeSrc.m_time) { - m_time = timeSrc.m_time; } const CSTime& CSTime::operator=(const CSTime& timeSrc) noexcept @@ -341,36 +393,36 @@ bool CSTime::operator!=( time_t t ) const noexcept time_t CSTime::GetTime() const noexcept { - // Although not defined by the C standard, this is almost always an integral value holding the number of seconds + // Although not defined by the C standard, this is almost always an integral value holding the number of seconds // (not counting leap seconds) since 00:00, Jan 1 1970 UTC, corresponding to UNIX time. - // + // // TODO: Is this on Windows defined since January 1, 1601 ? return m_time; } int CSTime::GetYear() const noexcept { - return (GetLocalTm(nullptr)->tm_year) + 1900; + return (GetLocalTm().tm_year) + 1900; } int CSTime::GetMonth() const noexcept // month of year (1 = Jan) { - return GetLocalTm(nullptr)->tm_mon + 1; + return GetLocalTm().tm_mon + 1; } int CSTime::GetDay() const noexcept // day of month { - return GetLocalTm(nullptr)->tm_mday; + return GetLocalTm().tm_mday; } int CSTime::GetHour() const noexcept { - return GetLocalTm(nullptr)->tm_hour; + return GetLocalTm().tm_hour; } int CSTime::GetMinute() const noexcept { - return GetLocalTm(nullptr)->tm_min; + return GetLocalTm().tm_min; } void CSTime::Init() noexcept diff --git a/src/common/sphere_library/CSTime.h b/src/common/sphere_library/CSTime.h index 0a5114f09..4d173aacf 100644 --- a/src/common/sphere_library/CSTime.h +++ b/src/common/sphere_library/CSTime.h @@ -6,13 +6,19 @@ #ifndef _INC_CSTIME_H #define _INC_CSTIME_H +/* #ifdef _WIN32 #include #else #include #endif +*/ +#define __STDC_WANT_LIB_EXT1__ 1 +#include + #include "../common.h" + // Similar to the MFC CTime and CTimeSpan or COleDateTime: // Get time stamp in the real world. based on struct tm class CSTime @@ -43,7 +49,7 @@ class CSTime // int tm_yday; // days since January 1 - [0, 365] // int tm_isdst; // daylight savings time flag 0, 0, 0, - 1, 0, 0, + 1, 0, 0, 0, 0, -1 }; */ @@ -64,7 +70,7 @@ class CSTime CSTime(time_t time) noexcept; CSTime(const CSTime& timeSrc) noexcept; - CSTime(struct tm time) noexcept; + //CSTime(struct tm time) noexcept; CSTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST = -1) noexcept; const CSTime& operator=(const CSTime& timeSrc) noexcept; @@ -77,6 +83,8 @@ class CSTime // Attributes time_t GetTime() const noexcept; + std::tm GetLocalTmPlain() const noexcept; + int GetYear() const noexcept; int GetMonth() const noexcept; int GetDay() const noexcept; @@ -94,7 +102,7 @@ class CSTime int GetDaysTotal() const noexcept; private: - struct tm* GetLocalTm(struct tm* ptm = nullptr) const noexcept; + std::tm GetLocalTm() const noexcept; }; #endif // _INC_CTIME_H diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index 78fea0a92..8156f2045 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -41,36 +41,76 @@ void Str_Reverse(char* string) } #endif -int Str_ToI (lpctstr ptcStr, int base) noexcept +std::optional Str_ToI8 (lpctstr ptcStr, int base) noexcept { - const auto e = errno; - const auto ret = int(::strtol(ptcStr, nullptr, base)); - errno = e; - return ret; + char val = 0; + const bool fSuccess = cstr_to_num(ptcStr, &val, base); + if (!fSuccess) + return std::nullopt; + return val; } -uint Str_ToUI(lpctstr ptcStr, int base) noexcept +std::optional Str_ToU8 (lpctstr ptcStr, int base) noexcept { - const auto e = errno; - const auto ret = uint(::strtoul(ptcStr, nullptr, base)); - errno = e; - return ret; + uchar val = 0; + const bool fSuccess = cstr_to_num(ptcStr, &val, base); + if (!fSuccess) + return std::nullopt; + return val; } -llong Str_ToLL(lpctstr ptcStr, int base) noexcept +std::optional Str_ToI16 (lpctstr ptcStr, int base) noexcept { - const auto e = errno; - const auto ret = ::strtoll(ptcStr, nullptr, base); - errno = e; - return ret; + short val = 0; + const bool fSuccess = cstr_to_num(ptcStr, &val, base); + if (!fSuccess) + return std::nullopt; + return val; } -ullong Str_ToULL(lpctstr ptcStr, int base) noexcept +std::optional Str_ToU16 (lpctstr ptcStr, int base) noexcept { - const auto e = errno; - const auto ret = ::strtoull(ptcStr, nullptr, base); - errno = e; - return ret; + ushort val = 0; + const bool fSuccess = cstr_to_num(ptcStr, &val, base); + if (!fSuccess) + return std::nullopt; + return val; +} + +std::optional Str_ToI (lpctstr ptcStr, int base) noexcept +{ + int val = 0; + const bool fSuccess = cstr_to_num(ptcStr, &val, base); + if (!fSuccess) + return std::nullopt; + return val; +} + +std::optional Str_ToU(lpctstr ptcStr, int base) noexcept +{ + uint val = 0; + const bool fSuccess = cstr_to_num(ptcStr, &val, base); + if (!fSuccess) + return std::nullopt; + return val; +} + +std::optional Str_ToLL(lpctstr ptcStr, int base) noexcept +{ + llong val = 0; + const bool fSuccess = cstr_to_num(ptcStr, &val, base); + if (!fSuccess) + return std::nullopt; + return val; +} + +std::optional Str_ToULL(lpctstr ptcStr, int base) noexcept +{ + ullong val = 0; + const bool fSuccess = cstr_to_num(ptcStr, &val, base); + if (!fSuccess) + return std::nullopt; + return val; } #define STR_FROM_SET_ZEROSTR \ diff --git a/src/common/sphere_library/sstring.h b/src/common/sphere_library/sstring.h index 0bf5063b3..acbfecfeb 100644 --- a/src/common/sphere_library/sstring.h +++ b/src/common/sphere_library/sstring.h @@ -4,6 +4,7 @@ #include "../common.h" #include #include +#include #include @@ -21,6 +22,7 @@ #define IsAlpha(c) iswalpha((wint_t)c) #define IsAlnum(c) iswalnum((wint_t)c) #else + // TODO: remove INT_CHARACTER and adapt those calls to unicode chars #define IsDigit(c) isdigit(INT_CHARACTER(c)) #define IsSpace(c) isspace(INT_CHARACTER(c)) #define IsAlpha(c) isalpha(INT_CHARACTER(c)) @@ -64,17 +66,23 @@ struct KeyTableDesc_s */ // If you want to use base = 16 to convert an hexadecimal string, it has to be in the format: 0x*** -int Str_ToI (lpctstr ptcStr, int base = 10) noexcept; -uint Str_ToUI (lpctstr ptcStr, int base = 10) noexcept; -llong Str_ToLL (lpctstr ptcStr, int base = 10) noexcept; -ullong Str_ToULL(lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] std::optional Str_ToI8 (lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] std::optional Str_ToU8 (lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] std::optional Str_ToI16(lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] std::optional Str_ToU16(lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] std::optional Str_ToI (lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] std::optional Str_ToU (lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] std::optional Str_ToLL (lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] std::optional Str_ToULL(lpctstr ptcStr, int base = 10) noexcept; +[[nodiscard]] inline +std::optional Str_ToST(lpctstr ptcStr, int base = 10) noexcept; // The _Fast variants write from the end of the given buffer and return a pointer to the new start of the string, which in most // cases is different from the pointer passed as argument! -NODISCARD tchar* Str_FromI_Fast (int val, tchar* buf, size_t buf_length, uint base = 10) noexcept; -NODISCARD tchar* Str_FromUI_Fast (uint val, tchar* buf, size_t buf_length, uint base = 10) noexcept; -NODISCARD tchar* Str_FromLL_Fast (llong val, tchar* buf, size_t buf_length, uint base = 10) noexcept; -NODISCARD tchar* Str_FromULL_Fast (ullong val, tchar* buf, size_t buf_length, uint base = 10) noexcept; +[[nodiscard]] tchar* Str_FromI_Fast (int val, tchar* buf, size_t buf_length, uint base = 10) noexcept; +[[nodiscard]] tchar* Str_FromUI_Fast (uint val, tchar* buf, size_t buf_length, uint base = 10) noexcept; +[[nodiscard]] tchar* Str_FromLL_Fast (llong val, tchar* buf, size_t buf_length, uint base = 10) noexcept; +[[nodiscard]] tchar* Str_FromULL_Fast (ullong val, tchar* buf, size_t buf_length, uint base = 10) noexcept; // These functions use the _Fast methods, but do move the string from the end of the buffer to the beginning, so that the input pointer is still valid. void Str_FromI (int val, tchar* buf, size_t buf_length, uint base = 10) noexcept; @@ -417,17 +425,47 @@ inline ssize_t sGetLine_StaticBuf(const char *data, const size_t datasize) noexc return sGetDelimiter_StaticBuf('\n', data, datasize); } -//--- + +//--- Inline methods + +std::optional Str_ToST(lpctstr ptcStr, int base) noexcept +{ + if constexpr (sizeof(size_t) == 4) + return Str_ToU(ptcStr, base); + else + return Str_ToULL(ptcStr, base); +} + + +//--- Template methods template -bool svtonum(std::string_view const& view, T& value) +bool sv_to_num(std::string_view const& view, T* value, int base = 10) noexcept { + static_assert(std::is_arithmetic_v, "Input variable is not an arithmetic type."); if (view.empty()) return false; const char* first = view.data(); const char* last = view.data() + view.length(); - const std::from_chars_result res = std::from_chars(first, last, value); + const std::from_chars_result res = std::from_chars(first, last, *value, base); + + if (res.ec != std::errc()) + return false; + if (res.ptr != last) + return false; + return true; +} + +template +bool cstr_to_num(const char * str, T* value, int base = 10, uint str_max_length = SCRIPT_MAX_LINE_LEN) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an arithmetic type."); + if (!str || ('\0' == *str)) + return false; + + const char* last = str + strnlen(str, str_max_length); + const std::from_chars_result res = std::from_chars(str, last, *value, base); if (res.ec != std::errc()) return false; diff --git a/src/common/sphere_library/stypecast.h b/src/common/sphere_library/stypecast.h new file mode 100644 index 000000000..3b02f5423 --- /dev/null +++ b/src/common/sphere_library/stypecast.h @@ -0,0 +1,255 @@ +#ifndef _INC_STYPECAST_H +#define _INC_STYPECAST_H + +#include +#include + +//-- Explicitly promote to a larger type or narrow to a smaller type, instead of inattentive casts. + +// Promote to the corresponding 32 bits numeric type a smaller numeric variable. +template +[[nodiscard]] +constexpr auto i_promote32(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an 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"); + static_assert(sizeof(T) < 4, "Input variable is not smaller than a 32 bit number."); + if constexpr (std::is_signed_v) + { + if constexpr (std::is_floating_point_v) + return static_cast(a); + return static_cast(a); + } + else + return static_cast(a); +} + +// Promote to the corresponding 64 bits numeric type a smaller numeric variable. +template +[[nodiscard]] +constexpr auto i_promote64(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an 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"); + static_assert(sizeof(T) < 8, "Input variable is not smaller than a 64 bit number."); + if constexpr (std::is_signed_v) + { + if constexpr (std::is_floating_point_v) + return static_cast(a); + return static_cast(a); + } + else + return static_cast(a); +} + +template +[[nodiscard]] +constexpr auto i_narrow32(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an 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"); + static_assert(sizeof(T) >= 4, "Input variable is smaller than a 32 bit number."); + + // Since the narrowing can be implementation specific, here we decide that we take only the lower 32 bytes and discard the upper ones. + constexpr uint64 umask = 0x0000'0000'FFFF'FFFF; + if constexpr (std::is_signed_v) + { + if constexpr (std::is_floating_point_v) + return static_cast(static_cast(a) & umask); + return static_cast(static_cast(a) & umask); + } + else + return static_cast(static_cast(a) & umask); +} + +template +[[nodiscard]] +constexpr auto i_narrow16(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an 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"); + static_assert(sizeof(T) >= 2, "Input variable is smaller than a 16 bit number."); + + // Since the narrowing can be implementation specific, here we decide that we take only the lower 32 bytes and discard the upper ones. + constexpr uint64 umask = 0x0000'0000'FFFF'FFFF; + if constexpr (std::is_signed_v) + { + if constexpr (std::is_floating_point_v) + return static_cast(static_cast(a) & umask); + return static_cast(static_cast(a) & umask); + } + else + return static_cast(static_cast(a) & umask); +} + +template +[[nodiscard]] +constexpr auto i64_narrow32(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an 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"); + static_assert(sizeof(T) == 8, "Input variable is not a 64 bit number."); + + // Since the narrowing can be implementation specific, here we decide that we take only the lower 32 bytes and discard the upper ones. + constexpr uint64 umask = 0x0000'0000'FFFF'FFFF; + if constexpr (std::is_signed_v) + { + if constexpr (std::is_floating_point_v) + return static_cast(a & umask); + return static_cast(a & umask); + } + else + return static_cast(a & umask); +} + +template +[[nodiscard]] +constexpr auto i64_narrow16(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an 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"); + static_assert(sizeof(T) == 8, "Input variable is not a 64 bit number."); + + // Since the narrowing can be implementation specific, here we decide that we take only the lower 32 bytes and discard the upper ones. + constexpr uint64 umask = 0x0000'0000'0000'FFFF; + if constexpr (std::is_signed_v) + { + if constexpr (std::is_floating_point_v) + return static_cast(a & umask); + return static_cast(a & umask); + } + else + return static_cast(a & umask); +} + +template +[[nodiscard]] +constexpr auto i32_narrow16(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an 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"); + static_assert(sizeof(T) == 4, "Input variable is not a 32 bit number."); + + // 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'FFFF; + if constexpr (std::is_signed_v) + { + if constexpr (std::is_floating_point_v) + return static_cast(a & umask); + return static_cast(a & umask); + } + else + return static_cast(a & umask); +} + +template +[[nodiscard]] +constexpr auto i16_narrow8(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an arithmetic type."); + static_assert(std::is_integral_v, "Only integral types are supported by this function."); + static_assert(sizeof(T) == 2, "Input variable is not a 16 bit number."); + + // 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); + else + return static_cast(a & umask); +} + + +[[nodiscard]] +inline int8 i8_from_u8_checked(const uint8 a) // not clamping/capping +{ + ASSERT(a <= INT8_MAX); + return static_cast(a); +} +template int8 i8_from_u8_checked(T) = delete; + +[[nodiscard]] +inline int16 i16_from_u16_checked(const uint16 a) // not clamping/capping +{ + ASSERT(a <= INT16_MAX); + return static_cast(a); +} +template int16 i16_from_u16_checked(T) = delete; + +[[nodiscard]] +inline int32 i32_from_u32_checked(const uint32 a) // not clamping/capping +{ + ASSERT(a <= INT32_MAX); + return static_cast(a); +} +template int32 i32_from_u32_checked(T) = delete; + +[[nodiscard]] +inline int64 i64_from_u64_checked(const uint64 a) // not clamping/capping +{ + ASSERT(a <= INT64_MAX); + return static_cast(a); +} +template int64 i64_from_u64_checked(T) = delete; + + +[[nodiscard]] constexpr +inline int8 i8_from_u8_clamping(const uint8 a) noexcept +{ + return (a > (uint8)INT8_MAX) ? INT8_MAX : (int8)a; +} + +[[nodiscard]] constexpr +inline int16 i16_from_u16_clamping(const uint16 a) noexcept +{ + return (a > (uint16)INT16_MAX) ? INT16_MAX : (int16)a; +} + +[[nodiscard]] constexpr +inline int32 i32_from_u32_clamping(const uint32 a) noexcept +{ + return (a > (uint32)INT32_MAX) ? INT32_MAX : (int32)a; +} + +[[nodiscard]] constexpr +inline int64 i64_from_u64_clamping(const uint64 a) noexcept +{ + return (a > (uint64)INT64_MAX) ? INT64_MAX : (int64)a; +} + +[[nodiscard]] constexpr +inline int32 i32_from_usize_clamping(const size_t a) noexcept +{ + return (a > (size_t)INT32_MAX) ? INT32_MAX : (int32)a; +} + +[[nodiscard]] constexpr +inline int64 i64_from_usize_clamping(const size_t a) noexcept +{ + return (a > (size_t)INT64_MAX) ? INT64_MAX : (int64)a; +} + +[[nodiscard]] constexpr +inline uint32 u32_from_usize_clamping(const size_t a) noexcept +{ + if constexpr (sizeof(size_t) == 8) + return (a > (size_t)UINT32_MAX) ? UINT32_MAX : (uint32)a; + else + return a; +} + + +// 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 num_alias_cast(const Tin a) noexcept +{ + static_assert(std::is_arithmetic_v || std::is_enum_v, "Input variable is not a numeric type."); + static_assert(std::is_arithmetic_v || std::is_enum_v, "Output variable is not a numeric type."); + static_assert(sizeof(Tin) == sizeof(Tout), "Input and output types do not have the same size."); + static_assert(!(std::is_signed_v && std::is_unsigned_v), "Casting signed to unsigned."); + static_assert(!(std::is_signed_v && std::is_unsigned_v ), "Casting unsigned to signed."); + return static_cast(a); +} + + +#endif // _INC_STYPECAST_H diff --git a/src/common/sphereproto.h b/src/common/sphereproto.h index be460764d..2f659c7a2 100644 --- a/src/common/sphereproto.h +++ b/src/common/sphereproto.h @@ -16,29 +16,30 @@ class CLanguageID { -private: // 3 letter code for language. // ENU,FRA,DEU,etc. (see langcode.iff) // terminate with a 0 // 0 = english default. char m_codes[4]; // UNICODE language pref. ('ENU'=english) + public: - CLanguageID() : + CLanguageID() noexcept : m_codes{} {} - CLanguageID( const char * pszInit ) + CLanguageID( const char * pszInit ) noexcept { Set( pszInit ); } - CLanguageID(int iDefault); - bool IsDef() const + CLanguageID(int iDefault) noexcept; + + bool IsDef() const noexcept { return ( m_codes[0] != 0 ); } - void GetStrDef(tchar* pszLang); - void GetStr(tchar* pszLang) const; + void GetStrDef(tchar* pszLang) noexcept; + void GetStr(tchar* pszLang) const noexcept; lpctstr GetStr() const; - bool Set(lpctstr pszLang); + bool Set(lpctstr pszLang) noexcept; }; enum XCMD_TYPE // XCMD_* messages are unique in both directions. @@ -384,11 +385,11 @@ enum SEASON_TYPE : uchar enum BBOARDF_TYPE // Bulletin Board Flags. m_flag { BBOARDF_NAME = 0, // board name - BBOARDF_MSG_HEAD, // 1=message header, + BBOARDF_MSG_HEAD, // 1=message header, BBOARDF_MSG_BODY, // 2=message body BBOARDF_REQ_FULL, // 3=request for full msg. BBOARDF_REQ_HEAD, // 4=request for just head. - BBOARDF_NEW_MSG, // 5=new message, + BBOARDF_NEW_MSG, // 5=new message, BBOARDF_DELETE // 6=Delete }; @@ -718,6 +719,7 @@ enum RACE_TYPE // character race, used in new character creation (0x8D) and sta /* Client Versions numeric constants. WARNING: DEPRECATED! Use CUOClientVersion (or at least wrap those numbers in that class). + TODO: convert them to CUOClientVersion. */ // client versions (expansions) diff --git a/src/common/target_info.h b/src/common/target_info.h index cf1f75b05..dfd9f643f 100644 --- a/src/common/target_info.h +++ b/src/common/target_info.h @@ -2,13 +2,13 @@ #define _INC_TARGET_INFO_H -[[maybe_unused]] +[[maybe_unused, nodiscard]] constexpr const char* get_target_os_str() { #if defined(_WIN32) return "Windows"; #elif defined(_BSD) - return "FreeBSD"; + return "BSD"; #elif defined(__linux__) return "Linux"; #elif defined(__APPLE__) @@ -18,14 +18,14 @@ constexpr const char* get_target_os_str() #endif } -[[maybe_unused]] +[[maybe_unused, nodiscard]] constexpr const char* get_target_word_size_str() { return (sizeof(void*) == 8) ? "64" : "32"; } // __ARM_ARCH -[[maybe_unused]] +[[maybe_unused, nodiscard]] constexpr const char* get_target_arch_str() { #if defined(__x86_64__) || defined(_M_X64) diff --git a/src/game/CObjBase.cpp b/src/game/CObjBase.cpp index 18fbbb946..d7c213b6a 100644 --- a/src/game/CObjBase.cpp +++ b/src/game/CObjBase.cpp @@ -1702,7 +1702,7 @@ bool CObjBase::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc, SKIP_SEPARATORS( ptcKey ); if (! *ptcKey ) - sVal.Format("%d=%s", ct->m_clilocid, ct->m_args); + sVal.Format("%u=%s", ct->m_clilocid, ct->m_args); else if ( !strnicmp( ptcKey, "ID", 2 )) //Cliloc. sVal.FormatDWVal(ct->m_clilocid); else if ( !strnicmp( ptcKey, "VAL", 3 )) //Arguments. @@ -2347,7 +2347,7 @@ bool CObjBase::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command fro CSString sPrompt; sPrompt.Format("%s (# = default)", (lpctstr)(Arg_ppCmd[0])); - pClientSrc->addGumpInpVal( true, INPVAL_STYLE_TEXTEDIT, + pClientSrc->addGumpInputVal( true, INPVAL_STYLE_TEXTEDIT, iMaxLength, sPrompt, sOrgValue, this ); } break; diff --git a/src/game/CRegion.cpp b/src/game/CRegion.cpp index 4de1a06e8..7ffcef3b4 100644 --- a/src/game/CRegion.cpp +++ b/src/game/CRegion.cpp @@ -193,9 +193,13 @@ bool CRegion::MakeRegionDefname() // Is this is subsequent key with a number? Get the highest (plus one) if ( IsStrNumericDec( ptcKey ) ) { - int iVarThis = Str_ToI( ptcKey ); - if ( iVarThis >= iVar ) - iVar = iVarThis + 1; + std::optional iconv = Str_ToI( ptcKey ); + if (iconv.has_value()) + { + int iVarThis = iconv.value(); + if ( iVarThis >= iVar ) + iVar = iVarThis + 1; + } } else ++iVar; diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index d93589ce9..2091b2c09 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -1060,8 +1060,8 @@ void CServer::ProfileDump( CTextConsole * pSrc, bool bDump ) ftDump->Printf("Profiles %s: (%d sec total)\n", GetCurrentProfileData().IsActive() ? "ON" : "OFF", GetCurrentProfileData().GetActiveWindow()); } - size_t iThreadCount = ThreadHolder::get().getActiveThreads(); - for ( size_t iThreads = 0; iThreads < iThreadCount; ++iThreads) + size_t uiThreadCount = ThreadHolder::get().getActiveThreads(); + for ( size_t iThreads = 0; iThreads < uiThreadCount; ++iThreads) { IThread* thrCurrent = ThreadHolder::get().getThreadAt(iThreads); if (thrCurrent == nullptr) diff --git a/src/game/CServer.h b/src/game/CServer.h index 608df7a48..7070f0aa2 100644 --- a/src/game/CServer.h +++ b/src/game/CServer.h @@ -73,7 +73,7 @@ extern class CServer : public CServerDef, public CTextConsole public: CServer(); - virtual ~CServer(); + virtual ~CServer() override; CServer(const CServer& copy) = delete; CServer& operator=(const CServer& other) = delete; diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 87f9f526f..456963106 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -2686,7 +2686,11 @@ CPointMap CServerConfig::GetRegionPoint( lpctstr pCmd ) const // Decode a telepo return CPointMap(); SKIP_NONNUM(pCmd); - size_t i = Str_ToUI(pCmd); + std::optional iconv = Str_ToU(pCmd); + if (!iconv.has_value()) + return CPointMap(); + + size_t i = iconv.value(); if (i > 0) { i -= 1; @@ -3764,8 +3768,14 @@ bool CServerConfig::LoadResourceSection( CScript * pScript ) if (iStartVersion == 2) { - if ( pScript->ReadKey()) - pStart->iClilocDescription = Str_ToI(pScript->GetKey()); + if ( pScript->ReadKey()) + { + std::optional iconv = Str_ToI(pScript->GetKey()); + if (!iconv.has_value()) + continue; + + pStart->iClilocDescription = *iconv; + } } } m_StartDefs.emplace_back(pStart); diff --git a/src/game/CServerDef.cpp b/src/game/CServerDef.cpp index ae0c78199..e23d12c29 100644 --- a/src/game/CServerDef.cpp +++ b/src/game/CServerDef.cpp @@ -63,6 +63,7 @@ size_t CServerDef::StatGet(SERV_STAT_TYPE i) const ADDTOCALLSTACK("CServerDef::StatGet"); ASSERT( i >= 0 && i < SERV_STAT_QTY ); size_t d = m_stStat[i]; + EXC_TRY("StatGet"); if ( i == SERV_STAT_MEM ) // memory information { @@ -168,7 +169,7 @@ void CServerDef::SetName( lpctstr pszName ) // No HTML tags using <> either. tchar szName[ 2*MAX_SERVER_NAME_SIZE ]; - size_t len = Str_GetBare( szName, pszName, sizeof(szName), "<>/\"\\" ); + const int len = Str_GetBare( szName, pszName, sizeof(szName), "<>/\"\\" ); if ( len <= 0 ) return; @@ -194,10 +195,10 @@ void CServerDef::StatDec(SERV_STAT_TYPE i) --m_stStat[i]; } -void CServerDef::SetStat(SERV_STAT_TYPE i, dword dwVal) +void CServerDef::SetStat(SERV_STAT_TYPE i, size_t uiVal) { ASSERT(i >= 0 && i < SERV_STAT_QTY); - m_stStat[i] = dwVal; + m_stStat[i] = uiVal; } lpctstr CServerDef::GetName() const // virtual override @@ -361,7 +362,7 @@ bool CServerDef::r_LoadVal( CScript & s ) SetName( s.GetArgStr() ); break; case SC_SERVPORT: - m_ip.SetPort( (word)s.GetArgVal() ); + m_ip.SetPort( s.GetArgWVal() ); break; case SC_ACCOUNTS: @@ -379,10 +380,10 @@ bool CServerDef::r_LoadVal( CScript & s ) } break; case SC_ITEMS: - SetStat( SERV_STAT_ITEMS, s.GetArgVal() ); + SetStat( SERV_STAT_ITEMS, s.GetArgDWVal() ); break; case SC_CHARS: - SetStat( SERV_STAT_CHARS, s.GetArgVal() ); + SetStat( SERV_STAT_CHARS, s.GetArgDWVal() ); break; case SC_TIMEZONE: m_TimeZone = (char)s.GetArgVal(); @@ -464,7 +465,7 @@ bool CServerDef::r_WriteVal( lpctstr ptcKey, CSString &sVal, CTextConsole * pSrc sVal = GetName(); // What the name should be. Fill in from ping. break; case SC_SERVPORT: - sVal.FormatVal( m_ip.GetPort() ); + sVal.FormatWVal( m_ip.GetPort() ); break; case SC_ACCOUNTS: sVal.FormatSTVal( StatGet( SERV_STAT_ACCOUNTS ) ); diff --git a/src/game/CServerDef.h b/src/game/CServerDef.h index b0da41106..b89174c20 100644 --- a/src/game/CServerDef.h +++ b/src/game/CServerDef.h @@ -81,7 +81,7 @@ class CServerDef : public CScriptObj size_t StatGet( SERV_STAT_TYPE i ) const; void StatInc( SERV_STAT_TYPE i ); void StatDec( SERV_STAT_TYPE i ); - void SetStat( SERV_STAT_TYPE i, dword dwVal ); + void SetStat( SERV_STAT_TYPE i, size_t uiVal ); virtual lpctstr GetName() const override; void SetName( lpctstr pszName ); diff --git a/src/game/CWorldImport.cpp b/src/game/CWorldImport.cpp index 0c8ca381f..56439bcac 100644 --- a/src/game/CWorldImport.cpp +++ b/src/game/CWorldImport.cpp @@ -424,58 +424,98 @@ bool CImportFile::ImportWSC( CScript & s, word wModeFlags ) if ( s.IsKey("X" )) { - CPointMap pt = pItem->GetUnkPoint(); - pt.m_x = (short)( atoi(pArg) ); + std::optional iconv = Str_ToI16(pArg); + if (!iconv.has_value()) + continue; + + CPointMap pt(pItem->GetUnkPoint()); + pt.m_x = *iconv; pItem->SetUnkPoint(pt); continue; } else if ( s.IsKey("Y" )) { - CPointMap pt = pItem->GetUnkPoint(); - pt.m_y = (short)( atoi(pArg) ); + std::optional iconv = Str_ToI16(pArg); + if (!iconv.has_value()) + continue; + + CPointMap pt(pItem->GetUnkPoint()); + pt.m_y = (*iconv); pItem->SetUnkPoint(pt); continue; } else if ( s.IsKey("Z" )) { - CPointMap pt = pItem->GetUnkPoint(); - pt.m_z = (char)( atoi(pArg) ); + std::optional iconv = Str_ToU8(pArg); + if (!iconv.has_value()) + continue; + + CPointMap pt(pItem->GetUnkPoint()); + pt.m_z = *iconv; pItem->SetUnkPoint(pt); continue; } else if ( s.IsKey("COLOR" )) { - pItem->SetHue( (HUE_TYPE)( atoi(pArg) ) ); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + continue; + + pItem->SetHue(*iconv); continue; } else if ( s.IsKey("AMOUNT" )) { - pItem->SetAmount( (word)atoi(pArg) ); + std::optional iconv = Str_ToI16(pArg); + if (!iconv.has_value()) + continue; + + pItem->SetAmount(*iconv); continue; } else if ( s.IsKey("MOREX" )) { - pItem->m_itNormal.m_morep.m_x = (short)(atoi(pArg)); + std::optional iconv = Str_ToI16(pArg); + if (!iconv.has_value()) + continue; + + pItem->m_itNormal.m_morep.m_x = *iconv; continue; } else if ( s.IsKey("MOREY" )) { - pItem->m_itNormal.m_morep.m_y = (short)(atoi(pArg)); + std::optional iconv = Str_ToI16(pArg); + if (!iconv.has_value()) + continue; + + pItem->m_itNormal.m_morep.m_y = *iconv; continue; } else if ( s.IsKey("MOREZ" )) { - pItem->m_itNormal.m_morep.m_z = (char)( atoi(pArg) ); + std::optional iconv = Str_ToI8(pArg); + if (!iconv.has_value()) + continue; + + pItem->m_itNormal.m_morep.m_z = *iconv; continue; } else if ( s.IsKey("MORE" )) { - pItem->m_itNormal.m_more1 = Str_ToUI(pArg); + std::optional iconv = Str_ToU(pArg); + if (!iconv.has_value()) + continue; + + pItem->m_itNormal.m_more1 = *iconv; continue; } else if ( s.IsKey("MORE2" )) { - pItem->m_itNormal.m_more2 = Str_ToUI(pArg); + std::optional iconv = Str_ToU(pArg); + if (!iconv.has_value()) + continue; + + pItem->m_itNormal.m_more2 = *iconv; continue; } else if ( s.IsKey("DYEABLE" )) @@ -484,6 +524,7 @@ bool CImportFile::ImportWSC( CScript & s, word wModeFlags ) pItem->m_CanMask |= CAN_I_DYE; continue; } + /* else if ( s.IsKey("ATT" )) { // pItem->m_pDef->m_attackBase = atoi(pArg); @@ -493,6 +534,7 @@ bool CImportFile::ImportWSC( CScript & s, word wModeFlags ) // ??? translate the type field. //int i = atoi(pArg); } + */ } if ( mode == IMPFLAGS_CHARS ) @@ -520,65 +562,109 @@ bool CImportFile::ImportWSC( CScript & s, word wModeFlags ) if ( s.IsKey("X" )) { - CPointMap pt = pChar->GetUnkPoint(); - pt.m_x = (short)(atoi(pArg)); + std::optional iconv = Str_ToI16(pArg); + if (!iconv.has_value()) + return false; + + CPointMap pt(pChar->GetUnkPoint()); + pt.m_x = *iconv; pChar->SetUnkPoint(pt); continue; } else if ( s.IsKey("Y" )) { - CPointMap pt = pChar->GetUnkPoint(); - pt.m_y = (short)(atoi(pArg)); + std::optional iconv = Str_ToI16(pArg); + if (!iconv.has_value()) + return false; + + CPointMap pt(pChar->GetUnkPoint()); + pt.m_y = *iconv; pChar->SetUnkPoint(pt); continue; } else if ( s.IsKey("Z" )) { - CPointMap pt = pChar->GetUnkPoint(); - pt.m_z = (char)(atoi(pArg)); + std::optional iconv = Str_ToI8(pArg); + if (!iconv.has_value()) + return false; + + CPointMap pt(pChar->GetUnkPoint()); + pt.m_z = *iconv; pChar->SetUnkPoint(pt); continue; } else if ( s.IsKey("BODY" )) { - pChar->SetID((CREID_TYPE)(atoi(pArg))); + std::optional iconv = Str_ToU(pArg); + if (!iconv.has_value()) + return false; + + pChar->SetID(num_alias_cast(*iconv)); continue; } else if ( s.IsKey("SKIN" )) { - pChar->SetHue( (HUE_TYPE)( atoi(pArg) )); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->SetHue(num_alias_cast(*iconv)); continue; } else if ( s.IsKey("DIR" )) { - pChar->m_dirFace = (DIR_TYPE)(atoi(pArg)); + std::optional iconv = Str_ToI(pArg); + if (!iconv.has_value()) + return false; + + pChar->m_dirFace = num_alias_cast(*iconv); if ( (pChar->m_dirFace < 0) || (pChar->m_dirFace >= DIR_QTY) ) pChar->m_dirFace = DIR_SE; continue; } else if ( s.IsKey("XBODY" )) { - pChar->_iPrev_id = (CREID_TYPE)(atoi(pArg)); + std::optional iconv = Str_ToU(pArg); + if (!iconv.has_value()) + return false; + + pChar->_iPrev_id = num_alias_cast(*iconv); continue; } else if ( s.IsKey("XSKIN" )) { - pChar->_wPrev_Hue = (HUE_TYPE)( atoi(pArg) ); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->_wPrev_Hue = num_alias_cast(*iconv); continue; } else if ( s.IsKey("FONT" )) { - pChar->m_fonttype = (FONT_TYPE)(atoi(pArg)); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->m_fonttype = num_alias_cast(*iconv); continue; } else if ( s.IsKey("KARMA" )) { - pChar->SetKarma((short)(atoi(pArg))); + std::optional iconv = Str_ToI16(pArg); + if (!iconv.has_value()) + return false; + + pChar->SetKarma(*iconv); continue; } else if ( s.IsKey("FAME" )) { - pChar->SetFame((ushort)(atoi(pArg))); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->SetFame(*iconv); continue; } else if ( s.IsKey("TITLE" )) @@ -588,33 +674,67 @@ bool CImportFile::ImportWSC( CScript & s, word wModeFlags ) } else if ( s.IsKey("STRENGTH" )) { - pChar->Stat_SetBase(STAT_STR, (ushort)(atoi(pArg))); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->Stat_SetBase(STAT_STR, *iconv); } else if ( s.IsKey("DEXTERITY" )) { - pChar->Stat_SetBase(STAT_DEX, (ushort)(atoi(pArg))); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->Stat_SetBase(STAT_DEX, *iconv); } else if ( s.IsKey("INTELLIGENCE" )) { - pChar->Stat_SetBase(STAT_INT, (ushort)(atoi(pArg))); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->Stat_SetBase(STAT_INT, *iconv); } else if ( s.IsKey("HITPOINTS" )) { - pChar->Stat_SetVal(STAT_STR,(ushort)(atoi(pArg))); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->Stat_SetVal(STAT_STR, *iconv); } else if ( s.IsKey("STAMINA" )) { - pChar->Stat_SetVal(STAT_DEX,(ushort)(atoi(pArg))); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->Stat_SetVal(STAT_DEX, *iconv); } else if ( s.IsKey( "MANA" )) { - pChar->Stat_SetVal(STAT_INT,(ushort)(atoi(pArg))); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->Stat_SetVal(STAT_INT, *iconv); } else if ( s.IsKeyHead( "SKILL", 5 )) { - SKILL_TYPE skill = (SKILL_TYPE)(atoi( &(s.GetKey()[5]) )); + // TODO: they are not saved like this anymore (not by index but by proper name) + std::optional iconv_int = Str_ToI(s.GetKey() + 5); + if (!iconv_int.has_value()) + return false; + + const auto skill = num_alias_cast(*iconv_int); + + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + if ( pChar->IsSkillBase(skill) && g_Cfg.m_SkillIndexDefs.valid_index(skill) ) - pChar->Skill_SetBase( skill, (ushort)atoi(pArg)); + pChar->Skill_SetBase( skill, *iconv); } else if ( s.IsKey("ACCOUNT" )) { @@ -623,20 +743,28 @@ bool CImportFile::ImportWSC( CScript & s, word wModeFlags ) } else if ( s.IsKey("KILLS" ) && pChar->m_pPlayer ) { - pChar->m_pPlayer->m_wMurders = (word)(atoi(pArg)); + std::optional iconv = Str_ToU16(pArg); + if (!iconv.has_value()) + return false; + + pChar->m_pPlayer->m_wMurders = *iconv; } else if ( s.IsKey("NPCAITYPE" )) { // Convert to proper NPC type. - int i = atoi( pArg ); + std::optional iconv = Str_ToI(pArg); + if (!iconv.has_value()) + return false; + + const int i = *iconv; switch ( i ) { case 0x01: pChar->SetNPCBrain( NPCBRAIN_HEALER ); break; case 0x02: pChar->SetNPCBrain( NPCBRAIN_MONSTER ); break; - case 0x04: + case 0x04: FALLTHROUGH; case 0x40: pChar->SetNPCBrain( NPCBRAIN_GUARD ); break; case 0x08: pChar->SetNPCBrain( NPCBRAIN_BANKER ); break; - default: pChar->SetNPCBrain( pChar->GetNPCBrainAuto() ); break; + default: pChar->SetNPCBrain( pChar->GetNPCBrainAuto() ); break; } } continue; diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index 60c3bba6d..bbeea5c4f 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -4540,15 +4540,21 @@ bool CChar::r_Verb( CScript &s, CTextConsole * pSrc ) // Execute command from sc GETNONWHITESPACE( psTmp ); tchar * ttVal[2]; int iTmp = 1; - int iArg = Str_ParseCmds( psTmp, ttVal, ARRAY_COUNT( ttVal ), " ,\t" ); + const int iArg = Str_ParseCmds( psTmp, ttVal, ARRAY_COUNT( ttVal ), " ,\t" ); if (!iArg) { return false; } if ( iArg == 2 ) { - if ( IsDigit( ttVal[1][0] ) ) - iTmp = Str_ToI( ttVal[1] ); + if ( IsDigit( ttVal[1][0] ) ) + { + std::optional iconv = Str_ToI(ttVal[1]); + if (!iconv.has_value()) + return false; + + iTmp = *iconv; + } } //DEBUG_ERR(( "CHV_MAKEITEM iTmp is %d, arg was %s\n",iTmp,psTmp )); diff --git a/src/game/chars/CChar.h b/src/game/chars/CChar.h index 6a7a52fcd..58b1b1dd2 100644 --- a/src/game/chars/CChar.h +++ b/src/game/chars/CChar.h @@ -1007,7 +1007,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 iC1); + CChar* Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE uiCreature); int64 GetSpellDuration( SPELL_TYPE spell, int iSkillLevel, CChar * pCharSrc = nullptr ); // in tenths of second // Memories about objects in the world. ------------------- diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index 481093daa..6e3ac6137 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -914,27 +914,35 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw { // Animals have certain anims. Monsters have others. - CUOMobTypesType mobTypesRow = { 0, 0 }; - + CUOMobTypesEntry mobTypesRow {MOBTE_QTY, 0}; CREID_TYPE dispID = GetDispID(); + bool fUseMobTypes = g_Cfg.m_fUseMobTypes && g_Install.m_mobtypes.IsLoaded(); //Check mobtypes.txt to get the anim type - if (g_Cfg.m_fUseMobTypes && g_Install.m_mobtypes.IsLoaded()) + if (fUseMobTypes) { - mobTypesRow.m_type = g_Install.m_mobtypes.GetEntry((ushort)dispID)->m_type; - mobTypesRow.m_flags = g_Install.m_mobtypes.GetEntry((ushort)dispID)->m_flags; + const CUOMobTypesEntry *pMobtEntry = g_Install.m_mobtypes.GetEntry(dispID); + + if (pMobtEntry->m_uiType == MOBTE_QTY) + { + // Invalid entry, fall back to old method. + fUseMobTypes = false; + } + else + { + mobTypesRow.m_uiType = pMobtEntry->m_uiType; + mobTypesRow.m_uiFlags = pMobtEntry->m_uiFlags; + } } - else + + if (!fUseMobTypes) { //Old Method - if (GetDispID() >= CREID_HORSE_TAN) - { - mobTypesRow.m_type = 2; - } + mobTypesRow.m_uiType = (dispID >= CREID_HORSE_TAN) ? MOBTE_ANIMAL : MOBTE_MONSTER; } //Standard UOP Animations - if (mobTypesRow.m_flags & ATFLAG_UseUopAnimation) + if (mobTypesRow.m_uiFlags & ATFLAG_UseUopAnimation) { //Only mount animations, 3 actions @@ -1031,7 +1039,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_RUN_UNARM: case ANIM_RUN_ARMED: //If the creature can fly we can use flying action for running - if (mobTypesRow.m_flags & ATFLAG_CanFlying) + if (mobTypesRow.m_uiFlags & ATFLAG_CanFlying) { //Phoenix and Parrot Bird don't have run anims if (IsStatFlag(STATF_FLY | STATF_HOVERING) || (dispID == CREID_PARROT_BIRD || dispID == CREID_PHOENIX) ) @@ -1080,7 +1088,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw else { return ANIM_UOP_STAND_COMBAT; - } + } case ANIM_FIDGET1: case ANIM_BOW: @@ -1124,7 +1132,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_THROW: // 2 actions for cast - if (dispID == CREID_CLOCKWORK_EXODUS) + if (dispID == CREID_CLOCKWORK_EXODUS) { switch (g_Rand.GetVal(2)) { @@ -1152,12 +1160,12 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } else if (dispID == CREID_TS_TENTACLE || dispID == CREID_CHARYBDIS_TENTACLE) { - return ANIM_UOP_ATTACK_1; + return ANIM_UOP_ATTACK_1; } else if (dispID == CREID_CHARYBDIS) { return ANIM_UOP_GETHIT; - } + } else if (dispID == CREID_PUMPKIN_DEMON) { return ANIM_UOP_PEACE_TO_COMBAT; @@ -1209,7 +1217,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case 1: return ANIM_UOP_ATTACK_2; break; } break; - } + } case ANIM_DIE_BACK: return ANIM_UOP_DIE_BACKWARD; @@ -1225,7 +1233,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw else { return ANIM_UOP_DIE_FORWARD; - } + } case ANIM_BLOCK: //No Block @@ -1305,7 +1313,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } //Sea Monster: H slot with only 9 actions in L slot order //Only Sea Serpent and Dolphin - else if (mobTypesRow.m_type == 1) + else if (mobTypesRow.m_uiType == MOBTE_SEA_MONSTER) { switch (action) { @@ -1330,13 +1338,13 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw return ANIM_MON_ATTACK1; case ANIM_CAST_DIR: - if (dispID == CREID_DOLPHIN) //Dolphin doesn't have ANIM_MON_ATTACK2 and ANIM_MON_ATTACK3 + if (dispID == CREID_DOLPHIN) //Dolphin doesn't have ANIM_MON_ATTACK2 and ANIM_MON_ATTACK3 return ANIM_MON_AttackBow; else return ANIM_MON_ATTACK2; case ANIM_CAST_AREA: - if (dispID == CREID_DOLPHIN) //Dolphin doesn't have ANIM_MON_ATTACK2 and ANIM_MON_ATTACK3 + if (dispID == CREID_DOLPHIN) //Dolphin doesn't have ANIM_MON_ATTACK2 and ANIM_MON_ATTACK3 return ANIM_MON_DIE2; else return ANIM_MON_ATTACK3; @@ -1353,7 +1361,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_ATTACK_BOW: case ANIM_ATTACK_XBOW: case ANIM_ATTACK_WRESTLE: - if (dispID == CREID_DOLPHIN) //Dolphin doesn't have ANIM_MON_ATTACK2 and ANIM_MON_ATTACK3 + if (dispID == CREID_DOLPHIN) //Dolphin doesn't have ANIM_MON_ATTACK2 and ANIM_MON_ATTACK3 { switch (g_Rand.GetVal(2)) { @@ -1388,7 +1396,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw return ANIM_MON_DIE2; case ANIM_THROW: - if (dispID == CREID_DOLPHIN) //Dolphin doesn't have ANIM_MON_ATTACK2 and ANIM_MON_ATTACK3 + if (dispID == CREID_DOLPHIN) //Dolphin doesn't have ANIM_MON_ATTACK2 and ANIM_MON_ATTACK3 { return ANIM_MON_AttackBow; } @@ -1405,8 +1413,8 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } } //Animal - else if ((mobTypesRow.m_type == 2 && !(mobTypesRow.m_flags & ATFLAG_CalculateOffsetLowGroupExtended)) - || (mobTypesRow.m_type == 0 && (mobTypesRow.m_flags & ATFLAG_CalculateOffsetByLowGroup))) + else if ((mobTypesRow.m_uiType == MOBTE_ANIMAL && !(mobTypesRow.m_uiFlags & ATFLAG_CalculateOffsetLowGroupExtended)) + || (mobTypesRow.m_uiType == MOBTE_MONSTER && (mobTypesRow.m_uiFlags & ATFLAG_CalculateOffsetByLowGroup))) { //Partial actions if (dispID == CREID_PARROT || dispID == CREID_BIRD_CROW) @@ -1693,7 +1701,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw ANIM_TYPE attack1 = ANIM_MON_ATTACK1; ANIM_TYPE cast1 = ANIM_MON_WALK, cast2 = ANIM_MON_WALK, cast3 = ANIM_MON_WALK; ushort castActions = 0, attackActions = 3, blockActions = 2; - + //Standard and Custom Anims switch (action) { @@ -1705,7 +1713,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_RUN_UNARM: case ANIM_RUN_ARMED: //If the creature can fly we can use flying action for running - if ((mobTypesRow.m_flags & ATFLAG_CanFlying) + if ((mobTypesRow.m_uiFlags & ATFLAG_CanFlying) || (pCharDef->m_Anims & AFLAG_CUST_MON_FLY)) //Custom anims with Fly action { return ANIM_MON_FLY; @@ -1719,7 +1727,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_STAND_WAR_1H: case ANIM_STAND_WAR_2H: //Stand action in crossbow - if (mobTypesRow.m_flags & ATFLAG_IdleAt8Frame) + if (mobTypesRow.m_uiFlags & ATFLAG_IdleAt8Frame) { return ANIM_MON_AttackXBow; } @@ -1770,13 +1778,13 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } case ANIM_CAST_DIR: - case ANIM_CAST_AREA: + case ANIM_CAST_AREA: case ANIM_ATTACK_BOW: //return ANIM_MON_AttackBow; //Always Empty except for standard ID 13, 15 and 16 case ANIM_ATTACK_XBOW: //return ANIM_MON_AttackXBow; //Always Empty except for standard ID 13, 15 and 16 //Check how many cast actions are present //Stomp action is not a cast for Corpser, Earth Elemental and Gorilla - if (((mobTypesRow.m_flags & ATFLAG_StompAction) + if (((mobTypesRow.m_uiFlags & ATFLAG_StompAction) && (dispID != CREID_CORPSER && dispID != CREID_EARTH_ELEM && dispID != CREID_GORILLA)) || (pCharDef->m_Anims & AFLAG_CUST_MON_STOMP)) //Custom Anim with Stomp action { @@ -1827,7 +1835,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } else //No cast Actions { - if (mobTypesRow.m_flags & ATFLAG_ReplaceAttack1With2) + if (mobTypesRow.m_uiFlags & ATFLAG_ReplaceAttack1With2) { attack1 = ANIM_MON_ATTACK2; } @@ -1886,17 +1894,17 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_GET_HIT: if (IsStatFlag(STATF_FLY | STATF_HOVERING)) //Running or Flying { - if (mobTypesRow.m_flags & ATFLAG_Use2IfHittedWhileRunning) + if (mobTypesRow.m_uiFlags & ATFLAG_Use2IfHittedWhileRunning) { return ANIM_MON_DIE1; } //GetHit while flying - else if ((mobTypesRow.m_flags & ATFLAG_CanFlying) && !(mobTypesRow.m_flags & ATFLAG_Use10IfHittedWhileFlying)) + else if ((mobTypesRow.m_uiFlags & ATFLAG_CanFlying) && !(mobTypesRow.m_uiFlags & ATFLAG_Use10IfHittedWhileFlying)) { return ANIM_MON_DIE_FLIGHT; } } - if (mobTypesRow.m_flags & ATFLAG_ReplaceGetHitBlockPillage) + if (mobTypesRow.m_uiFlags & ATFLAG_ReplaceGetHitBlockPillage) { return ANIM_MON_STAND; } @@ -1945,7 +1953,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_ATTACK_2H_PIERCE: case ANIM_ATTACK_WRESTLE: - if (mobTypesRow.m_flags & ATFLAG_ReplaceAttack1With2) + if (mobTypesRow.m_uiFlags & ATFLAG_ReplaceAttack1With2) { attack1 = ANIM_MON_ATTACK2; } @@ -2002,7 +2010,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_DIE_BACK: //Die while flying animation - if ((mobTypesRow.m_flags & ATFLAG_StompAction) && (mobTypesRow.m_flags & ATFLAG_CanFlying) && (mobTypesRow.m_flags & ATFLAG_Use10IfHittedWhileFlying) + if ((mobTypesRow.m_uiFlags & ATFLAG_StompAction) && (mobTypesRow.m_uiFlags & ATFLAG_CanFlying) && (mobTypesRow.m_uiFlags & ATFLAG_Use10IfHittedWhileFlying) && IsStatFlag(STATF_FLY | STATF_HOVERING)) { return ANIM_MON_DIE_FLIGHT; @@ -2014,7 +2022,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_DIE_FORWARD: //Die while flying animation - if ((mobTypesRow.m_flags & ATFLAG_StompAction) && (mobTypesRow.m_flags & ATFLAG_CanFlying) && (mobTypesRow.m_flags & ATFLAG_Use10IfHittedWhileFlying) + if ((mobTypesRow.m_uiFlags & ATFLAG_StompAction) && (mobTypesRow.m_uiFlags & ATFLAG_CanFlying) && (mobTypesRow.m_uiFlags & ATFLAG_Use10IfHittedWhileFlying) && IsStatFlag(STATF_FLY | STATF_HOVERING)) { return ANIM_MON_DIE_FLIGHT; @@ -2033,7 +2041,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } case ANIM_BLOCK: - if (mobTypesRow.m_flags & ATFLAG_ReplaceGetHitBlockPillage) + if (mobTypesRow.m_uiFlags & ATFLAG_ReplaceGetHitBlockPillage) { return ANIM_MON_STAND; } @@ -2075,11 +2083,11 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw break; case ANIM_THROW: - if (mobTypesRow.m_flags & ATFLAG_IdleAt8Frame) //Action Throw in Bow action + if (mobTypesRow.m_uiFlags & ATFLAG_IdleAt8Frame) //Action Throw in Bow action { return ANIM_MON_AttackBow; } - else if (((mobTypesRow.m_flags & ATFLAG_StompAction) + else if (((mobTypesRow.m_uiFlags & ATFLAG_StompAction) && (dispID != CREID_CORPSER && dispID != CREID_EARTH_ELEM && dispID != CREID_GORILLA)) || (pCharDef->m_Anims & AFLAG_CUST_MON_STOMP)) //Custom anims with Stomp action { @@ -2103,7 +2111,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } case ANIM_PILLAGE: - if (mobTypesRow.m_flags & ATFLAG_ReplaceGetHitBlockPillage) + if (mobTypesRow.m_uiFlags & ATFLAG_ReplaceGetHitBlockPillage) { return ANIM_MON_FIDGET1; } @@ -2135,11 +2143,11 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw case ANIM_STOMP: case ANIM_SPECIAL: case ANIM_SUMMON: - if ((mobTypesRow.m_flags & ATFLAG_StompAction) || (pCharDef->m_Anims & AFLAG_CUST_MON_STOMP)) + if ((mobTypesRow.m_uiFlags & ATFLAG_StompAction) || (pCharDef->m_Anims & AFLAG_CUST_MON_STOMP)) { return ANIM_MON_Stomp; } - else if ((mobTypesRow.m_flags & ATFLAG_CanFlying) && !(pCharDef->m_Anims & AFLAG_CUST_MON_FLY)) //Standard flying anims with Land/TakeOff action + else if ((mobTypesRow.m_uiFlags & ATFLAG_CanFlying) && !(pCharDef->m_Anims & AFLAG_CUST_MON_FLY)) //Standard flying anims with Land/TakeOff action { return ANIM_MON_LAND; } @@ -2164,12 +2172,12 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } case ANIM_ALERT: - if (((mobTypesRow.m_flags & ATFLAG_StompAction) && dispID != CREID_CORPSER && dispID != CREID_EARTH_ELEM) + if (((mobTypesRow.m_uiFlags & ATFLAG_StompAction) && dispID != CREID_CORPSER && dispID != CREID_EARTH_ELEM) || (pCharDef->m_Anims & AFLAG_CUST_MON_STOMP)) //Custom anims with Stomp action { return ANIM_MON_Stomp; } - else if ((mobTypesRow.m_flags & ATFLAG_CanFlying) && !(pCharDef->m_Anims & AFLAG_CUST_MON_FLY)) //Standard flying anims with Land/TakeOff action + else if ((mobTypesRow.m_uiFlags & ATFLAG_CanFlying) && !(pCharDef->m_Anims & AFLAG_CUST_MON_FLY)) //Standard flying anims with Land/TakeOff action { return ANIM_MON_LAND; } @@ -2179,7 +2187,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } case ANIM_FLY: - if (mobTypesRow.m_flags & ATFLAG_ReplaceFlyAction) + if (mobTypesRow.m_uiFlags & ATFLAG_ReplaceFlyAction) { return ANIM_MON_WALK; } @@ -2189,7 +2197,7 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } case ANIM_LAND: - if (mobTypesRow.m_flags & ATFLAG_ReplaceFlyAction) + if (mobTypesRow.m_uiFlags & ATFLAG_ReplaceFlyAction) { return ANIM_MON_STAND; } @@ -2199,11 +2207,11 @@ ANIM_TYPE CChar::GenerateAnimate( ANIM_TYPE action, bool fTranslate, bool fBackw } case ANIM_GETHIT_AIR: - if (mobTypesRow.m_flags & ATFLAG_ReplaceFlyAction) + if (mobTypesRow.m_uiFlags & ATFLAG_ReplaceFlyAction) { return ANIM_MON_STAND; } - else if (mobTypesRow.m_flags & ATFLAG_Use10IfHittedWhileFlying) + else if (mobTypesRow.m_uiFlags & ATFLAG_Use10IfHittedWhileFlying) { return ANIM_MON_GETHIT; } diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index 922a9f068..9b7aed598 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -2316,15 +2316,15 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo if ( !Skill_CanUse(skill) ) return false; - ushort iManaUse = g_Cfg.Calc_SpellManaCost(this, pSpellDef, pSrc); - ushort iTithingUse = g_Cfg.Calc_SpellTithingCost(this, pSpellDef, pSrc); + ushort uiManaUse = g_Cfg.Calc_SpellManaCost(this, pSpellDef, pSrc); + ushort uiTithingUse = g_Cfg.Calc_SpellTithingCost(this, pSpellDef, pSrc); - CScriptTriggerArgs Args( spellRef, iManaUse, pSrc ); + CScriptTriggerArgs Args( spellRef, uiManaUse, pSrc ); if ( fTest ) Args.m_iN3 |= 0x0001; if ( fFailMsg ) Args.m_iN3 |= 0x0002; - Args.m_VarsLocal.SetNum("TithingUse",iTithingUse); + Args.m_VarsLocal.SetNum("TithingUse",uiTithingUse); if ( IsTrigUsed(TRIGGER_SELECT) ) { @@ -2356,8 +2356,8 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo return false; spellRef = (SPELL_TYPE)(Args.m_iN1); } - iManaUse = (ushort)(Args.m_iN2); - iTithingUse = (ushort)(Args.m_VarsLocal.GetKeyNum("TithingUse")); + uiManaUse = (ushort)(Args.m_iN2); + uiTithingUse = (ushort)(Args.m_VarsLocal.GetKeyNum("TithingUse")); if ( !pSrc->IsChar() )// Looking for non-character sources { @@ -2365,12 +2365,14 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo CItem * pItem = dynamic_cast (pSrc); if ( !pItem ) return false; + if ( ! pItem->IsAttr( ATTR_MAGIC )) { if ( fFailMsg ) SysMessageDefault( DEFMSG_SPELL_ENCHANT_LACK ); return false; } + CObjBaseTemplate * pObjTop = pSrc->GetTopLevelObj(); if ( pObjTop != this ) // magic items must be on your person to use. { @@ -2430,6 +2432,7 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo SysMessageDefault( DEFMSG_SPELL_TRY_NOBOOK ); return false; } + if ( ! pBook->IsSpellInBook( spellRef )) { if ( fFailMsg ) @@ -2452,15 +2455,16 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo // Check for Tithing CVarDefContNum* pVarTithing = GetDefKeyNum("Tithing", false); int64 iValTithing = pVarTithing ? pVarTithing->GetValNum() : 0; - if (iValTithing < iTithingUse) + if (iValTithing < uiTithingUse) { if (fFailMsg) - SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_SPELL_TRY_NOTITHING), iTithingUse); + SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_SPELL_TRY_NOTITHING), uiTithingUse); return false; } + // Consume tithing points if casting is successfull. - if (!fTest && iTithingUse) - pVarTithing->SetValNum(iValTithing - iTithingUse); + if (!fTest && uiTithingUse) + pVarTithing->SetValNum(iValTithing - uiTithingUse); } } @@ -2470,25 +2474,26 @@ bool CChar::Spell_CanCast( SPELL_TYPE &spellRef, bool fTest, CObjBase * pSrc, bo { if ( fFailMsg ) SysMessageDefault( DEFMSG_MAGERY_6 ); // An anti-magic field disturbs the spells. + m_Act_Difficulty = -1; // Give very little credit for failure ! return false; } } // Check for mana - if (Stat_GetVal(STAT_INT) < iManaUse) + if (Stat_GetVal(STAT_INT) < uiManaUse) { if (fFailMsg) SysMessageDefault(DEFMSG_SPELL_TRY_NOMANA); return false; } // Consume mana if casting is successfull - if (!fTest && iManaUse) - UpdateStatVal(STAT_INT, -iManaUse); + if (!fTest && uiManaUse) + UpdateStatVal(STAT_INT, -uiManaUse); return true; } -CChar * CChar::Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE iC1) +CChar * CChar::Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE uiCreature) { ADDTOCALLSTACK("CChar::Spell_CanSummon"); //Create the NPC and check if we can actually place it in the world, but do not place it yet. @@ -2498,9 +2503,9 @@ CChar * CChar::Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE i if (!pSpellDef || !pSpellDef->GetPrimarySkill(&iSkill, nullptr)) return nullptr; - if (iC1)//if iC1 is set that means we are overriding the default summoned creature. + if (uiCreature)//if iC1 is set that means we are overriding the default summoned creature. { - m_atMagery.m_iSummonID = iC1; + m_atMagery.m_iSummonID = uiCreature; } else { @@ -2626,6 +2631,7 @@ CChar * CChar::Spell_Summon_Try(SPELL_TYPE spell, CPointMap ptTarg, CREID_TYPE i } return pChar; } + bool CChar::Spell_TargCheck_Face() { ADDTOCALLSTACK("CChar::Spell_TargCheck_Face"); diff --git a/src/game/clients/CAccount.cpp b/src/game/clients/CAccount.cpp index 6c1b536a4..8e40a704d 100644 --- a/src/game/clients/CAccount.cpp +++ b/src/game/clients/CAccount.cpp @@ -219,7 +219,7 @@ bool CAccounts::Account_Delete( CAccount * pAccount ) { return false; } - + pAccount->DeleteChars(); m_Accounts.RemovePtr( pAccount ); return true; @@ -498,7 +498,7 @@ bool CAccounts::Account_OnCmd( tchar * pszArgs, CTextConsole * pSrc ) CClient *pClient = pAccount->FindClient(); char *z = Str_GetTemp(); - snprintf(z, Str_TempLength(), + snprintf(z, Str_TempLength(), "Account '%s': PLEVEL:%d, BLOCK:%d, IP:%s, CONNECTED:%s, ONLINE:%s\n", pAccount->GetName(), pAccount->GetPrivLevel(), pAccount->IsPriv(PRIV_BLOCKED), pAccount->m_Last_IP.GetAddrStr(), pAccount->_dateConnectedLast.Format(nullptr), @@ -637,11 +637,9 @@ CAccount::~CAccount() DeleteChars(); if (CClient * pClient = FindClient()) - { pClient->m_pAccount = nullptr; - } - ClearPasswordTries(true); + //ClearPasswordTries(true); // I'm destroying the account. Does it even matter? -> no. } lpctstr CAccount::GetDefStr( lpctstr ptcKey, bool fZero ) const diff --git a/src/game/clients/CAccount.h b/src/game/clients/CAccount.h index 270c20e95..0d2913bcb 100644 --- a/src/game/clients/CAccount.h +++ b/src/game/clients/CAccount.h @@ -106,7 +106,7 @@ class CAccount : public CScriptObj * We should go track down and delete all the chars and clients that use this account ! */ // virtual not required at the moment but might be if subclassed - virtual ~CAccount(); + virtual ~CAccount() override; /************************************************************************ * SCP related section. @@ -349,8 +349,9 @@ class CAccounts static lpctstr const sm_szVerbKeys[]; // ACCOUNT action list. CObjNameSortArray m_Accounts; // Sorted CAccount list. public: - CAccounts() : m_fLoading(false) { - + CAccounts() noexcept + : m_fLoading(false) + { } /** * CAccount needs CAccounts methods. diff --git a/src/game/clients/CClient.cpp b/src/game/clients/CClient.cpp index 26c9f3335..d19e1fade 100644 --- a/src/game/clients/CClient.cpp +++ b/src/game/clients/CClient.cpp @@ -123,7 +123,7 @@ CClient::~CClient() } if (m_net->isClosed() == false) - g_Log.EventError("Client being deleted without being safely removed from the network system\n"); + g_Log.EventError("Client being deleted without being safely removed from the network system.\n"); EXC_CATCH; } @@ -932,7 +932,7 @@ bool CClient::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from } CResourceID rid = g_Cfg.ResourceGetID(RES_QTY, ppszArgs[0], 0, true); - /* + /* if (rid.IsEmpty()) { m_tmAdd.m_id = 0; diff --git a/src/game/clients/CClient.h b/src/game/clients/CClient.h index 4a1aae0dd..092ff56b7 100644 --- a/src/game/clients/CClient.h +++ b/src/game/clients/CClient.h @@ -536,7 +536,7 @@ class CClient : public CSObjListRec, public CScriptObj, public CChatChanMember, #define MAX_DIALOG_CONTROLTYPE_QTY 1000 void addGumpTextDisp( const CObjBase * pObj, GUMP_TYPE gump, lpctstr pszName, lpctstr pszText ); - void addGumpInpVal( bool fcancel, INPVAL_STYLE style, dword dwmask, lpctstr ptext1, lpctstr ptext2, CObjBase * pObj ); + void addGumpInputVal( bool fcancel, INPVAL_STYLE style, dword dwmask, lpctstr ptext1, lpctstr ptext2, CObjBase * pObj ); void addItemMenu( CLIMODE_TYPE mode, const CMenuItem * item, uint count, CObjBase * pObj = nullptr ); void addGumpDialog( CLIMODE_TYPE mode, std::vector const* vsControls, std::vector const* vsText, int x, int y, CObjBase * pObj = nullptr, dword dwRid = 0 ); diff --git a/src/game/clients/CClientDialog.cpp b/src/game/clients/CClientDialog.cpp index a88019a7b..981536aa3 100644 --- a/src/game/clients/CClientDialog.cpp +++ b/src/game/clients/CClientDialog.cpp @@ -16,8 +16,8 @@ bool CClient::Dialog_Setup( CLIMODE_TYPE mode, const CResourceID& rid, int iPage if ( pObj == nullptr ) return false; - CResourceDef * pRes = g_Cfg.RegisteredResourceGetDef( rid ); - CDialogDef * pDlg = dynamic_cast (pRes); + CResourceDef * pRes = g_Cfg.RegisteredResourceGetDef( rid ); + CDialogDef * pDlg = dynamic_cast (pRes); if ( !pRes || !pDlg ) { DEBUG_ERR(("Invalid RES_DIALOG.\n")); @@ -48,13 +48,13 @@ bool CClient::Dialog_Setup( CLIMODE_TYPE mode, const CResourceID& rid, int iPage } -void CClient::addGumpInpVal( bool fCancel, INPVAL_STYLE style, +void CClient::addGumpInputVal( bool fCancel, INPVAL_STYLE style, dword iMaxLength, lpctstr pszText1, lpctstr pszText2, CObjBase * pObj ) { - ADDTOCALLSTACK("CClient::addGumpInpVal"); + ADDTOCALLSTACK("CClient::addGumpInputVal"); // CLIMODE_INPVAL // Should result in PacketGumpValueInputResponse::onReceive // just input an objects attribute. diff --git a/src/game/clients/CClientLog.cpp b/src/game/clients/CClientLog.cpp index 29707aa9e..e4e18bc12 100644 --- a/src/game/clients/CClientLog.cpp +++ b/src/game/clients/CClientLog.cpp @@ -136,7 +136,7 @@ bool CClient::addLoginErr(byte code) if (code >= ARRAY_COUNT(sm_Login_ErrMsg)) code = PacketLoginError::Other; - + g_Log.EventWarn( "%x:Bad Login %d. %s.\n", GetSocketID(), code, sm_Login_ErrMsg[(size_t)code] ); // translate the code into a code the client will understand @@ -254,7 +254,7 @@ bool CClient::addRelay( const CServerDef * pServ ) EXC_SET_BLOCK("server relay packet"); new PacketServerRelay(this, dwAddr, pServ->m_ip.GetPort(), dwCustomerId); - + m_Targ_Mode = CLIMODE_SETUP_RELAY; return true; EXC_CATCH; @@ -680,7 +680,7 @@ bool CClient::OnRxPing( const byte * pData, uint iLen ) bool CClient::OnRxWebPageRequest( byte * pRequest, size_t uiLen ) { ADDTOCALLSTACK("CClient::OnRxWebPageRequest"); - + // Seems to be a web browser pointing at us ? typical stuff : if ( GetConnectType() != CONNECT_HTTP ) return false; @@ -730,7 +730,11 @@ bool CClient::OnRxWebPageRequest( byte * pRequest, size_t uiLen ) { pszArgs += 15; GETNONWHITESPACE(pszArgs); - uiContentLength = Str_ToUI(pszArgs, 10); + std::optional iconv = Str_ToU(pszArgs, 10); + if (!iconv.has_value()) + continue; + + uiContentLength = *iconv; } else if ( ! strnicmp( pszArgs, "If-Modified-Since:", 18 )) { @@ -787,7 +791,7 @@ bool CClient::OnRxWebPageRequest( byte * pRequest, size_t uiLen ) CheckReportNetAPIErr(iSocketRet, "CClient::Webpage.TCP_NODELAY"); if (iSocketRet) return false; - + if ( memcmp(ppLines[0], "POST", 4) == 0 ) { if (uiContentLength > strlen(ppLines[iQtyLines-1]) ) @@ -863,7 +867,7 @@ bool CClient::xProcessClientSetup( CEvent * pEvent, uint uiLen ) addLoginErr( PacketLoginError::BadEncLength ); return false; } - + GetNetState()->detectAsyncMode(); SetConnectType( m_Crypt.GetConnectType() ); @@ -877,7 +881,7 @@ bool CClient::xProcessClientSetup( CEvent * pEvent, uint uiLen ) addLoginErr( PacketLoginError::BadVersion ); return false; } - + ASSERT(uiLen <= sizeof(CEvent)); std::unique_ptr bincopy = std::make_unique(); // in buffer. (from client) memcpy(bincopy->m_Raw, pEvent->m_Raw, uiLen); @@ -886,7 +890,7 @@ bool CClient::xProcessClientSetup( CEvent * pEvent, uint uiLen ) g_Log.EventError("NET-IN: xProcessClientSetup failed (Decrypt).\n"); return false; } - + byte lErr = PacketLoginError::EncUnknown; tchar szAccount[MAX_ACCOUNT_NAME_SIZE+3]; @@ -993,7 +997,7 @@ bool CClient::xProcessClientSetup( CEvent * pEvent, uint uiLen ) } #endif } - + xRecordPacketData(this, pEvent->m_Raw, uiLen, "client->server"); if ( lErr != PacketLoginError::Success ) // it never matched any crypt format. @@ -1010,15 +1014,15 @@ bool CClient::xCanEncLogin(bool bCheckCliver) if ( !bCheckCliver ) { if ( m_Crypt.GetEncryptionType() == ENC_NONE ) - return ( g_Cfg.m_fUsenocrypt ); // Server don't want no-crypt clients - + return ( g_Cfg.m_fUsenocrypt ); // Server don't want no-crypt clients + return ( g_Cfg.m_fUsecrypt ); // Server don't want crypt clients } else { if ( !g_Serv.m_ClientVersion.GetClientVerNumber() ) // Any Client allowed return true; - + if ( m_Crypt.GetEncryptionType() != ENC_NONE ) return ( m_Crypt.GetClientVerNumber() == g_Serv.m_ClientVersion.GetClientVerNumber() ); else diff --git a/src/game/items/CItemMap.cpp b/src/game/items/CItemMap.cpp index bffacdd28..847d5e502 100644 --- a/src/game/items/CItemMap.cpp +++ b/src/game/items/CItemMap.cpp @@ -48,7 +48,7 @@ bool CItemMap::r_LoadVal(CScript & s) // load an item script CPointMap pntTemp; pntTemp.Read(s.GetArgStr()); CMapPinRec pin(pntTemp.m_x, pntTemp.m_y); - m_Pins.emplace_back(pin); + m_Pins.emplace_back(std::move(pin)); return true; } return CItem::r_LoadVal(s); @@ -73,7 +73,11 @@ bool CItemMap::r_WriteVal(lpctstr ptcKey, CSString &sVal, CTextConsole *pSrc, bo if ( !strnicmp(ptcKey, "PIN.", 4) ) { ptcKey += 4; - uint i = Exp_GetUVal(ptcKey) - 1; + uint i = Exp_GetUVal(ptcKey); + if (i == 0) + return false; + + i -= 1; if ( m_Pins.IsValidIndex(i) ) { sVal.Format("%i,%i", m_Pins[i].m_x, m_Pins[i].m_y); diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 0a3361304..b407cc5f4 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -121,6 +121,9 @@ void GlobalInitializer::PeriodicSyncTimeConstants() // static #endif // _WIN32 } + +/* Start global declarations */ + static GlobalInitializer g_GlobalInitializer; #ifdef _WIN32 @@ -135,8 +138,12 @@ UnixTerminal g_UnixTerminal; LinuxEv g_NetworkEvent; #endif +CServer g_Serv; // current state, stuff not saved. +CLog g_Log; + // Config data from sphere.ini is needed from the beginning. CServerConfig g_Cfg; +CAccounts g_Accounts; // All the player accounts. name sorted CAccount // Game servers stuff. CWorld g_World; // the world. (we save this stuff) @@ -150,15 +157,10 @@ CWorld g_World; // the world. (we save this stuff) // Again, game servers stuff. -CServer g_Serv; // current state, stuff not saved. - - CUOInstall g_Install; CVerDataMul g_VerData; CSRand g_Rand; CExpression g_Exp; // Global script variables. -CLog g_Log; -CAccounts g_Accounts; // All the player accounts. name sorted CAccount CSStringList g_AutoComplete; // auto-complete list CScriptProfiler g_profiler; // script profiler CUOMapList g_MapList; // global maps information @@ -509,23 +511,24 @@ void defragSphere(char *path) CSFileText inf; CSFile ouf; char z[_MAX_PATH], z1[_MAX_PATH], buf[1024]; - size_t i; char *p = nullptr, *p1 = nullptr; - size_t dBytesRead; + size_t uiBytesRead; size_t dTotalMb; const size_t mb10 = 10*1024*1024; const size_t mb5 = 5*1024*1024; - bool bSpecial; + bool fSpecial; 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"); - constexpr dword MAX_UID = 40'000'000U; // limit to 100mln of objects, takes 100mln*4 ~= 400mb + // 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 ( i = 0; i < 3; ++i ) + for ( uint i = 0; i < 3; ++i ) { Str_CopyLimitNull(z, path, sizeof(z)); if ( i == 0 ) strcat(z, SPHERE_FILE "statics" SPHERE_SCRIPT); @@ -538,14 +541,14 @@ void defragSphere(char *path) g_Log.Event(LOGM_INIT, "Cannot open file for reading. Skipped!\n"); continue; } - dBytesRead = dTotalMb = 0; + uiBytesRead = dTotalMb = 0; while ((dwIdxUID < MAX_UID) && !feof(inf._pStream)) { fgets(buf, sizeof(buf), inf._pStream); - dBytesRead += strlen(buf); - if ( dBytesRead > mb10 ) + uiBytesRead += strlen(buf); + if ( uiBytesRead > mb10 ) { - dBytesRead -= mb10; + uiBytesRead -= mb10; dTotalMb += 10; g_Log.Event(LOGM_INIT, "Total read %" PRIuSIZE_T " Mb\n", dTotalMb); } @@ -574,7 +577,7 @@ void defragSphere(char *path) g_Log.Event(LOGM_INIT, "Quick-Sorting the UIDs array...\n"); dword_q_sort(puids, 0, dwTotalUIDs -1); - for ( i = 0; i < 5; ++i ) + for ( uint i = 0; i < 5; ++i ) { Str_CopyLimitNull(z, path, sizeof(z)); if ( !i ) strcat(z, SPHERE_FILE "accu.scp"); @@ -595,7 +598,7 @@ void defragSphere(char *path) continue; } - dBytesRead = dTotalMb = 0; + uiBytesRead = dTotalMb = 0; while ( inf.ReadString(buf, sizeof(buf)) ) { dwIdxUID = (dword)strlen(buf); @@ -604,11 +607,11 @@ void defragSphere(char *path) buf[dwIdxUID] = buf[dwIdxUID +1] = 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 - bSpecial = false; - dBytesRead += dwIdxUID; - if ( dBytesRead > mb5 ) + fSpecial = false; + uiBytesRead += dwIdxUID; + if ( uiBytesRead > mb5 ) { - dBytesRead -= mb5; + uiBytesRead -= mb5; dTotalMb += 5; g_Log.Event(LOGM_INIT, "Total processed %" PRIuSIZE_T " Mb\n", dTotalMb); } @@ -631,7 +634,7 @@ void defragSphere(char *path) else if (( buf[0] == 'M' ) && ( strstr(buf, "MEMBER=0") == buf )) // MEMBER= { p += 7; - bSpecial = true; + fSpecial = true; } else if (( buf[0] == 'M' ) && ( strstr(buf, "MORE1=0") == buf )) // MORE1= p += 6; @@ -668,7 +671,7 @@ void defragSphere(char *path) ((( *p1 >= '0' ) && ( *p1 <= '9' )) || (( *p1 >= 'a' ) && ( *p1 <= 'f' ))) ) ++p1; - if ( !bSpecial ) + if ( !fSpecial ) { if ( *p1 && ( *p1 != '\r' ) && ( *p1 != '\n' )) // some more text in line p = nullptr; @@ -759,6 +762,7 @@ void defragSphere(char *path) inf.Close(); ouf.Close(); } + free(puids); g_Log.Event(LOGM_INIT, "Defragmentation complete.\n"); } @@ -788,9 +792,10 @@ 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 IThread* curthread = ThreadHolder::get().current(); ASSERT(curthread != nullptr); ASSERT(dynamic_cast(curthread)); + (void)curthread; } #ifndef _WIN32 diff --git a/src/game/triggers.cpp b/src/game/triggers.cpp index 8958d1114..760b686c5 100644 --- a/src/game/triggers.cpp +++ b/src/game/triggers.cpp @@ -38,10 +38,11 @@ bool IsTrigUsed(const char *name) if ( g_Serv.IsLoading() == true) return false; - int index = FindTableSorted(name, kOrderedTrigsNames, ARRAY_COUNT(kOrderedTrigsNames)); + const int index = FindTableSorted(name, kOrderedTrigsNames, ARRAY_COUNT(kOrderedTrigsNames)); if (index >= 0) return IsTrigUsed((E_TRIGGERS)index); - return false; + + return true; //Must return true for custom triggers } void TriglistInit() diff --git a/src/game/uo_files/CUOMobtypes.cpp b/src/game/uo_files/CUOMobtypes.cpp index 2cf64a7ee..0d289b957 100644 --- a/src/game/uo_files/CUOMobtypes.cpp +++ b/src/game/uo_files/CUOMobtypes.cpp @@ -10,89 +10,112 @@ #include "../../common/CUOInstall.h" #include "../../game/uo_files/uofiles_enums_creid.h" #include "CUOMobtypes.h" +#include void CUOMobTypes::Load() { ADDTOCALLSTACK("CUOMobTypes::Load"); g_Log.Event(LOGM_INIT, "Caching mobtypes.txt...\n"); - CUOMobTypesType mobTypesRow = {0,0}; - - _mobTypesEntries.clear(); - + _vMobTypesEntries.clear(); CSFileText csvMobTypes; if (g_Install.OpenFile(csvMobTypes, "mobtypes.txt", (word)(OF_READ | OF_TEXT | OF_DEFAULTMODE))) { - _mobTypesEntries.resize(CREID_QTY); - for (int i = 0; i < _mobTypesEntries.size(); i++) + _vMobTypesEntries.resize(CREID_QTY); + for (size_t i = 0; i < _vMobTypesEntries.size(); ++i) { - mobTypesRow.m_type = 4; - mobTypesRow.m_flags = 0; - _mobTypesEntries[i] = mobTypesRow; + _vMobTypesEntries[i] = CUOMobTypesEntry{MOBTE_QTY, 0}; } - tchar* pszTemp = Str_GetTemp(); - size_t count = 0; + tchar* ptcTemp = Str_GetTemp(); + size_t uiLineCount = 0; + CUOMobTypesEntry mobTypesRow {}; while (!csvMobTypes.IsEOF()) { - csvMobTypes.ReadString(pszTemp, 200); - if (*pszTemp) + csvMobTypes.ReadString(ptcTemp, 200); + if (*ptcTemp) { - count++; + ++ uiLineCount; - std::string tmpString = pszTemp; + int iLineLength = (int)strnlen(ptcTemp, Str_TempLength()); + iLineLength = Str_TrimEndWhitespace(ptcTemp, iLineLength); - int len = (int)tmpString.length(); - len = Str_TrimEndWhitespace(tmpString.data(), len); - - if (len == 0 || tmpString[0] == '#') //Empty line or commented + if (iLineLength == 0 || ptcTemp[0] == '#') //Empty line or commented continue; //Split the string - std::vector splitArray; - splitArray.resize(3); - size_t iQty = Str_ParseCmds(tmpString.data(), splitArray.data(), 3, " \t#"); - - if (splitArray.size() < 3) + tchar* pptcSplitArray[3]; + const int iQty = Str_ParseCmds(ptcTemp, pptcSplitArray, ARRAY_COUNT(pptcSplitArray), " \t#"); + if (iQty < 3) { - g_Log.EventError("Mobtypes.txt: not enough parameters on line %" PRIuSIZE_T " \n", count); + g_Log.EventError("Mobtypes.txt: not enough parameters on line %" PRIuSIZE_T " \n", uiLineCount); continue; } - if (!IsStrNumeric(splitArray[0])) + //if (!IsStrNumeric(pptcSplitArray[0])) + //{ + // g_Log.EventError("Mobtypes.txt: non numeric ID on line %" PRIuSIZE_T " \n", uiLineCount); + // continue; + //} + // const uint uiAnimIndex = (uint)std::stoul(pptcSplitArray[0]); + + std::optional iconv = Str_ToU(pptcSplitArray[0], 10); + if (!iconv.has_value()) { - g_Log.EventError("Mobtypes.txt: non numeric ID on line %" PRIuSIZE_T " \n", count); + g_Log.EventError( + "Mobtypes.txt: Invalid char ID on line %" PRIuSIZE_T ".\n", uiLineCount); continue; } + const uint uiAnimIndex = *iconv; - int animIndex = std::stoi(splitArray[0]); - std::string sType = splitArray[1]; + const std::string_view sType(pptcSplitArray[1]); if (sType == "MONSTER") - mobTypesRow.m_type = 0; + mobTypesRow.m_uiType = MOBTE_MONSTER; else if (sType == "SEA_MONSTER") - mobTypesRow.m_type = 1; + mobTypesRow.m_uiType = MOBTE_SEA_MONSTER; else if (sType == "ANIMAL") - mobTypesRow.m_type = 2; + mobTypesRow.m_uiType = MOBTE_ANIMAL; else if (sType == "HUMAN") - mobTypesRow.m_type = 3; + mobTypesRow.m_uiType = MOBTE_HUMAN; else if (sType == "EQUIPMENT") - mobTypesRow.m_type = 4; + mobTypesRow.m_uiType = MOBTE_EQUIPMENT; else { - mobTypesRow.m_type = 0; - g_Log.EventError("Mobtypes.txt: wrong type found on line %" PRIuSIZE_T " \n", count); + mobTypesRow.m_uiType = MOBTE_QTY; + g_Log.EventError("Mobtypes.txt: wrong type found on line %" PRIuSIZE_T " \n", uiLineCount); } - mobTypesRow.m_flags = std::strtol(splitArray[2], NULL, 16); + //mobTypesRow.m_flags = (dword)std::strtoul(pptcSplitArray[2], nullptr, 16); + iconv = Str_ToU(pptcSplitArray[2], 16); + if (!iconv.has_value()) + { + g_Log.EventError( + "Mobtypes.txt: Invalid flags for char ID %" PRIu32 " on line %" PRIuSIZE_T ".\n", + uiAnimIndex, uiLineCount); + continue; + } + mobTypesRow.m_uiFlags = *iconv; - if (animIndex <= _mobTypesEntries.size()) //Safety check + if (uiAnimIndex <= _vMobTypesEntries.size()) //Safety check + { + _vMobTypesEntries[uiAnimIndex] = mobTypesRow; + } + else { - _mobTypesEntries[animIndex] = mobTypesRow; + g_Log.EventError( + "Mobtypes.txt: trying to load data for an invalid char ID %" PRIu32 " on line %" PRIuSIZE_T ".\n", + uiAnimIndex, uiLineCount); } } } + csvMobTypes.Close(); } } +const CUOMobTypesEntry* CUOMobTypes::GetEntry(uint id) const +{ + ASSERT(id < _vMobTypesEntries.size()); + return &(_vMobTypesEntries[id]); +} diff --git a/src/game/uo_files/CUOMobtypes.h b/src/game/uo_files/CUOMobtypes.h index 7b7c2bbb7..d4514bddf 100644 --- a/src/game/uo_files/CUOMobtypes.h +++ b/src/game/uo_files/CUOMobtypes.h @@ -9,33 +9,40 @@ #include #include "../../common/common.h" + /** * mobtypes.txt */ -struct CUOMobTypesType + +enum MOBTYPES_ENTITY_TYPE : ushort { - ushort m_type; // 0 = MONSTER, 1 = SEA_MONSTER, 2 = ANIMAL, 3 = HUMAN, 4 = EQUIPMENT - dword m_flags; + MOBTE_MONSTER, + MOBTE_SEA_MONSTER, + MOBTE_ANIMAL, + MOBTE_HUMAN, + MOBTE_EQUIPMENT, + MOBTE_QTY +}; +struct CUOMobTypesEntry +{ + MOBTYPES_ENTITY_TYPE m_uiType; + uint m_uiFlags; }; + class CUOMobTypes { - std::vector _mobTypesEntries; + std::vector _vMobTypesEntries; public: void Load(); - inline const bool IsLoaded() { - if (_mobTypesEntries.size() > 0) - return true; - else - return false; + bool IsLoaded() const noexcept { + return !_vMobTypesEntries.empty(); } - inline const CUOMobTypesType* GetEntry(ushort id) { - return &(_mobTypesEntries[id]); - } + const CUOMobTypesEntry* GetEntry(uint id) const; }; diff --git a/src/network/CIPHistoryManager.h b/src/network/CIPHistoryManager.h index 03ec8c749..6fe6adb95 100644 --- a/src/network/CIPHistoryManager.h +++ b/src/network/CIPHistoryManager.h @@ -25,7 +25,7 @@ struct HistoryIP int m_connected; bool m_blocked; int m_ttl; - size_t m_connectionAttempts; // since i remember of this IP + int64 m_connectionAttempts; // since i remember of this IP int64 m_timeLastConnectedMs; int64 m_blockExpire; int m_pingDecay; diff --git a/src/network/CNetworkManager.cpp b/src/network/CNetworkManager.cpp index 8bb74068a..51231dc37 100644 --- a/src/network/CNetworkManager.cpp +++ b/src/network/CNetworkManager.cpp @@ -178,7 +178,7 @@ void CNetworkManager::acceptNewConnection(void) return; } - if ((g_Cfg._iMaxConnectRequestsPerIP > 0) && (ip.m_connectionAttempts >= (size_t)g_Cfg._iMaxConnectRequestsPerIP)) + if ((g_Cfg._iMaxConnectRequestsPerIP > 0) && (ip.m_connectionAttempts >= (int64)g_Cfg._iMaxConnectRequestsPerIP)) { // Call this special scripted function. CScriptTriggerArgs fargs_ex(client_addr.GetAddrStr()); @@ -192,7 +192,7 @@ void CNetworkManager::acceptNewConnection(void) { // reject CLOSESOCKET(h); - + _printIPBlocked(); g_Log.Event(LOGM_CLIENTS_LOG | LOGL_ERROR, "Reject reason: requested kick via script 'f_onserver_connectreq_ex'.\n"); } @@ -206,7 +206,7 @@ void CNetworkManager::acceptNewConnection(void) g_Log.Event(LOGM_CLIENTS_LOG | LOGL_ERROR, "Reject reason: requested kick + IP block via script 'f_onserver_connectreq_ex'.\n"); } } - + /* // Check if there's already more data waiting to be read (suspicious?). // Classic client (but not some 3rd party clients) have pending data after the second connection request @@ -272,8 +272,8 @@ void CNetworkManager::acceptNewConnection(void) { // not enough empty slots EXC_SET_BLOCK("no slot available"); - _printIPBlocked(); - + _printIPBlocked(); + g_Log.Event(LOGM_CLIENTS_LOG | LOGL_ERROR, "Reject reason: CLIENTMAX reached.\n"); CLOSESOCKET(h); return; diff --git a/src/network/CNetworkManager.h b/src/network/CNetworkManager.h index 2ebb8941b..18e95f1c9 100644 --- a/src/network/CNetworkManager.h +++ b/src/network/CNetworkManager.h @@ -51,15 +51,15 @@ class CNetworkManager void flushAllClients(void); // force each thread to flush output public: - inline const PacketManager& getPacketManager(void) const { return m_packets; } // get packet manager - inline IPHistoryManager& getIPHistoryManager(void) { return m_ips; } // get ip history manager - inline bool isThreaded(void) const { return m_isThreaded; } // are threads active - inline bool isInputThreaded(void) const // is network input handled by thread + inline const PacketManager& getPacketManager(void) const noexcept { return m_packets; } // get packet manager + inline IPHistoryManager& getIPHistoryManager(void) noexcept { return m_ips; } // get ip history manager + inline bool isThreaded(void) const noexcept { return m_isThreaded; } // are threads active + inline bool isInputThreaded(void) const noexcept // is network input handled by thread { return m_isThreaded; } - inline bool isOutputThreaded(void) const // is network output handled by thread + inline bool isOutputThreaded(void) const noexcept // is network output handled by thread { return m_isThreaded; } @@ -79,4 +79,4 @@ class CNetworkManager extern CNetworkManager g_NetworkManager; -#endif // _INC_CNETWORKMANAGER_H \ No newline at end of file +#endif // _INC_CNETWORKMANAGER_H diff --git a/src/network/send.cpp b/src/network/send.cpp index a4813b3f7..99c9e76c4 100644 --- a/src/network/send.cpp +++ b/src/network/send.cpp @@ -2,6 +2,7 @@ #ifndef _WIN32 #include #endif +#include #include "../common/resource/CResourceLock.h" #include "../common/CLog.h" @@ -3387,11 +3388,11 @@ PacketCharacterList::PacketCharacterList(CClient* target) : PacketSend(XCMD_Char } } - if (tmVerReported > 1260000) + if (tmVerReported > 1'26'00'00) { const CNetState* ns = target->GetNetState(); dword flags = g_Cfg.GetPacketFlag(true, (RESDISPLAY_VERSION)(account->GetResDisp()), - maximum(account->GetMaxChars(), (byte)(account->m_Chars.GetCharCount()))); + std::max(account->GetMaxChars(), (byte)(account->m_Chars.GetCharCount()))); if (ns->getClientType() == CLIENTTYPE_2D) flags |= 0x400; writeInt32(flags); @@ -3597,16 +3598,17 @@ void PacketGumpDialog::writeCompressedControls(std::vector const* cont // compress and write controls uint controlLength = 1; for (CSString const& ctrl : *controls) - controlLength += (uint)ctrl.GetLength() + 2; // String terminator not needed. + { + controlLength += (uint)ctrl.GetLength() + 2; // String terminator not needed. + } char* toCompress = new char[controlLength]; - uint controlLengthCurrent = 0; for (CSString const& ctrl : *controls) { const uint uiAvailableLength = std::max(0u, controlLength - controlLengthCurrent); const int iJustWrittenLength = snprintf(&toCompress[controlLengthCurrent], uiAvailableLength, "{%s}", ctrl.GetBuffer()); - controlLengthCurrent += iJustWrittenLength; + controlLengthCurrent += iJustWrittenLength; } ++ controlLengthCurrent; diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index bd69b5e74..3f93148c4 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -142,6 +142,12 @@ IThread* ThreadHolder::current() if (m_spherethreadpairs_systemid_ptr.empty()) { auto thread = static_cast(DummySphereThread::getInstance()); + if (!thread) { + // Should never happen. + EXC_NOTIFY_DEBUGGER; + std::abort(); + } + thread->m_threadSystemId = tid; lock.unlock(); push(thread);