#1 - ClientWorldController refactoring
This commit is contained in:
@@ -1,59 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* WorldStateWriter / WorldStateReader
|
||||
* =====================================
|
||||
* Project-specific serialisation for the per-frame world-state snapshot
|
||||
* sent from the server to each connected client.
|
||||
*
|
||||
* Wire format (all values little-endian):
|
||||
*
|
||||
* ┌──────────────────────────────────────────────────────────┐
|
||||
* │ Header (16 bytes) │
|
||||
* │ message_type : uint32 (supplied by the caller) │
|
||||
* │ sequence : uint32 (always 0, no reply expected) │
|
||||
* │ frame_idx : uint32 │
|
||||
* │ entity_count : uint32 (number of position records) │
|
||||
* ├──────────────────────────────────────────────────────────┤
|
||||
* │ Spawns section │
|
||||
* │ spawn_count : uint32 │
|
||||
* │ spawn[i].id : uint32 × spawn_count │
|
||||
* ├──────────────────────────────────────────────────────────┤
|
||||
* │ Despawns section │
|
||||
* │ despawn_count : uint32 │
|
||||
* │ despawn[i].id : uint32 × despawn_count │
|
||||
* ├──────────────────────────────────────────────────────────┤
|
||||
* │ Entity positions (hot path — tightly packed) │
|
||||
* │ [ id:uint32, x:float, y:float, z:float ] × entity_count│
|
||||
* └──────────────────────────────────────────────────────────┘
|
||||
*
|
||||
* Total minimum size : 20 bytes (header + empty spawns + empty despawns)
|
||||
* Per entity : 16 bytes
|
||||
* 300 entities : 20 + 300×16 = 4820 bytes (well under MTU for segmented)
|
||||
*
|
||||
* Usage (server side, called once per client per frame):
|
||||
*
|
||||
* tw::serial::WorldStateWriter w(buffer); // buffer is a BinaryBuffer
|
||||
* w.begin(frame_idx);
|
||||
* w.write_spawns(interest.spawns());
|
||||
* w.write_despawns(interest.despawns());
|
||||
* w.begin_entities(num_entities); // writes entity_count slot
|
||||
* for each entity in interest.entities():
|
||||
* w.write_entity(entity_id, position);
|
||||
* w.end(); // patches entity_count
|
||||
* // buffer.view() is ready to send
|
||||
*
|
||||
* Usage (client side):
|
||||
*
|
||||
* tw::serial::WorldStateReader r(payload_span);
|
||||
* auto header = r.read_header(); // frame_idx + counts
|
||||
* for (auto id : r.read_spawns()) { ... }
|
||||
* for (auto id : r.read_despawns()) { ... }
|
||||
* while (r.has_entity()) {
|
||||
* auto [id, pos] = r.read_entity();
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
|
||||
#include "Codec.hpp"
|
||||
#include "GlmCodec.hpp"
|
||||
@@ -67,10 +13,6 @@
|
||||
|
||||
namespace tw::serial {
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// WorldStateWriter
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class WorldStateWriter {
|
||||
BinaryWriter m_w;
|
||||
|
||||
@@ -81,13 +23,6 @@ class WorldStateWriter {
|
||||
public:
|
||||
explicit WorldStateWriter(BinaryBuffer& buf) noexcept : m_w(buf) {}
|
||||
|
||||
/**
|
||||
* Write the header. Call once per frame, before everything else.
|
||||
* entity_count is patched in end().
|
||||
*
|
||||
* message_type is supplied by the caller so that this module stays free of
|
||||
* the address the message is delivered to.
|
||||
*/
|
||||
void begin(uint32_t frame_idx, uint32_t message_type) noexcept {
|
||||
m_entity_count = 0;
|
||||
|
||||
@@ -104,11 +39,6 @@ public:
|
||||
m_entity_count_offset = m_w.reserve_u32();
|
||||
}
|
||||
|
||||
// ── Spawns ────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Write the spawn list. Pass any range of entt::entity.
|
||||
*/
|
||||
template<typename Range>
|
||||
void write_spawns(const Range& spawns) noexcept {
|
||||
auto count = static_cast<uint32_t>(std::size(spawns));
|
||||
@@ -118,8 +48,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// ── Despawns ──────────────────────────────────────────────────────────
|
||||
|
||||
template<typename Range>
|
||||
void write_despawns(const Range& despawns) noexcept {
|
||||
auto count = static_cast<uint32_t>(std::size(despawns));
|
||||
@@ -129,16 +57,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// ── Entity positions (the hot path) ───────────────────────────────────
|
||||
|
||||
/**
|
||||
* Write a single entity position record.
|
||||
* id : raw uint32 of the entity handle
|
||||
* pos : world-space position (x, y, z)
|
||||
*
|
||||
* This is the innermost loop of the replicator — every byte matters.
|
||||
* The compiler will inline both calls down to two contiguous memcpys.
|
||||
*/
|
||||
void write_entity(uint32_t id, const glm::vec3& pos) noexcept {
|
||||
m_w.write(id);
|
||||
// Write x, y, z as 3 contiguous floats
|
||||
@@ -146,20 +64,14 @@ public:
|
||||
++m_entity_count;
|
||||
}
|
||||
|
||||
// Convenience overload accepting an entt::entity handle directly
|
||||
void write_entity(entt::entity entity, const glm::vec3& pos) noexcept {
|
||||
write_entity(static_cast<uint32_t>(entity), pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch the entity_count field written in begin() and finalise the
|
||||
* buffer. Must be called exactly once after all write_entity() calls.
|
||||
*/
|
||||
void end() noexcept {
|
||||
m_w.patch_u32(m_entity_count_offset, m_entity_count);
|
||||
}
|
||||
|
||||
/** Expose the underlying buffer view (e.g. to pass to send_message). */
|
||||
std::span<const std::byte> view() noexcept {
|
||||
return m_w.buffer().view();
|
||||
}
|
||||
@@ -170,10 +82,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// WorldStateReader (client-side / test use)
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
struct WorldStateHeader {
|
||||
uint32_t packet_type;
|
||||
uint32_t frame_idx;
|
||||
|
||||
Reference in New Issue
Block a user