116 lines
3.6 KiB
Rust
116 lines
3.6 KiB
Rust
//! Shared game/protocol types that are independent of rendering, audio, and editor code.
|
|
//!
|
|
//! This crate is intentionally small in M2. It provides stable tick/input/event
|
|
//! shapes that local standalone play can use now and future networking can build on.
|
|
|
|
use bevy::prelude::*;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Fixed simulation tick index.
|
|
#[derive(
|
|
Debug,
|
|
Clone,
|
|
Copy,
|
|
Default,
|
|
PartialEq,
|
|
Eq,
|
|
PartialOrd,
|
|
Ord,
|
|
Hash,
|
|
Reflect,
|
|
Serialize,
|
|
Deserialize,
|
|
)]
|
|
#[reflect(Default, Debug, Hash, Serialize, Deserialize)]
|
|
pub struct SimTick(pub u64);
|
|
|
|
impl SimTick {
|
|
pub const ZERO: Self = Self(0);
|
|
|
|
pub const fn next(self) -> Self {
|
|
Self(self.0 + 1)
|
|
}
|
|
}
|
|
|
|
/// The target fixed simulation rate for gameplay systems.
|
|
pub const SIM_TICK_RATE_HZ: u32 = 64;
|
|
|
|
/// The target fixed simulation delta in seconds.
|
|
pub const SIM_DELTA_SECS: f32 = 1.0 / SIM_TICK_RATE_HZ as f32;
|
|
|
|
/// Stable identifier for a player in protocol messages.
|
|
///
|
|
/// M2 only has a local player, but keeping the ID explicit avoids baking local
|
|
/// single-player assumptions into the simulation layer.
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
|
|
#[reflect(Default, Debug, Hash, Serialize, Deserialize)]
|
|
pub struct PlayerId(pub u64);
|
|
|
|
/// Player input expressed as gameplay intent, not device state.
|
|
#[derive(Component, Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
|
|
#[reflect(Component, Default, Debug, PartialEq, Serialize, Deserialize)]
|
|
pub struct PlayerInputIntent {
|
|
/// Local-space movement intent: X is right, Y is forward.
|
|
pub movement: Vec2,
|
|
/// Accumulated look delta in device pixels since the sim/client last consumed it.
|
|
pub look_delta: Vec2,
|
|
/// Edge-triggered jump command, consumed by the simulation.
|
|
pub jump_pressed: bool,
|
|
/// Level-triggered sprint modifier.
|
|
pub sprint_held: bool,
|
|
/// Level-triggered crouch modifier.
|
|
pub crouch_held: bool,
|
|
}
|
|
|
|
impl Default for PlayerInputIntent {
|
|
fn default() -> Self {
|
|
Self {
|
|
movement: Vec2::ZERO,
|
|
look_delta: Vec2::ZERO,
|
|
jump_pressed: false,
|
|
sprint_held: false,
|
|
crouch_held: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Command envelope suitable for local replay or future transport.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
|
|
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
|
|
pub struct PlayerCommand {
|
|
pub tick: SimTick,
|
|
pub player: PlayerId,
|
|
pub intent: PlayerInputIntent,
|
|
}
|
|
|
|
/// Minimal client-to-server message vocabulary reserved for M3 networking.
|
|
#[derive(Debug, Clone, PartialEq, Reflect, Serialize, Deserialize)]
|
|
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
|
|
pub enum ClientMessage {
|
|
Input(PlayerCommand),
|
|
Join { requested_player: PlayerId },
|
|
}
|
|
|
|
/// Minimal server-to-client event vocabulary reserved for M3 networking.
|
|
#[derive(Debug, Clone, PartialEq, Reflect, Serialize, Deserialize)]
|
|
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
|
|
pub enum ServerEvent {
|
|
Accepted { player: PlayerId, tick: SimTick },
|
|
PlayerSpawned { player: PlayerId },
|
|
PlayerDespawned { player: PlayerId },
|
|
}
|
|
|
|
/// Registers protocol types used in reflected scenes/resources and future messages.
|
|
pub struct ProtocolPlugin;
|
|
|
|
impl Plugin for ProtocolPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.register_type::<SimTick>()
|
|
.register_type::<PlayerId>()
|
|
.register_type::<PlayerInputIntent>()
|
|
.register_type::<PlayerCommand>()
|
|
.register_type::<ClientMessage>()
|
|
.register_type::<ServerEvent>();
|
|
}
|
|
}
|