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