bevy_replicon_matchbox/
lib.rs1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5
6#[cfg(feature = "client")]
7mod client;
8#[cfg(feature = "server")]
9mod server;
10
11#[cfg(feature = "client")]
12pub use client::*;
13#[cfg(feature = "server")]
14pub use server::*;
15
16use bevy::{app::PluginGroupBuilder, prelude::*};
17use bevy_matchbox::MatchboxSocket;
18use bevy_matchbox::matchbox_socket::{ChannelConfig, Packet, PeerId};
19use bevy_replicon::postcard;
20use bevy_replicon::postcard::to_slice;
21use bevy_replicon::prelude::*;
22use bytes::Bytes;
23use serde::{Deserialize, Serialize};
24
25const SYSTEM_CHANNEL_ID: usize = 0;
27
28#[derive(Debug, Serialize, Deserialize, PartialEq)]
29enum SystemChannelMessage {
30 ConnectedToHost,
31 Disconnect,
32}
33
34
35pub struct RepliconMatchboxPlugins;
41
42impl PluginGroup for RepliconMatchboxPlugins {
43 fn build(self) -> PluginGroupBuilder {
44 let mut group = PluginGroupBuilder::start::<Self>();
45
46 #[cfg(feature = "server")]
47 {
48 group = group.add(RepliconMatchboxServerPlugin);
49 }
50
51 #[cfg(feature = "client")]
52 {
53 group = group.add(RepliconMatchboxClientPlugin);
54 }
55
56 group = group.add(RepliconMatchboxSharedPlugin);
57
58 group
59 }
60}
61
62pub(crate) trait RepliconChannelsExt<'a> {
63 type Iter: Iterator<Item = &'a Channel>;
64
65 fn all_channels(&'a self) -> Self::Iter;
66}
67
68impl<'a> RepliconChannelsExt<'a> for RepliconChannels {
69 type Iter = std::iter::Chain<std::slice::Iter<'a, Channel>, std::slice::Iter<'a, Channel>>;
70 fn all_channels(&'a self) -> Self::Iter {
71 self.server_channels()
72 .iter()
73 .chain(self.client_channels().iter())
74 }
75}
76
77struct RepliconMatchboxSharedPlugin;
78
79impl Plugin for RepliconMatchboxSharedPlugin {
80 fn build(&self, app: &mut App) {
81 app.add_systems(Last, cleanup_matchbox_socket_on_exit);
82 }
83}
84
85fn cleanup_matchbox_socket_on_exit(
86 mut exit_events: EventReader<AppExit>,
87 mut server: Option<ResMut<MatchboxHost>>,
88 mut client: Option<ResMut<MatchboxClient>>,
89) {
90 for _ in exit_events.read() {
92 if let Some(client) = &mut client {
93 client.socket.close();
94 }
95 if let Some(server) = &mut server {
96 error!("Closing matchbox socket");
97 server.socket.close();
98 server.client_entities.clear();
99 }
100 }
101}
102
103fn create_matchbox_socket(
104 room_url: impl Into<String>,
105 replicon_channels: &RepliconChannels,
106) -> MatchboxSocket {
107 let mut web_rtc_socket = bevy_matchbox::matchbox_socket::WebRtcSocketBuilder::new(room_url);
108 web_rtc_socket = web_rtc_socket.add_reliable_channel();
110 for &channel in replicon_channels.all_channels() {
111 match channel {
112 Channel::Unreliable => {
113 web_rtc_socket = web_rtc_socket.add_unreliable_channel();
114 }
115 Channel::Unordered => {
116 web_rtc_socket = web_rtc_socket.add_channel(ChannelConfig {
117 ordered: false,
118 max_retransmits: None,
119 });
120 }
121 Channel::Ordered => {
122 web_rtc_socket = web_rtc_socket.add_reliable_channel();
123 }
124 };
125 }
126 let socket = web_rtc_socket.build();
127 MatchboxSocket::from(socket)
128}
129
130fn uuid_to_u64_truncated(peer_id: PeerId) -> u64 {
131 let bytes = peer_id.0.as_bytes();
132 u64::from_le_bytes(bytes[0..8].try_into().unwrap())
133}
134
135fn add_marker(data: &[u8]) -> Packet {
137 let mut payload = Vec::with_capacity(data.len() + 1);
138 payload.push(0);
139 payload.extend_from_slice(data);
140 payload.into()
141}
142
143fn strip_marker(packet: &[u8]) -> Bytes {
145 Bytes::copy_from_slice(&packet[1..])
146}
147
148
149
150fn to_packet<'a, T: Serialize>(msg: &T, buf: &'a mut [u8]) -> &'a [u8] {
151 to_slice(msg, buf).expect("serialize failed")
152}
153
154fn from_packet<'a, T: Deserialize<'a>>(data: &'a [u8]) -> Result<T, postcard::Error> {
155 postcard::from_bytes(data)
156}
157
158
159#[test]
160fn test_packaging() {
161 let messages = [SystemChannelMessage::ConnectedToHost, SystemChannelMessage::Disconnect];
162 for msg in messages.iter() {
163 let msg = SystemChannelMessage::ConnectedToHost;
164 let mut buf = [0u8; 1];
165 let p = to_packet(&msg, &mut buf);
166 let deserialized: SystemChannelMessage = from_packet(&*p).unwrap();
167 assert_eq!(msg, deserialized);
168 }
169
170}