Skip to content

Commit 7004014

Browse files
committed
More checks for bulletsync
1 parent 2bb356c commit 7004014

File tree

3 files changed

+272
-31
lines changed

3 files changed

+272
-31
lines changed

Server/mods/deathmatch/logic/CElement.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class CElement
9797
void SetIsBeingDeleted(bool bBeingDeleted) { m_bIsBeingDeleted = bBeingDeleted; };
9898
virtual void Unlink() = 0;
9999

100-
ElementID GetID() { return m_ID; };
100+
ElementID GetID() const { return m_ID; };
101101

102102
virtual const CVector& GetPosition();
103103
virtual void SetPosition(const CVector& vecPosition);
Lines changed: 238 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/*****************************************************************************
22
*
3-
* PROJECT: Multi Theft Auto
3+
* PROJECT: Multi Theft Auto v1.0
44
* LICENSE: See LICENSE in the top level directory
5+
* FILE: mods/deathmatch/logic/packets/CBulletsyncPacket.cpp
56
*
67
* Multi Theft Auto is available from https://www.multitheftauto.com/
78
*
@@ -12,39 +13,226 @@
1213
#include "net/SyncStructures.h"
1314
#include "CPlayer.h"
1415
#include "CWeaponStatManager.h"
16+
#include "CElementIDs.h"
17+
#include "CElement.h"
1518

16-
CBulletsyncPacket::CBulletsyncPacket(CPlayer* player) : m_weapon(WEAPONTYPE_UNARMED), m_order(0), m_damage(0.0f), m_zone(0), m_damaged(INVALID_ELEMENT_ID)
19+
CBulletsyncPacket::CBulletsyncPacket(CPlayer* player)
20+
: m_weapon(WEAPONTYPE_UNARMED)
21+
, m_start()
22+
, m_end()
23+
, m_order(0)
24+
, m_damage(0.0f)
25+
, m_zone(0)
26+
, m_damaged(INVALID_ELEMENT_ID)
1727
{
1828
m_pSourceElement = player;
1929
}
2030

21-
bool CBulletsyncPacket::Read(NetBitStreamInterface& stream)
31+
bool CBulletsyncPacket::IsValidVector(const CVector& vec) noexcept
2232
{
23-
if (!m_pSourceElement)
33+
if (!vec.IsValid())
34+
return false;
35+
36+
if (IsNaN(vec.fX))
2437
return false;
38+
39+
if (IsNaN(vec.fY))
40+
return false;
41+
42+
if (IsNaN(vec.fZ))
43+
return false;
44+
45+
return true;
46+
}
2547

26-
char type = 0;
27-
if (!stream.Read(type) || !CWeaponStatManager::HasWeaponBulletSync(type))
48+
bool CBulletsyncPacket::ValidateVectorBounds(const CVector& vec) const noexcept
49+
{
50+
if (vec.fX < -MAX_WORLD_COORD || vec.fX > MAX_WORLD_COORD)
51+
return false;
52+
if (vec.fY < -MAX_WORLD_COORD || vec.fY > MAX_WORLD_COORD)
2853
return false;
54+
if (vec.fZ < -MAX_WORLD_COORD || vec.fZ > MAX_WORLD_COORD)
55+
return false;
56+
return true;
57+
}
2958

30-
m_weapon = static_cast<eWeaponType>(type);
59+
bool CBulletsyncPacket::IsValidWeaponId(unsigned char weaponId) noexcept
60+
{
61+
return CWeaponStatManager::HasWeaponBulletSync(static_cast<uint32_t>(weaponId));
62+
}
63+
64+
bool CBulletsyncPacket::ValidateTrajectory() const noexcept
65+
{
66+
const float dx = m_end.fX - m_start.fX;
67+
const float dy = m_end.fY - m_start.fY;
68+
const float dz = m_end.fZ - m_start.fZ;
69+
70+
const float movementSq = (dx * dx) + (dy * dy) + (dz * dz);
71+
72+
if (IsNaN(movementSq))
73+
return false;
74+
75+
if (movementSq < MIN_DISTANCE_SQ)
76+
return false;
77+
78+
if (movementSq > MAX_DISTANCE_SQ)
79+
return false;
80+
81+
return true;
82+
}
83+
84+
void CBulletsyncPacket::ResetDamageData() noexcept
85+
{
86+
m_damage = 0.0f;
87+
m_zone = 0;
88+
m_damaged = INVALID_ELEMENT_ID;
89+
}
3190

32-
if (!stream.Read(reinterpret_cast<char*>(&m_start), sizeof(CVector)) || !stream.Read(reinterpret_cast<char*>(&m_end), sizeof(CVector)))
91+
bool CBulletsyncPacket::ReadWeaponAndPositions(NetBitStreamInterface& stream)
92+
{
93+
unsigned char type = 0;
94+
if (!stream.Read(type))
3395
return false;
3496

35-
if (!m_start.IsValid() || !m_end.IsValid())
97+
if (!IsValidWeaponId(type))
3698
return false;
3799

38-
if (!stream.Read(m_order))
100+
m_weapon = static_cast<eWeaponType>(type);
101+
102+
if (!stream.Read(reinterpret_cast<char*>(&m_start), sizeof(CVector)))
103+
return false;
104+
105+
if (!stream.Read(reinterpret_cast<char*>(&m_end), sizeof(CVector)))
106+
return false;
107+
108+
if (!IsValidVector(m_start))
109+
return false;
110+
111+
if (!IsValidVector(m_end))
39112
return false;
113+
114+
if (!ValidateVectorBounds(m_start))
115+
return false;
116+
117+
if (!ValidateVectorBounds(m_end))
118+
return false;
119+
120+
if (!ValidateTrajectory())
121+
return false;
122+
123+
return true;
124+
}
40125

41-
if (stream.ReadBit())
126+
bool CBulletsyncPacket::ReadOptionalDamage(NetBitStreamInterface& stream)
127+
{
128+
if (!stream.ReadBit())
42129
{
43-
stream.Read(m_damage);
44-
stream.Read(m_zone);
45-
stream.Read(m_damaged);
130+
ResetDamageData();
131+
return true;
46132
}
47133

134+
stream.Read(m_damage);
135+
stream.Read(m_zone);
136+
stream.Read(m_damaged);
137+
138+
if (IsNaN(m_damage))
139+
{
140+
ResetDamageData();
141+
return false;
142+
}
143+
144+
if (m_damage < 0.0f || m_damage > MAX_DAMAGE)
145+
{
146+
ResetDamageData();
147+
return false;
148+
}
149+
150+
if (m_zone > MAX_BODY_ZONE)
151+
{
152+
ResetDamageData();
153+
return false;
154+
}
155+
156+
if (m_damaged == 0)
157+
{
158+
ResetDamageData();
159+
return false;
160+
}
161+
162+
// Validate that target element exists
163+
if (m_damaged != INVALID_ELEMENT_ID)
164+
{
165+
CElement* pElement = CElementIDs::GetElement(m_damaged);
166+
if (!pElement)
167+
{
168+
ResetDamageData();
169+
return false;
170+
}
171+
172+
// Check element type is valid for damage
173+
auto elementType = pElement->GetType();
174+
if (elementType != CElement::PLAYER &&
175+
elementType != CElement::PED &&
176+
elementType != CElement::VEHICLE)
177+
{
178+
ResetDamageData();
179+
return false;
180+
}
181+
}
182+
183+
return true;
184+
}
185+
186+
bool CBulletsyncPacket::Read(NetBitStreamInterface& stream)
187+
{
188+
if (!m_pSourceElement)
189+
return false;
190+
191+
CPlayer* pPlayer = static_cast<CPlayer*>(m_pSourceElement);
192+
if (pPlayer)
193+
{
194+
// Check if player is spawned and alive
195+
if (!pPlayer->IsSpawned() || pPlayer->IsDead())
196+
return false;
197+
198+
// Check player position is reasonable relative to bullet start
199+
const CVector& playerPos = pPlayer->GetPosition();
200+
const float maxShootDistance = 50.0f; // Max distance from player to bullet start
201+
202+
// This check will be done after we read positions
203+
}
204+
205+
if (!ReadWeaponAndPositions(stream))
206+
return false;
207+
208+
// Now validate player position relative to shot origin
209+
if (pPlayer)
210+
{
211+
const CVector& playerPos = pPlayer->GetPosition();
212+
float dx = m_start.fX - playerPos.fX;
213+
float dy = m_start.fY - playerPos.fY;
214+
float dz = m_start.fZ - playerPos.fZ;
215+
float distSq = dx*dx + dy*dy + dz*dz;
216+
217+
const float maxShootDistanceSq = 50.0f * 50.0f;
218+
if (distSq > maxShootDistanceSq)
219+
return false;
220+
221+
// Check if player has this weapon
222+
if (!pPlayer->HasWeaponType(static_cast<unsigned char>(m_weapon)))
223+
return false;
224+
225+
// Check if weapon has ammo
226+
if (pPlayer->GetWeaponAmmoInClip(static_cast<unsigned char>(m_weapon)) == 0)
227+
return false;
228+
}
229+
230+
if (!stream.Read(m_order))
231+
return false;
232+
233+
if (!ReadOptionalDamage(stream))
234+
return false;
235+
48236
return true;
49237
}
50238

@@ -53,26 +241,52 @@ bool CBulletsyncPacket::Write(NetBitStreamInterface& stream) const
53241
if (!m_pSourceElement)
54242
return false;
55243

56-
auto* player = static_cast<CPlayer*>(m_pSourceElement);
57-
auto id = player->GetID();
244+
const auto* pPlayer = static_cast<const CPlayer*>(m_pSourceElement);
245+
if (!pPlayer)
246+
return false;
247+
248+
const ElementID id = pPlayer->GetID();
249+
250+
if (id == INVALID_ELEMENT_ID)
251+
return false;
252+
253+
if (id == 0)
254+
return false;
255+
256+
if (!IsValidVector(m_start))
257+
return false;
258+
259+
if (!IsValidVector(m_end))
260+
return false;
261+
262+
if (!ValidateVectorBounds(m_start))
263+
return false;
264+
265+
if (!ValidateVectorBounds(m_end))
266+
return false;
267+
268+
if (!ValidateTrajectory())
269+
return false;
270+
271+
const unsigned char weaponType = static_cast<unsigned char>(m_weapon);
272+
if (!IsValidWeaponId(weaponType))
273+
return false;
58274

59275
stream.Write(id);
60-
stream.Write(static_cast<char>(m_weapon));
276+
stream.Write(weaponType);
61277
stream.Write(reinterpret_cast<const char*>(&m_start), sizeof(CVector));
62278
stream.Write(reinterpret_cast<const char*>(&m_end), sizeof(CVector));
63279
stream.Write(m_order);
64280

65-
if (m_damage > 0.0f && m_damaged != INVALID_ELEMENT_ID)
281+
const bool hasDamage = (m_damage > EPSILON) && (m_damaged != INVALID_ELEMENT_ID);
282+
stream.WriteBit(hasDamage);
283+
284+
if (hasDamage)
66285
{
67-
stream.WriteBit(true);
68286
stream.Write(m_damage);
69287
stream.Write(m_zone);
70288
stream.Write(m_damaged);
71289
}
72-
else
73-
{
74-
stream.WriteBit(false);
75-
}
76290

77291
return true;
78-
}
292+
}
Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
/*****************************************************************************
22
*
3-
* PROJECT: Multi Theft Auto
3+
* PROJECT: Multi Theft Auto v1.0
44
* LICENSE: See LICENSE in the top level directory
5+
* FILE: mods/deathmatch/logic/packets/CBulletsyncPacket.h
6+
* PURPOSE: Bullet synchronization packet class
57
*
68
* Multi Theft Auto is available from https://www.multitheftauto.com/
79
*
810
*****************************************************************************/
911

12+
#ifndef __CBULLETSYNCPACKET_H
13+
#define __CBULLETSYNCPACKET_H
14+
1015
#pragma once
1116

1217
#include "CPacket.h"
@@ -15,16 +20,36 @@
1520
class CBulletsyncPacket final : public CPacket
1621
{
1722
public:
23+
static constexpr float MIN_DISTANCE_SQ = 0.0001f;
24+
static constexpr float MAX_DISTANCE_SQ = 160000.0f;
25+
static constexpr float EPSILON = 0.0001f;
26+
static constexpr float EPSILON_SQ = EPSILON * EPSILON;
27+
static constexpr unsigned char MAX_BODY_ZONE = 9;
28+
static constexpr float MAX_WORLD_COORD = 3000.0f;
29+
static constexpr float MAX_DAMAGE = 200.0f;
30+
1831
CBulletsyncPacket() = default;
1932
explicit CBulletsyncPacket(class CPlayer* player);
2033

21-
bool HasSimHandler() const { return true; }
22-
ePacketID GetPacketID() const { return PACKET_ID_PLAYER_BULLETSYNC; }
23-
unsigned long GetFlags() const { return PACKET_MEDIUM_PRIORITY | PACKET_RELIABLE; }
34+
bool HasSimHandler() const noexcept override { return true; }
35+
ePacketID GetPacketID() const noexcept override { return PACKET_ID_PLAYER_BULLETSYNC; }
36+
unsigned long GetFlags() const noexcept override { return PACKET_MEDIUM_PRIORITY | PACKET_RELIABLE; }
37+
38+
bool Read(NetBitStreamInterface& stream) override;
39+
bool Write(NetBitStreamInterface& stream) const override;
2440

25-
bool Read(NetBitStreamInterface& stream);
26-
bool Write(NetBitStreamInterface& stream) const;
41+
private:
42+
bool ReadWeaponAndPositions(NetBitStreamInterface& stream);
43+
bool ReadOptionalDamage(NetBitStreamInterface& stream);
44+
bool ValidateTrajectory() const noexcept;
45+
bool ValidateVectorBounds(const CVector& vec) const noexcept;
46+
void ResetDamageData() noexcept;
47+
48+
static constexpr bool IsNaN(float value) noexcept { return value != value; }
49+
static bool IsValidVector(const CVector& vec) noexcept;
50+
static bool IsValidWeaponId(unsigned char weaponId) noexcept;
2751

52+
public:
2853
eWeaponType m_weapon{};
2954
CVector m_start{};
3055
CVector m_end{};
@@ -33,3 +58,5 @@ class CBulletsyncPacket final : public CPacket
3358
std::uint8_t m_zone{};
3459
ElementID m_damaged{INVALID_ELEMENT_ID};
3560
};
61+
62+
#endif // __CBULLETSYNCPACKET_H

0 commit comments

Comments
 (0)