diff --git a/Client/mods/deathmatch/StdInc.h b/Client/mods/deathmatch/StdInc.h index 465478454f5..9cbbb90f8bb 100644 --- a/Client/mods/deathmatch/StdInc.h +++ b/Client/mods/deathmatch/StdInc.h @@ -154,6 +154,7 @@ #include "CLatentTransferManager.h" #include "CDebugHookManager.h" #include "lua/CLuaShared.h" +#include "CStringName.h" // Deathmatch includes #include "ClientCommands.h" diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp index a9f8c9a46c1..ba5c1054d63 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp @@ -1767,13 +1767,13 @@ int CLuaElementDefs::SetElementData(lua_State* luaVM) { // bool setElementData ( element theElement, string key, var value, [bool synchronize = true] ) CClientEntity* pEntity; - SString strKey; + CStringName key; CLuaArgument value; bool bSynchronize; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pEntity); - argStream.ReadString(strKey); + argStream.ReadStringName(key); argStream.ReadLuaArgument(value); argStream.ReadBool(bSynchronize, true); @@ -1782,15 +1782,16 @@ int CLuaElementDefs::SetElementData(lua_State* luaVM) CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); if (pLuaMain) { - if (strKey.length() > MAX_CUSTOMDATA_NAME_LENGTH) + if (key->length() > MAX_CUSTOMDATA_NAME_LENGTH) { // Warn and truncate if key is too long m_pScriptDebugging->LogCustom(luaVM, SString("Truncated argument @ '%s' [%s]", lua_tostring(luaVM, lua_upvalueindex(1)), *SString("string length reduced to %d characters at argument 2", MAX_CUSTOMDATA_NAME_LENGTH))); - strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); + + key = key->substr(0, MAX_CUSTOMDATA_NAME_LENGTH); } - if (CStaticFunctionDefinitions::SetElementData(*pEntity, strKey, value, bSynchronize)) + if (CStaticFunctionDefinitions::SetElementData(*pEntity, key.ToCString(), value, bSynchronize)) { lua_pushboolean(luaVM, true); return 1; @@ -1809,26 +1810,27 @@ int CLuaElementDefs::RemoveElementData(lua_State* luaVM) { // bool removeElementData ( element theElement, string key ) CClientEntity* pEntity; - SString strKey; + CStringName key; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pEntity); - argStream.ReadString(strKey); + argStream.ReadStringName(key); if (!argStream.HasErrors()) { CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); if (pLuaMain) { - if (strKey.length() > MAX_CUSTOMDATA_NAME_LENGTH) + if (key->length() > MAX_CUSTOMDATA_NAME_LENGTH) { // Warn and truncate if key is too long m_pScriptDebugging->LogCustom(luaVM, SString("Truncated argument @ '%s' [%s]", lua_tostring(luaVM, lua_upvalueindex(1)), *SString("string length reduced to %d characters at argument 2", MAX_CUSTOMDATA_NAME_LENGTH))); - strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); + + key = key->substr(0, MAX_CUSTOMDATA_NAME_LENGTH); } - if (CStaticFunctionDefinitions::RemoveElementData(*pEntity, strKey)) + if (CStaticFunctionDefinitions::RemoveElementData(*pEntity, key.ToCString())) { lua_pushboolean(luaVM, true); return 1; diff --git a/Server/mods/deathmatch/StdInc.h b/Server/mods/deathmatch/StdInc.h index fb397aad5b3..e326d53aa62 100644 --- a/Server/mods/deathmatch/StdInc.h +++ b/Server/mods/deathmatch/StdInc.h @@ -46,6 +46,7 @@ #include #include #include +#include "CStringName.h" #include #include #include diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp index 81fec448825..c13299374cc 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp +++ b/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp @@ -1540,14 +1540,14 @@ int CLuaElementDefs::setElementData(lua_State* luaVM) { // bool setElementData ( element theElement, string key, var value, [var syncMode = true] ) CElement* pElement; - SString strKey; + CStringName key; CLuaArgument value; ESyncType syncType = ESyncType::BROADCAST; std::optional clientTrust{}; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pElement); - argStream.ReadString(strKey); + argStream.ReadStringName(key); argStream.ReadLuaArgument(value); if (argStream.NextIsBool()) @@ -1571,15 +1571,15 @@ int CLuaElementDefs::setElementData(lua_State* luaVM) { LogWarningIfPlayerHasNotJoinedYet(luaVM, pElement); - if (strKey.length() > MAX_CUSTOMDATA_NAME_LENGTH) + if (key->length() > MAX_CUSTOMDATA_NAME_LENGTH) { // Warn and truncate if key is too long m_pScriptDebugging->LogCustom(luaVM, SString("Truncated argument @ '%s' [%s]", lua_tostring(luaVM, lua_upvalueindex(1)), *SString("string length reduced to %d characters at argument 2", MAX_CUSTOMDATA_NAME_LENGTH))); - strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); + key = key->substr(0, MAX_CUSTOMDATA_NAME_LENGTH); } - if (CStaticFunctionDefinitions::SetElementData(pElement, strKey, value, syncType, clientTrust)) + if (CStaticFunctionDefinitions::SetElementData(pElement, key.ToCString(), value, syncType, clientTrust)) { lua_pushboolean(luaVM, true); return 1; @@ -1596,25 +1596,25 @@ int CLuaElementDefs::removeElementData(lua_State* luaVM) { // bool removeElementData ( element theElement, string key ) CElement* pElement; - SString strKey; + CStringName key; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pElement); - argStream.ReadString(strKey); + argStream.ReadStringName(key); if (!argStream.HasErrors()) { LogWarningIfPlayerHasNotJoinedYet(luaVM, pElement); - if (strKey.length() > MAX_CUSTOMDATA_NAME_LENGTH) + if (key->length() > MAX_CUSTOMDATA_NAME_LENGTH) { // Warn and truncate if key is too long m_pScriptDebugging->LogCustom(luaVM, SString("Truncated argument @ '%s' [%s]", lua_tostring(luaVM, lua_upvalueindex(1)), *SString("string length reduced to %d characters at argument 2", MAX_CUSTOMDATA_NAME_LENGTH))); - strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); + key = key->substr(0, MAX_CUSTOMDATA_NAME_LENGTH); } - if (CStaticFunctionDefinitions::RemoveElementData(pElement, strKey)) + if (CStaticFunctionDefinitions::RemoveElementData(pElement, key.ToCString())) { lua_pushboolean(luaVM, true); return 1; @@ -1631,19 +1631,19 @@ int CLuaElementDefs::addElementDataSubscriber(lua_State* luaVM) { // bool addElementDataSubscriber ( element theElement, string key, player thePlayer ) CElement* pElement; - SString strKey; + CStringName key; CPlayer* pPlayer; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pElement); - argStream.ReadString(strKey); + argStream.ReadStringName(key); argStream.ReadUserData(pPlayer); if (!argStream.HasErrors()) { LogWarningIfPlayerHasNotJoinedYet(luaVM, pElement); - if (CStaticFunctionDefinitions::AddElementDataSubscriber(pElement, strKey, pPlayer)) + if (CStaticFunctionDefinitions::AddElementDataSubscriber(pElement, key.ToCString(), pPlayer)) { lua_pushboolean(luaVM, true); return 1; @@ -1660,19 +1660,19 @@ int CLuaElementDefs::removeElementDataSubscriber(lua_State* luaVM) { // bool removeElementDataSubscriber ( element theElement, string key, player thePlayer ) CElement* pElement; - SString strKey; + CStringName key; CPlayer* pPlayer; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pElement); - argStream.ReadString(strKey); + argStream.ReadStringName(key); argStream.ReadUserData(pPlayer); if (!argStream.HasErrors()) { LogWarningIfPlayerHasNotJoinedYet(luaVM, pElement); - if (CStaticFunctionDefinitions::RemoveElementDataSubscriber(pElement, strKey, pPlayer)) + if (CStaticFunctionDefinitions::RemoveElementDataSubscriber(pElement, key.ToCString(), pPlayer)) { lua_pushboolean(luaVM, true); return 1; @@ -1689,19 +1689,19 @@ int CLuaElementDefs::hasElementDataSubscriber(lua_State* luaVM) { // bool hasElementDataSubscriber ( element theElement, string key, player thePlayer ) CElement* pElement; - SString strKey; + CStringName key; CPlayer* pPlayer; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pElement); - argStream.ReadString(strKey); + argStream.ReadStringName(key); argStream.ReadUserData(pPlayer); if (!argStream.HasErrors()) { LogWarningIfPlayerHasNotJoinedYet(luaVM, pElement); - bool bResult = CStaticFunctionDefinitions::HasElementDataSubscriber(pElement, strKey, pPlayer); + bool bResult = CStaticFunctionDefinitions::HasElementDataSubscriber(pElement, key.ToCString(), pPlayer); lua_pushboolean(luaVM, bResult); return 1; } diff --git a/Shared/mods/deathmatch/logic/CStringName.cpp b/Shared/mods/deathmatch/logic/CStringName.cpp new file mode 100644 index 00000000000..f3d6d18df81 --- /dev/null +++ b/Shared/mods/deathmatch/logic/CStringName.cpp @@ -0,0 +1,198 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: CStringName.cpp + * + * Multi Theft Auto is available from https://multitheftauto.com/ + * + *****************************************************************************/ +#include "StdInc.h" +#include "CStringName.h" + +#include +#include + +extern "C" +{ + #include "lua/src/lstring.h" +} + +static StringNameHash MakeStringNameHash(const std::string_view& str) +{ + return luaS_hash(str.data(), str.length()); +} + +/* + CStringNameStorage +*/ +class CStringNameStorage +{ +public: + CStringNameData* Get(const std::string_view& str) + { + if (str.empty()) + { + ZERO_NAME_DATA.AddRef(); + return &ZERO_NAME_DATA; + } + + const StringNameHash hash = MakeStringNameHash(str); + const uint32_t idx = hash & CStringName::STRING_TABLE_MASK; + CIntrusiveDoubleLinkedList& list = m_table[idx]; + + CStringNameData* data = list.First(); + while (data) + { + if (data->m_hash == hash && data->m_name == str) + break; + + data = list.Next(data); + } + + if (!data || data->m_refs == 0) + { + data = new CStringNameData(str, hash); + list.InsertFront(data); + } + + data->AddRef(); + + return data; + } + + void Release(CStringNameData* data) + { + const uint32_t idx = data->m_hash & CStringName::STRING_TABLE_MASK; + + if (data->m_refs == 0) + m_table[idx].Erase(data); + } + + CStringNameData* Find(const std::string_view& str, StringNameHash hash) + { + const uint32_t idx = hash & CStringName::STRING_TABLE_MASK; + CIntrusiveDoubleLinkedList& list = m_table[idx]; + + for (CStringNameData& data : list) + { + if (data.m_hash == hash && data.m_name == str) + return &data; + } + + return nullptr; + } + + static CStringNameStorage& Instance() + { + static CStringNameStorage storage{}; + return storage; + } + + static CStringNameData ZERO_NAME_DATA; + +private: + std::array, CStringName::STRING_TABLE_LEN> m_table; +}; + +CStringNameData CStringNameStorage::ZERO_NAME_DATA{ {}, 0u, 1 }; + +/* + CStringNameData +*/ +void CStringNameData::AddRef() +{ + ++m_refs; +} + +void CStringNameData::RemoveRef() +{ + if (m_hash == 0u) + return; + + if (--m_refs == 0) + CStringNameStorage::Instance().Release(this); +} + +/* + CStringName +*/ +const CStringName CStringName::ZERO{}; + +CStringName::CStringName() : + m_data(CStringNameStorage::Instance().Get({})) +{ +} + +CStringName::CStringName(const char* str) : + m_data(CStringNameStorage::Instance().Get(str)) +{ +} + +CStringName::CStringName(const std::string& str) : + m_data(CStringNameStorage::Instance().Get(str)) +{ +} + +CStringName::CStringName(const std::string_view& str) : + m_data(CStringNameStorage::Instance().Get(str)) +{ +} + +CStringName::CStringName(const CStringName& name) : + m_data(name.m_data) +{ + if (m_data) + m_data->AddRef(); +} + +CStringName::CStringName(CStringNameData* data) : + m_data(data) +{ + if (m_data) + m_data->AddRef(); +} + +CStringName& CStringName::operator=(const CStringName& name) +{ + if (m_data == name.m_data) + return *this; + + m_data->RemoveRef(); + m_data = name.m_data; + m_data->AddRef(); + + return *this; +} + +CStringName& CStringName::operator=(const std::string& str) +{ + *this = CStringName(str); + return *this; +} + +CStringName& CStringName::operator=(const std::string_view& str) +{ + *this = CStringName(str); + return *this; +} + +CStringName::~CStringName() +{ + if (m_data) + m_data->RemoveRef(); +} + +void CStringName::Clear() +{ + *this = CStringName::ZERO; +} + +CStringName CStringName::FromStringAndHash(const std::string_view& str, StringNameHash hash) +{ + if (CStringNameData* data = CStringNameStorage::Instance().Find(str, hash)) + return CStringName{data}; + + // Create a new name + return CStringName{str}; +} diff --git a/Shared/mods/deathmatch/logic/CStringName.h b/Shared/mods/deathmatch/logic/CStringName.h new file mode 100644 index 00000000000..cd3fa79f022 --- /dev/null +++ b/Shared/mods/deathmatch/logic/CStringName.h @@ -0,0 +1,135 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: CCStringName.h + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ +#pragma once + +#include +#include + +#include "CIntrusiveDoubleLinkedList.h" + +// unsigned here is the intentional choice because it is a type of hash that Lua basically uses +using StringNameHash = unsigned; + +class CStringNameData : public CIntrusiveDoubleLinkedListNode +{ +public: + CStringNameData() = default; + + CStringNameData(const std::string_view& name, StringNameHash hash, size_t refs = 0) : + m_name(name), + m_hash(hash), + m_refs(refs) + { + } + + void AddRef(); + + void RemoveRef(); + + std::string m_name; + + StringNameHash m_hash{}; + + std::size_t m_refs{0u}; +}; + +class CStringName +{ + friend class CStringNameStorage; + + enum { + STRING_TABLE_BITS = 16, + STRING_TABLE_LEN = 1 << STRING_TABLE_BITS, + STRING_TABLE_MASK = STRING_TABLE_LEN - 1 + }; + +public: + // Default constructor + CStringName(); + + // Construct from a C string. + CStringName(const char* str); + + // Construct from a string. + explicit CStringName(const std::string& str); + + // Construct from a string. + explicit CStringName(const std::string_view& str); + + // Copy-construct from another string name. + CStringName(const CStringName& name); + + // Copy-assignment from another string name. + CStringName& operator =(const CStringName& name); + + // Copy-assignment from string. + CStringName& operator =(const std::string& str); + + // Copy-assignment from string view. + CStringName& operator =(const std::string_view& str); + + // Destruct + ~CStringName(); + + // Test for equality with another string name. + bool operator ==(const CStringName& rhs) const { return m_data == rhs.m_data; } + + // Test for inequality with another string name. + bool operator !=(const CStringName& rhs) const { return m_data != rhs.m_data; } + + // Test if less than another string name. + bool operator <(const CStringName& rhs) const { return m_data->m_hash < rhs.m_data->m_hash; } + + // Test if greater than another string name. + bool operator >(const CStringName& rhs) const { return m_data->m_hash > rhs.m_data->m_hash; } + + const std::string* operator ->() const { return &m_data->m_name; } + + const std::string& operator *() const { return m_data->m_name; } + + // Return true if nonzero string name value. + explicit operator bool() const { return m_data != nullptr; } + + // Return as string. + const std::string& ToString() const { return m_data->m_name; } + + // Return as C-string + const char* ToCString() const { return m_data != nullptr ? m_data->m_name.c_str() : nullptr; } + + StringNameHash Hash() const noexcept { return m_data->m_hash; } + + bool Empty() const { return m_data->m_hash == 0u; } + + void Clear(); + + // Return name data + const CStringNameData* GetNameData() const { return m_data; } + + // Zero hash. + static const CStringName ZERO; + + static CStringName FromStringAndHash(const std::string_view& str, StringNameHash hash); + +private: + explicit CStringName(CStringNameData* data); + +private: + // String name data + CStringNameData* m_data{}; +}; + +template <> +struct std::hash +{ + std::size_t operator()(const CStringName& k) const + { + return static_cast(k.Hash()); + } +}; diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaElementDefsShared.cpp b/Shared/mods/deathmatch/logic/luadefs/CLuaElementDefsShared.cpp index 7d331163014..32d221f66d9 100644 --- a/Shared/mods/deathmatch/logic/luadefs/CLuaElementDefsShared.cpp +++ b/Shared/mods/deathmatch/logic/luadefs/CLuaElementDefsShared.cpp @@ -19,12 +19,12 @@ int CLuaElementDefs::GetElementData(lua_State* luaVM) // var getElementData ( element theElement, string key [, inherit = true] ) CElement* pElement; - SString strKey; + CStringName key; bool bInherit; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pElement); - argStream.ReadString(strKey); + argStream.ReadStringName(key); argStream.ReadBool(bInherit, true); if (!argStream.HasErrors()) @@ -32,18 +32,19 @@ int CLuaElementDefs::GetElementData(lua_State* luaVM) CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); if (pLuaMain) { - if (strKey.length() > MAX_CUSTOMDATA_NAME_LENGTH) + if (key->length() > MAX_CUSTOMDATA_NAME_LENGTH) { // Warn and truncate if key is too long m_pScriptDebugging->LogCustom(luaVM, SString("Truncated argument @ '%s' [%s]", lua_tostring(luaVM, lua_upvalueindex(1)), *SString("string length reduced to %d characters at argument 2", MAX_CUSTOMDATA_NAME_LENGTH))); - strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); + + key = key->substr(0, MAX_CUSTOMDATA_NAME_LENGTH); } #ifdef MTA_CLIENT - CLuaArgument* pVariable = pElement->GetCustomData(strKey, bInherit); + CLuaArgument* pVariable = pElement->GetCustomData(key.ToCString(), bInherit); #else - CLuaArgument* pVariable = CStaticFunctionDefinitions::GetElementData(pElement, strKey, bInherit); + CLuaArgument* pVariable = CStaticFunctionDefinitions::GetElementData(pElement, key.ToCString(), bInherit); #endif if (pVariable) { @@ -72,12 +73,12 @@ int CLuaElementDefs::HasElementData(lua_State* luaVM) // bool hasElementData ( element theElement, string key [, bool inherit = true ] ) CElement* pElement; - SString strKey; + CStringName key; bool bInherit; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pElement); - argStream.ReadString(strKey); + argStream.ReadStringName(key); argStream.ReadBool(bInherit, true); if (argStream.HasErrors()) @@ -85,16 +86,16 @@ int CLuaElementDefs::HasElementData(lua_State* luaVM) return luaL_error(luaVM, argStream.GetFullErrorMessage()); } - if (strKey.length() > MAX_CUSTOMDATA_NAME_LENGTH) + if (key->length() > MAX_CUSTOMDATA_NAME_LENGTH) { // Warn and truncate if key is too long m_pScriptDebugging->LogCustom(luaVM, SString("Truncated argument @ '%s' [%s]", lua_tostring(luaVM, lua_upvalueindex(1)), *SString("string length reduced to %d characters at argument 2", MAX_CUSTOMDATA_NAME_LENGTH))); - strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); + key = key->substr(0, MAX_CUSTOMDATA_NAME_LENGTH); } // Check if data exists with the given key - bool exists = pElement->GetCustomData(strKey, bInherit) != nullptr; + bool exists = pElement->GetCustomData(key.ToCString(), bInherit) != nullptr; lua_pushboolean(luaVM, exists); return 1; } diff --git a/Shared/sdk/CIntrusiveDoubleLinkedList.h b/Shared/sdk/CIntrusiveDoubleLinkedList.h new file mode 100644 index 00000000000..bfda4c9f88e --- /dev/null +++ b/Shared/sdk/CIntrusiveDoubleLinkedList.h @@ -0,0 +1,283 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/CIntrusiveDoubleLinkedList.h + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include + +// Double-linked list node base class. +struct CIntrusiveDoubleLinkedListNode +{ + // Pointer to previous node. + CIntrusiveDoubleLinkedListNode* m_previous{}; + + // Pointer to next node. + CIntrusiveDoubleLinkedListNode* m_next{}; +}; + +// Double-linked list template class. Elements must inherit from CIntrusiveDoubleLinkedListNode. +template class CIntrusiveDoubleLinkedList +{ +public: + struct SIterator + { + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using reference = T&; + + SIterator(T* ptr) : + m_ptr(ptr) + { + } + + // Point to the node value. + reference operator*() const { return *m_ptr; } + + // Dereference the node value. + pointer operator->() const { return m_ptr; } + + // Prefix increment + SIterator& operator++() + { + assert(m_ptr); + m_ptr = static_cast(m_ptr->m_next); + return *this; + } + + // Prefix decrement + SIterator& operator--() + { + assert(m_ptr); + m_ptr = static_cast(m_ptr->m_previous); + return *this; + } + + // Postfix increment + SIterator operator++(int) + { + SIterator tmp = *this; + ++(*this); + return tmp; + } + + // Postfix decrement + SIterator operator--(int) + { + SIterator tmp = *this; + --(*this); + return tmp; + } + + // Test for equality with another iterator. + friend bool operator== (const SIterator& a, const SIterator& b) { return a.m_ptr == b.m_ptr; }; + + // Test for inequality with another iterator. + friend bool operator!= (const SIterator& a, const SIterator& b) { return a.m_ptr != b.m_ptr; }; + + private: + T* m_ptr; + }; + + struct SConstIterator + { + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using reference = T&; + + SConstIterator(T* ptr) : + m_ptr(ptr) + { + } + + // Point to the node value. + const reference operator*() const { return *m_ptr; } + + // Dereference the node value. + const pointer operator->() const { return m_ptr; } + + // Prefix increment + SConstIterator& operator++() + { + assert(m_ptr); + m_ptr = static_cast(m_ptr->m_next); + return *this; + } + + // Prefix decrement + SConstIterator& operator--() + { + assert(m_ptr); + m_ptr = static_cast(m_ptr->m_previous); + return *this; + } + + // Postfix increment + SConstIterator operator++(int) + { + SConstIterator tmp = *this; + ++(*this); + return tmp; + } + + // Postfix decrement + SConstIterator operator--(int) + { + SConstIterator tmp = *this; + --(*this); + return tmp; + } + + // Test for equality with another iterator. + friend bool operator== (const SConstIterator& a, const SConstIterator& b) { return a.m_ptr == b.m_ptr; }; + + // Test for inequality with another iterator. + friend bool operator!= (const SConstIterator& a, const SConstIterator& b) { return a.m_ptr != b.m_ptr; }; + + private: + T* m_ptr; + }; + + // Construct empty. + CIntrusiveDoubleLinkedList() : + m_head(nullptr) + { + } + + // Non-copyable. + CIntrusiveDoubleLinkedList(const CIntrusiveDoubleLinkedList& list) = delete; + + // Aggregate initialization constructor. + CIntrusiveDoubleLinkedList(const std::initializer_list& list) : CIntrusiveDoubleLinkedList() + { + for (auto it = list.begin(); it != list.end(); it++) + Insert(*it); + } + + // Non-assignable. + CIntrusiveDoubleLinkedList& operator =(const CIntrusiveDoubleLinkedList& list) = delete; + + // Destruct. + ~CIntrusiveDoubleLinkedList() + { + Clear(); + } + + // Remove all elements. + void Clear() + { + T* element = m_head; + while (element) + { + T* next = Next(element); + delete element; + element = next; + } + m_head = nullptr; + } + + // Insert an element at the beginning. + void InsertFront(T* element) + { + if (element) + { + element->m_next = m_head; + + if (m_head) + m_head->m_previous = element; + + m_head = element; + } + } + + // Insert an element at the end. + void Insert(T* element) + { + if (m_head) + { + T* tail = Last(); + element->m_previous = tail; + tail->m_next = element; + } + else + m_head = element; + } + + // Erase an element. Return true if successful. + void Erase(T* element) + { + Detach(element); + delete element; + } + + void Detach(T* element) + { + assert(element); + + T* previous = Previous(element); + T* next = Next(element); + + if (previous) + previous->m_next = next; + + if (next) + next->m_previous = previous; + + if (element == m_head) + m_head = next; + + // Invalidate element's references + element->m_previous = nullptr; + element->m_next = nullptr; + } + + // Return first element, or null if empty. + T* First() const { return m_head; } + + // Return last element, or null if empty. + T* Last() const + { + T* element = m_head; + if (element) + { + while (element->m_next) + element = Next(element); + } + return element; + } + + // Return previous element, or null if no more elements. + T* Previous(T* element) const { return element ? static_cast(element->m_previous) : nullptr; } + + // Return next element, or null if no more elements. + T* Next(T* element) const { return element ? static_cast(element->m_next) : nullptr; } + + // Return whether is empty. + bool Empty() const { return m_head == nullptr; } + + // Return iterator to the first element. + SIterator begin() { return SIterator(m_head); } + + // Return iterator to the end. + SIterator end() { return SIterator(nullptr); } + + // Return iterator to the first element. + SConstIterator begin() const { return SConstIterator(m_head); } + + // Return iterator to the end. + SConstIterator end() const { return SConstIterator(nullptr); } + +private: + // First element. + T* m_head; +}; diff --git a/Shared/sdk/CScriptArgReader.h b/Shared/sdk/CScriptArgReader.h index 85c46720e83..70af7f9872d 100644 --- a/Shared/sdk/CScriptArgReader.h +++ b/Shared/sdk/CScriptArgReader.h @@ -17,6 +17,7 @@ #include #include "CStringMap.h" #include "CScriptDebugging.h" +#include "CStringName.h" #ifndef MTA_CLIENT #include "CGame.h" @@ -610,6 +611,35 @@ class CScriptArgReader m_iIndex++; } + // + // Read next string name + // + void ReadStringName(CStringName& outValue) + { + const int iArgument = lua_type(m_luaVM, m_iIndex); + if (iArgument == LUA_TSTRING) + { + size_t length; + const char* str = lua_tolstring(m_luaVM, m_iIndex, &length); + unsigned hash = lua_tostringhash(m_luaVM, m_iIndex++); + + try + { + outValue = CStringName::FromStringAndHash(std::string_view(str, length), hash); + } + catch (const std::bad_alloc&) + { + SetCustomError("out of memory", "Memory allocation"); + } + + return; + } + + outValue.Clear(); + SetTypeError("string"); + m_iIndex++; + } + // // Force-reads next argument as string // diff --git a/vendor/lua/src/lapi.c b/vendor/lua/src/lapi.c index 82880f9b505..20f11a68519 100644 --- a/vendor/lua/src/lapi.c +++ b/vendor/lua/src/lapi.c @@ -383,6 +383,14 @@ LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { return svalue(o); } +LUA_API unsigned (lua_tostringhash) (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + if (!ttisstring(o)) + return NULL; + + return tsvalue(o)->hash; +} + LUA_API size_t lua_objlen (lua_State *L, int idx) { StkId o = index2adr(L, idx); diff --git a/vendor/lua/src/lstring.c b/vendor/lua/src/lstring.c index 49113151cc7..20948aa7d6c 100644 --- a/vendor/lua/src/lstring.c +++ b/vendor/lua/src/lstring.c @@ -74,11 +74,7 @@ static TString *newlstr (lua_State *L, const char *str, size_t l, TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { GCObject *o; - unsigned int h = cast(unsigned int, l); /* seed */ - size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ - size_t l1; - for (l1=l; l1>=step; l1-=step) /* compute hash */ - h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); + unsigned int h = luaS_hash(str, l); for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; o != NULL; o = o->gch.next) { @@ -109,3 +105,16 @@ Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { return u; } +LUALIB_API unsigned int luaS_hash(const char *str, size_t l) +{ + if (!str || str[0] == '\0' || l == 0) + return 0; + + unsigned int h = cast(unsigned int, l); /* seed */ + size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ + size_t l1; + for (l1=l; l1>=step; l1-=step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); + + return h; +} diff --git a/vendor/lua/src/lstring.h b/vendor/lua/src/lstring.h index 73a2ff8b380..435f6d603c1 100644 --- a/vendor/lua/src/lstring.h +++ b/vendor/lua/src/lstring.h @@ -27,5 +27,8 @@ LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); +// Custom function added by MTA +LUALIB_API unsigned int luaS_hash(const char *str, size_t l); + #endif diff --git a/vendor/lua/src/lua.h b/vendor/lua/src/lua.h index 9eb284eee22..15e31585d75 100644 --- a/vendor/lua/src/lua.h +++ b/vendor/lua/src/lua.h @@ -171,6 +171,9 @@ LUA_API void *(lua_touserdata) (lua_State *L, int idx); LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); LUA_API const void *(lua_topointer) (lua_State *L, int idx); +// Custom function added by MTA +LUA_API unsigned (lua_tostringhash) (lua_State *L, int idx); + /* ** push functions (C -> stack)