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": [ "args": [
"--single", "--single",
"--port", "--port",
"8080" "8080-"
] ]
} }
} }

View File

@ -14,4 +14,5 @@ namespace deerith
float get_window_aspect_ratio(); float get_window_aspect_ratio();
int get_current_frame_id(); 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/graphics/shader.h"
#include <deerith/engine.h>
#include <glad/gl.h> #include <glad/gl.h>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
@ -6,6 +7,24 @@
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <iostream> #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) 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::failbit |
std::ifstream::badbit); std::ifstream::badbit);
std::filesystem::path cwd = get_executable_directory();
try try
{ {
vertexFile.open(vertexPath); vertexFile.open(cwd / vertexPath);
fragmentFile.open(fragmentPath); fragmentFile.open(cwd / fragmentPath);
std::stringstream vertexStream; std::stringstream vertexStream;
std::stringstream fragmentStream; std::stringstream fragmentStream;

View File

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

View File

@ -1,5 +1,6 @@
#include <deerith/engine.h> #include <deerith/engine.h>
#include <deerith/graphics/input.h> #include <deerith/graphics/input.h>
#include <deerith/graphics/window.h>
#include <deerith/network/router.h> #include <deerith/network/router.h>
#include <deerith/network/packets.h> #include <deerith/network/packets.h>
#include <deerith/world/entity.h> #include <deerith/world/entity.h>
@ -15,7 +16,13 @@ void deerith::process_player_input()
{ {
if (self_entity == entt::null) if (self_entity == entt::null)
return; return;
glm::vec2 movment = get_joystick_axes(0, 0); 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_client_position_t &entity_sync_position = get_component<entity_sync_client_position_t>(self_entity);
entity_sync_position.move_input = movment; 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_player_entity_packet_t>();
router_add_packet_processor<create_entity_packet_t>(); router_add_packet_processor<create_entity_packet_t>();
router_add_packet_processor<sync_entities_coordinates_packet_t>(); router_add_packet_processor<sync_entities_coordinates_packet_t>();
router_add_packet_processor<destroy_entity_packet_t>();
} }
template <> 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); 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); entity_network_id_t &entity_network_id = add_entity_network_with_id(entity, packet.network_sync_id);
if (packet.add_entity_sync_coordinates) 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); 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)); 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()) for (auto [entity, sync_coords] : entities_view.each())
{ {
glm::vec2 chords; 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)); 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() inline std::uint8_t is_little_endian()
{ {
static std::int32_t test = 1; 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 //! Swaps the order of bytes for some chunk of memory
@ -50,10 +50,10 @@ namespace cereal
@tparam DataSize The true size of the data @tparam DataSize The true size of the data
@ingroup Internal */ @ingroup Internal */
template <std::size_t DataSize> 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 ) for (std::size_t i = 0, end = DataSize / 2; i < end; ++i)
std::swap( data[i], data[DataSize - i - 1] ); std::swap(data[i], data[DataSize - i - 1]);
} }
} // end namespace portable_binary_detail } // end namespace portable_binary_detail
@ -84,30 +84,36 @@ namespace cereal
public: public:
//! Represents desired endianness //! Represents desired endianness
enum class Endianness : std::uint8_t enum class Endianness : std::uint8_t
{ big, little }; {
big,
little
};
//! Default options, preserve system endianness //! Default options, preserve system endianness
static Options Default(){ return Options(); } static Options Default() { return Options(); }
//! Save as little endian //! Save as little endian
static Options LittleEndian(){ return Options( Endianness::little ); } static Options LittleEndian() { return Options(Endianness::little); }
//! Save as big endian //! Save as big endian
static Options BigEndian(){ return Options( Endianness::big ); } static Options BigEndian() { return Options(Endianness::big); }
//! Specify specific options for the PortableBinaryOutputArchive //! Specify specific options for the PortableBinaryOutputArchive
/*! @param outputEndian The desired endianness of saved (output) data */ /*! @param outputEndian The desired endianness of saved (output) data */
explicit Options( Endianness outputEndian = getEndianness() ) : explicit Options(Endianness outputEndian = getEndianness()) : itsOutputEndianness(outputEndian) {}
itsOutputEndianness( outputEndian ) { }
private: private:
//! Gets the endianness of the system //! Gets the endianness of the system
inline static Endianness getEndianness() inline static Endianness getEndianness()
{ return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; } {
return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big;
}
//! Checks if Options is set for little endian //! Checks if Options is set for little endian
inline std::uint8_t is_little_endian() const inline std::uint8_t is_little_endian() const
{ return itsOutputEndianness == Endianness::little; } {
return itsOutputEndianness == Endianness::little;
}
friend class PortableBinaryOutputArchive; friend class PortableBinaryOutputArchive;
Endianness itsOutputEndianness; Endianness itsOutputEndianness;
@ -117,37 +123,36 @@ namespace cereal
/*! @param stream The stream to output to. Should be opened with std::ios::binary flag. /*! @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 @param options The PortableBinary specific options to use. See the Options struct
for the values of default parameters */ for the values of default parameters */
PortableBinaryOutputArchive(std::ostream & stream, Options const & options = Options::Default()) : PortableBinaryOutputArchive(std::ostream &stream, Options const &options = Options::Default()) : OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
itsStream(stream), itsStream(stream),
itsConvertEndianness( portable_binary_detail::is_little_endian() ^ options.is_little_endian() ) itsConvertEndianness(portable_binary_detail::is_little_endian() ^ options.is_little_endian())
{ {
this->operator()( options.is_little_endian() ); this->operator()(options.is_little_endian());
} }
~PortableBinaryOutputArchive() CEREAL_NOEXCEPT = default; ~PortableBinaryOutputArchive() CEREAL_NOEXCEPT = default;
//! Writes size bytes of data to the output stream //! Writes size bytes of data to the output stream
template <std::streamsize DataSize> inline template <std::streamsize DataSize>
void saveBinary( const void * data, std::streamsize size ) inline void saveBinary(const void *data, std::streamsize size)
{ {
std::streamsize writtenSize = 0; std::streamsize writtenSize = 0;
if( itsConvertEndianness ) if (itsConvertEndianness)
{ {
for( std::streamsize i = 0; i < size; i += DataSize ) for (std::streamsize i = 0; i < size; i += DataSize)
for( std::streamsize j = 0; j < DataSize; ++j ) for (std::streamsize j = 0; j < DataSize; ++j)
writtenSize += itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ) + DataSize - j - 1 + i, 1 ); writtenSize += itsStream.rdbuf()->sputn(reinterpret_cast<const char *>(data) + DataSize - j - 1 + i, 1);
} }
else else
writtenSize = itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size ); writtenSize = itsStream.rdbuf()->sputn(reinterpret_cast<const char *>(data), size);
if(writtenSize != size) if (writtenSize != size)
throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize)); throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
} }
private: private:
std::ostream & itsStream; std::ostream &itsStream;
const uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon saving const uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon saving
}; };
@ -186,30 +191,36 @@ namespace cereal
public: public:
//! Represents desired endianness //! Represents desired endianness
enum class Endianness : std::uint8_t enum class Endianness : std::uint8_t
{ big, little }; {
big,
little
};
//! Default options, preserve system endianness //! Default options, preserve system endianness
static Options Default(){ return Options(); } static Options Default() { return Options(); }
//! Load into little endian //! Load into little endian
static Options LittleEndian(){ return Options( Endianness::little ); } static Options LittleEndian() { return Options(Endianness::little); }
//! Load into big endian //! Load into big endian
static Options BigEndian(){ return Options( Endianness::big ); } static Options BigEndian() { return Options(Endianness::big); }
//! Specify specific options for the PortableBinaryInputArchive //! Specify specific options for the PortableBinaryInputArchive
/*! @param inputEndian The desired endianness of loaded (input) data */ /*! @param inputEndian The desired endianness of loaded (input) data */
explicit Options( Endianness inputEndian = getEndianness() ) : explicit Options(Endianness inputEndian = getEndianness()) : itsInputEndianness(inputEndian) {}
itsInputEndianness( inputEndian ) { }
private: private:
//! Gets the endianness of the system //! Gets the endianness of the system
inline static Endianness getEndianness() inline static Endianness getEndianness()
{ return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; } {
return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big;
}
//! Checks if Options is set for little endian //! Checks if Options is set for little endian
inline std::uint8_t is_little_endian() const inline std::uint8_t is_little_endian() const
{ return itsInputEndianness == Endianness::little; } {
return itsInputEndianness == Endianness::little;
}
friend class PortableBinaryInputArchive; friend class PortableBinaryInputArchive;
Endianness itsInputEndianness; Endianness itsInputEndianness;
@ -219,13 +230,12 @@ namespace cereal
/*! @param stream The stream to read from. Should be opened with std::ios::binary flag. /*! @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 @param options The PortableBinary specific options to use. See the Options struct
for the values of default parameters */ for the values of default parameters */
PortableBinaryInputArchive(std::istream & stream, Options const & options = Options::Default()) : PortableBinaryInputArchive(std::istream &stream, Options const &options = Options::Default()) : InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
itsStream(stream), itsStream(stream),
itsConvertEndianness( false ) itsConvertEndianness(false)
{ {
uint8_t streamLittleEndian; uint8_t streamLittleEndian;
this->operator()( streamLittleEndian ); this->operator()(streamLittleEndian);
itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian; itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian;
} }
@ -235,26 +245,26 @@ namespace cereal
/*! @param data The data to save /*! @param data The data to save
@param size The number of bytes in the data @param size The number of bytes in the data
@tparam DataSize T The size of the actual type of the data elements being loaded */ @tparam DataSize T The size of the actual type of the data elements being loaded */
template <std::streamsize DataSize> inline template <std::streamsize DataSize>
void loadBinary( void * const data, std::streamsize size ) inline void loadBinary(void *const data, std::streamsize size)
{ {
// load data // load data
auto const readSize = itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size ); auto const readSize = itsStream.rdbuf()->sgetn(reinterpret_cast<char *>(data), size);
if(readSize != size) if (readSize != size)
throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize)); throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
// flip bits if needed // flip bits if needed
if( itsConvertEndianness ) if (itsConvertEndianness)
{ {
std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( data ); std::uint8_t *ptr = reinterpret_cast<std::uint8_t *>(data);
for( std::streamsize i = 0; i < size; i += DataSize ) for (std::streamsize i = 0; i < size; i += DataSize)
portable_binary_detail::swap_bytes<DataSize>( ptr + i ); portable_binary_detail::swap_bytes<DataSize>(ptr + i);
} }
} }
private: private:
std::istream & itsStream; std::istream &itsStream;
uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading
}; };
@ -262,65 +272,67 @@ namespace cereal
// Common BinaryArchive serialization functions // Common BinaryArchive serialization functions
//! Saving for POD types to portable binary //! Saving for POD types to portable binary
template<class T> inline template <class T>
inline
typename std::enable_if<std::is_arithmetic<T>::value, void>::type typename std::enable_if<std::is_arithmetic<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, T const & t) CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive &ar, T const &t)
{ {
static_assert( !std::is_floating_point<T>::value || static_assert(!std::is_floating_point<T>::value ||
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559), (std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point" ); "Portable binary only supports IEEE 754 standardized floating point");
ar.template saveBinary<sizeof(T)>(std::addressof(t), sizeof(t)); ar.template saveBinary<sizeof(T)>(std::addressof(t), sizeof(t));
} }
//! Loading for POD types from portable binary //! Loading for POD types from portable binary
template<class T> inline template <class T>
inline
typename std::enable_if<std::is_arithmetic<T>::value, void>::type typename std::enable_if<std::is_arithmetic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, T & t) CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive &ar, T &t)
{ {
static_assert( !std::is_floating_point<T>::value || static_assert(!std::is_floating_point<T>::value ||
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559), (std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point" ); "Portable binary only supports IEEE 754 standardized floating point");
ar.template loadBinary<sizeof(T)>(std::addressof(t), sizeof(t)); ar.template loadBinary<sizeof(T)>(std::addressof(t), sizeof(t));
} }
//! Serializing NVP types to portable binary //! Serializing NVP types to portable binary
template <class Archive, class T> inline template <class Archive, class T>
CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive) inline CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t ) CEREAL_SERIALIZE_FUNCTION_NAME(Archive &ar, NameValuePair<T> &t)
{ {
ar( t.value ); ar(t.value);
} }
//! Serializing SizeTags to portable binary //! Serializing SizeTags to portable binary
template <class Archive, class T> inline template <class Archive, class T>
CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive) inline CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t ) CEREAL_SERIALIZE_FUNCTION_NAME(Archive &ar, SizeTag<T> &t)
{ {
ar( t.size ); ar(t.size);
} }
//! Saving binary data to portable binary //! Saving binary data to portable binary
template <class T> inline template <class T>
void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, BinaryData<T> const & bd) inline void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive &ar, BinaryData<T> const &bd)
{ {
typedef typename std::remove_pointer<T>::type TT; typedef typename std::remove_pointer<T>::type TT;
static_assert( !std::is_floating_point<TT>::value || static_assert(!std::is_floating_point<TT>::value ||
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559), (std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point" ); "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 //! Loading binary data from portable binary
template <class T> inline template <class T>
void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, BinaryData<T> & bd) inline void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive &ar, BinaryData<T> &bd)
{ {
typedef typename std::remove_pointer<T>::type TT; typedef typename std::remove_pointer<T>::type TT;
static_assert( !std::is_floating_point<TT>::value || static_assert(!std::is_floating_point<TT>::value ||
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559), (std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
"Portable binary only supports IEEE 754 standardized floating point" ); "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 } // namespace cereal

View File

@ -47,14 +47,16 @@ namespace deerith
static constexpr uint16_t packet_id = 4; static constexpr uint16_t packet_id = 4;
static constexpr bool reliable = true; static constexpr bool reliable = true;
id_t network_sync_id; id_t network_sync_id = 0;
bool add_entity_sync_coordinates; bool add_entity_sync_coordinates = false;
bool is_self_client = false;
template <typename Archive> template <typename Archive>
void serialize(Archive &archive) void serialize(Archive &archive)
{ {
archive(network_sync_id); archive(network_sync_id);
archive(add_entity_sync_coordinates); archive(add_entity_sync_coordinates);
archive(is_self_client);
} }
}; };
@ -73,4 +75,18 @@ namespace deerith
archive(positions_to_update); 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; std::vector<std::unique_ptr<client_t>> clients;
void create_client(ENetPeer *peer); 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) bool deerith::init_server(int port)
@ -71,6 +86,8 @@ void deerith::flush_network_events()
break; break;
case ENET_EVENT_TYPE_DISCONNECT: case ENET_EVENT_TYPE_DISCONNECT:
client_disconnects((client_t *)event.peer->data);
delete_client((client_t *)event.peer->data);
event.peer->data = NULL; event.peer->data = NULL;
break; 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 <> template <>
void deerith::process_packet(sync_entity_player_position_packet_t &packet, client_t *client) 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 // create entity player entity
entity_t entity = create_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); entity_sync_client_position_t &position_sync = add_component<entity_sync_client_position_t>(entity);
create_player_entity_packet_t create_player_entity; create_player_entity_packet_t create_player_entity;
router_send_packet(create_player_entity, client); router_send_packet(create_player_entity, client);
link.client = client;
client->entity_id = entity;
// create the server representation of the entity // create the server representation of the entity
add_entity_network_id(entity); add_entity_network_id(entity);
add_component<entity_sync_coordinates_t>(entity); add_component<entity_sync_coordinates_t>(entity);
send_create_entity_to_all_clients(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) 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); entity_network_id_t &entity_network_id = get_component<entity_network_id_t>(ent);
create_entity_packet_t create_entity; create_entity_packet_t create_entity;
create_entity.network_sync_id = entity_network_id.network_id; 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)) 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; 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>(); auto view = get_entity_registry().view<entity_link_to_client_t>();
for (auto [entity, link_to_client] : view.each()) 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 update_players_characters();
void client_connects(client_t *client); void client_connects(client_t *client);
void client_disconnects(client_t *client);
void send_entity_data(); void send_entity_data();
void send_create_entity_to_all_clients(entity_t ent); 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);
} }