diff --git a/Client/core/CConsole.h b/Client/core/CConsole.h
index 5899fa645d..677e1133ff 100644
--- a/Client/core/CConsole.h
+++ b/Client/core/CConsole.h
@@ -41,7 +41,6 @@ class CConsole : public CConsoleInterface
bool IsInputActive();
void ActivateInput();
- void HandleTextAccepted(bool bHandled);
void GetCommandInfo(const std::string& strIn, std::string& strCmdOut, std::string& strCmdLineOut);
void ResetHistoryChanges();
diff --git a/Server/mods/deathmatch/local.conf b/Server/mods/deathmatch/local.conf
index 99b97bee07..039e155d81 100644
--- a/Server/mods/deathmatch/local.conf
+++ b/Server/mods/deathmatch/local.conf
@@ -268,6 +268,12 @@
Values: 0 - Off, 1 - Enabled. Default - 1 -->
1
+
+ 0
+
diff --git a/Server/mods/deathmatch/logic/CCustomData.cpp b/Server/mods/deathmatch/logic/CCustomData.cpp
index f18bbd37d1..545df5a0c3 100644
--- a/Server/mods/deathmatch/logic/CCustomData.cpp
+++ b/Server/mods/deathmatch/logic/CCustomData.cpp
@@ -21,7 +21,7 @@ void CCustomData::Copy(CCustomData* pCustomData)
}
}
-SCustomData* CCustomData::Get(const char* szName)
+SCustomData* CCustomData::Get(const char* szName) const
{
assert(szName);
@@ -100,6 +100,7 @@ void CCustomData::Set(const char* szName, const CLuaArgument& Variable, ESyncTyp
SCustomData newData;
newData.Variable = Variable;
newData.syncType = syncType;
+ newData.clientChangesMode = eCustomDataClientTrust::UNSET;
m_Data[szName] = newData;
UpdateSynced(szName, Variable, syncType);
}
@@ -120,6 +121,12 @@ bool CCustomData::Delete(const char* szName)
return false;
}
+void CCustomData::SetClientChangesMode(const char* szName, eCustomDataClientTrust mode)
+{
+ SCustomData& pData = m_Data[szName];
+ pData.clientChangesMode = mode;
+}
+
CXMLNode* CCustomData::OutputToXML(CXMLNode* pNode)
{
std::map::const_iterator iter = m_Data.begin();
diff --git a/Server/mods/deathmatch/logic/CCustomData.h b/Server/mods/deathmatch/logic/CCustomData.h
index bfb17ff529..887ef34dee 100644
--- a/Server/mods/deathmatch/logic/CCustomData.h
+++ b/Server/mods/deathmatch/logic/CCustomData.h
@@ -25,10 +25,18 @@ enum class ESyncType
SUBSCRIBE,
};
+enum class eCustomDataClientTrust : std::uint8_t
+{
+ UNSET,
+ ALLOW,
+ DENY,
+};
+
struct SCustomData
{
- CLuaArgument Variable;
- ESyncType syncType;
+ CLuaArgument Variable;
+ ESyncType syncType;
+ eCustomDataClientTrust clientChangesMode;
};
class CCustomData
@@ -36,12 +44,14 @@ class CCustomData
public:
void Copy(CCustomData* pCustomData);
- SCustomData* Get(const char* szName);
+ SCustomData* Get(const char* szName) const;
SCustomData* GetSynced(const char* szName);
void Set(const char* szName, const CLuaArgument& Variable, ESyncType syncType = ESyncType::BROADCAST);
bool Delete(const char* szName);
+ void SetClientChangesMode(const char* szName, eCustomDataClientTrust mode);
+
unsigned short CountOnlySynchronized();
CXMLNode* OutputToXML(CXMLNode* pNode);
diff --git a/Server/mods/deathmatch/logic/CElement.cpp b/Server/mods/deathmatch/logic/CElement.cpp
index 59086a2f14..79a051b545 100644
--- a/Server/mods/deathmatch/logic/CElement.cpp
+++ b/Server/mods/deathmatch/logic/CElement.cpp
@@ -508,7 +508,7 @@ void CElement::ReadCustomData(CEvents* pEvents, CXMLNode& Node)
}
}
-CLuaArgument* CElement::GetCustomData(const char* szName, bool bInheritData, ESyncType* pSyncType)
+CLuaArgument* CElement::GetCustomData(const char* szName, bool bInheritData, ESyncType* pSyncType, eCustomDataClientTrust* clientChangesMode)
{
assert(szName);
@@ -518,13 +518,17 @@ CLuaArgument* CElement::GetCustomData(const char* szName, bool bInheritData, ESy
{
if (pSyncType)
*pSyncType = pData->syncType;
+
+ if (clientChangesMode)
+ *clientChangesMode = pData->clientChangesMode;
+
return &pData->Variable;
}
// If none, try returning parent's custom data
if (bInheritData && m_pParent)
{
- return m_pParent->GetCustomData(szName, true, pSyncType);
+ return m_pParent->GetCustomData(szName, true, pSyncType, clientChangesMode);
}
// None available
diff --git a/Server/mods/deathmatch/logic/CElement.h b/Server/mods/deathmatch/logic/CElement.h
index e3a0fa4d07..802981cd5a 100644
--- a/Server/mods/deathmatch/logic/CElement.h
+++ b/Server/mods/deathmatch/logic/CElement.h
@@ -136,7 +136,7 @@ class CElement
void ReadCustomData(CEvents* pEvents, CXMLNode& Node);
CCustomData& GetCustomDataManager() { return m_CustomData; }
- CLuaArgument* GetCustomData(const char* szName, bool bInheritData, ESyncType* pSyncType = NULL);
+ CLuaArgument* GetCustomData(const char* szName, bool bInheritData, ESyncType* pSyncType = nullptr, eCustomDataClientTrust* clientChangesMode = nullptr);
CLuaArguments* GetAllCustomData(CLuaArguments* table);
bool GetCustomDataString(const char* szName, char* pOut, size_t sizeBuffer, bool bInheritData);
bool GetCustomDataInt(const char* szName, int& iOut, bool bInheritData);
diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp
index d4efea393f..9543e1e447 100644
--- a/Server/mods/deathmatch/logic/CGame.cpp
+++ b/Server/mods/deathmatch/logic/CGame.cpp
@@ -1607,6 +1607,7 @@ void CGame::AddBuiltInEvents()
m_Events.AddEvent("onPlayerTriggerEventThreshold", "", nullptr, false);
m_Events.AddEvent("onPlayerTeamChange", "oldTeam, newTeam", nullptr, false);
m_Events.AddEvent("onPlayerTriggerInvalidEvent", "eventName, isAdded, isRemote", nullptr, false);
+ m_Events.AddEvent("onPlayerChangesProtectedData", "element, key, value", nullptr, false);
// Ped events
m_Events.AddEvent("onPedVehicleEnter", "vehicle, seat, jacked", NULL, false);
@@ -2652,7 +2653,24 @@ void CGame::Packet_CustomData(CCustomDataPacket& Packet)
}
ESyncType lastSyncType = ESyncType::BROADCAST;
- pElement->GetCustomData(szName, false, &lastSyncType);
+ eCustomDataClientTrust clientChangesMode{};
+
+ pElement->GetCustomData(szName, false, &lastSyncType, &clientChangesMode);
+
+ const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !m_pMainConfig->IsElementDataWhitelisted()
+ : clientChangesMode == eCustomDataClientTrust::ALLOW;
+ if (!changesAllowed)
+ {
+ CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)", Packet.GetSourcePlayer()->GetNick(),
+ szName);
+
+ CLuaArguments arguments;
+ arguments.PushElement(pElement);
+ arguments.PushString(szName);
+ arguments.PushArgument(Value);
+ pSourcePlayer->CallEvent("onPlayerChangesProtectedData", arguments);
+ return;
+ }
if (lastSyncType != ESyncType::LOCAL)
{
diff --git a/Server/mods/deathmatch/logic/CMainConfig.cpp b/Server/mods/deathmatch/logic/CMainConfig.cpp
index 4e6b84ce3f..bece65e885 100644
--- a/Server/mods/deathmatch/logic/CMainConfig.cpp
+++ b/Server/mods/deathmatch/logic/CMainConfig.cpp
@@ -79,6 +79,7 @@ CMainConfig::CMainConfig(CConsole* pConsole) : CXMLConfig(NULL)
m_iBackupInterval = 3;
m_iBackupAmount = 5;
m_bSyncMapElementData = true;
+ m_elementDataWhitelisted = false;
}
bool CMainConfig::Load()
@@ -526,6 +527,8 @@ bool CMainConfig::Load()
g_TickRateSettings.iLightSync = Clamp(200, g_TickRateSettings.iLightSync, 4000);
}
+ GetBoolean(m_pRootNode, "elementdata_whitelisted", m_elementDataWhitelisted);
+
ApplyNetOptions();
return true;
diff --git a/Server/mods/deathmatch/logic/CMainConfig.h b/Server/mods/deathmatch/logic/CMainConfig.h
index 1ddb0c8645..9758ae2dbf 100644
--- a/Server/mods/deathmatch/logic/CMainConfig.h
+++ b/Server/mods/deathmatch/logic/CMainConfig.h
@@ -126,6 +126,7 @@ class CMainConfig : public CXMLConfig
const std::vector& GetOwnerEmailAddressList() const { return m_OwnerEmailAddressList; }
bool IsDatabaseCredentialsProtectionEnabled() const { return m_bDatabaseCredentialsProtectionEnabled != 0; }
bool IsFakeLagCommandEnabled() const { return m_bFakeLagCommandEnabled != 0; }
+ bool IsElementDataWhitelisted() const { return m_elementDataWhitelisted; }
bool IsCheckResourceClientFilesEnabled() const noexcept { return m_checkResourceClientFiles != 0; }
SString GetSetting(const SString& configSetting);
@@ -228,5 +229,6 @@ class CMainConfig : public CXMLConfig
int m_bFakeLagCommandEnabled;
int m_iPlayerTriggeredEventIntervalMs;
int m_iMaxPlayerTriggeredEventsPerInterval;
+ bool m_elementDataWhitelisted;
int m_checkResourceClientFiles;
};
diff --git a/Server/mods/deathmatch/logic/CResource.cpp b/Server/mods/deathmatch/logic/CResource.cpp
index 1c2ebe3ea8..d1b985fcfe 100644
--- a/Server/mods/deathmatch/logic/CResource.cpp
+++ b/Server/mods/deathmatch/logic/CResource.cpp
@@ -35,6 +35,8 @@
#include
#include
#include
+#include "version.h"
+#include "CStaticFunctionDefinitions.h"
#ifdef WIN32
#include
diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
index 67764f8057..b4d1de641f 100644
--- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
+++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
@@ -954,14 +954,19 @@ bool CStaticFunctionDefinitions::SetElementID(CElement* pElement, const char* sz
return true;
}
-bool CStaticFunctionDefinitions::SetElementData(CElement* pElement, const char* szName, const CLuaArgument& Variable, ESyncType syncType)
+bool CStaticFunctionDefinitions::SetElementData(CElement* pElement, const char* szName, const CLuaArgument& Variable, ESyncType syncType,
+ std::optional clientTrust)
{
assert(pElement);
assert(szName);
assert(strlen(szName) <= MAX_CUSTOMDATA_NAME_LENGTH);
- ESyncType lastSyncType = ESyncType::BROADCAST;
- CLuaArgument* pCurrentVariable = pElement->GetCustomData(szName, false, &lastSyncType);
+ ESyncType lastSyncType = ESyncType::BROADCAST;
+ eCustomDataClientTrust lastClientTrust{};
+ CLuaArgument* pCurrentVariable = pElement->GetCustomData(szName, false, &lastSyncType, &lastClientTrust);
+
+ if (clientTrust.has_value() && lastClientTrust != clientTrust.value())
+ pElement->GetCustomDataManager().SetClientChangesMode(szName, clientTrust.value());
if (!pCurrentVariable || *pCurrentVariable != Variable || lastSyncType != syncType)
{
diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h
index dd6202208f..8c947d6f34 100644
--- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h
+++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h
@@ -83,7 +83,8 @@ class CStaticFunctionDefinitions
// Element set funcs
static bool ClearElementVisibleTo(CElement* pElement);
static bool SetElementID(CElement* pElement, const char* szID);
- static bool SetElementData(CElement* pElement, const char* szName, const CLuaArgument& Variable, ESyncType syncType);
+ static bool SetElementData(CElement* pElement, const char* szName, const CLuaArgument& Variable, ESyncType syncType,
+ std::optional clientTrust);
static bool RemoveElementData(CElement* pElement, const char* szName);
static bool AddElementDataSubscriber(CElement* pElement, const char* szName, CPlayer* pPlayer);
static bool RemoveElementDataSubscriber(CElement* pElement, const char* szName, CPlayer* pPlayer);
diff --git a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
index ef25750c05..76540327af 100644
--- a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
+++ b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
@@ -285,6 +285,12 @@ ADD_ENUM(ESyncType::LOCAL, "local")
ADD_ENUM(ESyncType::SUBSCRIBE, "subscribe")
IMPLEMENT_ENUM_CLASS_END("sync-mode")
+IMPLEMENT_ENUM_CLASS_BEGIN(eCustomDataClientTrust)
+ADD_ENUM(eCustomDataClientTrust::UNSET, "default")
+ADD_ENUM(eCustomDataClientTrust::ALLOW, "allow")
+ADD_ENUM(eCustomDataClientTrust::DENY, "deny")
+IMPLEMENT_ENUM_CLASS_END("client-trust-mode")
+
//
// CResource from userdata
//
diff --git a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h
index 52a4536012..796ca3c702 100644
--- a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h
+++ b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h
@@ -38,6 +38,7 @@ DECLARE_ENUM(CAccessControlListRight::ERightType);
DECLARE_ENUM(CElement::EElementType);
DECLARE_ENUM(CAccountPassword::EAccountPasswordType);
DECLARE_ENUM_CLASS(ESyncType);
+DECLARE_ENUM_CLASS(eCustomDataClientTrust)
enum eHudComponent
{
diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp
index 6cacbdcc98..f8effd7b89 100644
--- a/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp
+++ b/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp
@@ -1538,6 +1538,7 @@ int CLuaElementDefs::setElementData(lua_State* luaVM)
SString strKey;
CLuaArgument value;
ESyncType syncType = ESyncType::BROADCAST;
+ std::optional clientTrust{};
CScriptArgReader argStream(luaVM);
argStream.ReadUserData(pElement);
@@ -1554,6 +1555,13 @@ int CLuaElementDefs::setElementData(lua_State* luaVM)
else
argStream.ReadEnumString(syncType, ESyncType::BROADCAST);
+ if (!argStream.NextIsNone())
+ {
+ eCustomDataClientTrust trustReaded;
+ argStream.ReadEnumString(trustReaded);
+ clientTrust = trustReaded;
+ }
+
if (!argStream.HasErrors())
{
LogWarningIfPlayerHasNotJoinedYet(luaVM, pElement);
@@ -1566,7 +1574,7 @@ int CLuaElementDefs::setElementData(lua_State* luaVM)
strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH);
}
- if (CStaticFunctionDefinitions::SetElementData(pElement, strKey, value, syncType))
+ if (CStaticFunctionDefinitions::SetElementData(pElement, strKey, value, syncType, clientTrust))
{
lua_pushboolean(luaVM, true);
return 1;
diff --git a/Server/mods/deathmatch/mtaserver.conf b/Server/mods/deathmatch/mtaserver.conf
index 31faba9112..11ef497fa7 100644
--- a/Server/mods/deathmatch/mtaserver.conf
+++ b/Server/mods/deathmatch/mtaserver.conf
@@ -268,6 +268,12 @@
Values: 0 - Off, 1 - Enabled. Default - 1 -->
1
+
+ 0
+
1
+
+ 0
+