From 8dc8aeace5fb3b0df212085c7ac4eb23fd8e3024 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 7 Oct 2021 14:23:55 +0100 Subject: [PATCH] Added minimum server-side chunk loading --- server/src/lib.rs | 8 +++++++- server/src/sys/msg/terrain.rs | 19 +++++++++++++++++++ server/src/sys/terrain.rs | 4 +--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 704b4c7a17..bd0ba1567b 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -96,7 +96,7 @@ use prometheus_hyper::Server as PrometheusServer; use specs::{join::Join, Builder, Entity as EcsEntity, Entity, SystemData, WorldExt}; use std::{ i32, - ops::{Deref, DerefMut}, + ops::{Deref, DerefMut, Range}, sync::Arc, time::{Duration, Instant}, }; @@ -133,6 +133,12 @@ impl Default for SpawnPoint { fn default() -> Self { Self(Vec3::new(0.0, 0.0, 256.0)) } } +// This is the minimum chunk range that is kept loaded around each player +// server-side. This is independent of the client's view distance and exists to +// avoid exploits such as small view distance chunk reloading and also to keep +// various mechanics working fluidly (i.e: not unloading nearby entities). +pub const MIN_VD: Range = 5..6; + // Tick count used for throttling network updates // Note this doesn't account for dt (so update rate changes with tick rate) #[derive(Copy, Clone, Default)] diff --git a/server/src/sys/msg/terrain.rs b/server/src/sys/msg/terrain.rs index 91fa83680a..43e6cc4b9a 100644 --- a/server/src/sys/msg/terrain.rs +++ b/server/src/sys/msg/terrain.rs @@ -2,6 +2,7 @@ use crate::{client::Client, metrics::NetworkRequestMetrics, presence::Presence}; use common::{ comp::Pos, event::{EventBus, ServerEvent}, + spiral::Spiral2d, terrain::{TerrainChunkSize, TerrainGrid}, vol::RectVolSize, }; @@ -103,6 +104,24 @@ impl<'a> System<'a> for Sys { } Ok(()) }); + + // Load a minimum radius of chunks around each player. + // This is used to prevent view distance reloading exploits and make sure that + // entity simulation occurs within a minimum radius around the + // player. + if let Some(pos) = positions.get(entity) { + let player_chunk = pos + .0 + .xy() + .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); + for rpos in Spiral2d::new().take((crate::MIN_VD.start as usize + 1).pow(2)) { + let key = player_chunk + rpos; + if terrain.get_key(key).is_none() { + events.push(ServerEvent::ChunkRequest(entity, key)); + } + } + } + events }) .flatten() diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 6d9751e892..f8e7f1ae56 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -495,15 +495,13 @@ pub fn chunk_in_vd( terrain: &TerrainGrid, vd: u32, ) -> bool { - const MINIMUM_UNLOAD_DIST: u32 = 4; - let player_chunk_pos = terrain.pos_key(player_pos.map(|e| e as i32)); let adjusted_dist_sqr = (player_chunk_pos - chunk_pos) .map(|e: i32| (e.abs() as u32).saturating_sub(2)) .magnitude_squared(); - adjusted_dist_sqr <= vd.max(MINIMUM_UNLOAD_DIST).pow(2) + adjusted_dist_sqr <= vd.max(crate::MIN_VD.end).pow(2) } fn is_spawn_chunk(chunk_pos: Vec2, spawn_pos: SpawnPoint, terrain: &TerrainGrid) -> bool {