diff --git a/examples/basic.rs b/examples/basic.rs index 58e797e..eafbdb2 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,6 +1,5 @@ use std::net::SocketAddr; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; use log::LevelFilter; use valence::client::GameMode; @@ -17,13 +16,11 @@ pub fn main() -> ShutdownResult { .init(); valence::start_server(Game { - favicon: Arc::from(include_bytes!("favicon.png").as_slice()), player_count: AtomicUsize::new(0), }) } struct Game { - favicon: Arc<[u8]>, player_count: AtomicUsize, } @@ -102,7 +99,7 @@ impl Config for Game { online_players: self.player_count.load(Ordering::SeqCst) as i32, max_players: MAX_PLAYERS as i32, description: "Hello Valence!".color(Color::AQUA), - favicon_png: Some(self.favicon.clone()), + favicon_png: Some(include_bytes!("favicon.png")), } } diff --git a/src/client.rs b/src/client.rs index 90bdbe4..2930a79 100644 --- a/src/client.rs +++ b/src/client.rs @@ -27,8 +27,8 @@ use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance}; use crate::var_int::VarInt; use crate::world::WorldId; use crate::{ - glm, ident, ChunkPos, ChunkStore, EntityId, EntityStore, Server, SharedServer, Text, Ticks, - WorldStore, LIBRARY_NAMESPACE, + glm, ident, ChunkPos, ChunkStore, EntityId, EntityStore, Server, Text, Ticks, WorldStore, + LIBRARY_NAMESPACE, }; pub struct ClientStore { @@ -386,7 +386,7 @@ impl Client { } // Check if it's time to send another keepalive. - if other.last_keepalive == other.tick_start() { + if other.current_tick() % (other.tick_rate() * 8) == 0 { if self.got_keepalive { let id = rand::random(); self.send_packet(KeepAliveClientbound { id }); diff --git a/src/config.rs b/src/config.rs index 0d85645..56fd7f4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,13 +1,11 @@ use std::any::Any; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::panic::{RefUnwindSafe, UnwindSafe}; -use std::sync::Arc; -use std::time::Duration; use async_trait::async_trait; use tokio::runtime::Handle as TokioHandle; -use crate::{ident, Id, Identifier, NewClientData, Server, SharedServer, Text}; +use crate::{ident, Id, Identifier, NewClientData, Server, SharedServer, Text, Ticks}; /// A trait containing callbacks which are invoked by the running Minecraft /// server. @@ -41,20 +39,19 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe { SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 25565).into() } - /// Called once at startup to get the duration of each game update. + /// Called once at startup to get the tick rate, which is the number of game + /// updates that should occur in one second. /// - /// On each game update (a.k.a. tick), the server is expected to update game - /// logic and respond to packets from clients. Once this is complete, - /// the server will sleep for any remaining time until the full update - /// duration has passed. + /// On each game update (tick), the server is expected to update game logic + /// and respond to packets from clients. Once this is complete, the server + /// will sleep for any remaining time until a full tick has passed. /// - /// The duration must be nonzero. + /// The tick rate must be greater than zero. /// /// # Default Implementation - /// Returns 1/20th of a second, which is the same as Minecraft's official - /// server. - fn update_duration(&self) -> Duration { - Duration::from_secs_f64(1.0 / 20.0) + /// Returns `20`, which is the same as Minecraft's official server. + fn tick_rate(&self) -> Ticks { + 20 } /// Called once at startup to get the "online mode" option, which determines @@ -200,7 +197,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe { /// The result of the [`server_list_ping`](Handler::server_list_ping) callback. #[derive(Debug)] -pub enum ServerListPing { +pub enum ServerListPing<'a> { /// Responds to the server list ping with the given information. Respond { online_players: i32, @@ -210,7 +207,7 @@ pub enum ServerListPing { /// The image must be 64x64 pixels. /// /// No icon is used if the value is `None`. - favicon_png: Option>, + favicon_png: Option<&'a [u8]>, }, /// Ignores the query and disconnects from the client. Ignore, diff --git a/src/server.rs b/src/server.rs index 4f52e05..b90b3fb 100644 --- a/src/server.rs +++ b/src/server.rs @@ -67,8 +67,6 @@ pub struct Other { tick_counter: Ticks, /// The instant the current game tick began. tick_start: Instant, - /// The time the last keep alive packet was sent to all players. - pub(crate) last_keepalive: Instant, } /// A server handle providing the subset of functionality which can be performed @@ -80,7 +78,7 @@ pub struct SharedServer(Arc); struct SharedServerInner { cfg: Box, address: SocketAddr, - update_duration: Duration, + tick_rate: Ticks, online_mode: bool, max_connections: usize, incoming_packet_capacity: usize, @@ -153,8 +151,8 @@ impl SharedServer { self.0.address } - pub fn update_duration(&self) -> Duration { - self.0.update_duration + pub fn tick_rate(&self) -> Ticks { + self.0.tick_rate } pub fn online_mode(&self) -> bool { @@ -277,12 +275,9 @@ pub fn start_server(config: impl Config) -> ShutdownResult { fn setup_server(cfg: impl Config) -> anyhow::Result { let max_connections = cfg.max_connections(); let address = cfg.address(); - let update_duration = cfg.update_duration(); + let tick_rate = cfg.tick_rate(); - ensure!( - update_duration != Duration::ZERO, - "update duration must be nonzero" - ); + ensure!(tick_rate > 0, "tick rate must be greater than zero"); let online_mode = cfg.online_mode(); @@ -380,7 +375,7 @@ fn setup_server(cfg: impl Config) -> anyhow::Result { let shared = SharedServer(Arc::new(SharedServerInner { cfg: Box::new(cfg), address, - update_duration, + tick_rate, online_mode, max_connections, outgoing_packet_capacity, @@ -408,7 +403,6 @@ fn setup_server(cfg: impl Config) -> anyhow::Result { tick_counter: 0, tick_start: Instant::now(), new_players_rx, - last_keepalive: Instant::now(), }, }) } @@ -426,21 +420,14 @@ fn do_update_loop(server: &mut Server) -> ShutdownResult { join_player(server, msg); } - const KEEPALIVE_FREQ: Duration = Duration::from_secs(8); - if server.tick_start().duration_since(server.last_keepalive) >= KEEPALIVE_FREQ { - server.last_keepalive = server.tick_start(); - } - - { - server.clients.par_iter_mut().for_each(|(_, client)| { - client.update( - &server.entities, - &server.worlds, - &server.chunks, - &server.other, - ) - }); - } + server.clients.par_iter_mut().for_each(|(_, client)| { + client.update( + &server.entities, + &server.worlds, + &server.chunks, + &server.other, + ) + }); server.entities.update(); @@ -451,7 +438,7 @@ fn do_update_loop(server: &mut Server) -> ShutdownResult { shared.config().update(server); - // Chunks modified this tick can have their changes applied immediately because + // Chunks created this tick can have their changes applied immediately because // they have not been observed by clients yet. server.chunks.par_iter_mut().for_each(|(_, chunk)| { if chunk.created_this_tick() { @@ -461,14 +448,10 @@ fn do_update_loop(server: &mut Server) -> ShutdownResult { }); // Sleep for the remainder of the tick. - thread::sleep( - server - .0 - .update_duration - .saturating_sub(server.tick_start.elapsed()), - ); - server.tick_start = Instant::now(); + let tick_duration = Duration::from_secs_f64((server.0.tick_rate as f64).recip()); + thread::sleep(tick_duration.saturating_sub(server.tick_start.elapsed())); + server.tick_start = Instant::now(); server.tick_counter += 1; } } diff --git a/src/slotmap.rs b/src/slotmap.rs index d0e3a22..ec98ee9 100644 --- a/src/slotmap.rs +++ b/src/slotmap.rs @@ -59,7 +59,6 @@ impl SlotMap { pub fn insert(&mut self, val: T) -> Key { assert!( - // -1 so that NULL is always invalid. self.count < u32::MAX, "SlotMap: too many items inserted" );