valence_server_common/lib.rs
1#![doc = include_str!("../README.md")]
2
3mod despawn;
4mod uuid;
5
6use std::num::NonZeroU32;
7use std::time::Duration;
8
9use bevy_app::prelude::*;
10use bevy_app::ScheduleRunnerPlugin;
11use bevy_ecs::prelude::*;
12pub use despawn::*;
13use valence_protocol::CompressionThreshold;
14
15pub use crate::uuid::*;
16
17/// Minecraft's standard ticks per second (TPS).
18pub const DEFAULT_TPS: NonZeroU32 = match NonZeroU32::new(20) {
19 Some(n) => n,
20 None => unreachable!(),
21};
22
23#[derive(Clone, Resource)]
24pub struct ServerSettings {
25 /// The target ticks per second (TPS) of the server. This is the number of
26 /// game updates that should occur in one second.
27 ///
28 /// On each game update (tick), the server is expected to update game logic
29 /// and respond to packets from clients. Once this is complete, the server
30 /// will sleep for any remaining time until a full tick duration has passed.
31 ///
32 /// Note that the official Minecraft client only processes packets at 20hz,
33 /// so there is little benefit to a tick rate higher than the default 20.
34 ///
35 /// # Default Value
36 ///
37 /// [`DEFAULT_TPS`]
38 pub tick_rate: NonZeroU32,
39 /// The compression threshold to use for compressing packets. For a
40 /// compression threshold of `Some(N)`, packets with encoded lengths >= `N`
41 /// are compressed while all others are not. `None` disables compression
42 /// completely.
43 ///
44 /// If the server is used behind a proxy on the same machine, you will
45 /// likely want to disable compression.
46 ///
47 /// # Default Value
48 ///
49 /// Compression is enabled with an unspecified value. This value may
50 /// change in future versions.
51 pub compression_threshold: CompressionThreshold,
52}
53
54impl Default for ServerSettings {
55 fn default() -> Self {
56 Self {
57 tick_rate: DEFAULT_TPS,
58 compression_threshold: CompressionThreshold(256),
59 }
60 }
61}
62
63pub struct ServerPlugin;
64
65impl Plugin for ServerPlugin {
66 fn build(&self, app: &mut App) {
67 let settings = app
68 .world_mut()
69 .get_resource_or_insert_with(ServerSettings::default)
70 .clone();
71
72 app.insert_resource(Server {
73 current_tick: 0,
74 threshold: settings.compression_threshold,
75 tick_rate: settings.tick_rate,
76 });
77
78 let tick_period = Duration::from_secs_f64(f64::from(settings.tick_rate.get()).recip());
79
80 // Make the app loop forever at the configured TPS.
81 app.add_plugins(ScheduleRunnerPlugin::run_loop(tick_period));
82
83 fn increment_tick_counter(mut server: ResMut<Server>) {
84 server.current_tick += 1;
85 }
86
87 app.add_systems(Last, (increment_tick_counter, despawn_marked_entities));
88 }
89}
90
91/// Contains global server state accessible as a [`Resource`].
92#[derive(Resource, Clone)]
93pub struct Server {
94 /// Incremented on every tick.
95 current_tick: i64,
96 threshold: CompressionThreshold,
97 tick_rate: NonZeroU32,
98}
99
100impl Server {
101 /// Returns the number of ticks that have elapsed since the server began.
102 pub fn current_tick(&self) -> i64 {
103 self.current_tick
104 }
105
106 /// Returns the server's [compression
107 /// threshold](ServerSettings::compression_threshold).
108 pub fn compression_threshold(&self) -> CompressionThreshold {
109 self.threshold
110 }
111
112 // Returns the server's [tick rate](ServerPlugin::tick_rate).
113 pub fn tick_rate(&self) -> NonZeroU32 {
114 self.tick_rate
115 }
116}