Multiplayer working

This commit is contained in:
Chewico 2026-10-20 16:22:10 +02:00
parent 106d5ced0b
commit 2af77fa468
15 changed files with 350 additions and 188 deletions

View File

@ -3,7 +3,7 @@
"args": [
"--single",
"--port",
"8080"
"8080-"
]
}
}

View File

@ -14,4 +14,5 @@ namespace deerith
float get_window_aspect_ratio();
int get_current_frame_id();
bool is_windows_focused();
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <deerith/engine.h>
namespace deerith
{
struct chunk_render_t
{
};
}

View File

@ -1,4 +1,5 @@
#include "deerith/graphics/shader.h"
#include <deerith/engine.h>
#include <glad/gl.h>
#include <glm/gtc/type_ptr.hpp>
@ -6,6 +7,24 @@
#include <fstream>
#include <sstream>
#include <iostream>
#include <filesystem>
std::filesystem::path get_executable_directory()
{
std::array<char, 4096> buffer{};
const auto length = readlink(
"/proc/self/exe",
buffer.data(),
buffer.size() - 1);
if (length == -1)
throw std::runtime_error("Failed to get executable path");
return std::filesystem::path(
std::string(buffer.data(), length))
.parent_path();
}
deerith::Shader::Shader(const char *vertexPath, const char *fragmentPath)
{
@ -23,10 +42,11 @@ deerith::Shader::Shader(const char *vertexPath, const char *fragmentPath)
std::ifstream::failbit |
std::ifstream::badbit);
std::filesystem::path cwd = get_executable_directory();
try
{
vertexFile.open(vertexPath);
fragmentFile.open(fragmentPath);
vertexFile.open(cwd / vertexPath);
fragmentFile.open(cwd / fragmentPath);
std::stringstream vertexStream;
std::stringstream fragmentStream;

View File

@ -77,6 +77,11 @@ int deerith::init_window()
return 0;
}
bool deerith::is_windows_focused()
{
return window && glfwGetWindowAttrib(window, GLFW_FOCUSED);
}
bool deerith::begin_window_display()
{
glfwPollEvents();

View File

@ -1,5 +1,6 @@
#include <deerith/engine.h>
#include <deerith/graphics/input.h>
#include <deerith/graphics/window.h>
#include <deerith/network/router.h>
#include <deerith/network/packets.h>
#include <deerith/world/entity.h>
@ -15,7 +16,13 @@ void deerith::process_player_input()
{
if (self_entity == entt::null)
return;
glm::vec2 movment = get_joystick_axes(0, 0);
if (!is_windows_focused())
{
movment = glm::vec2(0, 0);
}
entity_sync_client_position_t &entity_sync_position = get_component<entity_sync_client_position_t>(self_entity);
entity_sync_position.move_input = movment;

View File

@ -13,6 +13,7 @@ void deerith::setup_base_protocols()
router_add_packet_processor<create_player_entity_packet_t>();
router_add_packet_processor<create_entity_packet_t>();
router_add_packet_processor<sync_entities_coordinates_packet_t>();
router_add_packet_processor<destroy_entity_packet_t>();
}
template <>
@ -34,7 +35,17 @@ void deerith::process_packet(deerith::create_entity_packet_t &packet, client_t *
{
deerith_trace("creating entity with network id {}", packet.network_sync_id);
entity_t entity = create_entity();
entity_t entity;
if (packet.is_self_client)
{
deerith_trace("creating self entity");
entity = self_entity;
}
else
{
entity = create_entity();
}
entity_network_id_t &entity_network_id = add_entity_network_with_id(entity, packet.network_sync_id);
if (packet.add_entity_sync_coordinates)
@ -60,3 +71,11 @@ void deerith::process_packet(deerith::sync_entities_coordinates_packet_t &packet
packet.positions_to_update[i].recive(sync_coords.position);
}
}
template <>
void deerith::process_packet(deerith::destroy_entity_packet_t &packet, client_t *client)
{
deerith_trace("destroying entity : {}", packet.network_sync_id);
entity_t specific_entity = get_network_entity_with_id(packet.network_sync_id);
get_entity_registry().destroy(specific_entity);
}

View File

@ -7,7 +7,7 @@ void deerith::render_world()
{
define_primitive_viewspace(glm::vec2(-10, -10), glm::vec2(10, 10));
auto entities_view = get_entity_registry().view<entity_sync_coordinates_t>();
auto entities_view = get_entity_registry().view<entity_sync_coordinates_t>(entt::exclude<entity_sync_client_position_t>);
for (auto [entity, sync_coords] : entities_view.each())
{
glm::vec2 chords;
@ -17,4 +17,12 @@ void deerith::render_world()
draw_primitive_rectangle(chords - glm::vec2(0.5f, 0.5f), chords + glm::vec2(0.5f, 0.5f));
}
}
// we should expect only one registry
auto self_view = get_entity_registry().view<entity_sync_coordinates_t, entity_sync_client_position_t>();
for (auto [entity, sync_coords, client_sync_coords] : self_view.each())
{
define_primitive_color(glm::vec4(1, 0.5f, 0.6f, 1));
draw_primitive_rectangle(client_sync_coords.position.read_value() - glm::vec2(0.5f, 0.5f), client_sync_coords.position.read_value() + glm::vec2(0.5f, 0.5f));
}
}

View File

@ -42,7 +42,7 @@ namespace cereal
inline std::uint8_t is_little_endian()
{
static std::int32_t test = 1;
return *reinterpret_cast<std::int8_t*>( &test ) == 1;
return *reinterpret_cast<std::int8_t *>(&test) == 1;
}
//! Swaps the order of bytes for some chunk of memory
@ -50,10 +50,10 @@ namespace cereal
@tparam DataSize The true size of the data
@ingroup Internal */
template <std::size_t DataSize>
inline void swap_bytes( std::uint8_t * data )
inline void swap_bytes(std::uint8_t *data)
{
for( std::size_t i = 0, end = DataSize / 2; i < end; ++i )
std::swap( data[i], data[DataSize - i - 1] );
for (std::size_t i = 0, end = DataSize / 2; i < end; ++i)
std::swap(data[i], data[DataSize - i - 1]);
}
} // end namespace portable_binary_detail
@ -77,78 +77,83 @@ namespace cereal
\ingroup Archives */
class PortableBinaryOutputArchive : public OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>
{
public:
//! A class containing various advanced options for the PortableBinaryOutput archive
class Options
{
public:
//! A class containing various advanced options for the PortableBinaryOutput archive
class Options
//! Represents desired endianness
enum class Endianness : std::uint8_t
{
public:
//! Represents desired endianness
enum class Endianness : std::uint8_t
{ big, little };
//! Default options, preserve system endianness
static Options Default(){ return Options(); }
//! Save as little endian
static Options LittleEndian(){ return Options( Endianness::little ); }
//! Save as big endian
static Options BigEndian(){ return Options( Endianness::big ); }
//! Specify specific options for the PortableBinaryOutputArchive
/*! @param outputEndian The desired endianness of saved (output) data */
explicit Options( Endianness outputEndian = getEndianness() ) :
itsOutputEndianness( outputEndian ) { }
private:
//! Gets the endianness of the system
inline static Endianness getEndianness()
{ return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
//! Checks if Options is set for little endian
inline std::uint8_t is_little_endian() const
{ return itsOutputEndianness == Endianness::little; }
friend class PortableBinaryOutputArchive;
Endianness itsOutputEndianness;
big,
little
};
//! Construct, outputting to the provided stream
/*! @param stream The stream to output to. Should be opened with std::ios::binary flag.
@param options The PortableBinary specific options to use. See the Options struct
for the values of default parameters */
PortableBinaryOutputArchive(std::ostream & stream, Options const & options = Options::Default()) :
OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
itsStream(stream),
itsConvertEndianness( portable_binary_detail::is_little_endian() ^ options.is_little_endian() )
{
this->operator()( options.is_little_endian() );
}
//! Default options, preserve system endianness
static Options Default() { return Options(); }
~PortableBinaryOutputArchive() CEREAL_NOEXCEPT = default;
//! Save as little endian
static Options LittleEndian() { return Options(Endianness::little); }
//! Writes size bytes of data to the output stream
template <std::streamsize DataSize> inline
void saveBinary( const void * data, std::streamsize size )
{
std::streamsize writtenSize = 0;
//! Save as big endian
static Options BigEndian() { return Options(Endianness::big); }
if( itsConvertEndianness )
{
for( std::streamsize i = 0; i < size; i += DataSize )
for( std::streamsize j = 0; j < DataSize; ++j )
writtenSize += itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ) + DataSize - j - 1 + i, 1 );
}
else
writtenSize = itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size );
if(writtenSize != size)
throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
}
//! Specify specific options for the PortableBinaryOutputArchive
/*! @param outputEndian The desired endianness of saved (output) data */
explicit Options(Endianness outputEndian = getEndianness()) : itsOutputEndianness(outputEndian) {}
private:
std::ostream & itsStream;
const uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon saving
//! Gets the endianness of the system
inline static Endianness getEndianness()
{
return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big;
}
//! Checks if Options is set for little endian
inline std::uint8_t is_little_endian() const
{
return itsOutputEndianness == Endianness::little;
}
friend class PortableBinaryOutputArchive;
Endianness itsOutputEndianness;
};
//! Construct, outputting to the provided stream
/*! @param stream The stream to output to. Should be opened with std::ios::binary flag.
@param options The PortableBinary specific options to use. See the Options struct
for the values of default parameters */
PortableBinaryOutputArchive(std::ostream &stream, Options const &options = Options::Default()) : OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
itsStream(stream),
itsConvertEndianness(portable_binary_detail::is_little_endian() ^ options.is_little_endian())
{
this->operator()(options.is_little_endian());
}
~PortableBinaryOutputArchive() CEREAL_NOEXCEPT = default;
//! Writes size bytes of data to the output stream
template <std::streamsize DataSize>
inline void saveBinary(const void *data, std::streamsize size)
{
std::streamsize writtenSize = 0;
if (itsConvertEndianness)
{
for (std::streamsize i = 0; i < size; i += DataSize)
for (std::streamsize j = 0; j < DataSize; ++j)
writtenSize += itsStream.rdbuf()->sputn(reinterpret_cast<const char *>(data) + DataSize - j - 1 + i, 1);
}
else
writtenSize = itsStream.rdbuf()->sputn(reinterpret_cast<const char *>(data), size);
if (writtenSize != size)
throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
}
private:
std::ostream &itsStream;
const uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon saving
};
// ######################################################################
@ -179,148 +184,155 @@ namespace cereal
\ingroup Archives */
class PortableBinaryInputArchive : public InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>
{
public:
//! A class containing various advanced options for the PortableBinaryInput archive
class Options
{
public:
//! A class containing various advanced options for the PortableBinaryInput archive
class Options
//! Represents desired endianness
enum class Endianness : std::uint8_t
{
public:
//! Represents desired endianness
enum class Endianness : std::uint8_t
{ big, little };
//! Default options, preserve system endianness
static Options Default(){ return Options(); }
//! Load into little endian
static Options LittleEndian(){ return Options( Endianness::little ); }
//! Load into big endian
static Options BigEndian(){ return Options( Endianness::big ); }
//! Specify specific options for the PortableBinaryInputArchive
/*! @param inputEndian The desired endianness of loaded (input) data */
explicit Options( Endianness inputEndian = getEndianness() ) :
itsInputEndianness( inputEndian ) { }
private:
//! Gets the endianness of the system
inline static Endianness getEndianness()
{ return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
//! Checks if Options is set for little endian
inline std::uint8_t is_little_endian() const
{ return itsInputEndianness == Endianness::little; }
friend class PortableBinaryInputArchive;
Endianness itsInputEndianness;
big,
little
};
//! Construct, loading from the provided stream
/*! @param stream The stream to read from. Should be opened with std::ios::binary flag.
@param options The PortableBinary specific options to use. See the Options struct
for the values of default parameters */
PortableBinaryInputArchive(std::istream & stream, Options const & options = Options::Default()) :
InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
itsStream(stream),
itsConvertEndianness( false )
{
uint8_t streamLittleEndian;
this->operator()( streamLittleEndian );
itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian;
}
//! Default options, preserve system endianness
static Options Default() { return Options(); }
~PortableBinaryInputArchive() CEREAL_NOEXCEPT = default;
//! Load into little endian
static Options LittleEndian() { return Options(Endianness::little); }
//! Reads size bytes of data from the input stream
/*! @param data The data to save
@param size The number of bytes in the data
@tparam DataSize T The size of the actual type of the data elements being loaded */
template <std::streamsize DataSize> inline
void loadBinary( void * const data, std::streamsize size )
{
// load data
auto const readSize = itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size );
//! Load into big endian
static Options BigEndian() { return Options(Endianness::big); }
if(readSize != size)
throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
// flip bits if needed
if( itsConvertEndianness )
{
std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( data );
for( std::streamsize i = 0; i < size; i += DataSize )
portable_binary_detail::swap_bytes<DataSize>( ptr + i );
}
}
//! Specify specific options for the PortableBinaryInputArchive
/*! @param inputEndian The desired endianness of loaded (input) data */
explicit Options(Endianness inputEndian = getEndianness()) : itsInputEndianness(inputEndian) {}
private:
std::istream & itsStream;
uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading
//! Gets the endianness of the system
inline static Endianness getEndianness()
{
return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big;
}
//! Checks if Options is set for little endian
inline std::uint8_t is_little_endian() const
{
return itsInputEndianness == Endianness::little;
}
friend class PortableBinaryInputArchive;
Endianness itsInputEndianness;
};
//! Construct, loading from the provided stream
/*! @param stream The stream to read from. Should be opened with std::ios::binary flag.
@param options The PortableBinary specific options to use. See the Options struct
for the values of default parameters */
PortableBinaryInputArchive(std::istream &stream, Options const &options = Options::Default()) : InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
itsStream(stream),
itsConvertEndianness(false)
{
uint8_t streamLittleEndian;
this->operator()(streamLittleEndian);
itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian;
}
~PortableBinaryInputArchive() CEREAL_NOEXCEPT = default;
//! Reads size bytes of data from the input stream
/*! @param data The data to save
@param size The number of bytes in the data
@tparam DataSize T The size of the actual type of the data elements being loaded */
template <std::streamsize DataSize>
inline void loadBinary(void *const data, std::streamsize size)
{
// load data
auto const readSize = itsStream.rdbuf()->sgetn(reinterpret_cast<char *>(data), size);
if (readSize != size)
throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
// flip bits if needed
if (itsConvertEndianness)
{
std::uint8_t *ptr = reinterpret_cast<std::uint8_t *>(data);
for (std::streamsize i = 0; i < size; i += DataSize)
portable_binary_detail::swap_bytes<DataSize>(ptr + i);
}
}
private:
std::istream &itsStream;
uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading
};
// ######################################################################
// Common BinaryArchive serialization functions
//! Saving for POD types to portable binary
template<class T> inline
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, T const & t)
template <class T>
inline
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive &ar, T const &t)
{
static_assert( !std::is_floating_point<T>::value ||
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point" );
static_assert(!std::is_floating_point<T>::value ||
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point");
ar.template saveBinary<sizeof(T)>(std::addressof(t), sizeof(t));
}
//! Loading for POD types from portable binary
template<class T> inline
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, T & t)
template <class T>
inline
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive &ar, T &t)
{
static_assert( !std::is_floating_point<T>::value ||
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point" );
static_assert(!std::is_floating_point<T>::value ||
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point");
ar.template loadBinary<sizeof(T)>(std::addressof(t), sizeof(t));
}
//! Serializing NVP types to portable binary
template <class Archive, class T> inline
CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t )
template <class Archive, class T>
inline CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
CEREAL_SERIALIZE_FUNCTION_NAME(Archive &ar, NameValuePair<T> &t)
{
ar( t.value );
ar(t.value);
}
//! Serializing SizeTags to portable binary
template <class Archive, class T> inline
CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t )
template <class Archive, class T>
inline CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
CEREAL_SERIALIZE_FUNCTION_NAME(Archive &ar, SizeTag<T> &t)
{
ar( t.size );
ar(t.size);
}
//! Saving binary data to portable binary
template <class T> inline
void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, BinaryData<T> const & bd)
template <class T>
inline void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive &ar, BinaryData<T> const &bd)
{
typedef typename std::remove_pointer<T>::type TT;
static_assert( !std::is_floating_point<TT>::value ||
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point" );
static_assert(!std::is_floating_point<TT>::value ||
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point");
ar.template saveBinary<sizeof(TT)>( bd.data, static_cast<std::streamsize>( bd.size ) );
ar.template saveBinary<sizeof(TT)>(bd.data, static_cast<std::streamsize>(bd.size));
}
//! Loading binary data from portable binary
template <class T> inline
void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, BinaryData<T> & bd)
template <class T>
inline void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive &ar, BinaryData<T> &bd)
{
typedef typename std::remove_pointer<T>::type TT;
static_assert( !std::is_floating_point<TT>::value ||
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point" );
static_assert(!std::is_floating_point<TT>::value ||
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point");
ar.template loadBinary<sizeof(TT)>( bd.data, static_cast<std::streamsize>( bd.size ) );
ar.template loadBinary<sizeof(TT)>(bd.data, static_cast<std::streamsize>(bd.size));
}
} // namespace cereal

View File

@ -47,14 +47,16 @@ namespace deerith
static constexpr uint16_t packet_id = 4;
static constexpr bool reliable = true;
id_t network_sync_id;
bool add_entity_sync_coordinates;
id_t network_sync_id = 0;
bool add_entity_sync_coordinates = false;
bool is_self_client = false;
template <typename Archive>
void serialize(Archive &archive)
{
archive(network_sync_id);
archive(add_entity_sync_coordinates);
archive(is_self_client);
}
};
@ -73,4 +75,18 @@ namespace deerith
archive(positions_to_update);
}
};
struct destroy_entity_packet_t
{
static constexpr uint16_t packet_id = 6;
static constexpr bool reliable = true;
id_t network_sync_id = 0;
template <typename Archive>
void serialize(Archive &archive)
{
archive(network_sync_id);
}
};
}

View File

@ -1,9 +0,0 @@
#include "network/network_internals.h"
#include <deerith/network/router.h>
#include <deerith/network/packets.h>
void deerith::load_base_protocols()
{
router_add_packet_processor<sync_entity_player_position_packet_t>();
}

View File

@ -21,6 +21,21 @@ namespace deerith
std::vector<std::unique_ptr<client_t>> clients;
void create_client(ENetPeer *peer);
void delete_client(client_t *client);
}
void deerith::delete_client(client_t *client)
{
auto it = std::find_if(
clients.begin(),
clients.end(),
[client](const std::unique_ptr<client_t> &ptr)
{ return ptr.get() == client; });
if (it != clients.end())
{
clients.erase(it);
}
}
bool deerith::init_server(int port)
@ -71,6 +86,8 @@ void deerith::flush_network_events()
break;
case ENET_EVENT_TYPE_DISCONNECT:
client_disconnects((client_t *)event.peer->data);
delete_client((client_t *)event.peer->data);
event.peer->data = NULL;
break;

View File

@ -12,6 +12,10 @@ namespace deerith
{
}
void deerith::load_base_protocols()
{
router_add_packet_processor<sync_entity_player_position_packet_t>();
}
template <>
void deerith::process_packet(sync_entity_player_position_packet_t &packet, client_t *client)
@ -32,37 +36,85 @@ void deerith::client_connects(client_t *client)
// create entity player entity
entity_t entity = create_entity();
entity_link_to_client_t &link = add_component<entity_link_to_client_t>(entity);
entity_sync_client_position_t &position_sync = add_component<entity_sync_client_position_t>(entity);
create_player_entity_packet_t create_player_entity;
router_send_packet(create_player_entity, client);
link.client = client;
client->entity_id = entity;
// create the server representation of the entity
add_entity_network_id(entity);
add_component<entity_sync_coordinates_t>(entity);
send_create_entity_to_all_clients(entity);
// we do this the last to avoid send_create_entity_to_all_clients send_create_actual_entities_to_client create the same entity for the player
entity_link_to_client_t &link = add_component<entity_link_to_client_t>(entity);
client->entity_id = entity;
link.client = client;
send_create_actual_entities_to_client(client);
}
void deerith::send_create_entity_to_all_clients(deerith::entity_t ent)
{
auto view = get_entity_registry().view<entity_link_to_client_t>();
for (auto [entity, link_to_client] : view.each())
{
send_create_entity_to_client(ent, link_to_client.client);
}
}
void deerith::send_create_entity_to_client(entity_t ent, client_t *client)
{
entity_network_id_t &entity_network_id = get_component<entity_network_id_t>(ent);
create_entity_packet_t create_entity;
create_entity.network_sync_id = entity_network_id.network_id;
// in case its the self entity we let it know to the client
if (has_component<entity_link_to_client_t>(ent))
{
entity_link_to_client_t &link = get_component<entity_link_to_client_t>(ent);
create_entity.is_self_client = link.client == client;
deerith_trace("entity could be self {}", create_entity.is_self_client);
}
if (has_component<entity_sync_coordinates_t>(ent))
{
create_entity.add_entity_sync_coordinates = true;
}
router_send_packet(create_entity, client);
}
void deerith::client_disconnects(client_t *client)
{
entity_t client_entity = client->entity_id;
send_delete_entity_to_all_clients(client_entity);
get_entity_registry().destroy(client_entity);
}
void deerith::send_delete_entity_to_all_clients(entity_t ent)
{
destroy_entity_packet_t destroy_entity;
entity_network_id_t &network_id = get_component<entity_network_id_t>(ent);
destroy_entity.network_sync_id = network_id.network_id;
auto view = get_entity_registry().view<entity_link_to_client_t>();
for (auto [entity, link_to_client] : view.each())
{
router_send_packet(create_entity, link_to_client.client);
router_send_packet(destroy_entity, link_to_client.client);
}
}
void deerith::send_create_actual_entities_to_client(client_t *client)
{
auto entities_view = get_entity_registry().view<entity_network_id_t>();
for (auto [ent, network_id] : entities_view.each())
{
send_create_entity_to_client(ent, client);
}
}

View File

@ -6,8 +6,13 @@ namespace deerith
{
void update_players_characters();
void client_connects(client_t *client);
void client_disconnects(client_t *client);
void send_entity_data();
void send_create_entity_to_all_clients(entity_t ent);
void send_create_entity_to_client(entity_t ent, client_t *client);
void send_create_actual_entities_to_client(client_t *client);
void send_delete_entity_to_all_clients(entity_t ent);
}