1use std::collections::VecDeque;
2use std::net::{IpAddr, Ipv4Addr};
3use std::sync::{Arc, Mutex, Once};
4use std::time::{Duration, Instant};
5
6use bevy_app::prelude::*;
7use bevy_ecs::prelude::*;
8use bevy_log::LogPlugin;
9use bytes::{Buf, BufMut, BytesMut};
10use chunkedge_binary::{Decode, Encode};
11use chunkedge_ident::ident;
12use chunkedge_network::NetworkPlugin;
13use chunkedge_registry::{BiomeRegistry, DimensionTypeRegistry};
14use chunkedge_server::client::{ClientBundle, ClientBundleArgs, ClientConnection, ReceivedPacket};
15use chunkedge_server::keepalive::KeepaliveSettings;
16use chunkedge_server::protocol::decode::PacketFrame;
17use chunkedge_server::protocol::packets::play::{AcceptTeleportationC2s, PlayerPositionS2c};
18use chunkedge_server::protocol::{Packet, PacketDecoder, PacketEncoder, VarInt};
19use chunkedge_server::{ChunkLayer, EntityLayer, Server, ServerSettings};
20use uuid::Uuid;
21
22use crate::DefaultPlugins;
23
24pub fn add_plugins(app: &mut App) {
32 static ONCE: Once = Once::new();
33 let mut is_first = false;
34
35 ONCE.call_once(|| {
36 is_first = true;
37 });
38
39 let plugins = if is_first {
40 DefaultPlugins.build().disable::<NetworkPlugin>()
41 } else {
42 DefaultPlugins
43 .build()
44 .disable::<NetworkPlugin>()
45 .disable::<LogPlugin>()
46 };
47
48 app.add_plugins(plugins);
49}
50
51pub struct ScenarioSingleClient {
52 pub app: App,
54 pub client: Entity,
56 pub helper: MockClientHelper,
58 pub layer: Entity,
60}
61
62impl ScenarioSingleClient {
63 pub fn new() -> Self {
68 let mut app = App::new();
69
70 app.insert_resource(KeepaliveSettings {
71 period: Duration::MAX,
72 })
73 .insert_resource(ServerSettings {
74 compression_threshold: Default::default(),
75 ..Default::default()
76 });
77
78 add_plugins(&mut app);
79
80 app.update(); let chunk_layer = ChunkLayer::new(
83 ident!("overworld"),
84 app.world().resource::<DimensionTypeRegistry>(),
85 app.world().resource::<BiomeRegistry>(),
86 app.world().resource::<Server>(),
87 );
88 let entity_layer = EntityLayer::new(app.world().resource::<Server>());
89 let layer = app.world_mut().spawn((chunk_layer, entity_layer)).id();
90
91 let (mut client, helper) = create_mock_client("test");
92 client.layer.0 = layer;
93 client.visible_chunk_layer.0 = layer;
94 client.visible_entity_layers.0.insert(layer);
95 let client = app.world_mut().spawn(client).id();
96
97 ScenarioSingleClient {
98 app,
99 client,
100 helper,
101 layer,
102 }
103 }
104}
105
106impl Default for ScenarioSingleClient {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112pub fn create_mock_client<N: Into<String>>(name: N) -> (ClientBundle, MockClientHelper) {
117 let conn = MockClientConnection::new();
118
119 let bundle = ClientBundle::new(ClientBundleArgs {
120 username: name.into(),
121 uuid: Uuid::from_bytes(rand::random()),
122 ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
123 properties: Default::default(),
124 conn: Box::new(conn.clone()),
125 view_distance: 2,
126 locale: Default::default(),
127 chat_mode: Default::default(),
128 chat_colors: false,
129 displayed_skin_parts: Default::default(),
130 main_arm: Default::default(),
131 enable_text_filtering: false,
132 allow_server_listings: false,
133 particle_mode: Default::default(),
134 enc: PacketEncoder::new(),
135 });
136
137 let helper = MockClientHelper::new(conn);
138
139 (bundle, helper)
140}
141
142#[derive(Clone)]
146pub struct MockClientConnection {
147 inner: Arc<Mutex<MockClientConnectionInner>>,
148}
149
150struct MockClientConnectionInner {
151 recv_buf: VecDeque<ReceivedPacket>,
154 send_buf: BytesMut,
156}
157
158impl MockClientConnection {
159 pub fn new() -> Self {
160 Self {
161 inner: Arc::new(Mutex::new(MockClientConnectionInner {
162 recv_buf: VecDeque::new(),
163 send_buf: BytesMut::new(),
164 })),
165 }
166 }
167
168 fn inject_send(&self, mut bytes: BytesMut) {
170 let id = VarInt::decode_partial((&mut bytes).reader()).expect("failed to decode packet ID");
171
172 self.inner
173 .lock()
174 .unwrap()
175 .recv_buf
176 .push_back(ReceivedPacket {
177 timestamp: Instant::now(),
178 id,
179 body: bytes.freeze(),
180 });
181 }
182
183 fn take_received(&self) -> BytesMut {
184 self.inner.lock().unwrap().send_buf.split()
185 }
186
187 fn clear_received(&self) {
188 self.inner.lock().unwrap().send_buf.clear();
189 }
190}
191
192impl ClientConnection for MockClientConnection {
193 fn try_send(&mut self, bytes: BytesMut) -> anyhow::Result<()> {
194 self.inner.lock().unwrap().send_buf.unsplit(bytes);
195 Ok(())
196 }
197
198 fn try_recv(&mut self) -> anyhow::Result<Option<ReceivedPacket>> {
199 Ok(self.inner.lock().unwrap().recv_buf.pop_front())
200 }
201
202 fn len(&self) -> usize {
203 self.inner.lock().unwrap().recv_buf.len()
204 }
205}
206
207impl Default for MockClientConnection {
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213pub struct MockClientHelper {
216 conn: MockClientConnection,
217 dec: PacketDecoder,
218 scratch: BytesMut,
219}
220
221impl MockClientHelper {
222 pub fn new(conn: MockClientConnection) -> Self {
223 Self {
224 conn,
225 dec: PacketDecoder::new(),
226 scratch: BytesMut::new(),
227 }
228 }
229
230 #[track_caller]
233 pub fn send<P>(&mut self, packet: &P)
234 where
235 P: Packet + Encode,
236 {
237 packet
238 .encode_with_id((&mut self.scratch).writer())
239 .expect("failed to encode packet");
240
241 self.conn.inject_send(self.scratch.split());
242 }
243
244 #[track_caller]
246 pub fn collect_received(&mut self) -> PacketFrames {
247 self.dec.queue_bytes(self.conn.take_received());
248
249 let mut res = vec![];
250
251 while let Some(frame) = self
252 .dec
253 .try_next_packet()
254 .expect("failed to decode packet frame")
255 {
256 res.push(frame);
257 }
258
259 PacketFrames(res)
260 }
261
262 pub fn clear_received(&mut self) {
263 self.conn.clear_received();
264 }
265
266 pub fn confirm_initial_pending_teleports(&mut self) {
267 let mut counter = 0;
268
269 for pkt in self.collect_received().0 {
270 if pkt.id == PlayerPositionS2c::ID {
271 pkt.decode::<PlayerPositionS2c>().unwrap();
272
273 self.send(&AcceptTeleportationC2s {
274 teleport_id: counter.into(),
275 });
276
277 counter += 1;
278 }
279 }
280 }
281}
282
283#[derive(Clone, Debug)]
284pub struct PacketFrames(pub Vec<PacketFrame>);
285
286impl PacketFrames {
287 #[track_caller]
288 pub fn assert_count<P: Packet>(&self, expected_count: usize) {
289 let actual_count = self.0.iter().filter(|f| f.id == P::ID).count();
290
291 assert_eq!(
292 expected_count,
293 actual_count,
294 "unexpected packet count for {} (expected {expected_count}, got {actual_count})",
295 P::NAME,
296 )
297 }
298
299 #[track_caller]
300 pub fn assert_order<L: PacketList>(&self) {
301 let positions: Vec<_> = self
302 .0
303 .iter()
304 .filter_map(|f| L::packets().iter().position(|(id, _)| f.id == *id))
305 .collect();
306
307 let is_sorted = positions.windows(2).all(|w| w[0] <= w[1]);
309
310 assert!(
311 is_sorted,
312 "packets out of order (expected {:?}, got {:?})",
313 L::packets(),
314 self.debug_order::<L>()
315 )
316 }
317
318 #[track_caller]
324 pub fn first<'a, P>(&'a self) -> P
325 where
326 P: Packet + Decode<'a>,
327 {
328 if let Some(frame) = self.0.iter().find(|p| p.id == P::ID) {
329 frame.decode::<P>().unwrap()
330 } else {
331 panic!("failed to find packet {}", P::NAME)
332 }
333 }
334
335 pub fn debug_order<L: PacketList>(&self) -> impl std::fmt::Debug + use<L> {
336 self.0
337 .iter()
338 .filter_map(|f| L::packets().iter().find(|(id, _)| f.id == *id).copied())
339 .collect::<Vec<_>>()
340 }
341}
342
343pub trait PacketList {
344 fn packets() -> &'static [(i32, &'static str)];
345}
346
347macro_rules! impl_packet_list {
348 ($($ty:ident),*) => {
349 impl<$($ty: Packet,)*> PacketList for ($($ty,)*) {
350 fn packets() -> &'static [(i32, &'static str)] {
351 &[
352 $(
353 (
354 $ty::ID,
355 $ty::NAME
356 ),
357 )*
358 ]
359 }
360 }
361 }
362}
363
364impl_packet_list!(A);
365impl_packet_list!(A, B);
366impl_packet_list!(A, B, C);
367impl_packet_list!(A, B, C, D);
368impl_packet_list!(A, B, C, D, E);
369impl_packet_list!(A, B, C, D, E, F);
370impl_packet_list!(A, B, C, D, E, F, G);
371impl_packet_list!(A, B, C, D, E, F, G, H);
372impl_packet_list!(A, B, C, D, E, F, G, H, I);
373impl_packet_list!(A, B, C, D, E, F, G, H, I, J);
374impl_packet_list!(A, B, C, D, E, F, G, H, I, J, K);