From 2af77fa468d0f475fb225f621972c176283a1c65 Mon Sep 17 00:00:00 2001 From: Chewico Date: Tue, 20 Oct 2026 16:22:10 +0200 Subject: [PATCH] Multiplayer working --- .vscode/settings.json | 2 +- .../include/deerith/graphics/window.h | 1 + .../include/deerith/world/chunk_render.h | 9 + deerith_engine_client/src/graphics/shader.cpp | 24 +- deerith_engine_client/src/graphics/window.cpp | 5 + .../src/network/base_protocol.cpp | 0 .../src/world/player_movment.cpp | 7 + .../src/world/server_communication.cpp | 21 +- .../src/world/world_render.cpp | 10 +- .../cereal/archives/portable_binary.hpp | 346 +++++++++--------- .../include/deerith/network/packets.h | 20 +- .../src/network/base_protocols.cpp | 9 - deerith_engine_server/src/network/network.cpp | 17 + .../src/world/player_communication.cpp | 62 +++- .../src/world/world_internals.h | 5 + 15 files changed, 350 insertions(+), 188 deletions(-) create mode 100644 deerith_engine_client/include/deerith/world/chunk_render.h delete mode 100644 deerith_engine_client/src/network/base_protocol.cpp delete mode 100644 deerith_engine_server/src/network/base_protocols.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 463c0e3..64b5fc8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,7 @@ "args": [ "--single", "--port", - "8080" + "8080-" ] } } \ No newline at end of file diff --git a/deerith_engine_client/include/deerith/graphics/window.h b/deerith_engine_client/include/deerith/graphics/window.h index 7ead6d4..2c4e4f4 100644 --- a/deerith_engine_client/include/deerith/graphics/window.h +++ b/deerith_engine_client/include/deerith/graphics/window.h @@ -14,4 +14,5 @@ namespace deerith float get_window_aspect_ratio(); int get_current_frame_id(); + bool is_windows_focused(); } \ No newline at end of file diff --git a/deerith_engine_client/include/deerith/world/chunk_render.h b/deerith_engine_client/include/deerith/world/chunk_render.h new file mode 100644 index 0000000..bfe8532 --- /dev/null +++ b/deerith_engine_client/include/deerith/world/chunk_render.h @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace deerith +{ + struct chunk_render_t + { + }; +} \ No newline at end of file diff --git a/deerith_engine_client/src/graphics/shader.cpp b/deerith_engine_client/src/graphics/shader.cpp index 993170d..f894c7d 100644 --- a/deerith_engine_client/src/graphics/shader.cpp +++ b/deerith_engine_client/src/graphics/shader.cpp @@ -1,4 +1,5 @@ #include "deerith/graphics/shader.h" +#include #include #include @@ -6,6 +7,24 @@ #include #include #include +#include + +std::filesystem::path get_executable_directory() +{ + std::array 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; diff --git a/deerith_engine_client/src/graphics/window.cpp b/deerith_engine_client/src/graphics/window.cpp index f51e8fd..31e462a 100644 --- a/deerith_engine_client/src/graphics/window.cpp +++ b/deerith_engine_client/src/graphics/window.cpp @@ -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(); diff --git a/deerith_engine_client/src/network/base_protocol.cpp b/deerith_engine_client/src/network/base_protocol.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/deerith_engine_client/src/world/player_movment.cpp b/deerith_engine_client/src/world/player_movment.cpp index 4f4424f..8d44a5d 100644 --- a/deerith_engine_client/src/world/player_movment.cpp +++ b/deerith_engine_client/src/world/player_movment.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -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(self_entity); entity_sync_position.move_input = movment; diff --git a/deerith_engine_client/src/world/server_communication.cpp b/deerith_engine_client/src/world/server_communication.cpp index 68bf44f..605148e 100644 --- a/deerith_engine_client/src/world/server_communication.cpp +++ b/deerith_engine_client/src/world/server_communication.cpp @@ -13,6 +13,7 @@ void deerith::setup_base_protocols() router_add_packet_processor(); router_add_packet_processor(); router_add_packet_processor(); + router_add_packet_processor(); } 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) @@ -59,4 +70,12 @@ 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); } \ No newline at end of file diff --git a/deerith_engine_client/src/world/world_render.cpp b/deerith_engine_client/src/world/world_render.cpp index 00044ef..ea24230 100644 --- a/deerith_engine_client/src/world/world_render.cpp +++ b/deerith_engine_client/src/world/world_render.cpp @@ -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(); + auto entities_view = get_entity_registry().view(entt::exclude); 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(); + 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)); + } } \ No newline at end of file diff --git a/deerith_engine_core/include/cereal/archives/portable_binary.hpp b/deerith_engine_core/include/cereal/archives/portable_binary.hpp index 4288321..9d2ae26 100644 --- a/deerith_engine_core/include/cereal/archives/portable_binary.hpp +++ b/deerith_engine_core/include/cereal/archives/portable_binary.hpp @@ -42,7 +42,7 @@ namespace cereal inline std::uint8_t is_little_endian() { static std::int32_t test = 1; - return *reinterpret_cast( &test ) == 1; + return *reinterpret_cast(&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 - 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 { + 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(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 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( data ) + DataSize - j - 1 + i, 1 ); - } - else - writtenSize = itsStream.rdbuf()->sputn( reinterpret_cast( 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(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 + 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(data) + DataSize - j - 1 + i, 1); + } + else + writtenSize = itsStream.rdbuf()->sputn(reinterpret_cast(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 { + 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(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 inline - void loadBinary( void * const data, std::streamsize size ) - { - // load data - auto const readSize = itsStream.rdbuf()->sgetn( reinterpret_cast( 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( data ); - for( std::streamsize i = 0; i < size; i += DataSize ) - portable_binary_detail::swap_bytes( 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(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 + inline void loadBinary(void *const data, std::streamsize size) + { + // load data + auto const readSize = itsStream.rdbuf()->sgetn(reinterpret_cast(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(data); + for (std::streamsize i = 0; i < size; i += DataSize) + portable_binary_detail::swap_bytes(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 inline - typename std::enable_if::value, void>::type - CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, T const & t) + template + inline + typename std::enable_if::value, void>::type + CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive &ar, T const &t) { - static_assert( !std::is_floating_point::value || - (std::is_floating_point::value && std::numeric_limits::is_iec559), - "Portable binary only supports IEEE 754 standardized floating point" ); + static_assert(!std::is_floating_point::value || + (std::is_floating_point::value && std::numeric_limits::is_iec559), + "Portable binary only supports IEEE 754 standardized floating point"); ar.template saveBinary(std::addressof(t), sizeof(t)); } //! Loading for POD types from portable binary - template inline - typename std::enable_if::value, void>::type - CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, T & t) + template + inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive &ar, T &t) { - static_assert( !std::is_floating_point::value || - (std::is_floating_point::value && std::numeric_limits::is_iec559), - "Portable binary only supports IEEE 754 standardized floating point" ); + static_assert(!std::is_floating_point::value || + (std::is_floating_point::value && std::numeric_limits::is_iec559), + "Portable binary only supports IEEE 754 standardized floating point"); ar.template loadBinary(std::addressof(t), sizeof(t)); } //! Serializing NVP types to portable binary - template inline - CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive) - CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair & t ) + template + inline CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive) + CEREAL_SERIALIZE_FUNCTION_NAME(Archive &ar, NameValuePair &t) { - ar( t.value ); + ar(t.value); } //! Serializing SizeTags to portable binary - template inline - CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive) - CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag & t ) + template + inline CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive) + CEREAL_SERIALIZE_FUNCTION_NAME(Archive &ar, SizeTag &t) { - ar( t.size ); + ar(t.size); } //! Saving binary data to portable binary - template inline - void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, BinaryData const & bd) + template + inline void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive &ar, BinaryData const &bd) { typedef typename std::remove_pointer::type TT; - static_assert( !std::is_floating_point::value || - (std::is_floating_point::value && std::numeric_limits::is_iec559), - "Portable binary only supports IEEE 754 standardized floating point" ); + static_assert(!std::is_floating_point::value || + (std::is_floating_point::value && std::numeric_limits::is_iec559), + "Portable binary only supports IEEE 754 standardized floating point"); - ar.template saveBinary( bd.data, static_cast( bd.size ) ); + ar.template saveBinary(bd.data, static_cast(bd.size)); } //! Loading binary data from portable binary - template inline - void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, BinaryData & bd) + template + inline void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive &ar, BinaryData &bd) { typedef typename std::remove_pointer::type TT; - static_assert( !std::is_floating_point::value || - (std::is_floating_point::value && std::numeric_limits::is_iec559), - "Portable binary only supports IEEE 754 standardized floating point" ); + static_assert(!std::is_floating_point::value || + (std::is_floating_point::value && std::numeric_limits::is_iec559), + "Portable binary only supports IEEE 754 standardized floating point"); - ar.template loadBinary( bd.data, static_cast( bd.size ) ); + ar.template loadBinary(bd.data, static_cast(bd.size)); } } // namespace cereal diff --git a/deerith_engine_core/include/deerith/network/packets.h b/deerith_engine_core/include/deerith/network/packets.h index 849a71c..429ae03 100644 --- a/deerith_engine_core/include/deerith/network/packets.h +++ b/deerith_engine_core/include/deerith/network/packets.h @@ -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 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 + void serialize(Archive &archive) + { + archive(network_sync_id); + } + }; } \ No newline at end of file diff --git a/deerith_engine_server/src/network/base_protocols.cpp b/deerith_engine_server/src/network/base_protocols.cpp deleted file mode 100644 index 220dd4f..0000000 --- a/deerith_engine_server/src/network/base_protocols.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "network/network_internals.h" -#include - -#include - -void deerith::load_base_protocols() -{ - router_add_packet_processor(); -} \ No newline at end of file diff --git a/deerith_engine_server/src/network/network.cpp b/deerith_engine_server/src/network/network.cpp index eb222fb..dadd321 100644 --- a/deerith_engine_server/src/network/network.cpp +++ b/deerith_engine_server/src/network/network.cpp @@ -21,6 +21,21 @@ namespace deerith std::vector> 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 &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; diff --git a/deerith_engine_server/src/world/player_communication.cpp b/deerith_engine_server/src/world/player_communication.cpp index 4e4a703..f619ae6 100644 --- a/deerith_engine_server/src/world/player_communication.cpp +++ b/deerith_engine_server/src/world/player_communication.cpp @@ -12,6 +12,10 @@ namespace deerith { } +void deerith::load_base_protocols() +{ + router_add_packet_processor(); +} 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); entity_sync_client_position_t &position_sync = add_component(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); 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); + 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(); + 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(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(ent)) + { + entity_link_to_client_t &link = get_component(ent); + create_entity.is_self_client = link.client == client; + deerith_trace("entity could be self {}", create_entity.is_self_client); + } + + if (has_component(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(ent); + + destroy_entity.network_sync_id = network_id.network_id; + auto view = get_entity_registry().view(); 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(); + + for (auto [ent, network_id] : entities_view.each()) + { + send_create_entity_to_client(ent, client); } } \ No newline at end of file diff --git a/deerith_engine_server/src/world/world_internals.h b/deerith_engine_server/src/world/world_internals.h index 160ce31..2cbcfcb 100644 --- a/deerith_engine_server/src/world/world_internals.h +++ b/deerith_engine_server/src/world/world_internals.h @@ -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); } \ No newline at end of file