Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fd7a4e9
Add buffer Read and Write data functions
DanielJimenezMorales Nov 22, 2025
06a167b
A step forward to protect the library against tampered network packets
DanielJimenezMorales Nov 23, 2025
40cb957
Remove packet read functions
DanielJimenezMorales Nov 23, 2025
04e9399
Refactor Peer functions
DanielJimenezMorales Nov 23, 2025
1094c58
Add network library execution flow docs and input state validation
DanielJimenezMorales Nov 23, 2025
7e0ae01
fix image
DanielJimenezMorales Nov 23, 2025
9e8dcfc
Improve readability of execution flow docs
DanielJimenezMorales Nov 23, 2025
53b07b4
More changes to improve readability
DanielJimenezMorales Nov 23, 2025
3c8a93a
Add sub indexes in each phase
DanielJimenezMorales Nov 23, 2025
e960187
More readabilirty changes
DanielJimenezMorales Nov 23, 2025
154551a
Fix some links
DanielJimenezMorales Nov 23, 2025
5287097
Added full execution flow diagram
DanielJimenezMorales Nov 23, 2025
9b0b199
Add sum up section
DanielJimenezMorales Nov 23, 2025
197c1f6
Changes in sum up
DanielJimenezMorales Nov 23, 2025
4f26cda
Refactor metrics component
DanielJimenezMorales Nov 25, 2025
95e8803
Improve metrics handler
DanielJimenezMorales Nov 26, 2025
504541f
Created connection component, not integrated yet though
DanielJimenezMorales Dec 9, 2025
b7f68d7
First functional version of the connection component is now fully int…
DanielJimenezMorales Dec 9, 2025
19e0092
More improvements on connection pipeline
DanielJimenezMorales Dec 12, 2025
91b89fa
Solve a few bugs
DanielJimenezMorales Dec 12, 2025
7309328
Add connection timeout
DanielJimenezMorales Dec 12, 2025
99f01a5
Add subnamespace for connection and a bit of refactor
DanielJimenezMorales Dec 12, 2025
a359370
Add documentation
DanielJimenezMorales Dec 12, 2025
92a01ce
Remove redundant client-side connection state
DanielJimenezMorales Dec 12, 2025
c83393a
A bit more of refactor
DanielJimenezMorales Dec 12, 2025
e963601
Solve a few bugs
DanielJimenezMorales Dec 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Common/src/asserts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Common
// Try to break if a debugger exists and then exit the application
if ( IsDebuggerPresent() )
{
DebugBreak();
DEBUG_BREAK();
}
exit( -1 );
}
Expand Down
12 changes: 12 additions & 0 deletions Common/src/asserts.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
#pragma once

#ifdef DEBUG
#if defined( _MSC_VER )
#define DEBUG_BREAK() __debugbreak()
#elif defined( __GNUC__ ) || defined( __clang__ )
#include <signal.h>
#define DEBUG_BREAK() raise( SIGTRAP )
#else
// raise(SIGTRAP) is like the most standard way to do a debug break in unix-like systems
#include <signal.h>
#define DEBUG_BREAK() raise( SIGTRAP )
#endif

#define ASSERT( expression, text_format, ... ) \
if ( !( expression ) ) \
{ \
Common::ForceCrash( #expression, text_format, ##__VA_ARGS__ ); \
}
#else
#define DEBUG_BREAK()
#define ASSERT( expression, text_format, ... )
#endif
namespace Common
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ void ClientLocalPlayerPredictorSystem::ExecuteLocalPrediction( Engine::ECS::Worl
void ClientLocalPlayerPredictorSystem::Execute( Engine::ECS::World& world, float32 elapsed_time )
{
const NetworkPeerGlobalComponent& networkPeerComponent = world.GetGlobalComponent< NetworkPeerGlobalComponent >();
if ( networkPeerComponent.peer->GetConnectionState() != NetLib::PCS_Connected )
if ( networkPeerComponent.peer->GetConnectionState() != NetLib::PeerConnectionState::Connected )
{
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ static void EvaluateReconciliation( Engine::ECS::GameEntity& entity, const Netwo
void ClientLocalPlayerServerReconciliatorSystem::Execute( Engine::ECS::World& world, float32 elapsed_time )
{
const NetworkPeerGlobalComponent& networkPeerComponent = world.GetGlobalComponent< NetworkPeerGlobalComponent >();
if ( networkPeerComponent.peer->GetConnectionState() != NetLib::PCS_Connected )
if ( networkPeerComponent.peer->GetConnectionState() != NetLib::PeerConnectionState::Connected )
{
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "server_hit_registration_system.h"

#include "logger.h"
#include "asserts.h"
#include "AlgorithmUtils.h"

#include "ecs/world.h"
Expand Down Expand Up @@ -202,12 +203,9 @@ static void RollbackEntities( Engine::ECS::World& world, float32 serverTime )
int32 previousIndex = -1;
int32 nextIndex = -1;
FindPreviousAndNextTimeIndexes( transformHistoryComponent, serverTime, previousIndex, nextIndex );
if ( nextIndex < 0 )
{
bool a = true;
}

// TODO Investigate hit reg issue that is hitting this assert.
assert( nextIndex >= 0 );
ASSERT( nextIndex >= 0, "ServerHitRegistrationSystem.%s Couldn't find next index" );

Engine::TransformComponent& transform = it->GetComponent< Engine::TransformComponent >();

Expand Down
19 changes: 18 additions & 1 deletion DemoGame/src/shared/InputState.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "InputState.h"

#include "logger.h"

#include "core/buffer.h"

InputState::InputState()
Expand Down Expand Up @@ -32,14 +34,27 @@ void InputState::Serialize( NetLib::Buffer& buffer ) const
buffer.WriteByte( isShooting ? 1 : 0 );
}

void InputState::Deserialize( NetLib::Buffer& buffer )
bool InputState::Deserialize( NetLib::Buffer& buffer )
{
if ( buffer.GetRemainingSize() < GetSize() )
{
LOG_ERROR( "Not enough data in buffer to read InputState." );
return false;
}

tick = buffer.ReadInteger();
serverTime = buffer.ReadFloat();

movement.X( buffer.ReadFloat() );
movement.Y( buffer.ReadFloat() );

if ( movement.X() >= MAXIMUM_MOVEMENT_VALUE || movement.Y() >= MAXIMUM_MOVEMENT_VALUE )
{
LOG_ERROR( "[InputState::%s] Movement value too high, possible cheating detected. X: %.3f, Y: %.3f",
THIS_FUNCTION_NAME, movement.X(), movement.Y() );
return false;
}

virtualMousePosition.X( buffer.ReadFloat() );
virtualMousePosition.Y( buffer.ReadFloat() );

Expand All @@ -48,4 +63,6 @@ void InputState::Deserialize( NetLib::Buffer& buffer )

const uint8 isShootingByte = buffer.ReadByte();
isShooting = ( isShootingByte == 1 ) ? true : false;

return true;
}
4 changes: 3 additions & 1 deletion DemoGame/src/shared/InputState.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class InputState : public NetLib::IInputState

int32 GetSize() const override;
void Serialize( NetLib::Buffer& buffer ) const override;
void Deserialize( NetLib::Buffer& buffer ) override;
bool Deserialize( NetLib::Buffer& buffer ) override;

static constexpr float32 MAXIMUM_MOVEMENT_VALUE = 10.f;

// Header fields
uint32 tick;
Expand Down
2 changes: 1 addition & 1 deletion DemoGame/src/shared/systems/pre_tick_network_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void PreTickNetworkSystem::Execute( Engine::ECS::World& world, float32 elapsed_t
{
NetworkPeerGlobalComponent& networkPeerComponent = world.GetGlobalComponent< NetworkPeerGlobalComponent >();

if ( networkPeerComponent.peer->GetConnectionState() == NetLib::PCS_Disconnected )
if ( networkPeerComponent.peer->GetConnectionState() == NetLib::PeerConnectionState::Disconnected )
{
networkPeerComponent.peer->Start( _ip, _port );
}
Expand Down
5 changes: 5 additions & 0 deletions NetworkLibrary/src/Core/Address.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ namespace NetLib
return !( *this == other );
}

bool Address::IsValid() const
{
return *this != Address::GetInvalid();
}

void Address::GetFull( std::string& buffer ) const
{
buffer.append( _ip.c_str() );
Expand Down
12 changes: 11 additions & 1 deletion NetworkLibrary/src/Core/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ namespace NetLib
static Address GetInvalid() { return Address( "0.0.0.0", 0 ); }

Address( const std::string& ip, uint32 port );

Address( const Address& other ) = default;

bool operator==( const Address& other ) const;
bool operator!=( const Address& other ) const;

bool IsValid() const;

uint32 GetPort() const { return _port; }
const std::string& GetIP() const { return _ip; }
void GetFull( std::string& buffer ) const;
Expand All @@ -48,4 +49,13 @@ namespace NetLib

friend class Socket;
};

struct AddressHasher
{
size_t operator()( const Address& address ) const noexcept
{
return std::hash< std::string >()( address.GetIP() ) ^
( std::hash< uint32 >()( address.GetPort() ) << 1 );
}
};
} // namespace NetLib
118 changes: 106 additions & 12 deletions NetworkLibrary/src/Core/Buffer.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "buffer.h"

#include <cassert>
#include "Logger.h"
#include "asserts.h"

#include <cmath>
#include <cstring>

Expand All @@ -20,74 +22,121 @@ namespace NetLib

void Buffer::CopyUsedData( uint8* dst, uint32 dst_size ) const
{
assert( dst_size >= _index );
ASSERT( dst_size >= _index, "Buffer.%s The destination buffer is smaller than the used data to copy.",
THIS_FUNCTION_NAME );

std::memcpy( dst, _data, _index );
}

void Buffer::WriteLong( uint64 value )
{
assert( _index + 8 <= _size );
ASSERT( _index + 8 <= _size, "Buffer.%s Write operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
*( ( uint64* ) ( _data + _index ) ) = value;

_index += 8;
}

void Buffer::WriteInteger( uint32 value )
{
assert( _index + 4 <= _size );
ASSERT( _index + 4 <= _size, "Buffer.%s Write operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
*( ( uint32* ) ( _data + _index ) ) = value;

_index += 4;
}

void Buffer::WriteShort( uint16 value )
{
assert( _index + 2 <= _size );
ASSERT( _index + 2 <= _size, "Buffer.%s Write operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
*( ( uint16* ) ( _data + _index ) ) = value;

_index += 2;
}

void Buffer::WriteByte( uint8 value )
{
assert( _index + 1 <= _size );
ASSERT( _index + 1 <= _size, "Buffer.%s Write operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
*( ( uint8* ) ( _data + _index ) ) = value;

++_index;
}

void Buffer::WriteFloat( float32 value )
{
assert( _index + 4 <= _size );
ASSERT( _index + 4 <= _size, "Buffer.%s Write operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
// This memcpy needs to be done using memcpy to keep the bits configuration too
std::memcpy( ( _data + _index ), &value, sizeof( uint32 ) );
_index += 4;
}

void Buffer::WriteData( const uint8* data, uint32 size )
{
ASSERT( data != nullptr, "Buffer.%s Can't write nullptr data.", THIS_FUNCTION_NAME );
ASSERT( _index + size <= _size, "Buffer.%s Write operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
std::memcpy( _data + _index, data, size );
_index += size;
}

uint64 Buffer::ReadLong()
{
assert( _index + 8 <= _size );
ASSERT( _index + 8 <= _size, "Buffer.%s Read operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
uint64 value;
value = *( ( uint64* ) ( _data + _index ) );

_index += 8;
return value;
}

bool Buffer::ReadLong( uint64& value )
{
if ( _index + 8 <= _size )
{
value = *( ( uint64* ) ( _data + _index ) );
_index += 8;
return true;
}
else
{
return false;
}
}

uint32 Buffer::ReadInteger()
{
assert( _index + 4 <= _size );
ASSERT( _index + 4 <= _size, "Buffer.%s Read operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
uint32 value;
value = *( ( uint32* ) ( _data + _index ) );

_index += 4;
return value;
}

bool Buffer::ReadInteger( uint32& value )
{
if ( _index + 4 <= _size )
{
value = *( ( uint32* ) ( _data + _index ) );

_index += 4;
return true;
}
else
{
return false;
}
}

uint16 Buffer::ReadShort()
{
assert( _index + 2 <= _size );
ASSERT( _index + 2 <= _size, "Buffer.%s Read operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
uint16 value;

value = *( ( uint16* ) ( _data + _index ) );
Expand All @@ -97,9 +146,24 @@ namespace NetLib
return value;
}

bool Buffer::ReadShort( uint16& value )
{
if ( _index + 2 <= _size )
{
value = *( ( uint16* ) ( _data + _index ) );
_index += 2;
return true;
}
else
{
return false;
}
}

uint8 Buffer::ReadByte()
{
assert( _index + 1 <= _size );
ASSERT( _index + 1 <= _size, "Buffer.%s Read operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
uint8 value;

value = *( ( uint8* ) ( _data + _index ) );
Expand All @@ -109,9 +173,24 @@ namespace NetLib
return value;
}

bool Buffer::ReadByte( uint8& value )
{
if ( _index + 1 <= _size )
{
value = *( ( uint8* ) ( _data + _index ) );
++_index;
return true;
}
else
{
return false;
}
}

float32 Buffer::ReadFloat()
{
assert( _index + 4 <= _size );
ASSERT( _index + 4 <= _size, "Buffer.%s Read operation exceeds buffer bounds. Size: %u, Remaining: %u",
THIS_FUNCTION_NAME, _size, _size - _index );
float32 value;
// This memcpy needs to be done using memcpy to recover the bits configuration too
std::memcpy( &value, ( _data + _index ), sizeof( float32 ) );
Expand All @@ -120,6 +199,21 @@ namespace NetLib
return value;
}

bool Buffer::ReadData( uint8* data, uint32 size )
{
ASSERT( data != nullptr, "Buffer.%s Can't read into nullptr data.", THIS_FUNCTION_NAME );
if ( _index + size <= _size )
{
std::memcpy( data, _data + _index, size );
_index += size;
return true;
}
else
{
return false;
}
}

void Buffer::ResetAccessIndex()
{
_index = 0;
Expand Down
Loading