diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 07ac611c09..918091d482 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -848,6 +848,34 @@ void CGameSA::SetRoadSignsTextEnabled(bool isEnabled) m_isRoadSignsTextEnabled = isEnabled; } +void CGameSA::SetIgnoreFireStateEnabled(bool isEnabled) +{ + if (isEnabled == m_isIgnoreFireStateEnabled) + return; + + if (isEnabled) + { + MemSet((void*)0x6511B9, 0x90, 10); // CCarEnterExit::IsVehicleStealable + MemSet((void*)0x643A95, 0x90, 14); // CTaskComplexEnterCar::CreateFirstSubTask + MemSet((void*)0x6900B5, 0x90, 14); // CTaskComplexCopInCar::ControlSubTask + MemSet((void*)0x64F3DB, 0x90, 14); // CCarEnterExit::IsPlayerToQuitCarEnter + + MemSet((void*)0x685A7F, 0x90, 14); // CTaskSimplePlayerOnFoot::ProcessPlayerWeapon + } + else + { + // Restore original bytes + MemCpy((void*)0x6511B9, "\x88\x86\x90\x04\x00\x00\x85\xC0\x75\x3E", 10); + MemCpy((void*)0x643A95, "\x8B\x88\x90\x04\x00\x00\x85\xC9\x0F\x85\x99\x01\x00\x00", 14); + MemCpy((void*)0x6900B5, "\x8B\x81\x90\x04\x00\x00\x85\xC0\x0F\x85\x1A\x01\x00\x00", 14); + MemCpy((void*)0x64F3DB, "\x8B\x85\x90\x04\x00\x00\x85\xC0\x0F\x85\x1B\x01\x00\x00", 14); + + MemCpy((void*)0x685A7F, "\x8B\x86\x30\x07\x00\x00\x85\xC0\x0F\x85\x1D\x01\x00\x00", 14); + } + + m_isIgnoreFireStateEnabled = isEnabled; +} + bool CGameSA::PerformChecks() { std::map::iterator it; diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index cf4788d953..33731af858 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -249,6 +249,8 @@ class CGameSA : public CGame bool IsTunnelWeatherBlendEnabled() const noexcept override { return m_isTunnelWeatherBlendEnabled; } void SetTunnelWeatherBlendEnabled(bool isEnabled) override; + bool IsIgnoreFireStateEnabled() const noexcept override { return m_isIgnoreFireStateEnabled; } + void SetIgnoreFireStateEnabled(bool isEnabled) override; unsigned long GetMinuteDuration(); void SetMinuteDuration(unsigned long ulTime); @@ -378,6 +380,7 @@ class CGameSA : public CGame bool m_isRoadSignsTextEnabled{true}; bool m_isBuildingsRemoved{false}; bool m_isExtendedWaterCannonsEnabled{false}; + bool m_isIgnoreFireStateEnabled{false}; static unsigned int& ClumpOffset; diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index e6acca9064..52ca69f3f0 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -135,6 +135,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_Glitches[GLITCH_BADDRIVEBYHITBOX] = false; m_Glitches[GLITCH_QUICKSTAND] = false; m_Glitches[GLITCH_KICKOUTOFVEHICLE_ONMODELREPLACE] = false; + g_pMultiplayer->DisableBadDrivebyHitboxes(true); // Remove Night & Thermal vision view (if enabled). @@ -6025,6 +6026,9 @@ bool CClientGame::SetWorldSpecialProperty(WorldSpecialProperty property, bool is case WorldSpecialProperty::TUNNELWEATHERBLEND: g_pGame->SetTunnelWeatherBlendEnabled(isEnabled); return true; + case WorldSpecialProperty::IGNOREFIRESTATE: + g_pGame->SetIgnoreFireStateEnabled(isEnabled); + return true; } return false; } @@ -6062,6 +6066,8 @@ bool CClientGame::IsWorldSpecialProperty(WorldSpecialProperty property) return g_pGame->IsRoadSignsTextEnabled(); case WorldSpecialProperty::TUNNELWEATHERBLEND: return g_pGame->IsTunnelWeatherBlendEnabled(); + case WorldSpecialProperty::IGNOREFIRESTATE: + return g_pGame->IsIgnoreFireStateEnabled(); } return false; } @@ -6475,7 +6481,7 @@ void CClientGame::OutputServerInfo() { SString strEnabledGlitches; const char* szGlitchNames[] = {"Quick reload", "Fast fire", "Fast move", "Crouch bug", "Close damage", "Hit anim", "Fast sprint", - "Bad driveby hitboxes", "Quick stand"}; + "Bad driveby hitboxes", "Quick stand", "Kickout of vehicle on model replace"}; for (uint i = 0; i < NUM_GLITCHES; i++) { if (IsGlitchEnabled(i)) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index ebf2ba5140..787cd4c239 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -572,6 +572,10 @@ void CPacketHandler::Packet_ServerDisconnected(NetBitStreamInterface& bitStream) strReason = _("Disconnected: Serial verification failed"); strErrorCode = _E("CD44"); break; + case ePlayerDisconnectType::SERIAL_DUPLICATE: + strReason = _("Disconnected: Serial already in use"); + strErrorCode = _E("CD50"); + break; case ePlayerDisconnectType::CONNECTION_DESYNC: strReason = _("Disconnected: Connection desync %s"); strErrorCode = _E("CD45"); @@ -2393,6 +2397,7 @@ void CPacketHandler::Packet_MapInfo(NetBitStreamInterface& bitStream) g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::ROADSIGNSTEXT, wsProps.data3.roadsignstext); g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::EXTENDEDWATERCANNONS, wsProps.data4.extendedwatercannons); g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::TUNNELWEATHERBLEND, wsProps.data5.tunnelweatherblend); + g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::IGNOREFIRESTATE, wsProps.data6.ignoreFireState); float fJetpackMaxHeight = 100; if (!bitStream.Read(fJetpackMaxHeight)) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.h b/Client/mods/deathmatch/logic/CPacketHandler.h index ca0f6f3d69..9a50b24615 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.h +++ b/Client/mods/deathmatch/logic/CPacketHandler.h @@ -42,7 +42,8 @@ class CPacketHandler BAN, KICK, CUSTOM, - SHUTDOWN + SHUTDOWN, + SERIAL_DUPLICATE }; struct SEntityDependantStuff diff --git a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp index d92ba4788b..fd305f6955 100644 --- a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp @@ -619,6 +619,8 @@ void CWorldRPCs::SetSyncIntervals(NetBitStreamInterface& bitStream) bitStream.Read(g_TickRateSettings.iObjectSync); bitStream.Read(g_TickRateSettings.iKeySyncRotation); bitStream.Read(g_TickRateSettings.iKeySyncAnalogMove); + bitStream.Read(g_TickRateSettings.iPedSyncerDistance); + bitStream.Read(g_TickRateSettings.iUnoccupiedVehicleSyncerDistance); } void CWorldRPCs::SetMoonSize(NetBitStreamInterface& bitStream) diff --git a/Client/multiplayer_sa/CMultiplayerSA.cpp b/Client/multiplayer_sa/CMultiplayerSA.cpp index 1b21f79a23..73c8fd9df4 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA.cpp @@ -1574,6 +1574,7 @@ void CMultiplayerSA::InitHooks() MemSet((void*)0x6C4453, 0x90, 0x68); InitHooks_CrashFixHacks(); + InitHooks_DeviceSelection(); // Init our 1.3 hooks. Init_13(); @@ -1861,6 +1862,7 @@ void CMultiplayerSA::DisableCloseRangeDamage(bool bDisabled) MemPut(0x73BA00, 0x86); } } + bool CMultiplayerSA::GetInteriorSoundsEnabled() { return bInteriorSoundsEnabled; diff --git a/Client/multiplayer_sa/CMultiplayerSA.h b/Client/multiplayer_sa/CMultiplayerSA.h index 802b9b56ea..d97300e6a1 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.h +++ b/Client/multiplayer_sa/CMultiplayerSA.h @@ -80,6 +80,7 @@ class CMultiplayerSA : public CMultiplayer void InitHooks_ProjectileCollisionFix(); void InitHooks_ObjectStreamerOptimization(); void InitHooks_Postprocess(); + void InitHooks_DeviceSelection(); CRemoteDataStorage* CreateRemoteDataStorage(); void DestroyRemoteDataStorage(CRemoteDataStorage* pData); void AddRemoteDataStorage(CPlayerPed* pPed, CRemoteDataStorage* pData); diff --git a/Client/multiplayer_sa/CMultiplayerSA_DeviceSelection.cpp b/Client/multiplayer_sa/CMultiplayerSA_DeviceSelection.cpp new file mode 100644 index 0000000000..586f387270 --- /dev/null +++ b/Client/multiplayer_sa/CMultiplayerSA_DeviceSelection.cpp @@ -0,0 +1,155 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: multiplayer_sa/CMultiplayerSA_DeviceSelection.cpp + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#define FUNC_rwDeviceSystemRequest 0x7F2AB0 +#define FUNC_DialogFunc 0x745E50 +#define FUNC_RwEngineGetSubSystemInfo 0x7F2C30 +#define CLASS_RwGlobals 0xC97B24 +#define CLASS_IDirect3D9 0xC97C20 +#define NUM_DialogFuncStackPushAddress 0x746239 + +// This is copied from SilentPatch: +// https://github.com/CookiePLMonster/SilentPatch/blob/dev/SilentPatch/FriendlyMonitorNames.cpp +std::unordered_map GetFriendlyMonitorNamesForDevicePaths() +{ + std::unordered_map monitorNames; + + HMODULE user32Lib = LoadLibrary(TEXT("user32")); + if (!user32Lib) + return monitorNames; + + auto* getDisplayConfigBufferSizes = (decltype(GetDisplayConfigBufferSizes)*)GetProcAddress(user32Lib, "GetDisplayConfigBufferSizes"); + auto* queryDisplayConfig = (decltype(QueryDisplayConfig)*)GetProcAddress(user32Lib, "QueryDisplayConfig"); + auto* displayConfigGetDeviceInfo = (decltype(DisplayConfigGetDeviceInfo)*)GetProcAddress(user32Lib, "DisplayConfigGetDeviceInfo"); + if (!getDisplayConfigBufferSizes || !queryDisplayConfig || !displayConfigGetDeviceInfo) + { + FreeLibrary(user32Lib); + return monitorNames; + } + + UINT32 pathCount, modeCount; + std::unique_ptr paths; + std::unique_ptr modes; + + LONG result = ERROR_SUCCESS; + do + { + result = getDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount); + if (result != ERROR_SUCCESS) + { + break; + } + paths = std::make_unique(pathCount); + modes = std::make_unique(modeCount); + result = queryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.get(), &modeCount, modes.get(), nullptr); + } while (result == ERROR_INSUFFICIENT_BUFFER); + + if (result != ERROR_SUCCESS) + { + FreeLibrary(user32Lib); + return monitorNames; + } + + for (size_t i = 0; i < pathCount; i++) + { + DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {}; + targetName.header.adapterId = paths[i].targetInfo.adapterId; + targetName.header.id = paths[i].targetInfo.id; + targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; + targetName.header.size = sizeof(targetName); + const LONG targetNameResult = DisplayConfigGetDeviceInfo(&targetName.header); + + DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {}; + sourceName.header.adapterId = paths[i].sourceInfo.adapterId; + sourceName.header.id = paths[i].sourceInfo.id; + sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; + sourceName.header.size = sizeof(sourceName); + const LONG sourceNameResult = DisplayConfigGetDeviceInfo(&sourceName.header); + if (targetNameResult == ERROR_SUCCESS && sourceNameResult == ERROR_SUCCESS && targetName.monitorFriendlyDeviceName[0] != '\0') + { + char gdiDeviceName[std::size(sourceName.viewGdiDeviceName)]; + char monitorFriendlyDeviceName[std::size(targetName.monitorFriendlyDeviceName)]; + WideCharToMultiByte(CP_ACP, 0, sourceName.viewGdiDeviceName, -1, gdiDeviceName, static_cast(std::size(gdiDeviceName)), nullptr, nullptr); + WideCharToMultiByte(CP_ACP, 0, targetName.monitorFriendlyDeviceName, -1, monitorFriendlyDeviceName, + static_cast(std::size(monitorFriendlyDeviceName)), nullptr, nullptr); + + monitorNames.try_emplace(gdiDeviceName, monitorFriendlyDeviceName); + } + } + + FreeLibrary(user32Lib); + return monitorNames; +} + +struct RwSubSystemInfo +{ + char name[80]; +}; + +using rwDeviceSystemRequest = RwSubSystemInfo*(__cdecl*)(RwDevice* device, std::int32_t requestId, RwSubSystemInfo* pOut, void* pInOut, std::int32_t numIn); +static RwSubSystemInfo* RwEngineGetSubSystemInfo_Hooked(RwSubSystemInfo* subSystemInfo, std::int32_t subSystemIndex) +{ + auto* rwGlobals = *(RwGlobals**)CLASS_RwGlobals; + auto* rwDeviceSystemRequestFunc = (rwDeviceSystemRequest)(FUNC_rwDeviceSystemRequest); + if (!rwDeviceSystemRequestFunc(&rwGlobals->dOpenDevice, 14, subSystemInfo, nullptr, subSystemIndex)) + return nullptr; + + auto* pDxDevice = *(IDirect3D9**)CLASS_IDirect3D9; + if (!pDxDevice) + return subSystemInfo; + + D3DADAPTER_IDENTIFIER9 identifier; + if (FAILED(pDxDevice->GetAdapterIdentifier(subSystemIndex, 0, &identifier))) + return subSystemInfo; + + static const auto friendlyNames = GetFriendlyMonitorNamesForDevicePaths(); + + // If we can't find the friendly name, either because it doesn't exist or we're on an ancient Windows, fall back to the device name + auto it = friendlyNames.find(identifier.DeviceName); + if (it != friendlyNames.end()) + { + strncpy_s(subSystemInfo->name, it->second.c_str(), _TRUNCATE); + } + else + { + strncpy_s(subSystemInfo->name, identifier.Description, _TRUNCATE); + } + + return subSystemInfo; +} + +INT_PTR CALLBACK CustomDlgProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) +{ + auto* orgDialogFunc = (DLGPROC)FUNC_DialogFunc; + if (msg != WM_INITDIALOG) + return orgDialogFunc(window, msg, wParam, lParam); + + orgDialogFunc(window, msg, wParam, lParam); + + // Set Icon + HMODULE hGameModule = GetModuleHandle(nullptr); + SendMessage(window, WM_SETICON, ICON_SMALL, reinterpret_cast(LoadIcon(hGameModule, MAKEINTRESOURCE(100)))); + + // Make the dialog visible in the task bar + // https://stackoverflow.com/a/1462811 + SetWindowLongPtr(window, GWL_EXSTYLE, WS_EX_APPWINDOW); + ShowWindow(window, SW_HIDE); + ShowWindow(window, SW_SHOW); + return FALSE; +} + +void CMultiplayerSA::InitHooks_DeviceSelection() +{ + // 0x746239 -> Exact address where the original DialogFunc address is being pushed as an argument to DialogBoxParamA(), + // we're replacing it with out own proxy function + MemPut(NUM_DialogFuncStackPushAddress, (DLGPROC)&CustomDlgProc); + HookInstall(FUNC_RwEngineGetSubSystemInfo, (DWORD)RwEngineGetSubSystemInfo_Hooked, 6); +} diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index 2314fdad0e..06866e06ce 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -230,6 +230,9 @@ class __declspec(novtable) CGame virtual bool IsTunnelWeatherBlendEnabled() const noexcept = 0; virtual void SetTunnelWeatherBlendEnabled(bool isEnabled) = 0; + virtual bool IsIgnoreFireStateEnabled() const noexcept = 0; + virtual void SetIgnoreFireStateEnabled(bool isEnabled) = 0; + virtual CWeapon* CreateWeapon() = 0; virtual CWeaponStat* CreateWeaponStat(eWeaponType weaponType, eWeaponSkill weaponSkill) = 0; diff --git a/Client/sdk/game/RenderWare.h b/Client/sdk/game/RenderWare.h index 20249e01fd..d85011b291 100644 --- a/Client/sdk/game/RenderWare.h +++ b/Client/sdk/game/RenderWare.h @@ -526,3 +526,49 @@ struct RwError { int err1, err2; }; + +/*****************************************************************************/ +/** RenderWare Globals **/ +/*****************************************************************************/ + +typedef bool (*RwSystemFunc)(std::int32_t, void*, void*, std::int32_t); +struct RwDevice +{ + float gammaCorrection; + RwSystemFunc fpSystem; + float zBufferNear; + float zBufferFar; + // RwRenderStateSetFunction fpRenderStateSet; + // RwRenderStateGetFunction fpRenderStateGet; + // RwIm2DRenderLineFunction fpIm2DRenderLine; + // RwIm2DRenderTriangleFunction fpIm2DRenderTriangle; + // RwIm2DRenderPrimitiveFunction fpIm2DRenderPrimitive; + // RwIm2DRenderIndexedPrimitiveFunction fpIm2DRenderIndexedPrimitive; + // RwIm3DRenderLineFunction fpIm3DRenderLine; + // RwIm3DRenderTriangleFunction fpIm3DRenderTriangle; + // RwIm3DRenderPrimitiveFunction fpIm3DRenderPrimitive; + // RwIm3DRenderIndexedPrimitiveFunction fpIm3DRenderIndexedPrimitive; +}; +// static_assert(sizeof(RwDevice) == 0x38, "Incorrect class size: RwDevice"); + +typedef bool (*RwStandardFunc)(void*, void*, std::int32_t); +struct RwGlobals +{ + void* curCamera; + void* curWorld; + std::uint16_t renderFrame; + std::uint16_t lightFrame; + std::uint16_t pad[2]; + RwDevice dOpenDevice; + RwStandardFunc stdFunc[29]; + // RwLinkList dirtyFrameList; + // RwFileFunctions fileFuncs; + // RwStringFunctions stringFuncs; + // RwMemoryFunctions memoryFuncs; + // RwMemoryAllocFn memoryAlloc; + // RwMemoryFreeFn memoryFree; + // RwMetrics* metrics; + // RwEngineStatus engineStatus; + // RwUInt32 resArenaInitSize; +}; +//static_assert(sizeof(RwGlobals) == 0x158, "Incorrect class size: RwGlobals"); diff --git a/Server/mods/deathmatch/StdInc.h b/Server/mods/deathmatch/StdInc.h index c2e6355739..fb397aad5b 100644 --- a/Server/mods/deathmatch/StdInc.h +++ b/Server/mods/deathmatch/StdInc.h @@ -49,6 +49,7 @@ #include #include #include +#include "version.h" extern class CNetServer* g_pRealNetServer; extern class CGame* g_pGame; diff --git a/Server/mods/deathmatch/editor.conf b/Server/mods/deathmatch/editor.conf index 3b2b75fa80..1355e673ee 100644 --- a/Server/mods/deathmatch/editor.conf +++ b/Server/mods/deathmatch/editor.conf @@ -268,6 +268,12 @@ Values: 0 - Off, 1 - Enabled. Default - 1 --> 1 + + 1 + diff --git a/Server/mods/deathmatch/local.conf b/Server/mods/deathmatch/local.conf index 039e155d81..d30a608781 100644 --- a/Server/mods/deathmatch/local.conf +++ b/Server/mods/deathmatch/local.conf @@ -274,6 +274,12 @@ Values: 0 - Off, 1 - Enabled. Default - 0 --> 0 + + 1 + diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 9543e1e447..98ea8250b2 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -258,6 +258,7 @@ CGame::CGame() : m_FloodProtect(4, 30000, 30000) // Max of 4 connecti m_WorldSpecialProps[WorldSpecialProperty::EXTENDEDWATERCANNONS] = true; m_WorldSpecialProps[WorldSpecialProperty::ROADSIGNSTEXT] = true; m_WorldSpecialProps[WorldSpecialProperty::TUNNELWEATHERBLEND] = true; + m_WorldSpecialProps[WorldSpecialProperty::IGNOREFIRESTATE] = false; m_JetpackWeapons[WEAPONTYPE_MICRO_UZI] = true; m_JetpackWeapons[WEAPONTYPE_TEC9] = true; @@ -1798,6 +1799,21 @@ void CGame::Packet_PlayerJoinData(CPlayerJoinDataPacket& Packet) return; } + // Check if another player is using the same serial + if (m_pMainConfig->IsCheckDuplicateSerialsEnabled() && m_pPlayerManager->GetBySerial(strSerial)) + { + // Tell the console + CLogger::LogPrintf("CONNECT: %s failed to connect (Serial already in use) (%s)\n", szNick, strIPAndSerial.c_str()); + + // Tell the player the problem + if (pPlayer->CanBitStream(eBitStreamVersion::CheckDuplicateSerials)) + DisconnectPlayer(this, *pPlayer, CPlayerDisconnectedPacket::SERIAL_DUPLICATE); + else + DisconnectPlayer(this, *pPlayer, CPlayerDisconnectedPacket::KICK); + + return; + } + // Check the nick is valid if (!CheckNickProvided(szNick)) { diff --git a/Server/mods/deathmatch/logic/CMainConfig.cpp b/Server/mods/deathmatch/logic/CMainConfig.cpp index bece65e885..49c73419ca 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.cpp +++ b/Server/mods/deathmatch/logic/CMainConfig.cpp @@ -80,6 +80,7 @@ CMainConfig::CMainConfig(CConsole* pConsole) : CXMLConfig(NULL) m_iBackupAmount = 5; m_bSyncMapElementData = true; m_elementDataWhitelisted = false; + m_checkDuplicateSerials = true; } bool CMainConfig::Load() @@ -528,6 +529,7 @@ bool CMainConfig::Load() } GetBoolean(m_pRootNode, "elementdata_whitelisted", m_elementDataWhitelisted); + GetBoolean(m_pRootNode, "check_duplicate_serials", m_checkDuplicateSerials); ApplyNetOptions(); diff --git a/Server/mods/deathmatch/logic/CMainConfig.h b/Server/mods/deathmatch/logic/CMainConfig.h index 9758ae2dbf..05df0e2f32 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.h +++ b/Server/mods/deathmatch/logic/CMainConfig.h @@ -127,6 +127,7 @@ class CMainConfig : public CXMLConfig bool IsDatabaseCredentialsProtectionEnabled() const { return m_bDatabaseCredentialsProtectionEnabled != 0; } bool IsFakeLagCommandEnabled() const { return m_bFakeLagCommandEnabled != 0; } bool IsElementDataWhitelisted() const { return m_elementDataWhitelisted; } + bool IsCheckDuplicateSerialsEnabled() const noexcept { return m_checkDuplicateSerials; } bool IsCheckResourceClientFilesEnabled() const noexcept { return m_checkResourceClientFiles != 0; } SString GetSetting(const SString& configSetting); @@ -230,5 +231,6 @@ class CMainConfig : public CXMLConfig int m_iPlayerTriggeredEventIntervalMs; int m_iMaxPlayerTriggeredEventsPerInterval; bool m_elementDataWhitelisted; + bool m_checkDuplicateSerials; int m_checkResourceClientFiles; }; diff --git a/Server/mods/deathmatch/logic/CPlayerManager.cpp b/Server/mods/deathmatch/logic/CPlayerManager.cpp index abdf923df7..227d3677f2 100644 --- a/Server/mods/deathmatch/logic/CPlayerManager.cpp +++ b/Server/mods/deathmatch/logic/CPlayerManager.cpp @@ -138,6 +138,17 @@ CPlayer* CPlayerManager::Get(const char* szNick, bool bCaseSensitive) return NULL; } +CPlayer* CPlayerManager::GetBySerial(const std::string_view serial) const noexcept +{ + for (const auto& player : m_Players) + { + if (player->GetSerial() == serial) + return player; + } + + return nullptr; +} + void CPlayerManager::DeleteAll() { // Delete all the items in the list diff --git a/Server/mods/deathmatch/logic/CPlayerManager.h b/Server/mods/deathmatch/logic/CPlayerManager.h index bd503380f4..0380c4bce4 100644 --- a/Server/mods/deathmatch/logic/CPlayerManager.h +++ b/Server/mods/deathmatch/logic/CPlayerManager.h @@ -40,6 +40,7 @@ class CPlayerManager CPlayer* Get(const NetServerPlayerID& PlayerSocket); CPlayer* Get(const char* szNick, bool bCaseSensitive = false); + CPlayer* GetBySerial(const std::string_view serial) const noexcept; std::list::const_iterator IterBegin() { return m_Players.begin(); }; std::list::const_iterator IterEnd() { return m_Players.end(); }; diff --git a/Server/mods/deathmatch/logic/CResource.cpp b/Server/mods/deathmatch/logic/CResource.cpp index d1b985fcfe..279f1937a7 100644 --- a/Server/mods/deathmatch/logic/CResource.cpp +++ b/Server/mods/deathmatch/logic/CResource.cpp @@ -35,7 +35,6 @@ #include #include #include -#include "version.h" #include "CStaticFunctionDefinitions.h" #ifdef WIN32 diff --git a/Server/mods/deathmatch/logic/CResourceChecker.Data.h b/Server/mods/deathmatch/logic/CResourceChecker.Data.h index e6bdee9212..630f70fd81 100644 --- a/Server/mods/deathmatch/logic/CResourceChecker.Data.h +++ b/Server/mods/deathmatch/logic/CResourceChecker.Data.h @@ -167,8 +167,8 @@ namespace //{false, "doesPedHaveJetPack", "isPedWearingJetpack"}, // Base Encoding & Decoding - {false, "base64Encode", "encodeString"}, - {false, "base64Decode", "decodeString"}, + {true, "base64Encode", "Please manually change this to encodeString (different syntax). Refer to the wiki for details"}, + {true, "base64Decode", "Please manually change this to decodeString (different syntax). Refer to the wiki for details"}, {false, "setHelicopterRotorSpeed", "setVehicleRotorSpeed"} }; @@ -271,7 +271,7 @@ namespace {true, "setPlayerDiscordJoinParams", "See GitHub PR #2499 for more details"}, // Base Encoding & Decoding - {false, "base64Encode", "encodeString"}, - {false, "base64Decode", "decodeString"} + {true, "base64Encode", "Please manually change this to encodeString (different syntax). Refer to the wiki for details"}, + {true, "base64Decode", "Please manually change this to decodeString (different syntax). Refer to the wiki for details"} }; } // namespace diff --git a/Server/mods/deathmatch/logic/CResourceChecker.cpp b/Server/mods/deathmatch/logic/CResourceChecker.cpp index 07f433f34f..9998b089ca 100644 --- a/Server/mods/deathmatch/logic/CResourceChecker.cpp +++ b/Server/mods/deathmatch/logic/CResourceChecker.cpp @@ -367,16 +367,25 @@ void CResourceChecker::CheckMetaSourceForIssues(CXMLNode* pRootNode, const strin attributes.Delete("client"); attributes.Delete("both"); - if (!m_strReqServerVersion.empty()) + // Use "both" if client and server versions are the same + if (!m_strReqServerVersion.empty() && !m_strReqClientVersion.empty() && m_strReqServerVersion == m_strReqClientVersion) { - CXMLAttribute* pAttr = attributes.Create("server"); + CXMLAttribute* pAttr = attributes.Create("both"); pAttr->SetValue(m_strReqServerVersion); } - - if (!m_strReqClientVersion.empty()) + else { - CXMLAttribute* pAttr = attributes.Create("client"); - pAttr->SetValue(m_strReqClientVersion); + if (!m_strReqServerVersion.empty()) + { + CXMLAttribute* pAttr = attributes.Create("server"); + pAttr->SetValue(m_strReqServerVersion); + } + + if (!m_strReqClientVersion.empty()) + { + CXMLAttribute* pAttr = attributes.Create("client"); + pAttr->SetValue(m_strReqClientVersion); + } } if (pbOutHasChanged) diff --git a/Server/mods/deathmatch/logic/CResourceChecker.h b/Server/mods/deathmatch/logic/CResourceChecker.h index 50c7ec00bb..ea9e7a1be9 100644 --- a/Server/mods/deathmatch/logic/CResourceChecker.h +++ b/Server/mods/deathmatch/logic/CResourceChecker.h @@ -2,7 +2,7 @@ * * PROJECT: Multi Theft Auto v1.0 * LICENSE: See LICENSE in the top level directory - * FILE: mods/deathmatch/logic/CResourceChecker.cpp + * FILE: mods/deathmatch/logic/CResourceChecker.h * PURPOSE: Resource file content checker/validator/upgrader * * Multi Theft Auto is available from http://www.multitheftauto.com/ diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index b4d1de641f..930e814879 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -10861,6 +10861,8 @@ bool CStaticFunctionDefinitions::SendSyncIntervals(CPlayer* pPlayer) BitStream.pBitStream->Write(g_TickRateSettings.iObjectSync); BitStream.pBitStream->Write(g_TickRateSettings.iKeySyncRotation); BitStream.pBitStream->Write(g_TickRateSettings.iKeySyncAnalogMove); + BitStream.pBitStream->Write(g_TickRateSettings.iPedSyncerDistance); + BitStream.pBitStream->Write(g_TickRateSettings.iUnoccupiedVehicleSyncerDistance); if (pPlayer) pPlayer->Send(CLuaPacket(SET_SYNC_INTERVALS, *BitStream.pBitStream)); else diff --git a/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp b/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp index e62547b3fb..e2adf9a5bc 100644 --- a/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp @@ -192,6 +192,7 @@ bool CMapInfoPacket::Write(NetBitStreamInterface& BitStream) const wsProps.data3.roadsignstext = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::ROADSIGNSTEXT); wsProps.data4.extendedwatercannons = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::EXTENDEDWATERCANNONS); wsProps.data5.tunnelweatherblend = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::TUNNELWEATHERBLEND); + wsProps.data6.ignoreFireState = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::IGNOREFIRESTATE); BitStream.Write(&wsProps); } diff --git a/Server/mods/deathmatch/logic/packets/CPlayerDisconnectedPacket.h b/Server/mods/deathmatch/logic/packets/CPlayerDisconnectedPacket.h index 82fdfbd1b0..541f9b191c 100644 --- a/Server/mods/deathmatch/logic/packets/CPlayerDisconnectedPacket.h +++ b/Server/mods/deathmatch/logic/packets/CPlayerDisconnectedPacket.h @@ -39,7 +39,8 @@ class CPlayerDisconnectedPacket final : public CPacket BAN, KICK, CUSTOM, - SHUTDOWN + SHUTDOWN, + SERIAL_DUPLICATE }; CPlayerDisconnectedPacket(const char* szReason); diff --git a/Server/mods/deathmatch/mtaserver.conf b/Server/mods/deathmatch/mtaserver.conf index 11ef497fa7..726199d548 100644 --- a/Server/mods/deathmatch/mtaserver.conf +++ b/Server/mods/deathmatch/mtaserver.conf @@ -274,6 +274,12 @@ Values: 0 - Off, 1 - Enabled. Default - 0 --> 0 + + 1 + 0 + + 1 +