valence/
testing.rs

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 uuid::Uuid;
10use valence_ident::ident;
11use valence_network::NetworkPlugin;
12use valence_registry::{BiomeRegistry, DimensionTypeRegistry};
13use valence_server::client::{ClientBundle, ClientBundleArgs, ClientConnection, ReceivedPacket};
14use valence_server::keepalive::KeepaliveSettings;
15use valence_server::protocol::decode::PacketFrame;
16use valence_server::protocol::packets::play::{PlayerPositionLookS2c, TeleportConfirmC2s};
17use valence_server::protocol::{Decode, Encode, Packet, PacketDecoder, PacketEncoder, VarInt};
18use valence_server::{ChunkLayer, EntityLayer, Server, ServerSettings};
19
20use crate::DefaultPlugins;
21pub struct ScenarioSingleClient {
22    /// The new bevy application.
23    pub app: App,
24    /// Entity handle for the single client.
25    pub client: Entity,
26    /// Helper for sending and receiving packets from the mock client.
27    pub helper: MockClientHelper,
28    /// Entity with [`ChunkLayer`] and [`EntityLayer`] components.
29    pub layer: Entity,
30}
31
32impl ScenarioSingleClient {
33    /// Sets up Valence with a single mock client and entity+chunk layer. The
34    /// client is configured to be placed within the layer.
35    ///
36    /// Reduces boilerplate in unit tests.
37    pub fn new() -> Self {
38        let mut app = App::new();
39
40        app.insert_resource(KeepaliveSettings {
41            period: Duration::MAX,
42        })
43        .insert_resource(ServerSettings {
44            compression_threshold: Default::default(),
45            ..Default::default()
46        })
47        .add_plugins(DefaultPlugins.build().disable::<NetworkPlugin>());
48
49        app.update(); // Initialize plugins.
50
51        let chunk_layer = ChunkLayer::new(
52            ident!("overworld"),
53            app.world().resource::<DimensionTypeRegistry>(),
54            app.world().resource::<BiomeRegistry>(),
55            app.world().resource::<Server>(),
56        );
57        let entity_layer = EntityLayer::new(app.world().resource::<Server>());
58        let layer = app.world_mut().spawn((chunk_layer, entity_layer)).id();
59
60        let (mut client, helper) = create_mock_client("test");
61        client.player.layer.0 = layer;
62        client.visible_chunk_layer.0 = layer;
63        client.visible_entity_layers.0.insert(layer);
64        let client = app.world_mut().spawn(client).id();
65
66        ScenarioSingleClient {
67            app,
68            client,
69            helper,
70            layer,
71        }
72    }
73}
74
75impl Default for ScenarioSingleClient {
76    fn default() -> Self {
77        Self::new()
78    }
79}
80
81/// Creates a mock client bundle that can be used for unit testing.
82///
83/// Returns the client, and a helper to inject packets as if the client sent
84/// them and receive packets as if the client received them.
85pub fn create_mock_client<N: Into<String>>(name: N) -> (ClientBundle, MockClientHelper) {
86    let conn = MockClientConnection::new();
87
88    let bundle = ClientBundle::new(ClientBundleArgs {
89        username: name.into(),
90        uuid: Uuid::from_bytes(rand::random()),
91        ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
92        properties: Default::default(),
93        conn: Box::new(conn.clone()),
94        enc: PacketEncoder::new(),
95    });
96
97    let helper = MockClientHelper::new(conn);
98
99    (bundle, helper)
100}
101
102/// A mock client connection that can be used for testing.
103///
104/// Safe to clone, but note that the clone will share the same buffers.
105#[derive(Clone)]
106pub struct MockClientConnection {
107    inner: Arc<Mutex<MockClientConnectionInner>>,
108}
109
110struct MockClientConnectionInner {
111    /// The queue of packets to receive from the client to be processed by the
112    /// server.
113    recv_buf: VecDeque<ReceivedPacket>,
114    /// The queue of packets to send from the server to the client.
115    send_buf: BytesMut,
116}
117
118impl MockClientConnection {
119    pub fn new() -> Self {
120        Self {
121            inner: Arc::new(Mutex::new(MockClientConnectionInner {
122                recv_buf: VecDeque::new(),
123                send_buf: BytesMut::new(),
124            })),
125        }
126    }
127
128    /// Injects a (Packet ID + data) frame to be received by the server.
129    fn inject_send(&self, mut bytes: BytesMut) {
130        let id = VarInt::decode_partial((&mut bytes).reader()).expect("failed to decode packet ID");
131
132        self.inner
133            .lock()
134            .unwrap()
135            .recv_buf
136            .push_back(ReceivedPacket {
137                timestamp: Instant::now(),
138                id,
139                body: bytes.freeze(),
140            });
141    }
142
143    fn take_received(&self) -> BytesMut {
144        self.inner.lock().unwrap().send_buf.split()
145    }
146
147    fn clear_received(&self) {
148        self.inner.lock().unwrap().send_buf.clear();
149    }
150}
151
152impl ClientConnection for MockClientConnection {
153    fn try_send(&mut self, bytes: BytesMut) -> anyhow::Result<()> {
154        self.inner.lock().unwrap().send_buf.unsplit(bytes);
155        Ok(())
156    }
157
158    fn try_recv(&mut self) -> anyhow::Result<Option<ReceivedPacket>> {
159        Ok(self.inner.lock().unwrap().recv_buf.pop_front())
160    }
161
162    fn len(&self) -> usize {
163        self.inner.lock().unwrap().recv_buf.len()
164    }
165}
166
167impl Default for MockClientConnection {
168    fn default() -> Self {
169        Self::new()
170    }
171}
172
173/// Contains the mocked client connection and helper methods to inject packets
174/// and read packets from the send stream.
175pub struct MockClientHelper {
176    conn: MockClientConnection,
177    dec: PacketDecoder,
178    scratch: BytesMut,
179}
180
181impl MockClientHelper {
182    pub fn new(conn: MockClientConnection) -> Self {
183        Self {
184            conn,
185            dec: PacketDecoder::new(),
186            scratch: BytesMut::new(),
187        }
188    }
189
190    /// Inject a packet to be treated as a packet inbound to the server. Panics
191    /// if the packet cannot be sent.
192    #[track_caller]
193    pub fn send<P>(&mut self, packet: &P)
194    where
195        P: Packet + Encode,
196    {
197        packet
198            .encode_with_id((&mut self.scratch).writer())
199            .expect("failed to encode packet");
200
201        self.conn.inject_send(self.scratch.split());
202    }
203
204    /// Collect all packets that have been received by the client.
205    #[track_caller]
206    pub fn collect_received(&mut self) -> PacketFrames {
207        self.dec.queue_bytes(self.conn.take_received());
208
209        let mut res = vec![];
210
211        while let Some(frame) = self
212            .dec
213            .try_next_packet()
214            .expect("failed to decode packet frame")
215        {
216            res.push(frame);
217        }
218
219        PacketFrames(res)
220    }
221
222    pub fn clear_received(&mut self) {
223        self.conn.clear_received();
224    }
225
226    pub fn confirm_initial_pending_teleports(&mut self) {
227        let mut counter = 0;
228
229        for pkt in self.collect_received().0 {
230            if pkt.id == PlayerPositionLookS2c::ID {
231                pkt.decode::<PlayerPositionLookS2c>().unwrap();
232
233                self.send(&TeleportConfirmC2s {
234                    teleport_id: counter.into(),
235                });
236
237                counter += 1;
238            }
239        }
240    }
241}
242
243#[derive(Clone, Debug)]
244pub struct PacketFrames(pub Vec<PacketFrame>);
245
246impl PacketFrames {
247    #[track_caller]
248    pub fn assert_count<P: Packet>(&self, expected_count: usize) {
249        let actual_count = self.0.iter().filter(|f| f.id == P::ID).count();
250
251        assert_eq!(
252            expected_count,
253            actual_count,
254            "unexpected packet count for {} (expected {expected_count}, got {actual_count})",
255            P::NAME,
256        )
257    }
258
259    #[track_caller]
260    pub fn assert_order<L: PacketList>(&self) {
261        let positions: Vec<_> = self
262            .0
263            .iter()
264            .filter_map(|f| L::packets().iter().position(|(id, _)| f.id == *id))
265            .collect();
266
267        // TODO: replace with slice::is_sorted when stabilized.
268        let is_sorted = positions.windows(2).all(|w| w[0] <= w[1]);
269
270        assert!(
271            is_sorted,
272            "packets out of order (expected {:?}, got {:?})",
273            L::packets(),
274            self.debug_order::<L>()
275        )
276    }
277
278    /// Finds the first occurrence of `P` in the packet list and decodes it.
279    ///
280    /// # Panics
281    ///
282    /// Panics if the packet was not found or a decoding error occurs.
283    #[track_caller]
284    pub fn first<'a, P>(&'a self) -> P
285    where
286        P: Packet + Decode<'a>,
287    {
288        if let Some(frame) = self.0.iter().find(|p| p.id == P::ID) {
289            frame.decode::<P>().unwrap()
290        } else {
291            panic!("failed to find packet {}", P::NAME)
292        }
293    }
294
295    pub fn debug_order<L: PacketList>(&self) -> impl std::fmt::Debug {
296        self.0
297            .iter()
298            .filter_map(|f| L::packets().iter().find(|(id, _)| f.id == *id).copied())
299            .collect::<Vec<_>>()
300    }
301}
302
303pub trait PacketList {
304    fn packets() -> &'static [(i32, &'static str)];
305}
306
307macro_rules! impl_packet_list {
308    ($($ty:ident),*) => {
309        impl<$($ty: Packet,)*> PacketList for ($($ty,)*) {
310            fn packets() -> &'static [(i32, &'static str)] {
311                &[
312                    $(
313                        (
314                            $ty::ID,
315                            $ty::NAME
316                        ),
317                    )*
318                ]
319            }
320        }
321    }
322}
323
324impl_packet_list!(A);
325impl_packet_list!(A, B);
326impl_packet_list!(A, B, C);
327impl_packet_list!(A, B, C, D);
328impl_packet_list!(A, B, C, D, E);
329impl_packet_list!(A, B, C, D, E, F);
330impl_packet_list!(A, B, C, D, E, F, G);
331impl_packet_list!(A, B, C, D, E, F, G, H);
332impl_packet_list!(A, B, C, D, E, F, G, H, I);
333impl_packet_list!(A, B, C, D, E, F, G, H, I, J);
334impl_packet_list!(A, B, C, D, E, F, G, H, I, J, K);