diff --git a/Client/game_sa/CPedSA.cpp b/Client/game_sa/CPedSA.cpp index 4f26a3785c7..b8a2fef136e 100644 --- a/Client/game_sa/CPedSA.cpp +++ b/Client/game_sa/CPedSA.cpp @@ -20,6 +20,7 @@ #include "CProjectileInfoSA.h" #include "CWeaponStatManagerSA.h" #include "CFireManagerSA.h" +#include "CAnimManagerSA.h" extern CGameSA* pGame; @@ -588,6 +589,25 @@ void CPedSA::SetInWaterFlags(bool inWater) physicalInterface->bSubmergedInWater = inWater; } +bool CPedSA::IsPedCuttingWithChainsaw() const +{ + if (GetPedIntelligence()->GetTaskManager()->GetActiveTask()->GetTaskType() == TASK_SIMPLE_FIGHT) + { + CPedSAInterface* gamePed = GetPedInterface(); + + if (gamePed->weaponAudioEntity.m_chainsawState == eChainsawState::CUTTING) + { + auto cuttingAnim = pGame->GetAnimManager()->RpAnimBlendClumpGetAssociation(gamePed->m_pRwObject, "csaw_g"); + if (!cuttingAnim) + cuttingAnim = pGame->GetAnimManager()->RpAnimBlendClumpGetAssociation(gamePed->m_pRwObject, "csaw_part"); + + return cuttingAnim != nullptr; + } + } + + return false; +} + //////////////////////////////////////////////////////////////// // // CPed_PreRenderAfterTest diff --git a/Client/game_sa/CPedSA.h b/Client/game_sa/CPedSA.h index 34e2de6f414..f9f1ca05da3 100644 --- a/Client/game_sa/CPedSA.h +++ b/Client/game_sa/CPedSA.h @@ -476,6 +476,7 @@ class CPedSA : public virtual CPed, public virtual CPhysicalSA void GetAttachedSatchels(std::vector &satchelsList) const override; void SetInWaterFlags(bool inWater) override; + bool IsPedCuttingWithChainsaw() const override; static void StaticSetHooks(); diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 7632881badd..1c62a87812e 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -4443,6 +4443,68 @@ bool CClientGame::ApplyPedDamageFromGame(eWeaponType weaponUsed, float fDamage, } pDamagedPed->GetGamePlayer()->SetHealth(fPreviousHealth); pDamagedPed->GetGamePlayer()->SetArmor(fPreviousArmor); + + if (m_triggerEventDamageCancelledForPeds && GetTickCount64_() - pDamagedPed->m_lastEventDamageCancelledTime >= g_TickRateSettings.cancelledDamageInterval) + { + bool sendPacket = true; + + if (!m_triggerEventDamageCancelledForDamageEveryFrame) + { + switch (weaponUsed) + { + case WEAPONTYPE_TEARGAS: + case WEAPONTYPE_SPRAYCAN: + case WEAPONTYPE_EXTINGUISHER: + case WEAPONTYPE_FLAMETHROWER: + case WEAPONTYPE_MOLOTOV: + case WEAPONTYPE_DROWNING: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_RUNOVERBYCAR: + { + sendPacket = false; + break; + } + case WEAPONTYPE_CHAINSAW: + { + if (pInflictingEntity && (pInflictingEntity->GetType() == eClientEntityType::CCLIENTPED || pInflictingEntity->GetType() == eClientEntityType::CCLIENTPLAYER)) + { + CClientPed* attackerPed = static_cast(pInflictingEntity); + if (!attackerPed->m_pPlayerPed || !attackerPed->m_pPlayerPed->IsPedCuttingWithChainsaw()) + sendPacket = false; + } + + break; + } + } + } + + if (sendPacket) + { + NetBitStreamInterface* bitStream = g_pNet->AllocateNetBitStream(); + + bitStream->Write(pDamagedPed->GetID()); + + bitStream->WriteBit(pInflictingEntity != nullptr); + if (pInflictingEntity) + bitStream->Write(pInflictingEntity->GetID()); + + SWeaponTypeSync weapon; + weapon.data.ucWeaponType = weaponUsed; + bitStream->Write(&weapon); + + SFloatSync<8, 10> damage; + damage.data.fValue = fDamage; + bitStream->Write(&damage); + + bitStream->WriteString(m_pLuaManager->GetEvents()->GetEventCancellingResourceName()); + + g_pNet->SendPacket(PACKET_ID_CANCEL_DAMAGE_EVENT, bitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(bitStream); + + pDamagedPed->m_lastEventDamageCancelledTime = GetTickCount64_(); + } + } + return false; } @@ -4814,6 +4876,61 @@ bool CClientGame::VehicleDamageHandler(CEntitySAInterface* pVehicleInterface, fl if (!pClientVehicle->CallEvent("onClientVehicleDamage", Arguments, true)) { bAllowDamage = false; + + if (m_triggerEventDamageCancelledForVehicles && GetTickCount64_() - pClientVehicle->m_lastEventDamageCancelledTime >= g_TickRateSettings.cancelledDamageInterval) + { + bool sendPacket = true; + + if (m_triggerEventDamageCancelledForDamageEveryFrame) + { + switch (weaponType) + { + case WEAPONTYPE_FLAMETHROWER: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_MOLOTOV: + { + sendPacket = false; + break; + } + case WEAPONTYPE_CHAINSAW: + { + if (pClientAttacker && (pClientAttacker->GetType() == eClientEntityType::CCLIENTPED || pClientAttacker->GetType() == eClientEntityType::CCLIENTPLAYER)) + { + CClientPed* attackerPed = static_cast(pClientAttacker); + if (!attackerPed->m_pPlayerPed || !attackerPed->m_pPlayerPed->IsPedCuttingWithChainsaw()) + sendPacket = false; + } + break; + } + } + } + + if (sendPacket) + { + NetBitStreamInterface* bitStream = g_pNet->AllocateNetBitStream(); + + bitStream->Write(pClientVehicle->GetID()); + + bitStream->WriteBit(pClientAttacker != nullptr); + if (pClientAttacker) + bitStream->Write(pClientAttacker->GetID()); + + SWeaponTypeSync weapon; + weapon.data.ucWeaponType = weaponType; + bitStream->Write(&weapon); + + SFloatSync<8, 10> damage; + damage.data.fValue = fLoss; + bitStream->Write(&damage); + + bitStream->WriteString(m_pLuaManager->GetEvents()->GetEventCancellingResourceName()); + + g_pNet->SendPacket(PACKET_ID_CANCEL_DAMAGE_EVENT, bitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(bitStream); + + pClientVehicle->m_lastEventDamageCancelledTime = GetTickCount64_(); + } + } } } diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 8337c22ea27..53e4e98953a 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -710,6 +710,13 @@ class CClientGame void PedStepHandler(CPedSAInterface* pPed, bool bFoot); void VehicleWeaponHitHandler(SVehicleWeaponHitEvent& event); + void SetEventDamageCancelledSettings(bool calledForVehicles, bool calledForPeds, bool calledForDmgEveryFrame) noexcept + { + m_triggerEventDamageCancelledForVehicles = calledForVehicles; + m_triggerEventDamageCancelledForPeds = calledForPeds; + m_triggerEventDamageCancelledForDamageEveryFrame = calledForDmgEveryFrame; + } + private: eStatus m_Status; eServerType m_ServerType; @@ -923,6 +930,10 @@ class CClientGame MultiCommandHandlerPolicy m_allowMultiCommandHandlers; long long m_timeLastDiscordStateUpdate; + + bool m_triggerEventDamageCancelledForVehicles{false}; + bool m_triggerEventDamageCancelledForPeds{false}; + bool m_triggerEventDamageCancelledForDamageEveryFrame{false}; }; extern CClientGame* g_pClientGame; diff --git a/Client/mods/deathmatch/logic/CClientPed.h b/Client/mods/deathmatch/logic/CClientPed.h index e4153ee5b76..55f3c86e30f 100644 --- a/Client/mods/deathmatch/logic/CClientPed.h +++ b/Client/mods/deathmatch/logic/CClientPed.h @@ -747,6 +747,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule uint m_uiFrameLastRebuildPlayer; bool m_bIsSyncing; bool m_shouldRecreate{false}; + std::int64_t m_lastEventDamageCancelledTime{0}; bool m_bBulletImpactData; CClientEntityPtr m_pBulletImpactEntity; diff --git a/Client/mods/deathmatch/logic/CClientVehicle.h b/Client/mods/deathmatch/logic/CClientVehicle.h index 99c00b46334..9deaac657d0 100644 --- a/Client/mods/deathmatch/logic/CClientVehicle.h +++ b/Client/mods/deathmatch/logic/CClientVehicle.h @@ -759,4 +759,6 @@ class CClientVehicle : public CClientStreamElement std::array(VehicleDummies::VEHICLE_DUMMY_COUNT)> m_dummyPositions; bool m_copyDummyPositions = true; + + std::int64_t m_lastEventDamageCancelledTime{0}; }; diff --git a/Client/mods/deathmatch/logic/CEvents.cpp b/Client/mods/deathmatch/logic/CEvents.cpp index 1a8ef75f6f8..d6a0bf88e32 100644 --- a/Client/mods/deathmatch/logic/CEvents.cpp +++ b/Client/mods/deathmatch/logic/CEvents.cpp @@ -147,8 +147,9 @@ void CEvents::PostEventPulse() m_CancelledList.pop_back(); } -void CEvents::CancelEvent(bool bCancelled) +void CEvents::CancelEvent(bool bCancelled, const std::string& resourceName) { + m_eventCancellingResourceName = resourceName; m_bEventCancelled = bCancelled; } diff --git a/Client/mods/deathmatch/logic/CEvents.h b/Client/mods/deathmatch/logic/CEvents.h index 23c4cb513a0..4f6f636cb0b 100644 --- a/Client/mods/deathmatch/logic/CEvents.h +++ b/Client/mods/deathmatch/logic/CEvents.h @@ -42,9 +42,11 @@ class CEvents void PreEventPulse(); void PostEventPulse(); - void CancelEvent(bool bCancelled = true); + void CancelEvent(bool bCancelled = true, const std::string& resourceName = std::string()); bool WasEventCancelled(); + std::string GetEventCancellingResourceName() const noexcept { return m_eventCancellingResourceName; } + private: void RemoveAllEvents(); @@ -52,4 +54,5 @@ class CEvents std::vector m_CancelledList; bool m_bEventCancelled; bool m_bWasEventCancelled; + std::string m_eventCancellingResourceName{}; }; diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 339110ff311..da7d80f7c97 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -5504,6 +5504,17 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream) bitStream.Read(allowMultiCommandHandlers); g_pClientGame->SetAllowMultiCommandHandlers(static_cast(allowMultiCommandHandlers)); + bool triggerEventDamageCancelledForVehicles; + bitStream.ReadBit(triggerEventDamageCancelledForVehicles); + + bool trigggerEventDamageCancelledForPeds; + bitStream.ReadBit(trigggerEventDamageCancelledForPeds); + + bool triggerEventDamageCancelledForDamageEveryFrame; + bitStream.ReadBit(triggerEventDamageCancelledForDamageEveryFrame); + + g_pClientGame->SetEventDamageCancelledSettings(triggerEventDamageCancelledForVehicles, trigggerEventDamageCancelledForPeds, triggerEventDamageCancelledForDamageEveryFrame); + SMiscGameSettings miscGameSettings; miscGameSettings.bUseAltPulseOrder = (ucUseAltPulseOrder != 0); miscGameSettings.bAllowFastSprintFix = (ucAllowFastSprintFix != 0); diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 0b3fae16e93..68c650f4298 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -224,9 +224,9 @@ bool CStaticFunctionDefinitions::TriggerLatentServerEvent(const char* szName, CC return false; } -bool CStaticFunctionDefinitions::CancelEvent(bool bCancel) +bool CStaticFunctionDefinitions::CancelEvent(bool bCancel, CLuaMain* luaMain) { - m_pEvents->CancelEvent(bCancel); + m_pEvents->CancelEvent(bCancel, luaMain->GetResource()->GetName()); return true; } diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h index e1f05f1d108..b6c0f9d4d25 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h @@ -35,7 +35,7 @@ class CStaticFunctionDefinitions static bool TriggerServerEvent(const char* szName, CClientEntity& CallWithEntity, CLuaArguments& Arguments); static bool TriggerLatentServerEvent(const char* szName, CClientEntity& CallWithEntity, CLuaArguments& Arguments, int bandwidth, CLuaMain* pLuaMain, ushort usResourceNetId); - static bool CancelEvent(bool bCancel); + static bool CancelEvent(bool bCancel, CLuaMain* luaMain); static bool WasEventCancelled(); // Misc funcs diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.Event.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.Event.cpp index 183abe241d2..fe51be06128 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.Event.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.Event.cpp @@ -270,7 +270,7 @@ int CLuaFunctionDefs::TriggerServerEvent(lua_State* luaVM) int CLuaFunctionDefs::CancelEvent(lua_State* luaVM) { // Cancel it - if (CStaticFunctionDefinitions::CancelEvent(true)) + if (CStaticFunctionDefinitions::CancelEvent(true, m_pLuaManager->GetVirtualMachine(luaVM))) { lua_pushboolean(luaVM, true); return 1; diff --git a/Client/mods/deathmatch/logic/lua/CLuaManager.h b/Client/mods/deathmatch/logic/lua/CLuaManager.h index 04f2a552f76..bf3aad88a6a 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaManager.h +++ b/Client/mods/deathmatch/logic/lua/CLuaManager.h @@ -42,6 +42,7 @@ class CLuaManager void ProcessPendingDeleteList(); bool IsLuaVMValid(lua_State* luaVM) { return MapFindRef(m_VirtualMachineMap, luaVM) != nullptr; }; + CEvents* GetEvents() const noexcept { return m_pEvents; } CClientGUIManager* m_pGUIManager; diff --git a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp index 38a15493fad..d0a25c71a13 100644 --- a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp @@ -603,6 +603,7 @@ void CWorldRPCs::SetSyncIntervals(NetBitStreamInterface& bitStream) bitStream.Read(g_TickRateSettings.iKeySyncAnalogMove); bitStream.Read(g_TickRateSettings.iPedSyncerDistance); bitStream.Read(g_TickRateSettings.iUnoccupiedVehicleSyncerDistance); + bitStream.Read(g_TickRateSettings.cancelledDamageInterval); } void CWorldRPCs::SetMoonSize(NetBitStreamInterface& bitStream) diff --git a/Client/sdk/game/CPed.h b/Client/sdk/game/CPed.h index 7f032596b0b..b6028bb5594 100644 --- a/Client/sdk/game/CPed.h +++ b/Client/sdk/game/CPed.h @@ -309,4 +309,5 @@ class CPed : public virtual CPhysical virtual void Say(const ePedSpeechContext& speechId, float probability) = 0; virtual void SetInWaterFlags(bool inWater) = 0; + virtual bool IsPedCuttingWithChainsaw() const = 0; }; diff --git a/Server/mods/deathmatch/editor.conf b/Server/mods/deathmatch/editor.conf index 513bdeb5ebb..a38632c86f7 100644 --- a/Server/mods/deathmatch/editor.conf +++ b/Server/mods/deathmatch/editor.conf @@ -153,6 +153,10 @@ 100 + + 1000 + 1 @@ -296,6 +300,18 @@ Values: 0 - Off, 1 - Enabled. Default - 1 --> 1 + + 0 + + + 0 + + + 1 + diff --git a/Server/mods/deathmatch/local.conf b/Server/mods/deathmatch/local.conf index 812499c830a..ddff55ccb6c 100644 --- a/Server/mods/deathmatch/local.conf +++ b/Server/mods/deathmatch/local.conf @@ -153,6 +153,10 @@ 100 + + 1000 + 1 @@ -302,6 +306,18 @@ Values: 0 - Off, 1 - Enabled. Default - 1 --> 1 + + 0 + + + 0 + + + 1 + diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index afac77f2a46..ab7add9cd29 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -60,6 +60,7 @@ #include "packets/CPlayerListPacket.h" #include "packets/CPlayerClothesPacket.h" #include "packets/CPlayerWorldSpecialPropertyPacket.h" +#include "packets/CDamageCancelEventPacket.h" #include "packets/CServerInfoSyncPacket.h" #include "packets/CLuaPacket.h" #include "../utils/COpenPortsTester.h" @@ -1331,6 +1332,12 @@ bool CGame::ProcessPacket(CPacket& Packet) return true; } + case PACKET_ID_CANCEL_DAMAGE_EVENT: + { + Packet_CancelDamageEvent(static_cast(Packet)); + return true; + } + default: break; } @@ -1655,6 +1662,7 @@ void CGame::AddBuiltInEvents() m_Events.AddEvent("onPlayerChangesProtectedData", "element, key, value", nullptr, false); m_Events.AddEvent("onPlayerChangesWorldSpecialProperty", "property, enabled", nullptr, false); m_Events.AddEvent("onPlayerTeleport", "previousX, previousY, previousZ, currentX, currentY, currentZ", nullptr, false); + m_Events.AddEvent("onDamageEventCancelled", "attacker, damagedEntity, weapon, damage, resourceName", nullptr, false); // Ped events m_Events.AddEvent("onPedVehicleEnter", "vehicle, seat, jacked", NULL, false); @@ -4246,6 +4254,34 @@ void CGame::Packet_PlayerWorldSpecialProperty(CPlayerWorldSpecialPropertyPacket& player->CallEvent("onPlayerChangesWorldSpecialProperty", arguments, nullptr); } +void CGame::Packet_CancelDamageEvent(CDamageCancelEventPacket& packet) noexcept +{ + CPlayer* player = packet.GetSourcePlayer(); + if (!player) + return; + + CElement* damagedEntity = CElementIDs::GetElement(packet.GetDamagedEntityID()); + if (!damagedEntity) + return; + + CElement* attackerEntity = CElementIDs::GetElement(packet.GetAtackerEntityID()); + + CLuaArguments arguments; + if (attackerEntity) + arguments.PushElement(attackerEntity); + else + arguments.PushNil(); + + arguments.PushElement(damagedEntity); + arguments.PushNumber(packet.GetWeaponType()); + arguments.PushNumber(packet.GetDamage()); + + const std::string& resourceName = packet.GetResourceName(); + arguments.PushString(resourceName); + + player->CallEvent("onDamageEventCancelled", arguments, nullptr); +} + void CGame::Packet_PlayerModInfo(CPlayerModInfoPacket& Packet) { CPlayer* pPlayer = Packet.GetSourcePlayer(); @@ -4762,8 +4798,11 @@ void CGame::SendSyncSettings(CPlayer* pPlayer) uchar ucAllowShotgunDamageFix = true; std::uint8_t multiCommandHandlerPolicy = static_cast(m_pMainConfig->GetAllowMultiCommandHandlers()); + const SEVentDamageCancelledSettings& damageCancelledSettings = m_pMainConfig->GetEventDamageCancelledSettings(); + CSyncSettingsPacket packet(weaponTypesUsingBulletSync, ucVehExtrapolateEnabled, sVehExtrapolateBaseMs, sVehExtrapolatePercent, sVehExtrapolateMaxMs, - ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, multiCommandHandlerPolicy); + ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, multiCommandHandlerPolicy, damageCancelledSettings.triggerOnVehicleDamage == 1, damageCancelledSettings.triggerOnPedDamage == 1, damageCancelledSettings.triggerForDamageCalledEveryFrame == 1); + if (pPlayer) pPlayer->Send(packet); else diff --git a/Server/mods/deathmatch/logic/CGame.h b/Server/mods/deathmatch/logic/CGame.h index 07c4f227e65..7bda325753a 100644 --- a/Server/mods/deathmatch/logic/CGame.h +++ b/Server/mods/deathmatch/logic/CGame.h @@ -523,6 +523,7 @@ class CGame void Packet_PlayerNetworkStatus(class CPlayerNetworkStatusPacket& Packet); void Packet_PlayerResourceStart(class CPlayerResourceStartPacket& Packet); void Packet_PlayerWorldSpecialProperty(class CPlayerWorldSpecialPropertyPacket& packet) noexcept; + void Packet_CancelDamageEvent(class CDamageCancelEventPacket& packet) noexcept; static void PlayerCompleteConnect(CPlayer* pPlayer); diff --git a/Server/mods/deathmatch/logic/CMainConfig.cpp b/Server/mods/deathmatch/logic/CMainConfig.cpp index e40d2adb96c..c11160c93c6 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.cpp +++ b/Server/mods/deathmatch/logic/CMainConfig.cpp @@ -540,6 +540,10 @@ bool CMainConfig::Load() GetInteger(m_pRootNode, "allow_multi_command_handlers", m_allowMultiCommandHandlers); m_allowMultiCommandHandlers = Clamp(0, m_allowMultiCommandHandlers, 2); + GetInteger(m_pRootNode, "cancelled_damage_for_vehicles", m_eventDamageCancelledSettings.triggerOnVehicleDamage); + GetInteger(m_pRootNode, "cancelled_damage_for_peds", m_eventDamageCancelledSettings.triggerOnPedDamage); + GetInteger(m_pRootNode, "cancelled_damage_send_frame_based_dmg", m_eventDamageCancelledSettings.triggerForDamageCalledEveryFrame); + ApplyNetOptions(); return true; @@ -1512,6 +1516,7 @@ const std::vector& CMainConfig::GetIntSettingList() {true, true, 50, 100, 4000, "keysync_mouse_sync_interval", &g_TickRateSettings.iKeySyncRotation, &CMainConfig::OnTickRateChange}, {true, true, 50, 100, 4000, "keysync_analog_sync_interval", &g_TickRateSettings.iKeySyncAnalogMove, &CMainConfig::OnTickRateChange}, {true, true, 50, 100, 4000, "donkey_work_interval", &g_TickRateSettings.iNearListUpdate, &CMainConfig::OnTickRateChange}, + {true, true, 50, 1000, 4000, "cancelled_damage_send_interval", &g_TickRateSettings.cancelledDamageInterval, &CMainConfig::OnTickRateChange}, {true, true, 0, 0, 1, "bullet_sync", &m_bBulletSyncEnabled, &CMainConfig::OnTickRateChange}, {true, true, 0, 0, 120, "vehext_percent", &m_iVehExtrapolatePercent, &CMainConfig::OnTickRateChange}, {true, true, 0, 150, 500, "vehext_ping_limit", &m_iVehExtrapolatePingLimit, &CMainConfig::OnTickRateChange}, @@ -1535,7 +1540,10 @@ const std::vector& CMainConfig::GetIntSettingList() {true, true, 50, 1000, 5000, "player_triggered_event_interval", &m_iPlayerTriggeredEventIntervalMs, &CMainConfig::OnPlayerTriggeredEventIntervalChange}, {true, true, 1, 100, 1000, "max_player_triggered_events_per_interval", &m_iMaxPlayerTriggeredEventsPerInterval, &CMainConfig::OnPlayerTriggeredEventIntervalChange}, {true, true, 0, 1, 1, "resource_client_file_checks", &m_checkResourceClientFiles, nullptr}, - {true, true, 0, 1, 2, "allow_multi_command_handlers", &m_allowMultiCommandHandlers, &CMainConfig::OnAllowMultiCommandHandlersChange}, + {true, true, 0, 1, 2, "allow_multi_command_handlers", &m_allowMultiCommandHandlers, &CMainConfig::OnSettingChange}, + {true, true, 0, 0, 1, "cancelled_damage_for_vehicles", &m_eventDamageCancelledSettings.triggerOnVehicleDamage, &CMainConfig::OnSettingChange}, + {true, true, 0, 0, 1, "cancelled_damage_for_peds", &m_eventDamageCancelledSettings.triggerOnPedDamage, &CMainConfig::OnSettingChange}, + {true, true, 0, 0, 1, "cancelled_damage_send_frame_based_dmg", &m_eventDamageCancelledSettings.triggerForDamageCalledEveryFrame, &CMainConfig::OnSettingChange}, }; static std::vector settingsList; @@ -1585,7 +1593,7 @@ void CMainConfig::OnPlayerTriggeredEventIntervalChange() g_pGame->ApplyPlayerTriggeredEventIntervalChange(); } -void CMainConfig::OnAllowMultiCommandHandlersChange() +void CMainConfig::OnSettingChange() { g_pGame->SendSyncSettings(); } diff --git a/Server/mods/deathmatch/logic/CMainConfig.h b/Server/mods/deathmatch/logic/CMainConfig.h index 0d76fa2bf6a..776ae9fe90b 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.h +++ b/Server/mods/deathmatch/logic/CMainConfig.h @@ -39,6 +39,13 @@ struct SIntSetting PFN_SettingChangeCallback changeCallback; }; +struct SEVentDamageCancelledSettings +{ + int triggerOnVehicleDamage{false}; + int triggerOnPedDamage{false}; + int triggerForDamageCalledEveryFrame{true}; +}; + class CMainConfig : public CXMLConfig { public: @@ -68,25 +75,25 @@ class CMainConfig : public CXMLConfig unsigned int GetVoiceQuality() { return m_ucVoiceQuality; }; unsigned int GetVoiceBitrate() { return m_uiVoiceBitrate; }; - bool GetAseInternetPushEnabled() { return m_iAseMode == 2 && !IsFakeLagCommandEnabled(); } - bool GetAseInternetListenEnabled() { return m_iAseMode == 1 && !IsFakeLagCommandEnabled(); } - bool GetAseLanListenEnabled() { return m_bDontBroadcastLan ? false : true; } - unsigned short GetHTTPPort(); - eHTTPDownloadType GetHTTPDownloadType() { return m_ucHTTPDownloadType; }; - const std::string& GetHTTPDownloadURL() { return m_strHTTPDownloadURL; }; - int GetHTTPMaxConnectionsPerClient() { return m_iHTTPMaxConnectionsPerClient; }; - int GetHTTPThreadCount() { return m_iHTTPThreadCount; }; - int GetHTTPDosThreshold() { return m_iHTTPDosThreshold; }; - const SString& GetHTTPDosExclude() { return m_strHTTPDosExclude; }; - int GetEnableClientChecks() { return m_iEnableClientChecks; }; - const std::string& GetLogFile() { return m_strLogFile; }; - const std::string& GetAuthFile() { return m_strAuthFile; }; - bool GetJoinFloodProtectionEnabled() { return m_bJoinFloodProtectionEnabled; }; - bool GetScriptDebugLogEnabled() { return m_bScriptDebugLogEnabled && !m_strScriptDebugLogFile.empty(); }; - const std::string& GetScriptDebugLogFile() { return m_strScriptDebugLogFile; }; - unsigned int GetScriptDebugLogLevel() { return m_uiScriptDebugLogLevel; }; - const std::string& GetAccessControlListFile() { return m_strAccessControlListFile; }; - bool GetSerialVerificationEnabled() { return m_bVerifySerials; }; + bool GetAseInternetPushEnabled() { return m_iAseMode == 2 && !IsFakeLagCommandEnabled(); } + bool GetAseInternetListenEnabled() { return m_iAseMode == 1 && !IsFakeLagCommandEnabled(); } + bool GetAseLanListenEnabled() { return m_bDontBroadcastLan ? false : true; } + unsigned short GetHTTPPort(); + eHTTPDownloadType GetHTTPDownloadType() { return m_ucHTTPDownloadType; }; + const std::string& GetHTTPDownloadURL() { return m_strHTTPDownloadURL; }; + int GetHTTPMaxConnectionsPerClient() { return m_iHTTPMaxConnectionsPerClient; }; + int GetHTTPThreadCount() { return m_iHTTPThreadCount; }; + int GetHTTPDosThreshold() { return m_iHTTPDosThreshold; }; + const SString& GetHTTPDosExclude() { return m_strHTTPDosExclude; }; + int GetEnableClientChecks() { return m_iEnableClientChecks; }; + const std::string& GetLogFile() { return m_strLogFile; }; + const std::string& GetAuthFile() { return m_strAuthFile; }; + bool GetJoinFloodProtectionEnabled() { return m_bJoinFloodProtectionEnabled; }; + bool GetScriptDebugLogEnabled() { return m_bScriptDebugLogEnabled && !m_strScriptDebugLogFile.empty(); }; + const std::string& GetScriptDebugLogFile() { return m_strScriptDebugLogFile; }; + unsigned int GetScriptDebugLogLevel() { return m_uiScriptDebugLogLevel; }; + const std::string& GetAccessControlListFile() { return m_strAccessControlListFile; }; + bool GetSerialVerificationEnabled() { return m_bVerifySerials; }; const std::map& GetRulesForASE() const noexcept { return m_RulesForASEMap; }; bool IsDisableAC(const char* szTagAC) { return MapContains(m_DisableComboACMap, szTagAC); }; bool IsEnableDiagnostic(const char* szTag) { return MapContains(m_EnableDiagnosticMap, szTag); }; @@ -147,12 +154,14 @@ class CMainConfig : public CXMLConfig void OnTickRateChange(); void OnAseSettingChange(); void OnPlayerTriggeredEventIntervalChange(); - void OnAllowMultiCommandHandlersChange(); + void OnSettingChange(); int GetPlayerTriggeredEventInterval() const { return m_iPlayerTriggeredEventIntervalMs; } int GetMaxPlayerTriggeredEventsPerInterval() const { return m_iMaxPlayerTriggeredEventsPerInterval; } int GetAllowMultiCommandHandlers() const noexcept { return m_allowMultiCommandHandlers; } + SEVentDamageCancelledSettings GetEventDamageCancelledSettings() const noexcept { return m_eventDamageCancelledSettings; } + private: void RegisterCommand(const char* szName, FCommandHandler* pFunction, bool bRestricted, const char* szConsoleHelpText); bool GetSettingTable(const SString& strName, const char** szAttribNames, uint uiNumAttribNames, CLuaArguments* outTable); @@ -238,4 +247,5 @@ class CMainConfig : public CXMLConfig bool m_checkDuplicateSerials; int m_checkResourceClientFiles; int m_allowMultiCommandHandlers; + SEVentDamageCancelledSettings m_eventDamageCancelledSettings{}; }; diff --git a/Server/mods/deathmatch/logic/CPacketTranslator.cpp b/Server/mods/deathmatch/logic/CPacketTranslator.cpp index 2d12509c502..0fc5dae0e41 100644 --- a/Server/mods/deathmatch/logic/CPacketTranslator.cpp +++ b/Server/mods/deathmatch/logic/CPacketTranslator.cpp @@ -49,6 +49,7 @@ #include "packets/CPlayerNetworkStatusPacket.h" #include "packets/CPlayerResourceStartPacket.h" #include "packets/CPlayerWorldSpecialPropertyPacket.h" +#include "packets/CDamageCancelEventPacket.h" CPacketTranslator::CPacketTranslator(CPlayerManager* pPlayerManager) { @@ -217,6 +218,10 @@ CPacket* CPacketTranslator::Translate(const NetServerPlayerID& Socket, ePacketID pTemp = new CPlayerWorldSpecialPropertyPacket; break; + case PACKET_ID_CANCEL_DAMAGE_EVENT: + pTemp = new CDamageCancelEventPacket; + break; + default: break; } diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 52695b056fc..efcd792e611 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -11028,6 +11028,7 @@ bool CStaticFunctionDefinitions::SendSyncIntervals(CPlayer* pPlayer) BitStream.pBitStream->Write(g_TickRateSettings.iKeySyncAnalogMove); BitStream.pBitStream->Write(g_TickRateSettings.iPedSyncerDistance); BitStream.pBitStream->Write(g_TickRateSettings.iUnoccupiedVehicleSyncerDistance); + BitStream.pBitStream->Write(g_TickRateSettings.cancelledDamageInterval); pPlayer->Send(CLuaPacket(SET_SYNC_INTERVALS, *BitStream.pBitStream)); }; diff --git a/Server/mods/deathmatch/logic/packets/CDamageCancelEventPacket.cpp b/Server/mods/deathmatch/logic/packets/CDamageCancelEventPacket.cpp new file mode 100644 index 00000000000..d8d4dd809a0 --- /dev/null +++ b/Server/mods/deathmatch/logic/packets/CDamageCancelEventPacket.cpp @@ -0,0 +1,33 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/packets/CDamageCancelEventPacket.cpp + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CDamageCancelEventPacket.h" +#include + +bool CDamageCancelEventPacket::Read(NetBitStreamInterface& bitStream) noexcept +{ + bitStream.Read(m_damagedEntityID); + + if (bitStream.ReadBit()) + bitStream.Read(m_atackerEntityID); + + SWeaponTypeSync weaponType; + bitStream.Read(&weaponType); + m_weaponType = static_cast(weaponType.data.ucWeaponType); + + SFloatSync<8, 10> damage; + bitStream.Read(&damage); + m_damage = damage.data.fValue; + + bitStream.ReadString(m_resourceName); + + return true; +} diff --git a/Server/mods/deathmatch/logic/packets/CDamageCancelEventPacket.h b/Server/mods/deathmatch/logic/packets/CDamageCancelEventPacket.h new file mode 100644 index 00000000000..c006ba1f8fa --- /dev/null +++ b/Server/mods/deathmatch/logic/packets/CDamageCancelEventPacket.h @@ -0,0 +1,39 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/packets/CDamageCancelEventPacket.h + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "CPacket.h" +#include + +class CDamageCancelEventPacket final : public CPacket +{ +public: + CDamageCancelEventPacket() noexcept {} + + ePacketID GetPacketID() const noexcept { return PACKET_ID_CANCEL_DAMAGE_EVENT; } + unsigned long GetFlags() const noexcept { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; } + virtual ePacketOrdering GetPacketOrdering() const noexcept { return PACKET_ORDERING_DEFAULT; } + + bool Read(NetBitStreamInterface& bitStream) noexcept; + + ElementID GetDamagedEntityID() const noexcept { return m_damagedEntityID; } + ElementID GetAtackerEntityID() const noexcept { return m_atackerEntityID; } + eWeaponType GetWeaponType() const noexcept { return m_weaponType; } + float GetDamage() const noexcept { return m_damage; } + std::string GetResourceName() const noexcept { return m_resourceName; } + +private: + std::string m_resourceName{}; + ElementID m_damagedEntityID; + ElementID m_atackerEntityID{INVALID_ELEMENT_ID}; + float m_damage; + eWeaponType m_weaponType; +}; diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp index 55886337c0a..7119fd30627 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp @@ -15,7 +15,7 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs, short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix, - uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, std::uint8_t multiCommandHandlerPolicy) + uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, std::uint8_t multiCommandHandlerPolicy, bool triggerDamageEventCancelledForVehicles, bool triggerDamageEventCancelledForPeds, bool triggerDamageEventCancelledForDamageEveryFrame) { m_weaponTypesUsingBulletSync = weaponTypesUsingBulletSync; m_ucVehExtrapolateEnabled = ucVehExtrapolateEnabled; @@ -27,6 +27,9 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set& weaponType m_ucAllowDrivebyAnimationFix = ucAllowDrivebyAnimationFix; m_ucAllowShotgunDamageFix = ucAllowShotgunDamageFix; m_multiCommandHandlerPolicy = multiCommandHandlerPolicy; + m_triggerDamageEventCancelledForVehicles = triggerDamageEventCancelledForVehicles; + m_triggerEventDamageCancelledForPeds = triggerDamageEventCancelledForPeds; + m_triggerDamageEventCancelledForDamageEveryFrame = triggerDamageEventCancelledForDamageEveryFrame; } bool CSyncSettingsPacket::Read(NetBitStreamInterface& BitStream) @@ -53,6 +56,8 @@ bool CSyncSettingsPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(m_ucAllowDrivebyAnimationFix); BitStream.Write(m_ucAllowShotgunDamageFix); BitStream.Write(m_multiCommandHandlerPolicy); - + BitStream.WriteBit(m_triggerDamageEventCancelledForVehicles); + BitStream.WriteBit(m_triggerEventDamageCancelledForPeds); + BitStream.WriteBit(m_triggerDamageEventCancelledForDamageEveryFrame); return true; } diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h index f20ef002c59..a7385084d21 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h @@ -19,7 +19,7 @@ class CSyncSettingsPacket final : public CPacket CSyncSettingsPacket(){}; CSyncSettingsPacket(const std::set& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs, short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix, - uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, std::uint8_t multiCommandHandlerPolicy); + uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, std::uint8_t multiCommandHandlerPolicy, bool triggerDamageEventCancelledForVehicles, bool triggerDamageEventCancelledForPeds, bool triggerDamageEventCancelledForDamageEveryFrame); ePacketID GetPacketID() const { return PACKET_ID_SYNC_SETTINGS; }; unsigned long GetFlags() const { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; }; @@ -37,4 +37,7 @@ class CSyncSettingsPacket final : public CPacket uchar m_ucAllowDrivebyAnimationFix; uchar m_ucAllowShotgunDamageFix; std::uint8_t m_multiCommandHandlerPolicy; + bool m_triggerDamageEventCancelledForVehicles{}; + bool m_triggerEventDamageCancelledForPeds{}; + bool m_triggerDamageEventCancelledForDamageEveryFrame{}; }; diff --git a/Server/mods/deathmatch/mtaserver.conf b/Server/mods/deathmatch/mtaserver.conf index 21b736a6bb1..688a5f1c83c 100644 --- a/Server/mods/deathmatch/mtaserver.conf +++ b/Server/mods/deathmatch/mtaserver.conf @@ -158,6 +158,10 @@ 100 + + 1000 + 1 @@ -322,6 +326,18 @@ Values: 0 - Off, 1 - Enabled. Default - 1 --> 1 + + 0 + + + 0 + + + 0 + diff --git a/Shared/mods/deathmatch/logic/CTickRateSettings.h b/Shared/mods/deathmatch/logic/CTickRateSettings.h index bc6e8d7a411..f6c3bd22857 100644 --- a/Shared/mods/deathmatch/logic/CTickRateSettings.h +++ b/Shared/mods/deathmatch/logic/CTickRateSettings.h @@ -27,6 +27,7 @@ struct CTickRateSettings iUnoccupiedVehicleSyncerDistance = 130; iVehicleContactSyncRadius = 30; playerTeleportAlert = 100; + cancelledDamageInterval = 1000; } int iPureSync; @@ -43,6 +44,7 @@ struct CTickRateSettings int iUnoccupiedVehicleSyncerDistance; int iVehicleContactSyncRadius; int playerTeleportAlert; + int cancelledDamageInterval; }; extern CTickRateSettings g_TickRateSettings; diff --git a/Shared/mods/deathmatch/logic/Enums.cpp b/Shared/mods/deathmatch/logic/Enums.cpp index 8bb99db974f..921cd07c074 100644 --- a/Shared/mods/deathmatch/logic/Enums.cpp +++ b/Shared/mods/deathmatch/logic/Enums.cpp @@ -233,4 +233,5 @@ ADD_ENUM1(PACKET_ID_SERVER_INFO_SYNC) ADD_ENUM1(PACKET_ID_DISCORD_JOIN) ADD_ENUM1(PACKET_ID_PLAYER_RESOURCE_START) ADD_ENUM1(PACKET_ID_PLAYER_WORLD_SPECIAL_PROPERTY) +ADD_ENUM1(PACKET_ID_CANCEL_DAMAGE_EVENT) IMPLEMENT_ENUM_END("ePacketID") diff --git a/Shared/sdk/net/Packets.h b/Shared/sdk/net/Packets.h index 2f587cad140..443d3807572 100644 --- a/Shared/sdk/net/Packets.h +++ b/Shared/sdk/net/Packets.h @@ -177,5 +177,7 @@ enum ePacketID PACKET_ID_SERVER_INFO_SYNC, PACKET_ID_DISCORD_JOIN, PACKET_ID_PLAYER_RESOURCE_START, - PACKET_ID_PLAYER_WORLD_SPECIAL_PROPERTY + PACKET_ID_PLAYER_WORLD_SPECIAL_PROPERTY, + + PACKET_ID_CANCEL_DAMAGE_EVENT, };