feat(shared): add chunk subscribe and delivery message types
This commit is contained in:
parent
ce79449a6c
commit
b3f10d8f12
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
//! Network protocol types and constants.
|
||||
//!
|
||||
//! The module is split by stream purpose, starting with the [`control`]-stream handshake and disconnect messages. Control-stream types are re-exported here so callers continue to refer to `shared::protocol::<Type>` regardless of the internal layout.
|
||||
//! The module is split by stream purpose: [`control`]-stream handshake and disconnect messages, and the [`chunk`]-sync request/delivery messages. Control-stream types are re-exported here so callers continue to refer to `shared::protocol::<Type>` regardless of the internal layout, while the chunk types stay namespaced under `shared::protocol::chunk` to keep the two protocols visually distinct.
|
||||
|
||||
pub mod chunk;
|
||||
mod control;
|
||||
|
||||
pub use control::{
|
||||
|
|
|
|||
38
crates/shared/src/protocol/chunk.rs
Normal file
38
crates/shared/src/protocol/chunk.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
//! Chunk-sync message types carried between server and client.
|
||||
|
||||
use crate::world::{ChunkData, ChunkPos};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A client's request for the chunks it wants resident, expressed as a center and radius.
|
||||
// TODO: per-chunk request/ack + flow control for the fuller protocol.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ChunkSubscribe {
|
||||
/// Chunk-space center the client wants chunks around (derived from its camera/player).
|
||||
pub center: ChunkPos,
|
||||
/// Load radius in chunks. The server clamps this to a server-side maximum.
|
||||
pub radius: u16,
|
||||
}
|
||||
|
||||
/// One chunk delivered to the client.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ChunkMessage {
|
||||
/// Full chunk payload at a position.
|
||||
// TODO: LOD tier selection so a chunk can be delivered at a coarser stream tier.
|
||||
Chunk {
|
||||
/// Position of the delivered chunk.
|
||||
pos: ChunkPos,
|
||||
/// Serializable chunk contents (reuses the save/diff representation).
|
||||
data: ChunkData,
|
||||
},
|
||||
/// The server has dropped this chunk from the client's set; the client should unload it. This is the server-authoritative counterpart to the client's own radius-based unload: the server can force a discard even when the chunk is still within the client's radius.
|
||||
Drop {
|
||||
/// Position the client should discard.
|
||||
pos: ChunkPos,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../tests/protocol_chunk.rs"]
|
||||
mod tests;
|
||||
51
crates/shared/src/tests/protocol_chunk.rs
Normal file
51
crates/shared/src/tests/protocol_chunk.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
use super::*;
|
||||
use crate::world::BlockId;
|
||||
|
||||
/// Round-trips a `ChunkSubscribe` through postcard and asserts the decoded value is identical.
|
||||
fn roundtrip_subscribe(msg: &ChunkSubscribe) -> Result<(), postcard::Error> {
|
||||
let bytes = postcard::to_stdvec(msg)?;
|
||||
let decoded: ChunkSubscribe = postcard::from_bytes(&bytes)?;
|
||||
assert_eq!(msg, &decoded);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Round-trips a `ChunkMessage` through postcard and asserts the decoded value is identical.
|
||||
fn roundtrip_message(msg: &ChunkMessage) -> Result<(), postcard::Error> {
|
||||
let bytes = postcard::to_stdvec(msg)?;
|
||||
let decoded: ChunkMessage = postcard::from_bytes(&bytes)?;
|
||||
assert_eq!(msg, &decoded);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_chunk_subscribe() -> Result<(), postcard::Error> {
|
||||
// Negative coordinates exercise the signed ChunkPos fields under serialization.
|
||||
let msg = ChunkSubscribe {
|
||||
center: ChunkPos::new(-4, 0, 7),
|
||||
radius: 12,
|
||||
};
|
||||
roundtrip_subscribe(&msg)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_chunk_message_chunk() -> Result<(), postcard::Error> {
|
||||
// A non-empty ChunkData: private fields force construction via new + set.
|
||||
let mut data = ChunkData::new(ChunkPos::new(1, 2, 3), 0);
|
||||
data.set(0, BlockId(1));
|
||||
data.set(42, BlockId(1));
|
||||
let msg = ChunkMessage::Chunk {
|
||||
pos: ChunkPos::new(1, 2, 3),
|
||||
data,
|
||||
};
|
||||
roundtrip_message(&msg)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_chunk_message_drop() -> Result<(), postcard::Error> {
|
||||
let msg = ChunkMessage::Drop {
|
||||
pos: ChunkPos::new(0, -1, 0),
|
||||
};
|
||||
roundtrip_message(&msg)
|
||||
}
|
||||
Loading…
Reference in a new issue