diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6667901e0..458594d8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - name: CMake run: cmake -G "Visual Studio 17 2022" .\ - name: MSBuild - run: msbuild SphereServer.sln /verbosity:minimal /maxcpucount /p:Configuration=Nightly + run: msbuild SphereServer.sln /verbosity:normal /maxcpucount /p:Configuration=Nightly - name: Create package run: | pwd @@ -50,7 +50,7 @@ jobs: - name: CMake run: cmake -G "Visual Studio 17 2022" -A Win32 .\ - name: MSBuild - run: msbuild SphereServer.sln /verbosity:minimal /maxcpucount /p:Configuration=Nightly + run: msbuild SphereServer.sln /verbosity:normal /maxcpucount /p:Configuration=Nightly - name: Create package run: | pwd diff --git a/Changelog.txt b/Changelog.txt index 2bf40fc2f..dee2695e7 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -3718,14 +3718,12 @@ Added: 'H' shortcut for variables to get the value as hexadecimal. when you switch this setting. - Fixed: SPELLFLAG_NOUNPARALYZE flag wasn't working as intended. -02-06-2024, Nolok +03-06-2024, Nolok - Added ini settings: -// Maximum number of connection requests before rejecting/blocking IP. Reset after seconds elapsed after last connection attempt. -MaxConnectRequestsPerIP=5 - -// Maximum time in milliseconds to wait before closing a connection request wich did not make it into a successful login (classic DDoS just asks to connect without doing anything else, or sends garbage data). -TimeoutIncompleteConn=5 * 1000 // 5 seconds - + // Maximum number of connection requests before rejecting/blocking IP. Reset after seconds elapsed after last connection attempt. + MaxConnectRequestsPerIP=5 + // Maximum time in milliseconds to wait before closing a connection request wich did not make it into a successful login (classic DDoS just asks to connect without doing anything else, or sends garbage data). + TimeoutIncompleteConn=5 * 1000 // 5 seconds - Added serv_trig function f_onserver_connectreq_ex, called after an IP exceeds MaxConnectRequestsPerIP: ARGS (RO): IP address. ARGN1 (RO): System time in milliseconds since the last connection. @@ -3734,8 +3732,11 @@ TimeoutIncompleteConn=5 * 1000 // 5 seconds LOCAL.BAN_TIMEOUT (RW): 5 * 60 (default), seconds to keep banned the ip from new connections to the server (permanent if negative). RETURN 1: Only reject the connection. RETURN 2: Reject and ban the IP for seconds. - - Added serv_trig function f_onserver_connection_acquired, called after an IP successifully establishes a connection with the server and passess the security checks: ARGS (RO): IP address. ARGN1 (RO): System time in milliseconds since the last connection. ARGN2 (RO): System time in milliseconds of this connection attempt. + +04-06-2024, Nolok +- Added: client commands ADDITEM and ADDCHAR, to be used in place of .ADD when there's the need to force the argument to be an item or a char. + This can be useful when using raw IDs (like 01, 0bd1, etc) instead of defnames. diff --git a/cmake/toolchains/Windows-MSVC.cmake b/cmake/toolchains/Windows-MSVC.cmake index a401a2eb0..356e04f85 100644 --- a/cmake/toolchains/Windows-MSVC.cmake +++ b/cmake/toolchains/Windows-MSVC.cmake @@ -118,9 +118,10 @@ function (toolchain_exe_stuff) set(CMAKE_EXE_LINKER_FLAGS_NIGHTLY CACHE INTERNAL ${CMAKE_EXE_LINKER_FLAGS_RELEASE} "") target_link_options(spheresvr PRIVATE - $<$: ${EXE_LINKER_EXTRA} /OPT:REF,ICF /LTCG> - $<$: ${EXE_LINKER_EXTRA} /OPT:REF,ICF /LTCG> - $<$: ${EXE_LINKER_EXTRA} /DEBUG /LTCG:OFF /SAFESEH:NO $<$>:/INCREMENTAL /EDITANDCONTINUE>> + $<$: ${EXE_LINKER_EXTRA} /OPT:REF,ICF /LTCG /NODEFAULTLIB:libcmtd> + $<$: ${EXE_LINKER_EXTRA} /OPT:REF,ICF /LTCG /NODEFAULTLIB:libcmtd> + $<$: ${EXE_LINKER_EXTRA} /DEBUG /LTCG:OFF /SAFESEH:NO /NODEFAULTLIB:libcmt + $<$>:/INCREMENTAL /EDITANDCONTINUE> > ) # MSVC doesn't yet have an option to statically link against *san sanitizer libraries. # /INCREMENTAL and /EDITANDCONTINUE not compatible with a MSVC Asan build. diff --git a/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake b/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake index 55389634f..a888ec6dd 100644 --- a/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake +++ b/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake @@ -95,7 +95,7 @@ function (toolchain_exe_stuff_common) SET (COMPILE_OPTIONS_EXTRA -fno-omit-frame-pointer -fno-inline) ENDIF () IF (TARGET spheresvr_release) - TARGET_COMPILE_OPTIONS ( spheresvr_release PUBLIC -s -O3 ${COMPILE_OPTIONS_EXTRA}) + TARGET_COMPILE_OPTIONS ( spheresvr_release PUBLIC -O3 ${COMPILE_OPTIONS_EXTRA}) ENDIF () IF (TARGET spheresvr_nightly) IF (ENABLED_SANITIZER) @@ -119,6 +119,15 @@ function (toolchain_exe_stuff_common) ) + #-- Apply linker flags, only the ones specific per build type. + IF (TARGET spheresvr_release) + target_link_options (spheresvr_release PRIVATE -s) + ENDIF () + IF (TARGET spheresvr_nightly AND NOT ${ENABLED_SANITIZER}) + target_link_options (spheresvr_nightly PRIVATE -s) + ENDIF () + + #-- Store common define macros. set(cxx_compiler_definitions_common diff --git a/lib/lib_build_flags_common_c.cmake b/lib/lib_build_flags_common_c.cmake index 85da1827e..f3606203c 100644 --- a/lib/lib_build_flags_common_c.cmake +++ b/lib/lib_build_flags_common_c.cmake @@ -11,10 +11,9 @@ if (MSVC) $<$: $,/MTd,/MDd> $,/Zi,/ZI>> ) set (c_linker_options_common - /NODEFAULTLIB:libcmt /NODEFAULTLIB:libcmtd - $<$: /OPT:REF,ICF /LTCG:ON> - $<$: /OPT:REF,ICF /LTCG:ON> - $<$: /DEBUG /LTCG:OFF> + $<$: /OPT:REF,ICF /LTCG:ON /NODEFAULTLIB:libcmtd> + $<$: /OPT:REF,ICF /LTCG:ON /NODEFAULTLIB:libcmtd> + $<$: /DEBUG /LTCG:OFF /NODEFAULTLIB:libcmt> ) else (MSVC) diff --git a/src/common/CCacheableScriptFile.cpp b/src/common/CCacheableScriptFile.cpp index 0f0386a54..2bcff75de 100644 --- a/src/common/CCacheableScriptFile.cpp +++ b/src/common/CCacheableScriptFile.cpp @@ -1,5 +1,5 @@ - #include "../sphere/threads.h" +#include "CLog.h" #include "CCacheableScriptFile.h" #ifdef _WIN32 @@ -68,49 +68,59 @@ bool CCacheableScriptFile::_Open(lpctstr ptcFilename, uint uiModeFlags) } else { - TemporaryString tsBuf; - tchar* ptcBuf = tsBuf.buffer(); - size_t uiStrLen; - bool fUTF = false, fFirstLine = true; const int iFileLength = _GetLength(); - _fileContent = new std::vector; - _fileContent->reserve(iFileLength / 20); - - while ( !feof(_pStream) ) + ASSERT(iFileLength >= 0); + bool fUTF = false, fFirstLine = true; + if (iFileLength > 5 * 1'000'000) { - tsBuf.setAt(0, '\0'); - fgets(ptcBuf, SCRIPT_MAX_LINE_LEN, _pStream); - uiStrLen = strlen(ptcBuf); - ASSERT(SCRIPT_MAX_LINE_LEN < INT_MAX); - if (uiStrLen > SCRIPT_MAX_LINE_LEN) - uiStrLen = SCRIPT_MAX_LINE_LEN; - - // first line may contain utf marker (byte order mark) - if (fFirstLine && uiStrLen >= 3 && - (uchar)(ptcBuf[0]) == 0xEF && - (uchar)(ptcBuf[1]) == 0xBB && - (uchar)(ptcBuf[2]) == 0xBF) - { - fUTF = true; - } + g_Log.EventError("Single script file bigger than %d MB? Skipping.\n", iFileLength / 1'000'000); + } + else + { + // Fastest method: read the script file all at once. + auto fileContentCopy = std::make_unique((size_t)iFileLength + 1u); + fread(fileContentCopy.get(), sizeof(char), (size_t)iFileLength, _pStream); + + // Allocate string vectors for each script line. + _fileContent = new std::vector; + _fileContent->reserve(iFileLength / 25); - const lpctstr str_start = (fUTF ? &ptcBuf[3] : ptcBuf); - size_t len_to_copy = uiStrLen - (fUTF ? 3 : 0); - while (len_to_copy > 0) + ssize_t iStrLen; + for (const char *fileCursor = fileContentCopy.get();; fileCursor += (size_t)iStrLen + 1u) { - const int ch = str_start[len_to_copy - 1]; - if (ch == '\n' || ch == '\r') - len_to_copy -= 1; - else + iStrLen = sGetLine_StaticBuf(fileCursor, SCRIPT_MAX_LINE_LEN); + if (iStrLen < 0) break; - } - if (len_to_copy == 0) - _fileContent->emplace_back(); - else - _fileContent->emplace_back(str_start, len_to_copy); - fFirstLine = false; - fUTF = false; - } + if (iStrLen < 1 || (fileCursor[iStrLen] != '\n')) + continue; + + // first line may contain utf marker (byte order mark) + if (fFirstLine && iStrLen >= 3 && + (uchar)(fileCursor[0]) == 0xEF && + (uchar)(fileCursor[1]) == 0xBB && + (uchar)(fileCursor[2]) == 0xBF) + { + fUTF = true; + } + + 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') + len_to_copy -= 1; + else + break; + } + if (len_to_copy == 0) + _fileContent->emplace_back(); + else + _fileContent->emplace_back(str_start, len_to_copy); + fFirstLine = false; + fUTF = false; + } // closes while + } // closes else fclose(_pStream); _pStream = nullptr; diff --git a/src/common/resource/CResourceHolder.cpp b/src/common/resource/CResourceHolder.cpp index 4e7c32269..9b1475798 100644 --- a/src/common/resource/CResourceHolder.cpp +++ b/src/common/resource/CResourceHolder.cpp @@ -337,9 +337,9 @@ CResourceScript * CResourceHolder::GetResourceFile( size_t i ) return m_ResourceFiles[i]; } -CResourceID CResourceHolder::ResourceGetID_Advance(RES_TYPE restype, lpctstr &ptcName, word wPage) +CResourceID CResourceHolder::ResourceGetID_EatStr(RES_TYPE restype, lpctstr &ptcName, word wPage, bool fCanFail) { - ADDTOCALLSTACK("CResourceHolder::ResourceGetID_Advance"); + ADDTOCALLSTACK("CResourceHolder::ResourceGetID_EatStr"); // Find the Resource ID given this name. // We are NOT creating a new resource. just looking up an existing one // NOTE: Do not enforce the restype. @@ -366,6 +366,7 @@ CResourceID CResourceHolder::ResourceGetID_Advance(RES_TYPE restype, lpctstr &pt } */ + lpctstr ptcNameStart = ptcName; dword dwEvalPrivateUID = Exp_GetDWVal(ptcName); // May be some complex expression {} int iEvalResType = RES_GET_TYPE(dwEvalPrivateUID); int iEvalResIndex = RES_GET_INDEX(dwEvalPrivateUID); @@ -374,22 +375,33 @@ CResourceID CResourceHolder::ResourceGetID_Advance(RES_TYPE restype, lpctstr &pt if ((restype != RES_UNKNOWN) && (iEvalResType == RES_UNKNOWN)) { // Label it with the type we want. + ASSERT(restype > RES_UNKNOWN && restype <= RES_QTY); + if (restype == RES_QTY) + { + // RES_QTY means i don't care, because i pass a defname and it already carries data about which kind of resource it is. + // If it doesn't (?!), or simply i pass an ID instead of a defname (which i expected), i'll have unexpected results, so better throw an error. + // If i pass a bare ID, Sphere cannot know if you meant a char, item, etc... + if (!fCanFail) + g_Log.EventError("Can't get the resource type from a bare ID ('%s')!\n", ptcNameStart); + return CResourceID(RES_UNKNOWN /* 0 */, 0, 0u); + } return CResourceID(restype, iEvalResIndex, wPage); } // CResourceID always needs to be a valid resource (there's an ASSERT in CResourceID copy constructor). return CResourceID((RES_TYPE)iEvalResType, iEvalResIndex, wPage); } -CResourceID CResourceHolder::ResourceGetID( RES_TYPE restype, lpctstr ptcName, word wPage ) +CResourceID CResourceHolder::ResourceGetID( RES_TYPE restype, lpctstr ptcName, word wPage, bool fCanFail ) { ADDTOCALLSTACK("CResourceHolder::ResourceGetID"); - return ResourceGetID_Advance(restype, ptcName, wPage); + return ResourceGetID_EatStr(restype, ptcName, wPage, fCanFail); } CResourceID CResourceHolder::ResourceGetIDType( RES_TYPE restype, lpctstr pszName, word wPage ) { // Get a resource of just this index type. - CResourceID rid = ResourceGetID( restype, pszName, wPage ); + ASSERT(restype != RES_QTY); + CResourceID rid = ResourceGetID( restype, pszName, wPage, true ); if ( rid.GetResType() != restype ) { rid.Init(); diff --git a/src/common/resource/CResourceHolder.h b/src/common/resource/CResourceHolder.h index 6174bb637..abd276e03 100644 --- a/src/common/resource/CResourceHolder.h +++ b/src/common/resource/CResourceHolder.h @@ -43,8 +43,8 @@ class CResourceHolder : public CScriptObj lpctstr ResourceGetName( const CResourceID& rid ) const; lpctstr ResourceGetName(const CResourceIDBase& rid, const RES_TYPE iExpectedType); CResourceScript * GetResourceFile( size_t i ); - CResourceID ResourceGetID_Advance( RES_TYPE restype, lpctstr &pszName, word wPage = 0 ); // this moves forward (changes!) the ptcName pointer! - CResourceID ResourceGetID( RES_TYPE restype, lpctstr ptcName, word wPage = 0 ); + CResourceID ResourceGetID_EatStr( RES_TYPE restype, lpctstr &pszName, word wPage = 0, bool fCanFail = false ); // this moves forward (changes!) the ptcName pointer! + CResourceID ResourceGetID( RES_TYPE restype, lpctstr ptcName, word wPage = 0, bool fCanFail = false); CResourceID ResourceGetIDType( RES_TYPE restype, lpctstr pszName, word wPage = 0 ); int ResourceGetIndexType( RES_TYPE restype, lpctstr pszName, word wPage = 0 ); bool ResourceLock( CResourceLock & s, const CResourceID& rid ); diff --git a/src/common/resource/CResourceID.cpp b/src/common/resource/CResourceID.cpp index acd8d4f03..d2606788a 100644 --- a/src/common/resource/CResourceID.cpp +++ b/src/common/resource/CResourceID.cpp @@ -103,3 +103,24 @@ CResourceID& CResourceID::operator = (const CResourceIDBase& rid) m_wPage = 0; return *this; } + +bool CResourceID::operator == (const CResourceID & rid) const noexcept +{ + return ((rid.m_wPage == m_wPage) && (rid.m_dwInternalVal == m_dwInternalVal)); +} + +void CResourceID::Init() noexcept +{ + m_dwInternalVal = UID_UNUSED; + m_wPage = 0; +} +void CResourceID::Clear() noexcept +{ + m_dwInternalVal = UID_PLAIN_CLEAR; + m_wPage = 0; +} + +bool CResourceID::IsEmpty() const noexcept +{ + return ((m_dwInternalVal & UID_O_INDEX_MASK) == 0 /* means also that restype = 0/RES_UNKNOWN */) && (m_wPage == 0); +} diff --git a/src/common/resource/CResourceID.h b/src/common/resource/CResourceID.h index 49de79c13..5dee21a15 100644 --- a/src/common/resource/CResourceID.h +++ b/src/common/resource/CResourceID.h @@ -123,7 +123,7 @@ struct CResourceIDBase : public CUID // It has not the "page" part/variable. m_dwInternalVal = UID_PLAIN_CLEAR; } - CResourceIDBase() noexcept + inline CResourceIDBase() noexcept { Init(); } @@ -141,15 +141,15 @@ struct CResourceIDBase : public CUID // It has not the "page" part/variable. void FixRes(); - RES_TYPE GetResType() const noexcept + inline RES_TYPE GetResType() const noexcept { return (RES_TYPE)(RES_GET_TYPE(m_dwInternalVal)); } - uint GetResIndex() const noexcept + inline uint GetResIndex() const noexcept { return RES_GET_INDEX(m_dwInternalVal); } - bool operator == (const CResourceIDBase & rid) const noexcept + inline bool operator == (const CResourceIDBase & rid) const noexcept { return (rid.m_dwInternalVal == m_dwInternalVal); } @@ -174,62 +174,39 @@ struct CResourceID : public CResourceIDBase // It has the "page" part. Use i #define RES_PAGE_MAX UINT16_MAX - 1 #define RES_PAGE_ANY UINT16_MAX // Pick a CResourceID independently from the page - void Init() - { - m_dwInternalVal = UID_UNUSED; - m_wPage = 0; - } - void Clear() - { - m_dwInternalVal = UID_PLAIN_CLEAR; - m_wPage = 0; - } + void Init() noexcept; + void Clear() noexcept; - CResourceID() : CResourceIDBase() - { - m_wPage = 0; - } + CResourceID() : CResourceIDBase(), m_wPage(0) + {} // Create an empty, valid CResourceID of a given type - explicit CResourceID(RES_TYPE restype) : CResourceIDBase(restype) - { - m_wPage = 0; - } + explicit CResourceID(RES_TYPE restype) : CResourceIDBase(restype), m_wPage(0) + {} explicit CResourceID(RES_TYPE, const CResourceIDBase&) = delete; - explicit CResourceID(RES_TYPE restype, int iIndex) : CResourceIDBase(restype, iIndex) - { - m_wPage = 0; - } + explicit CResourceID(RES_TYPE restype, int iIndex) : CResourceIDBase(restype, iIndex), m_wPage(0) + {} explicit CResourceID(RES_TYPE, const CResourceIDBase&, word) = delete; - explicit CResourceID(RES_TYPE restype, int iIndex, word wPage) : CResourceIDBase(restype, iIndex) - { - m_wPage = wPage; - } + explicit CResourceID(RES_TYPE restype, int iIndex, word wPage) : CResourceIDBase(restype, iIndex), m_wPage(wPage) + {} explicit CResourceID(const CResourceIDBase&, word) = delete; - explicit CResourceID(dword dwPrivateID, word wPage) : CResourceIDBase(dwPrivateID) - { - m_wPage = wPage; - } + explicit CResourceID(dword dwPrivateID, word wPage) : CResourceIDBase(dwPrivateID), m_wPage(wPage) + {} - CResourceID(const CResourceID & rid) : CResourceIDBase(rid) // copy constructor - { - m_wPage = rid.m_wPage; - } - CResourceID(const CResourceIDBase & rid): CResourceIDBase(rid) - { - m_wPage = 0; - } + // copy constructor + CResourceID(const CResourceID & rid) : CResourceIDBase(rid), m_wPage(rid.m_wPage) + {} + CResourceID(const CResourceIDBase & rid): CResourceIDBase(rid), m_wPage(0) + {} CResourceID& operator = (const CResourceID& rid); // assignment operator CResourceID& operator = (const CResourceIDBase& rid); + bool operator == (const CResourceID & rid) const noexcept; - word GetResPage() const noexcept + inline word GetResPage() const noexcept { return m_wPage; } - bool operator == (const CResourceID & rid) const noexcept - { - return ((rid.m_wPage == m_wPage) && (rid.m_dwInternalVal == m_dwInternalVal)); - } + bool IsEmpty() const noexcept; }; diff --git a/src/common/resource/CResourceQty.cpp b/src/common/resource/CResourceQty.cpp index 43f121197..ed511b652 100644 --- a/src/common/resource/CResourceQty.cpp +++ b/src/common/resource/CResourceQty.cpp @@ -74,7 +74,7 @@ bool CResourceQty::Load(lpctstr &pszCmds) return false; } - m_rid = g_Cfg.ResourceGetID_Advance(RES_UNKNOWN, pszCmds); + m_rid = g_Cfg.ResourceGetID_EatStr(RES_UNKNOWN, pszCmds); if ( m_rid.GetResType() == RES_UNKNOWN ) { m_rid.Init(); diff --git a/src/common/sphere_library/sstring.cpp b/src/common/sphere_library/sstring.cpp index 903bdaf8a..dac8bf94c 100644 --- a/src/common/sphere_library/sstring.cpp +++ b/src/common/sphere_library/sstring.cpp @@ -1627,3 +1627,129 @@ size_t UTF8MBSTR::ConvertUTF8ToString(const char* strInUTF8MB, lptstr& strOut) n return len; } + + +/* $NetBSD: getdelim.c,v 1.2 2015/12/25 20:12:46 joerg Exp $ */ +/* NetBSD-src: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +ssize_t +//getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) +fReadUntilDelimiter(char **buf, size_t *bufsiz, int delimiter, FILE *fp) noexcept +{ + char *ptr, *eptr; + + + if (*buf == NULL || *bufsiz == 0) { + *bufsiz = BUFSIZ; + if ((*buf = (char*)malloc(*bufsiz)) == NULL) + return -1; + } + + for (ptr = *buf, eptr = *buf + *bufsiz;;) { + int c = fgetc(fp); + if (c == -1) { + if (feof(fp)) { + ssize_t diff = (ssize_t)(ptr - *buf); + if (diff != 0) { + *ptr = '\0'; + return diff; + } + } + return -1; + } + *ptr++ = (char)c; + if (c == delimiter) { + *ptr = '\0'; + return ptr - *buf; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbufsiz = *bufsiz * 2; + ssize_t d = ptr - *buf; + if ((nbuf = (char*)realloc(*buf, nbufsiz)) == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + eptr = nbuf + nbufsiz; + ptr = nbuf + d; + } + } + return -1; +} + +ssize_t fReadUntilDelimiter_StaticBuf(char *buf, const size_t bufsiz, const int delimiter, FILE *fp) noexcept +{ + // buf: line array + char *ptr, *eptr; + + for (ptr = buf, eptr = buf + bufsiz;;) { + const int c = fgetc(fp); + if (c == -1) { + if (feof(fp)) { + ssize_t diff = (ssize_t)(ptr - buf); + if (diff != 0) { + *ptr = '\0'; + return diff; + } + } + return -1; + } + *ptr++ = static_cast(c); + if (c == delimiter) { + *ptr = '\0'; + return ptr - buf; + } + if (ptr + 2 >= eptr) { + return -1; // buffer too small + } + } + return -1; +} + +ssize_t sGetDelimiter_StaticBuf(const int delimiter, const char *sp, const size_t datasize) noexcept +{ + // buf: line array + const char *ptr, *eptr; + + for (ptr = sp, eptr = sp + datasize;; ++ptr) { + if (*ptr == '\0') { + return -1; + } + if (*ptr == delimiter) { + return ptr - sp; + } + if (ptr + 2 >= eptr) { + return -1; // buffer too small + } + } + return -1; +} diff --git a/src/common/sphere_library/sstring.h b/src/common/sphere_library/sstring.h index 522ab1332..530279676 100644 --- a/src/common/sphere_library/sstring.h +++ b/src/common/sphere_library/sstring.h @@ -369,8 +369,7 @@ int Str_RegExMatch(lpctstr pPattern, lpctstr pText, tchar * lastError); tchar * Str_UnQuote(tchar * pStr) noexcept; ///@} - -//-- +//--- void CharToMultiByteNonNull(byte* Dest, const char* Src, int MBytes) noexcept; @@ -402,4 +401,19 @@ class UTF8MBSTR }; +//--- + +ssize_t fReadUntilDelimiter(char **buf, size_t *bufsiz, int delimiter, FILE *fp) noexcept; // equivalent to POSIX getline +ssize_t fReadUntilDelimiter_StaticBuf(char *buf, const size_t bufsiz, const int delimiter, FILE *fp) noexcept; +inline ssize_t fReadLine_StaticBuf(char *buf, const size_t bufsiz, FILE *fp) noexcept +{ + return fReadUntilDelimiter_StaticBuf(buf, bufsiz, '\n', fp); +} +ssize_t sGetDelimiter_StaticBuf(const int delimiter, const char *data, const size_t datasize) noexcept; +inline ssize_t sGetLine_StaticBuf(const char *data, const size_t datasize) noexcept +{ + return sGetDelimiter_StaticBuf('\n', data, datasize); +} + + #endif // _INC_SSTRING_H diff --git a/src/game/CContainer.cpp b/src/game/CContainer.cpp index 9265fdc20..46177bed0 100644 --- a/src/game/CContainer.cpp +++ b/src/game/CContainer.cpp @@ -661,7 +661,7 @@ bool CContainer::r_GetRefContainer( lpctstr &ptcKey, CScriptObj *&pRef ) { ptcKey += 2; SKIP_SEPARATORS(ptcKey); - pRef = ContentFind(g_Cfg.ResourceGetID_Advance(RES_ITEMDEF, ptcKey)); + pRef = ContentFind(g_Cfg.ResourceGetID_EatStr(RES_ITEMDEF, ptcKey)); SKIP_SEPARATORS(ptcKey); return true; } @@ -680,7 +680,7 @@ bool CContainer::r_GetRefContainer( lpctstr &ptcKey, CScriptObj *&pRef ) { ptcKey += 4; SKIP_SEPARATORS(ptcKey); - pRef = ContentFind(g_Cfg.ResourceGetID_Advance(RES_TYPEDEF, ptcKey)); + pRef = ContentFind(g_Cfg.ResourceGetID_EatStr(RES_TYPEDEF, ptcKey)); SKIP_SEPARATORS(ptcKey); return true; } diff --git a/src/game/clients/CClient.cpp b/src/game/clients/CClient.cpp index 6eb848b97..1e8b3551a 100644 --- a/src/game/clients/CClient.cpp +++ b/src/game/clients/CClient.cpp @@ -932,16 +932,17 @@ bool CClient::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from return true; } - CResourceID rid = g_Cfg.ResourceGetID( RES_QTY, ppszArgs[0]); + const CResourceID rid = g_Cfg.ResourceGetID(RES_QTY, ppszArgs[0], 0, false); + if (rid.IsEmpty()) + { + m_tmAdd.m_id = 0; + return true; + } + m_tmAdd.m_id = rid.GetResIndex(); - if (iQty > 1) - { - m_tmAdd.m_vcAmount = (word)atoi(ppszArgs[1]); - m_tmAdd.m_vcAmount = maximum(m_tmAdd.m_vcAmount, 1); - } - else - m_tmAdd.m_vcAmount = 1; - if ( (rid.GetResType() == RES_CHARDEF) || (rid.GetResType() == RES_SPAWN) ) + m_tmAdd.m_vcAmount = (iQty > 1) ? std::max((word)1, (word)atoi(ppszArgs[1])) : 1; + + if ( (rid.GetResType() == RES_CHARDEF) || (rid.GetResType() == RES_SPAWN) ) { m_Targ_Prv_UID.InitUID(); return addTargetChars(CLIMODE_TARG_ADDCHAR, (CREID_TYPE)m_tmAdd.m_id, false); @@ -959,6 +960,72 @@ bool CClient::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from Menu_Setup( g_Cfg.ResourceGetIDType( RES_MENU, "MENU_ADDITEM")); } break; + case CV_ADDITEM: + if (!s.HasArgs()) + { + SysMessageDefault(DEFMSG_CMD_INVALID); + return true; + } + { + tchar *ppszArgs[2]; + size_t iQty = Str_ParseCmds(s.GetArgStr(), ppszArgs, ARRAY_COUNT(ppszArgs)); + + if (!IsValidGameObjDef(ppszArgs[0])) + { + SysMessageDefault(DEFMSG_CMD_INVALID); + return true; + } + + CResourceID rid = g_Cfg.ResourceGetID(RES_QTY, ppszArgs[0], 0, true); + if (rid.IsEmpty()) + rid = g_Cfg.ResourceGetID(RES_ITEMDEF, ppszArgs[0], 0, true); + + const RES_TYPE restype = rid.GetResType(); + if ((restype != RES_ITEMDEF) /* multi are still considered as items */ && + (restype != RES_CHAMPION) && (restype != RES_SPAWN) /* item spawns? */) + { + SysMessageDefault(DEFMSG_CMD_INVALID); + return true; + } + + m_tmAdd.m_id = rid.GetResIndex(); + m_tmAdd.m_vcAmount = (iQty > 1) ? std::max((word)1, (word)atoi(ppszArgs[1])) : 1; + + return addTargetItems(CLIMODE_TARG_ADDITEM, (ITEMID_TYPE)m_tmAdd.m_id); + } + case CV_ADDCHAR: + if (!s.HasArgs()) + { + SysMessageDefault(DEFMSG_CMD_INVALID); + return true; + } + { + tchar *ppszArgs[2]; + size_t iQty = Str_ParseCmds(s.GetArgStr(), ppszArgs, ARRAY_COUNT(ppszArgs)); + + if (!IsValidGameObjDef(ppszArgs[0])) + { + SysMessageDefault(DEFMSG_CMD_INVALID); + return true; + } + + CResourceID rid = g_Cfg.ResourceGetID(RES_QTY, ppszArgs[0], 0, true); + if (rid.IsEmpty()) + rid = g_Cfg.ResourceGetID(RES_CHARDEF, ppszArgs[0], 0, true); + + const RES_TYPE restype = rid.GetResType(); + if ((restype != RES_CHARDEF) && (restype != RES_SPAWN)) + { + SysMessageDefault(DEFMSG_CMD_INVALID); + return true; + } + + m_tmAdd.m_id = rid.GetResIndex(); + m_tmAdd.m_vcAmount = (iQty > 1) ? std::max((word)1, (word)atoi(ppszArgs[1])) : 1; + + m_Targ_Prv_UID.InitUID(); + return addTargetChars(CLIMODE_TARG_ADDCHAR, (CREID_TYPE)m_tmAdd.m_id, false); + } case CV_ADDBUFF: { tchar * ppArgs[11]; diff --git a/src/network/CNetworkManager.cpp b/src/network/CNetworkManager.cpp index b7c81319a..b1e6f70a3 100644 --- a/src/network/CNetworkManager.cpp +++ b/src/network/CNetworkManager.cpp @@ -178,7 +178,7 @@ void CNetworkManager::acceptNewConnection(void) return; } - if (ip.m_connectionAttempts >= g_Cfg._iMaxConnectRequestsPerIP) + if ((g_Cfg._iMaxConnectRequestsPerIP > 0) && (ip.m_connectionAttempts >= (size_t)g_Cfg._iMaxConnectRequestsPerIP)) { // Call this special scripted function. CScriptTriggerArgs fargs_ex(client_addr.GetAddrStr()); diff --git a/src/network/CNetworkThread.cpp b/src/network/CNetworkThread.cpp index 54201de7b..59ae9909c 100644 --- a/src/network/CNetworkThread.cpp +++ b/src/network/CNetworkThread.cpp @@ -11,7 +11,7 @@ static const char* GenerateNetworkThreadName(size_t id) { - char* name = new char[IThread::m_nameMaxLength]; + char* name = Str_GetTemp(); snprintf(name, IThread::m_nameMaxLength, "T_Net #%" PRIuSIZE_T, id); return name; } @@ -25,8 +25,6 @@ CNetworkThread::CNetworkThread(CNetworkManager* manager, size_t id) CNetworkThread::~CNetworkThread(void) { - // thread name was allocated by GenerateNetworkThreadName, so should be delete[]'d - delete[] getName(); } void CNetworkThread::assignNetworkState(CNetState* state) diff --git a/src/network/CNetworkThread.h b/src/network/CNetworkThread.h index 05f8c0155..69ee3a58c 100644 --- a/src/network/CNetworkThread.h +++ b/src/network/CNetworkThread.h @@ -40,9 +40,8 @@ class CNetworkThread : public AbstractSphereThread CNetworkThread(CNetworkManager* manager, size_t id); virtual ~CNetworkThread(void); -private: - CNetworkThread(const CNetworkThread& copy); - CNetworkThread& operator=(const CNetworkThread& other); + CNetworkThread(const CNetworkThread& copy) = delete; + CNetworkThread& operator=(const CNetworkThread& other) = delete; public: void assignNetworkState(CNetState* state); // assign a network state to this thread @@ -118,4 +117,4 @@ class NetworkThreadStateIterator }; -#endif // _INC_CNETWORKTHREAD_H \ No newline at end of file +#endif // _INC_CNETWORKTHREAD_H diff --git a/src/sphere/containers.h b/src/sphere/containers.h index 10bc01c16..5029a5e22 100644 --- a/src/sphere/containers.h +++ b/src/sphere/containers.h @@ -26,22 +26,21 @@ class ThreadSafeQueue iterator m_tail; public: - ThreadSafeQueue() + ThreadSafeQueue() noexcept { - m_list.push_back( T() ); // at least one element must be in the queue + m_list.emplace_back(T{}); // at least one element must be in the queue m_head = m_list.begin(); m_tail = m_list.end(); } -private: - ThreadSafeQueue( const ThreadSafeQueue& copy ); - ThreadSafeQueue& operator=( const ThreadSafeQueue& other ); + ThreadSafeQueue( const ThreadSafeQueue& copy ) = delete; + ThreadSafeQueue& operator=( const ThreadSafeQueue& other ) = delete; public: // Append an element to the end of the queue (writer) - void push( const T& value ) + void push( const T& value ) noexcept { - m_list.push_back( value ); + m_list.emplace_back( value ); m_tail = m_list.end(); clean(); } @@ -59,7 +58,7 @@ class ThreadSafeQueue return 0; size_t toSkip = 1; - for ( const_iterator it = m_list.begin(), end = m_list.end(); (it != m_head) && (it != end); ++it ) + for ( const_iterator it = m_list.cbegin(), end = m_list.cend(); (it != m_head) && (it != end); ++it ) ++toSkip; return m_list.size() - toSkip; diff --git a/src/sphere/threads.cpp b/src/sphere/threads.cpp index f2db6b046..516bd1b34 100644 --- a/src/sphere/threads.cpp +++ b/src/sphere/threads.cpp @@ -104,6 +104,10 @@ void IThread::setThreadName(const char* name) #elif defined(__NetBSD__) pthread_setname_np(getCurrentThreadId(), "%s", name_trimmed); #endif + + auto athr = static_cast(ThreadHolder::get().current()); + ASSERT(athr); + athr->overwriteInternalThreadName(name_trimmed); } @@ -254,7 +258,7 @@ AbstractThread::AbstractThread(const char *name, IThread::Priority priority) ++AbstractThread::m_threadsAvailable; } m_id = 0; - m_name = name; + Str_CopyLimitNull(m_name, name, sizeof(m_name)); m_handle = 0; m_hangCheck = 0; _thread_selfTerminateAfterThisTick = true; @@ -277,6 +281,13 @@ AbstractThread::~AbstractThread() } } +void AbstractThread::overwriteInternalThreadName(const char* name) noexcept +{ + // Use it only if you know what you are doing! + // This doesn't actually do the change of the thread name! + Str_CopyLimitNull(m_name, name, sizeof(m_name)); +} + void AbstractThread::start() { #ifdef _WIN32 diff --git a/src/sphere/threads.h b/src/sphere/threads.h index 1a92aaaf4..dd4278a1e 100644 --- a/src/sphere/threads.h +++ b/src/sphere/threads.h @@ -197,7 +197,7 @@ class IThread return isSameThreadId(getCurrentThreadId(), otherThreadId); } - static const uint m_nameMaxLength = 16; // Unix support a max 16 bytes thread name. + static constexpr uint m_nameMaxLength = 16; // Unix support a max 16 bytes thread name. static void setThreadName(const char* name); size_t m_threadHolderId; @@ -262,7 +262,7 @@ class AbstractThread : public IThread private: threadid_t m_id; - const char *m_name; + char m_name[30]; static int m_threadsAvailable; spherethread_t m_handle; uint m_hangCheck; @@ -283,11 +283,8 @@ class AbstractThread : public IThread public: threadid_t getId() const noexcept { return m_id; } const char *getName() const noexcept { return m_name; } - void overwriteInternalThreadName(const char* name) noexcept { - // Use it only if you know what you are doing! - // This doesn't actually do the change of the thread name! - m_name = name; - } + + void overwriteInternalThreadName(const char* name) noexcept; bool isActive() const; bool isCurrentThread() const; diff --git a/src/tables/CClient_functions.tbl b/src/tables/CClient_functions.tbl index ab55c4224..3ec981b58 100644 --- a/src/tables/CClient_functions.tbl +++ b/src/tables/CClient_functions.tbl @@ -6,7 +6,9 @@ ADD(ADD, "ADD") ADD(ADDBUFF, "ADDBUFF") -ADD(ADDCONTEXTENTRY, "ADDCONTEXTENTRY") +ADD(ADDCHAR, "ADDCHAR") +ADD(ADDCONTEXTENTRY,"ADDCONTEXTENTRY") +ADD(ADDITEM, "ADDITEM") ADD(ARROWQUEST, "ARROWQUEST") ADD(BADSPAWN, "BADSPAWN") ADD(BANKSELF, "BANKSELF")