diff --git a/Client/mods/deathmatch/logic/CNetAPI.cpp b/Client/mods/deathmatch/logic/CNetAPI.cpp index 74320154034..ce802ac2ba9 100644 --- a/Client/mods/deathmatch/logic/CNetAPI.cpp +++ b/Client/mods/deathmatch/logic/CNetAPI.cpp @@ -132,7 +132,7 @@ bool CNetAPI::ProcessPacket(unsigned char bytePacketID, NetBitStreamInterface& B if (!BitStream.Read(id)) return true; - auto* player = m_pPlayerManager->Get(id); + CClientPlayer* player = m_pPlayerManager->Get(id); if (!player) return true; @@ -2236,48 +2236,37 @@ void CNetAPI::ReadBulletsync(CClientPlayer* player, NetBitStreamInterface& strea if (!stream.Read(weapon) || !CClientWeaponManager::HasWeaponBulletSync(weapon)) return; - const auto type = static_cast(weapon); + auto type = static_cast(weapon); + + SPositionSync startPosition; + SPositionSync endPosition; + if (!stream.Read(&startPosition) || !stream.Read(&endPosition)) + return; - CVector start; - CVector end; - if (!stream.Read(reinterpret_cast(&start), sizeof(CVector)) || !stream.Read(reinterpret_cast(&end), sizeof(CVector)) || !start.IsValid() || - !end.IsValid()) + if (!startPosition.data.vecPosition.IsValid() || !endPosition.data.vecPosition.IsValid()) return; - std::uint8_t order = 0; - if (!stream.Read(order)) + // Huge coordinates can crash other players + if (!startPosition.data.vecPosition.IsInWorldBounds(true) || !endPosition.data.vecPosition.IsInWorldBounds(true)) return; - float damage = 0.0f; + // 200 is MAX weapon damage + SFloatAsBitsSync<8> damage(0, 200.0f, true, false); + damage.data.fValue = 0.0f; + std::uint8_t zone = 0; CClientPlayer* damaged = nullptr; if (stream.ReadBit()) { ElementID id = INVALID_ELEMENT_ID; - if (!stream.Read(damage) || !stream.Read(zone) || !stream.Read(id)) + if (!stream.Read(&damage) || !stream.Read(zone) || !stream.Read(id)) return; damaged = DynamicCast(CElementIDs::GetElement(id)); } - bool duplicate = false; - - if (start == player->m_vecPrevBulletSyncStart && end == player->m_vecPrevBulletSyncEnd) - duplicate = true; - - player->m_vecPrevBulletSyncStart = start; - player->m_vecPrevBulletSyncEnd = end; - - if (static_cast(order - player->m_ucPrevBulletSyncOrderCounter) > 0) - duplicate = false; - - player->m_ucPrevBulletSyncOrderCounter = order; - - if (duplicate) - return; - - player->DischargeWeapon(type, start, end, damage, zone, damaged); + player->DischargeWeapon(type, startPosition.data.vecPosition, endPosition.data.vecPosition, damage.data.fValue, zone, damaged); } void CNetAPI::ReadWeaponBulletsync(CClientPlayer* player, NetBitStreamInterface& stream) @@ -2290,39 +2279,47 @@ void CNetAPI::ReadWeaponBulletsync(CClientPlayer* player, NetBitStreamInterface& if (!weapon || !CClientWeaponManager::HasWeaponBulletSync(weapon->GetWeaponType())) return; - CVector start; - CVector end; - if (!stream.Read(reinterpret_cast(&start), sizeof(CVector)) || !stream.Read(reinterpret_cast(&end), sizeof(CVector)) || !start.IsValid() || - !end.IsValid()) + SPositionSync startPosition; + SPositionSync endPosition; + if (!stream.Read(&startPosition) || !stream.Read(&endPosition)) return; - uint8_t order = 0; - if (!stream.Read(order)) + if (!startPosition.data.vecPosition.IsValid() || !endPosition.data.vecPosition.IsValid()) return; - weapon->FireInstantHit(start, end, false, true); + // Huge coordinates can crash other players + if (!startPosition.data.vecPosition.IsInWorldBounds(true) || !endPosition.data.vecPosition.IsInWorldBounds(true)) + return; + + weapon->FireInstantHit(startPosition.data.vecPosition, endPosition.data.vecPosition, false, true); } void CNetAPI::SendBulletSyncFire(eWeaponType weapon, const CVector& start, const CVector& end, float damage, std::uint8_t zone, CClientPlayer* damaged) { - auto* stream = g_pNet->AllocateNetBitStream(); + NetBitStreamInterface* stream = g_pNet->AllocateNetBitStream(); + SPositionSync startPosition; + startPosition.data.vecPosition = start; + + SPositionSync endPosition; + endPosition.data.vecPosition = end; - stream->Write(static_cast(weapon)); - stream->Write(reinterpret_cast(&start), sizeof(CVector)); - stream->Write(reinterpret_cast(&end), sizeof(CVector)); - stream->Write(m_ucBulletSyncOrderCounter++); + stream->Write(static_cast(weapon)); - if (damage > 0.0f && damaged) + stream->Write(&startPosition); + stream->Write(&endPosition); + + bool hasDamaged = damaged && damage > 0.0f; + stream->WriteBit(hasDamaged); + + if (hasDamaged) { - stream->WriteBit(true); - stream->Write(damage); + SFloatAsBitsSync<8> damageF(0, 200.0f, false); + damageF.data.fValue = damage; + + stream->Write(&damageF); stream->Write(zone); stream->Write(damaged->GetID()); } - else - { - stream->WriteBit(false); - } g_pNet->SendPacket(PACKET_ID_PLAYER_BULLETSYNC, stream, PACKET_PRIORITY_MEDIUM, PACKET_RELIABILITY_RELIABLE); g_pNet->DeallocateNetBitStream(stream); @@ -2333,12 +2330,17 @@ void CNetAPI::SendBulletSyncCustomWeaponFire(CClientWeapon* weapon, const CVecto if (weapon->IsLocalEntity()) return; - auto* stream = g_pNet->AllocateNetBitStream(); + NetBitStreamInterface* stream = g_pNet->AllocateNetBitStream(); + + SPositionSync startPosition; + startPosition.data.vecPosition = start; + + SPositionSync endPosition; + endPosition.data.vecPosition = end; stream->Write(weapon->GetID()); - stream->Write(reinterpret_cast(&start), sizeof(CVector)); - stream->Write(reinterpret_cast(&end), sizeof(CVector)); - stream->Write(m_ucCustomWeaponBulletSyncOrderCounter++); + stream->Write(&startPosition); + stream->Write(&endPosition); g_pNet->SendPacket(PACKET_ID_WEAPON_BULLETSYNC, stream, PACKET_PRIORITY_MEDIUM, PACKET_RELIABILITY_RELIABLE); g_pNet->DeallocateNetBitStream(stream); diff --git a/Client/mods/deathmatch/logic/CNetAPI.h b/Client/mods/deathmatch/logic/CNetAPI.h index d3ed4cec57c..988412743f4 100644 --- a/Client/mods/deathmatch/logic/CNetAPI.h +++ b/Client/mods/deathmatch/logic/CNetAPI.h @@ -122,6 +122,4 @@ class CNetAPI CControllerState m_LastSentControllerState; float m_fLastSentCameraRotation; float m_fLastSentAimY; - uchar m_ucBulletSyncOrderCounter; - uchar m_ucCustomWeaponBulletSyncOrderCounter; }; diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 84deaf29b2f..6cf922eeaf3 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -2527,10 +2527,11 @@ void CGame::Packet_Keysync(CKeysyncPacket& Packet) void CGame::Packet_Bulletsync(CBulletsyncPacket& packet) { - auto* player = packet.GetSourcePlayer(); + CPlayer* player = packet.GetSourcePlayer(); if (!player || !player->IsJoined()) return; + // Early return when the player attempts to fire a weapon they do not have const auto type = static_cast(packet.m_weapon); if (!player->HasWeaponType(type)) return; @@ -2546,7 +2547,7 @@ void CGame::Packet_Bulletsync(CBulletsyncPacket& packet) const auto level = player->GetPlayerStat(stat); auto* stats = g_pGame->GetWeaponStatManager()->GetWeaponStatsFromSkillLevel(packet.m_weapon, level); - const float distanceSq = (packet.m_start - packet.m_end).LengthSquared(); + const float distanceSq = (packet.m_start.data.vecPosition - packet.m_end.data.vecPosition).LengthSquared(); const float range = stats->GetWeaponRange(); const float rangeSq = range * range; @@ -2554,40 +2555,39 @@ void CGame::Packet_Bulletsync(CBulletsyncPacket& packet) if (distanceSq > maxRangeSq) return; + RelayNearbyPacket(packet); + CLuaArguments args; args.PushNumber(packet.m_weapon); - args.PushNumber(packet.m_end.fX); - args.PushNumber(packet.m_end.fY); - args.PushNumber(packet.m_end.fZ); + args.PushNumber(packet.m_end.data.vecPosition.fX); + args.PushNumber(packet.m_end.data.vecPosition.fY); + args.PushNumber(packet.m_end.data.vecPosition.fZ); if (packet.m_damaged == INVALID_ELEMENT_ID) args.PushNil(); else args.PushElement(CElementIDs::GetElement(packet.m_damaged)); - args.PushNumber(packet.m_start.fX); - args.PushNumber(packet.m_start.fY); - args.PushNumber(packet.m_start.fZ); + args.PushNumber(packet.m_start.data.vecPosition.fX); + args.PushNumber(packet.m_start.data.vecPosition.fY); + args.PushNumber(packet.m_start.data.vecPosition.fZ); player->CallEvent("onPlayerWeaponFire", args); } void CGame::Packet_WeaponBulletsync(CCustomWeaponBulletSyncPacket& packet) { - auto* player = packet.GetSourcePlayer(); + CPlayer* player = packet.GetSourcePlayer(); if (!player || !player->IsJoined()) return; if (player != packet.GetWeaponOwner()) return; - auto* weapon = packet.GetWeapon(); + CCustomWeapon* weapon = packet.GetWeapon(); if (weapon->GetAmmo() <= 0) return; - if (weapon->GetClipAmmo() <= 0) - return; - CLuaArguments args; args.PushElement(player); diff --git a/Server/mods/deathmatch/logic/CWeaponStatManager.cpp b/Server/mods/deathmatch/logic/CWeaponStatManager.cpp index 0aa0aef874b..f316e87ec96 100644 --- a/Server/mods/deathmatch/logic/CWeaponStatManager.cpp +++ b/Server/mods/deathmatch/logic/CWeaponStatManager.cpp @@ -1643,7 +1643,7 @@ float CWeaponStatManager::GetWeaponRangeFromSkillLevel(eWeaponType eWeapon, floa return fWeaponRange; } -bool CWeaponStatManager::HasWeaponBulletSync(uint32_t weaponID) noexcept +bool CWeaponStatManager::HasWeaponBulletSync(std::uint8_t weaponID) noexcept { return weaponID >= 22 && weaponID <= 34; } diff --git a/Server/mods/deathmatch/logic/CWeaponStatManager.h b/Server/mods/deathmatch/logic/CWeaponStatManager.h index 61ef6db791b..b6be412d48a 100644 --- a/Server/mods/deathmatch/logic/CWeaponStatManager.h +++ b/Server/mods/deathmatch/logic/CWeaponStatManager.h @@ -91,7 +91,7 @@ class CWeaponStatManager // Static Methods static eStats GetSkillStatIndex(eWeaponType weapon); - static bool HasWeaponBulletSync(uint32_t weaponID) noexcept; + static bool HasWeaponBulletSync(std::uint8_t weaponID) noexcept; private: std::list m_OriginalWeaponData; diff --git a/Server/mods/deathmatch/logic/net/CSimBulletsyncPacket.cpp b/Server/mods/deathmatch/logic/net/CSimBulletsyncPacket.cpp index 1d5c4735e95..78c452608df 100644 --- a/Server/mods/deathmatch/logic/net/CSimBulletsyncPacket.cpp +++ b/Server/mods/deathmatch/logic/net/CSimBulletsyncPacket.cpp @@ -9,34 +9,49 @@ #include "StdInc.h" #include "SimHeaders.h" -#include "CPickupManager.h" #include "CWeaponStatManager.h" +#include CSimBulletsyncPacket::CSimBulletsyncPacket(ElementID id) : m_id(id) { + m_cache.damage.data.fValue = 0.0f; } bool CSimBulletsyncPacket::Read(NetBitStreamInterface& stream) { - char type = 0; - if (!stream.Read(type) || !CWeaponStatManager::HasWeaponBulletSync(type)) + std::uint8_t weaponType = 0; + if (!stream.Read(weaponType) || !CWeaponStatManager::HasWeaponBulletSync(weaponType)) return false; - m_cache.weapon = static_cast(type); + m_cache.weapon = static_cast(weaponType); - if (!stream.Read(reinterpret_cast(&m_cache.start), sizeof(CVector)) || !stream.Read(reinterpret_cast(&m_cache.end), sizeof(CVector))) + if (!stream.Read(&m_cache.start) || !stream.Read(&m_cache.end)) return false; - if (!m_cache.start.IsValid() || !m_cache.end.IsValid()) + if (!m_cache.start.data.vecPosition.IsValid() || !m_cache.end.data.vecPosition.IsValid()) return false; - if (!stream.Read(m_cache.order)) + // Huge coordinates can crash other players + if (!m_cache.start.data.vecPosition.IsInWorldBounds(true) || !m_cache.end.data.vecPosition.IsInWorldBounds(true)) + return false; + + if (!CBulletsyncPacket::ValidateTrajectory(m_cache.start.data.vecPosition, m_cache.end.data.vecPosition)) return false; if (stream.ReadBit()) { - stream.Read(m_cache.damage); + stream.Read(&m_cache.damage); + + if (!std::isfinite(m_cache.damage.data.fValue)) + return false; + + if (m_cache.damage.data.fValue < 0.0f || m_cache.damage.data.fValue > CBulletsyncPacket::MAX_DAMAGE) + return false; + stream.Read(m_cache.zone); + if (m_cache.zone > CBulletsyncPacket::MAX_BODY_ZONE) + return false; + stream.Read(m_cache.damaged); } @@ -46,22 +61,19 @@ bool CSimBulletsyncPacket::Read(NetBitStreamInterface& stream) bool CSimBulletsyncPacket::Write(NetBitStreamInterface& stream) const { stream.Write(m_id); - stream.Write(static_cast(m_cache.weapon)); - stream.Write(reinterpret_cast(&m_cache.start), sizeof(CVector)); - stream.Write(reinterpret_cast(&m_cache.end), sizeof(CVector)); - stream.Write(m_cache.order); + stream.Write(static_cast(m_cache.weapon)); + stream.Write(&m_cache.start); + stream.Write(&m_cache.end); + + bool hasDamaged = m_cache.damage.data.fValue > 0.0f && m_cache.damaged != INVALID_ELEMENT_ID; - if (m_cache.damage > 0 && m_cache.damaged != INVALID_ELEMENT_ID) + stream.WriteBit(hasDamaged); + if (hasDamaged) { - stream.WriteBit(true); - stream.Write(m_cache.damage); + stream.Write(&m_cache.damage); stream.Write(m_cache.zone); stream.Write(m_cache.damaged); } - else - { - stream.WriteBit(false); - } return true; } diff --git a/Server/mods/deathmatch/logic/net/CSimBulletsyncPacket.h b/Server/mods/deathmatch/logic/net/CSimBulletsyncPacket.h index 9c920151de6..a035080e7f4 100644 --- a/Server/mods/deathmatch/logic/net/CSimBulletsyncPacket.h +++ b/Server/mods/deathmatch/logic/net/CSimBulletsyncPacket.h @@ -25,12 +25,11 @@ class CSimBulletsyncPacket : public CSimPacket struct { - eWeaponType weapon = eWeaponType::WEAPONTYPE_UNARMED; - CVector start{}; - CVector end{}; - std::uint8_t order{}; - float damage{}; - uchar zone{}; - ElementID damaged = INVALID_ELEMENT_ID; + eWeaponType weapon = eWeaponType::WEAPONTYPE_UNARMED; + SPositionSync start{}; + SPositionSync end{}; + SFloatAsBitsSync<8> damage{0.0f, 200.0f, true, false}; + std::uint8_t zone{}; + ElementID damaged = INVALID_ELEMENT_ID; } m_cache; }; diff --git a/Server/mods/deathmatch/logic/packets/CBulletsyncPacket.cpp b/Server/mods/deathmatch/logic/packets/CBulletsyncPacket.cpp index a185a2d892b..73a3a1dbf79 100644 --- a/Server/mods/deathmatch/logic/packets/CBulletsyncPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CBulletsyncPacket.cpp @@ -17,49 +17,23 @@ #include "CElement.h" #include "CWeaponNames.h" -CBulletsyncPacket::CBulletsyncPacket(CPlayer* player) - : m_weapon(WEAPONTYPE_UNARMED), m_start(), m_end(), m_order(0), m_damage(0.0f), m_zone(0), m_damaged(INVALID_ELEMENT_ID) +CBulletsyncPacket::CBulletsyncPacket(CPlayer* player) : m_weapon(WEAPONTYPE_UNARMED), m_start(), m_end(), m_zone(0), m_damaged(INVALID_ELEMENT_ID) { m_pSourceElement = player; } -bool CBulletsyncPacket::IsValidVector(const CVector& vec) noexcept +bool CBulletsyncPacket::ValidateTrajectory(const CVector& start, const CVector& end) noexcept { - if (!vec.IsValid()) - return false; - - if (IsNaN(vec.fX)) - return false; - - if (IsNaN(vec.fY)) - return false; - - if (IsNaN(vec.fZ)) - return false; - - return true; -} - -bool CBulletsyncPacket::IsValidWeaponId(unsigned char weaponId) noexcept -{ - return CWeaponStatManager::HasWeaponBulletSync(static_cast(weaponId)); -} - -bool CBulletsyncPacket::ValidateTrajectory() const noexcept -{ - const float dx = m_end.fX - m_start.fX; - const float dy = m_end.fY - m_start.fY; - const float dz = m_end.fZ - m_start.fZ; + const float dx = end.fX - start.fX; + const float dy = end.fY - start.fY; + const float dz = end.fZ - start.fZ; const float movementSq = (dx * dx) + (dy * dy) + (dz * dz); - if (IsNaN(movementSq)) + if (!std::isfinite(movementSq)) return false; - if (movementSq < MIN_DISTANCE_SQ) - return false; - - if (movementSq > MAX_DISTANCE_SQ) + if (movementSq < MIN_DISTANCE_SQ || movementSq > MAX_DISTANCE_SQ) return false; return true; @@ -67,35 +41,33 @@ bool CBulletsyncPacket::ValidateTrajectory() const noexcept void CBulletsyncPacket::ResetDamageData() noexcept { - m_damage = 0.0f; + m_damage.data.fValue = 0.0f; m_zone = 0; m_damaged = INVALID_ELEMENT_ID; } bool CBulletsyncPacket::ReadWeaponAndPositions(NetBitStreamInterface& stream) { - unsigned char type = 0; - if (!stream.Read(type)) + std::uint8_t weaponType = 0; + if (!stream.Read(weaponType)) return false; - if (!IsValidWeaponId(type)) + if (!CWeaponStatManager::HasWeaponBulletSync(weaponType)) return false; - m_weapon = static_cast(type); + m_weapon = static_cast(weaponType); - if (!stream.Read(reinterpret_cast(&m_start), sizeof(CVector))) + if (!stream.Read(&m_start) || !stream.Read(&m_end)) return false; - if (!stream.Read(reinterpret_cast(&m_end), sizeof(CVector))) + if (!m_start.data.vecPosition.IsValid() || !m_end.data.vecPosition.IsValid()) return false; - if (!IsValidVector(m_start)) + // Huge coordinates can crash other players + if (!m_start.data.vecPosition.IsInWorldBounds(true) || !m_end.data.vecPosition.IsInWorldBounds(true)) return false; - if (!IsValidVector(m_end)) - return false; - - if (!ValidateTrajectory()) + if (!ValidateTrajectory(m_start.data.vecPosition, m_end.data.vecPosition)) return false; return true; @@ -109,17 +81,17 @@ bool CBulletsyncPacket::ReadOptionalDamage(NetBitStreamInterface& stream) return true; } - stream.Read(m_damage); + stream.Read(&m_damage); stream.Read(m_zone); stream.Read(m_damaged); - if (IsNaN(m_damage)) + if (!std::isfinite(m_damage.data.fValue)) { ResetDamageData(); return false; } - if (m_damage < 0.0f || m_damage > MAX_DAMAGE) + if (m_damage.data.fValue < 0.0f || m_damage.data.fValue > MAX_DAMAGE) { ResetDamageData(); return false; @@ -131,12 +103,6 @@ bool CBulletsyncPacket::ReadOptionalDamage(NetBitStreamInterface& stream) return false; } - if (m_damaged == 0) - { - ResetDamageData(); - return false; - } - // Check that target element exists (if specified) // Note: m_damaged can be INVALID_ELEMENT_ID when shooting at ground/world if (m_damaged != INVALID_ELEMENT_ID) @@ -160,48 +126,36 @@ bool CBulletsyncPacket::Read(NetBitStreamInterface& stream) CPlayer* pPlayer = static_cast(m_pSourceElement); if (pPlayer) - { + // Check if player is spawned and alive - if (!pPlayer->IsSpawned() || pPlayer->IsDead()) + if (!pPlayer || !pPlayer->IsSpawned() || pPlayer->IsDead()) return false; - // Check player position is reasonable relative to bullet start - const CVector& playerPos = pPlayer->GetPosition(); - const float maxShootDistance = 50.0f; // Max distance from player to bullet start - - // This check will be done after we read positions - } - + // Check player position is reasonable relative to bullet start if (!ReadWeaponAndPositions(stream)) return false; // Now validate player position relative to shot origin - if (pPlayer) - { - const CVector& playerPos = pPlayer->GetPosition(); - float dx = m_start.fX - playerPos.fX; - float dy = m_start.fY - playerPos.fY; - float dz = m_start.fZ - playerPos.fZ; - float distSq = dx * dx + dy * dy + dz * dz; - - // Allow larger distance if player is in vehicle (vehicle guns like Hunter have offsets of ~5m, - // plus vehicle size, plus network lag compensation) - const float maxShootDistanceSq = pPlayer->GetOccupiedVehicle() ? (100.0f * 100.0f) : (50.0f * 50.0f); - if (distSq > maxShootDistanceSq) - return false; + const CVector& playerPos = pPlayer->GetPosition(); + float dx = m_start.data.vecPosition.fX - playerPos.fX; + float dy = m_start.data.vecPosition.fY - playerPos.fY; + float dz = m_start.data.vecPosition.fZ - playerPos.fZ; + float distSq = dx * dx + dy * dy + dz * dz; - // Check if player has this weapon - if (!pPlayer->HasWeaponType(static_cast(m_weapon))) - return false; + // Allow larger distance if player is in vehicle (vehicle guns like Hunter have offsets of ~5m, + // plus vehicle size, plus network lag compensation) + const float maxShootDistanceSq = pPlayer->GetOccupiedVehicle() ? (100.0f * 100.0f) : (50.0f * 50.0f); + if (distSq > maxShootDistanceSq) + return false; - // Check if weapon has ammo - const auto type = static_cast(m_weapon); - const auto slot = CWeaponNames::GetSlotFromWeapon(type); - if (pPlayer->GetWeaponTotalAmmo(slot) <= 0) - return false; - } + // Check if player has this weapon + if (!pPlayer->HasWeaponType(static_cast(m_weapon))) + return false; - if (!stream.Read(m_order)) + // Check if weapon has ammo + const auto type = static_cast(m_weapon); + const auto slot = CWeaponNames::GetSlotFromWeapon(type); + if (pPlayer->GetWeaponTotalAmmo(slot) <= 0) return false; if (!ReadOptionalDamage(stream)) @@ -224,34 +178,31 @@ bool CBulletsyncPacket::Write(NetBitStreamInterface& stream) const if (id == INVALID_ELEMENT_ID) return false; - if (id == 0) - return false; - - if (!IsValidVector(m_start)) + if (!m_start.data.vecPosition.IsValid() || !m_end.data.vecPosition.IsValid()) return false; - if (!IsValidVector(m_end)) + // Huge coordinates can crash other players + if (!m_start.data.vecPosition.IsInWorldBounds(true) || !m_end.data.vecPosition.IsInWorldBounds(true)) return false; - if (!ValidateTrajectory()) + if (!ValidateTrajectory(m_start.data.vecPosition, m_end.data.vecPosition)) return false; - const unsigned char weaponType = static_cast(m_weapon); - if (!IsValidWeaponId(weaponType)) + auto weaponType = static_cast(m_weapon); + if (!CWeaponStatManager::HasWeaponBulletSync(weaponType)) return false; stream.Write(id); stream.Write(weaponType); - stream.Write(reinterpret_cast(&m_start), sizeof(CVector)); - stream.Write(reinterpret_cast(&m_end), sizeof(CVector)); - stream.Write(m_order); + stream.Write(&m_start); + stream.Write(&m_end); - const bool hasDamage = (m_damage > EPSILON) && (m_damaged != INVALID_ELEMENT_ID); + const bool hasDamage = (m_damage.data.fValue > 0.0f) && (m_damaged != INVALID_ELEMENT_ID); stream.WriteBit(hasDamage); if (hasDamage) { - stream.Write(m_damage); + stream.Write(&m_damage); stream.Write(m_zone); stream.Write(m_damaged); } diff --git a/Server/mods/deathmatch/logic/packets/CBulletsyncPacket.h b/Server/mods/deathmatch/logic/packets/CBulletsyncPacket.h index 0f46f8e9646..2c5d4bc8213 100644 --- a/Server/mods/deathmatch/logic/packets/CBulletsyncPacket.h +++ b/Server/mods/deathmatch/logic/packets/CBulletsyncPacket.h @@ -16,6 +16,7 @@ #include "CPacket.h" #include "CCommon.h" +#include "net/SyncStructures.h" class CBulletsyncPacket final : public CPacket { @@ -37,24 +38,20 @@ class CBulletsyncPacket final : public CPacket bool Read(NetBitStreamInterface& stream) override; bool Write(NetBitStreamInterface& stream) const override; + static bool ValidateTrajectory(const CVector& start, const CVector& end) noexcept; + private: bool ReadWeaponAndPositions(NetBitStreamInterface& stream); bool ReadOptionalDamage(NetBitStreamInterface& stream); - bool ValidateTrajectory() const noexcept; void ResetDamageData() noexcept; - static constexpr bool IsNaN(float value) noexcept { return value != value; } - static bool IsValidVector(const CVector& vec) noexcept; - static bool IsValidWeaponId(unsigned char weaponId) noexcept; - public: - eWeaponType m_weapon{}; - CVector m_start{}; - CVector m_end{}; - std::uint8_t m_order{}; - float m_damage{}; - std::uint8_t m_zone{}; - ElementID m_damaged{INVALID_ELEMENT_ID}; + eWeaponType m_weapon{}; + SPositionSync m_start{}; + SPositionSync m_end{}; + SFloatAsBitsSync<8> m_damage{0.0f, 200.0f, true, false}; + std::uint8_t m_zone{}; + ElementID m_damaged{INVALID_ELEMENT_ID}; }; #endif // __CBULLETSYNCPACKET_H diff --git a/Shared/sdk/CVector.h b/Shared/sdk/CVector.h index fb55ede23b3..d80adf535eb 100644 --- a/Shared/sdk/CVector.h +++ b/Shared/sdk/CVector.h @@ -197,16 +197,20 @@ class CVector return false; } - bool IsValid() const + [[nodiscard]] bool IsValid() const noexcept { return std::isfinite(fX) && std::isfinite(fY) && std::isfinite(fZ); } + + // Checks if the vector is within the world bounds. + // If maxLimit = false (default), it checks coordinates in the range -3000 to 3000. + // If maxLimit = true, it checks the full map range from -8192 to 8192. + // Currently, the effective map size is limited to 16384x16384 due to synchronization constraints. + [[nodiscard]] bool IsInWorldBounds(bool maxLimit = false) const noexcept { - const float values[3] = {fX, fY, fZ}; - for (std::size_t i = 0; i < 3; ++i) - { - if (std::isnan(values[i]) || std::isinf(values[i])) - return false; - } + const float minXY = maxLimit ? -8192.0f : -3000.0f; + const float maxXY = maxLimit ? 8192.0f : 3000.0f; + const float minZ = -100.0f; + const float maxZ = 8192.0f; - return true; + return fX >= minXY && fX <= maxXY && fY >= minXY && fY <= maxXY && fZ >= minZ && fZ <= maxZ; } constexpr CVector operator+(const CVector& vecRight) const noexcept { return CVector(fX + vecRight.fX, fY + vecRight.fY, fZ + vecRight.fZ); }