Commit 15128490 authored by Filip Hauzvic's avatar Filip Hauzvic
Browse files

Synchronize replicated values

parent 33bae45a
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
#pragma once
#include <com/runner.hpp>
#include <net/types.hpp>
#include <utils/serialization.hpp>

namespace net {
    struct ReplicationSystem final : com::Runner
@@ -16,8 +17,20 @@ namespace net {
            com::ContextItem* owner,
            const std::string& getter_name,
            const std::string& setter_name);
        void handle_packet(ConnectionId from, utils::Deserializer& deserializer);

    private:
        utils::Buffer create_packet(const ReplicatedProperty& property, const com::Reflection::ValuePtr& value,
                                    int& written_bytes) const;
        void call_setter(const std::vector<std::string>& context_item_path, const std::string& setter_name,
                         const com::Reflection::ValuePtr& value);

        void initialize() override;

        const size_t INITIAL_BUFFER_SIZE = 512;
        std::vector<ReplicatedProperty> replicated_properties;

        double replication_interval = 0.3;
        double time_since_last_replication = 0.0;
    };
}
+3 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ namespace net
         * @brief Processes an incoming RPC packet, deserializes it, and calls the appropriate local function.
         * Should not be called directly, only by the NetDriver when receiving an RPC packet.
         */
        void process_packet(ConnectionId from, utils::Deserializer& deserializer);
        void handle_packet(ConnectionId from, utils::Deserializer& deserializer);

        void call_remote(
            com::ContextItem* context_item,
@@ -48,6 +48,8 @@ namespace net


    private:
        const size_t RPC_PACKET_INITIAL_BUFFER_SIZE = 1024;

        void register_functions() override;

        std::vector<uint8_t> create_packet(
+2 −1
Original line number Diff line number Diff line
@@ -39,7 +39,8 @@ namespace net {

    enum class PacketType : uint8_t
    {
        RPC
        RPC,
        REPLICATION
    };

    enum class SocketMode : uint8_t
+5 −1
Original line number Diff line number Diff line
@@ -143,7 +143,11 @@ void Driver::handle_packet(ConnectionId from, NetworkMessage* message)
    switch (packet_type)
    {
        case PacketType::RPC:
            rpc_system()->process_packet(from, deserializer);
            rpc_system()->handle_packet(from, deserializer);
            break;

        case PacketType::REPLICATION:
            replication()->handle_packet(from, deserializer);
            break;

        default:
+101 −2
Original line number Diff line number Diff line
#include <net/index.hpp>
#include <net/replication_system.hpp>
#include <phx/timer.hpp>

namespace net {
    void ReplicationSystem::next_round()
    {
        // TODO: put this code into some fixed_next_round function that is called at a fixed timestep
        time_since_last_replication += osi::timer()->dt();
        if (time_since_last_replication > replication_interval)
        {
            for (const auto& prop : replicated_properties)
            {
                // TODO: Delta compression here
                com::Reflection::ValuePtr current_value = com::make_default_value(prop.last_value->typeID());
                std::vector<com::Reflection::ValuePtr> params;
                params.push_back(current_value);
                prop.getter_func(params);

                int written_bytes = 0;
                auto buffer = create_packet(prop, current_value, written_bytes);
                std::span<const uint8_t> buffer_span(buffer.data(), written_bytes);
                driver()->broadcast(buffer_span, ReliabilityMode::UNRELIABLE);
            }
            time_since_last_replication -= replication_interval;
        }
    }


@@ -23,12 +42,14 @@ namespace net {
        }
        if (!owner->reflection()->functions().contains(getter_name))
        {
            LOG(LSL_ERROR, "[ReplicationSystem] Getter function " << getter_name << " not found in owner " << owner->name());
            LOG(LSL_ERROR,
                "[ReplicationSystem] Getter function " << getter_name << " not found in owner " << owner->name());
            return;
        }
        if (!owner->reflection()->functions().contains(setter_name))
        {
            LOG(LSL_ERROR, "[ReplicationSystem] Setter function " << setter_name << " not found in owner " << owner->name());
            LOG(LSL_ERROR,
                "[ReplicationSystem] Setter function " << setter_name << " not found in owner " << owner->name());
            return;
        }

@@ -48,6 +69,84 @@ namespace net {
        replicated_properties.push_back(property);
    }

    void ReplicationSystem::handle_packet(ConnectionId from, utils::Deserializer& deserializer)
    {
        std::vector<std::string> context_item_path;
        utils::deserialize_path(deserializer, context_item_path);
        if (context_item_path.empty())
        {
            LOG(LSL_ERROR, "[ReplicationSystem] Received replication packet with empty context item path");
            return;
        }

        std::string setter_name;
        deserializer.text1b(setter_name, MAX_STRING_LENGTH);

        auto value = utils::deserialize_reflection_value(deserializer);

        call_setter(context_item_path, setter_name, value);
    }

    void ReplicationSystem::call_setter(const std::vector<std::string>& context_item_path, const std::string& setter_name, const com::Reflection::ValuePtr& value)
    {
        const auto context_item = com::Folder::root()->locate<com::ContextItem>(context_item_path);
        if (!context_item)
        {
            LOG(LSL_ERROR, "[ReplicationSystem] Context item not found");
            return;
        }

        auto& functions = context_item->reflection()->functions();
        if (!functions.contains(setter_name))
        {
            LOG(LSL_ERROR, "[ReplicationSystem] Setter Function not found");
            return;
        }
        const auto& function = functions.at(setter_name);

        std::vector<com::Reflection::ValuePtr> params;
        params.push_back(value);

        if (function.param_types.size() != params.size())
        {
            LOG(LSL_ERROR,
                "[ReplicationSystem] Setter function should have exactly one parameter, but found " << function.param_types.size());
            return;
        }
        if (function.param_types[0] != params[0]->typeID())
        {
            LOG(LSL_ERROR,
                "[ReplicationSystem] Parameter type mismatch when calling setter function " << setter_name << " in context item " << context_item->name());
            return;
        }

        function.code(params);
    }


    utils::Buffer ReplicationSystem::create_packet(const ReplicatedProperty& property, const com::Reflection::ValuePtr& value,
                                                   int& written_bytes) const
    {
        utils::Buffer buffer(INITIAL_BUFFER_SIZE);
        auto serializer = utils::create_serializer(buffer);

        serializer.value1b(PacketType::REPLICATION);

        utils::serialize_path(serializer, property.owner->path(com::Folder::root()));
        serializer.text1b(property.setter_name, MAX_STRING_LENGTH);
        utils::serialize_reflection_value(serializer, value);

        written_bytes = static_cast<int>(serializer.adapter().writtenBytesCount());

        return buffer;
    }

    void ReplicationSystem::initialize()
    {
        osi::updaters()->find<com::Folder>("net")->push_back<com::Link>(self_name() + ".link", this);
    }


    ReplicationSystem::ReplicationSystem() : com::Runner(self_name())
    {
    }
Loading