From 5cf40812d974cfba3510e0eb3a1c5e0a15a905a4 Mon Sep 17 00:00:00 2001
From: Sam <samuelkeiffer@gmail.com>
Date: Sat, 17 Apr 2021 17:33:54 -0400
Subject: [PATCH] Safezone is now permanently in existence at spawn.

---
 common/src/event.rs                  |  4 ++++
 server/src/cmd.rs                    | 17 +----------------
 server/src/events/entity_creation.rs |  4 ++++
 server/src/events/mod.rs             |  8 ++++++--
 server/src/lib.rs                    | 14 +++++++++-----
 server/src/state_ext.rs              | 26 ++++++++++++++++++++++++++
 server/src/sys/terrain.rs            | 18 +++++++++++++++++-
 7 files changed, 67 insertions(+), 24 deletions(-)

diff --git a/common/src/event.rs b/common/src/event.rs
index f537e02150..bf5d43f3f4 100644
--- a/common/src/event.rs
+++ b/common/src/event.rs
@@ -172,6 +172,10 @@ pub enum ServerEvent {
         target: Uid,
         max_range: Option<f32>,
     },
+    CreateSafezone {
+        range: Option<f32>,
+        pos: Pos,
+    },
 }
 
 pub struct EventBus<E> {
diff --git a/server/src/cmd.rs b/server/src/cmd.rs
index f2805f1b47..313579730f 100644
--- a/server/src/cmd.rs
+++ b/server/src/cmd.rs
@@ -1069,22 +1069,7 @@ fn handle_safezone(
 ) -> CmdResult<()> {
     let range = scan_fmt_some!(&args, &action.arg_fmt(), f32);
     let pos = position(server, target, "target")?;
-    server
-        .state
-        .create_object(pos, comp::object::Body::BoltNature)
-        .with(comp::Mass(10_f32.powi(10)))
-        .with(comp::Auras::new(vec![Aura::new(
-            AuraKind::Buff {
-                kind: BuffKind::Invulnerability,
-                data: BuffData::new(1.0, Some(Duration::from_secs(1))),
-                category: BuffCategory::Natural,
-                source: BuffSource::World,
-            },
-            range.unwrap_or(100.0),
-            None,
-            AuraTarget::All,
-        )]))
-        .build();
+    server.state.create_safezone(range, pos).build();
 
     server.notify_client(
         client,
diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs
index 284aebb86a..c42b2f86a5 100644
--- a/server/src/events/entity_creation.rs
+++ b/server/src/events/entity_creation.rs
@@ -255,3 +255,7 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
         )]))
         .build();
 }
+
+pub fn handle_create_safezone(server: &mut Server, range: Option<f32>, pos: Pos) {
+    server.state.create_safezone(range, pos).build();
+}
diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs
index 418cdd930d..9a3d6f1781 100644
--- a/server/src/events/mod.rs
+++ b/server/src/events/mod.rs
@@ -2,8 +2,9 @@ use crate::{state_ext::StateExt, Server};
 use common::event::{EventBus, ServerEvent};
 use common_base::span;
 use entity_creation::{
-    handle_beam, handle_create_npc, handle_create_ship, handle_create_waypoint,
-    handle_initialize_character, handle_loaded_character_data, handle_shockwave, handle_shoot,
+    handle_beam, handle_create_npc, handle_create_safezone, handle_create_ship,
+    handle_create_waypoint, handle_initialize_character, handle_loaded_character_data,
+    handle_shockwave, handle_shoot,
 };
 use entity_manipulation::{
     handle_aura, handle_buff, handle_combo_change, handle_damage, handle_delete, handle_destroy,
@@ -212,6 +213,9 @@ impl Server {
                     target,
                     max_range,
                 } => handle_teleport_to(&self, entity, target, max_range),
+                ServerEvent::CreateSafezone { range, pos } => {
+                    handle_create_safezone(self, range, pos)
+                },
             }
         }
 
diff --git a/server/src/lib.rs b/server/src/lib.rs
index f14848d3ce..3cccef1e4f 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -114,7 +114,11 @@ use world::{
 };
 
 #[derive(Copy, Clone)]
-struct SpawnPoint(Vec3<f32>);
+pub struct SpawnPoint(Vec3<f32>);
+
+impl Default for SpawnPoint {
+    fn default() -> Self { Self(Vec3::new(0.0, 0.0, 256.0)) }
+}
 
 // Tick count used for throttling network updates
 // Note this doesn't account for dt (so update rate changes with tick rate)
@@ -299,7 +303,7 @@ impl Server {
         };
 
         #[cfg(feature = "worldgen")]
-        let spawn_point = {
+        let spawn_point = SpawnPoint({
             let index = index.as_index_ref();
             // NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
             // but are needed to be explicit about casting (and to make the compiler stop
@@ -327,12 +331,12 @@ impl Server {
             };
 
             world.find_lowest_accessible_pos(index, spawn_chunk)
-        };
+        });
         #[cfg(not(feature = "worldgen"))]
-        let spawn_point = Vec3::new(0.0, 0.0, 256.0);
+        let spawn_point = SpawnPoint::default();
 
         // Set the spawn point we calculated above
-        state.ecs_mut().insert(SpawnPoint(spawn_point));
+        state.ecs_mut().insert(spawn_point);
 
         // Insert a default AABB for the world
         // TODO: prevent this from being deleted
diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs
index 76cbddd317..93682f4870 100644
--- a/server/src/state_ext.rs
+++ b/server/src/state_ext.rs
@@ -24,6 +24,7 @@ use specs::{
     saveload::MarkerAllocator, Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder,
     Join, WorldExt,
 };
+use std::time::Duration;
 use tracing::warn;
 use vek::*;
 
@@ -72,6 +73,8 @@ pub trait StateExt {
         pos: comp::Pos,
         ori: comp::Ori,
     ) -> EcsEntityBuilder;
+    /// Creates a safezone
+    fn create_safezone(&mut self, range: Option<f32>, pos: comp::Pos) -> EcsEntityBuilder;
     // NOTE: currently only used for testing
     /// Queues chunk generation in the view distance of the persister, this
     /// entity must be built before those chunks are received (the builder
@@ -318,6 +321,29 @@ impl StateExt for State {
             })
     }
 
+    fn create_safezone(&mut self, range: Option<f32>, pos: comp::Pos) -> EcsEntityBuilder {
+        use comp::{
+            aura::{Aura, AuraKind, AuraTarget, Auras},
+            buff::{BuffCategory, BuffData, BuffKind, BuffSource},
+            object, Body,
+        };
+        self.ecs_mut()
+            .create_entity_synced()
+            .with(pos)
+            .with(Body::Object(object::Body::BoltNature))
+            .with(Auras::new(vec![Aura::new(
+                AuraKind::Buff {
+                    kind: BuffKind::Invulnerability,
+                    data: BuffData::new(1.0, Some(Duration::from_secs(1))),
+                    category: BuffCategory::Natural,
+                    source: BuffSource::World,
+                },
+                range.unwrap_or(100.0),
+                None,
+                AuraTarget::All,
+            )]))
+    }
+
     // NOTE: currently only used for testing
     /// Queues chunk generation in the view distance of the persister, this
     /// entity must be built before those chunks are received (the builder
diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs
index 9a43236f29..27afdda57f 100644
--- a/server/src/sys/terrain.rs
+++ b/server/src/sys/terrain.rs
@@ -1,5 +1,6 @@
 use crate::{
-    chunk_generator::ChunkGenerator, client::Client, presence::Presence, rtsim::RtSim, Tick,
+    chunk_generator::ChunkGenerator, client::Client, presence::Presence, rtsim::RtSim, SpawnPoint,
+    Tick,
 };
 use common::{
     comp::{
@@ -33,6 +34,7 @@ impl<'a> System<'a> for Sys {
     type SystemData = (
         Read<'a, EventBus<ServerEvent>>,
         Read<'a, Tick>,
+        Read<'a, SpawnPoint>,
         WriteExpect<'a, ChunkGenerator>,
         WriteExpect<'a, TerrainGrid>,
         Write<'a, TerrainChanges>,
@@ -51,6 +53,7 @@ impl<'a> System<'a> for Sys {
         (
             server_event_bus,
             tick,
+            spawn_point,
             mut chunk_generator,
             mut terrain,
             mut terrain_changes,
@@ -218,6 +221,14 @@ impl<'a> System<'a> for Sys {
                     rtsim_entity: None,
                 })
             }
+
+            // Insert a safezone if chunk contains the spawn position
+            if is_spawn_chunk(key, *spawn_point, &terrain) {
+                server_emitter.emit(ServerEvent::CreateSafezone {
+                    range: Some(100.0),
+                    pos: Pos(spawn_point.0),
+                });
+            }
         }
 
         // Remove chunks that are too far from players.
@@ -271,3 +282,8 @@ pub fn chunk_in_vd(
 
     adjusted_dist_sqr <= vd.pow(2)
 }
+
+fn is_spawn_chunk(chunk_pos: Vec2<i32>, spawn_pos: SpawnPoint, terrain: &TerrainGrid) -> bool {
+    let spawn_chunk_pos = terrain.pos_key(spawn_pos.0.map(|e| e as i32));
+    chunk_pos == spawn_chunk_pos
+}