use crate::types::{ClientId, ComponentKind, NetworkId};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PlatformEvent {
ResourceExhausted {
network_id: NetworkId,
},
Possession {
network_id: NetworkId,
},
WorkspaceManifest {
manifest: BTreeMap<String, String>,
},
Interaction {
source: NetworkId,
target: NetworkId,
amount: u16,
},
Termination { target: NetworkId },
Reinitialization { target: NetworkId, x: f32, y: f32 },
PayloadCollected {
network_id: NetworkId,
amount: u16,
},
}
impl PlatformEvent {
#[must_use]
pub fn into_wire_event(self) -> WireEvent {
WireEvent::PlatformEvent(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FragmentedEvent {
pub message_id: u32,
pub fragment_index: u16,
pub total_fragments: u16,
#[serde(with = "serde_bytes")]
pub payload: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReplicationEvent {
pub network_id: NetworkId,
pub component_kind: ComponentKind,
#[serde(with = "serde_bytes")]
pub payload: Vec<u8>,
pub tick: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ComponentUpdate {
pub network_id: NetworkId,
pub component_kind: ComponentKind,
#[serde(with = "serde_bytes")]
pub payload: Vec<u8>,
pub tick: u64,
}
impl From<ReplicationEvent> for ComponentUpdate {
fn from(event: ReplicationEvent) -> Self {
Self {
network_id: event.network_id,
component_kind: event.component_kind,
payload: event.payload,
tick: event.tick,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum NetworkEvent {
ClientConnected(ClientId),
ClientDisconnected(ClientId),
UnreliableMessage {
client_id: ClientId,
#[serde(with = "serde_bytes")]
data: Vec<u8>,
},
ReliableMessage {
client_id: ClientId,
#[serde(with = "serde_bytes")]
data: Vec<u8>,
},
Ping {
client_id: ClientId,
tick: u64,
},
Pong {
tick: u64,
},
EntitySpawned {
client_id: ClientId,
network_id: NetworkId,
kind: u16,
},
EntityDespawned {
client_id: ClientId,
network_id: NetworkId,
},
Auth {
session_token: String,
},
SessionClosed(ClientId),
StreamReset(ClientId),
Fragment {
client_id: ClientId,
fragment: FragmentedEvent,
},
StressTest {
client_id: ClientId,
count: u16,
rotate: bool,
},
Spawn {
client_id: ClientId,
entity_type: u16,
x: f32,
y: f32,
rot: f32,
},
ClearWorld {
client_id: ClientId,
},
StartSession {
client_id: ClientId,
},
RequestWorkspaceManifest {
client_id: ClientId,
},
Disconnected(ClientId),
PlatformEvent {
client_id: ClientId,
event: PlatformEvent,
},
ReplicationBatch {
client_id: ClientId,
events: Vec<ReplicationEvent>,
},
}
impl NetworkEvent {
#[must_use]
pub const fn is_wire(&self) -> bool {
match self {
Self::Ping { .. }
| Self::Pong { .. }
| Self::Auth { .. }
| Self::Fragment { .. }
| Self::StressTest { .. }
| Self::Spawn { .. }
| Self::ClearWorld { .. }
| Self::StartSession { .. }
| Self::RequestWorkspaceManifest { .. }
| Self::EntitySpawned { .. }
| Self::EntityDespawned { .. }
| Self::PlatformEvent { .. }
| Self::ReplicationBatch { .. } => true,
Self::ClientConnected(_)
| Self::ClientDisconnected(_)
| Self::UnreliableMessage { .. }
| Self::ReliableMessage { .. }
| Self::SessionClosed(_)
| Self::StreamReset(_)
| Self::Disconnected(_) => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum WireEvent {
Ping {
tick: u64,
},
Pong {
tick: u64,
},
Auth {
session_token: String,
},
Fragment(FragmentedEvent),
StressTest {
count: u16,
rotate: bool,
},
Spawn {
entity_type: u16,
x: f32,
y: f32,
rot: f32,
},
ClearWorld,
EntitySpawned {
network_id: NetworkId,
kind: u16,
},
EntityDespawned {
network_id: NetworkId,
},
StartSession,
RequestWorkspaceManifest,
PlatformEvent(PlatformEvent),
ReplicationBatch(Vec<ReplicationEvent>),
}
impl WireEvent {
#[must_use]
pub fn into_network_event(self, client_id: crate::types::ClientId) -> NetworkEvent {
match self {
Self::Ping { tick } => NetworkEvent::Ping { client_id, tick },
Self::Pong { tick } => NetworkEvent::Pong { tick },
Self::Auth { session_token } => NetworkEvent::Auth { session_token },
Self::Fragment(fragment) => NetworkEvent::Fragment {
client_id,
fragment,
},
Self::StressTest { count, rotate } => NetworkEvent::StressTest {
client_id,
count,
rotate,
},
Self::Spawn {
entity_type,
x,
y,
rot,
} => NetworkEvent::Spawn {
client_id,
entity_type,
x,
y,
rot,
},
Self::ClearWorld => NetworkEvent::ClearWorld { client_id },
Self::StartSession => NetworkEvent::StartSession { client_id },
Self::RequestWorkspaceManifest => NetworkEvent::RequestWorkspaceManifest { client_id },
Self::PlatformEvent(event) => NetworkEvent::PlatformEvent { client_id, event },
Self::EntitySpawned { network_id, kind } => NetworkEvent::EntitySpawned {
client_id,
network_id,
kind,
},
Self::EntityDespawned { network_id } => NetworkEvent::EntityDespawned {
client_id,
network_id,
},
Self::ReplicationBatch(events) => NetworkEvent::ReplicationBatch { client_id, events },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_network_event_is_wire() {
assert!(
NetworkEvent::Ping {
client_id: ClientId(1),
tick: 100
}
.is_wire()
);
assert!(
NetworkEvent::PlatformEvent {
client_id: ClientId(1),
event: PlatformEvent::ResourceExhausted {
network_id: NetworkId(1)
}
}
.is_wire()
);
assert!(
NetworkEvent::ReplicationBatch {
client_id: ClientId(1),
events: vec![]
}
.is_wire()
);
assert!(!NetworkEvent::ClientConnected(ClientId(1)).is_wire());
assert!(!NetworkEvent::ClientDisconnected(ClientId(1)).is_wire());
assert!(!NetworkEvent::Disconnected(ClientId(1)).is_wire());
assert!(
NetworkEvent::EntitySpawned {
client_id: ClientId(1),
network_id: NetworkId(1),
kind: 1
}
.is_wire()
);
assert!(
NetworkEvent::EntityDespawned {
client_id: ClientId(1),
network_id: NetworkId(1)
}
.is_wire()
);
assert!(
NetworkEvent::RequestWorkspaceManifest {
client_id: ClientId(1)
}
.is_wire()
);
}
#[test]
fn test_wire_event_conversion_roundtrip() {
let wire = WireEvent::PlatformEvent(PlatformEvent::ResourceExhausted {
network_id: NetworkId(42),
});
let client_id = ClientId(7);
let network = wire.clone().into_network_event(client_id);
if let NetworkEvent::PlatformEvent {
client_id: cid,
event,
} = network
{
assert_eq!(cid, client_id);
assert_eq!(
event,
PlatformEvent::ResourceExhausted {
network_id: NetworkId(42)
}
);
} else {
panic!("Conversion failed to preserve PlatformEvent variant");
}
let event = ReplicationEvent {
network_id: NetworkId(1),
component_kind: ComponentKind(1),
payload: vec![1, 2, 3],
tick: 100,
};
let batch_wire = WireEvent::ReplicationBatch(vec![event.clone()]);
let batch_network = batch_wire.into_network_event(client_id);
if let NetworkEvent::ReplicationBatch {
client_id: cid,
events,
} = batch_network
{
assert_eq!(cid, client_id);
assert!(!events.is_empty());
assert_eq!(events[0].tick, 100);
assert_eq!(events[0].payload, vec![1, 2, 3]);
assert_eq!(events[0].network_id, NetworkId(1));
assert_eq!(events[0].component_kind, ComponentKind(1));
} else {
panic!("Conversion failed to preserve ReplicationBatch variant");
}
}
}