From 7555be0e254e86a9f4ca61898a86fecd22ae712c Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Thu, 15 Jul 2021 17:52:20 -0400 Subject: [PATCH] Spawn players aboveground when using `/site` or when their waypoint is underground. --- CHANGELOG.md | 1 + server/src/cmd.rs | 8 +++++--- server/src/events/entity_creation.rs | 9 ++++++--- server/src/lib.rs | 2 +- server/src/state_ext.rs | 23 ++++++++++++++++++++--- world/src/lib.rs | 28 ++++++++++++++++++++++------ 6 files changed, 55 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0add171a5..4388709cfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Significantly improved the performance of playing sound effects - Dismantle and Material crafting tabs don't have duplicated recipes - Campfires now despawn when underwater +- Players no longer spawn underground if their waypoint is underground ## [0.10.0] - 2021-06-12 diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 239a73b95e..76d58dc25a 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -676,9 +676,11 @@ fn handle_site( }) .ok_or_else(|| "Site not found".to_string())?; - let site_pos = server - .world - .find_lowest_accessible_pos(server.index.as_index_ref(), site.center); + let site_pos = server.world.find_accessible_pos( + server.index.as_index_ref(), + TerrainChunkSize::center_wpos(site.center), + false, + ); position_mut(server, target, "target", |current_pos| { current_pos.0 = site_pos diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 8112123c96..b9fd5bee70 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -41,9 +41,12 @@ pub fn handle_loaded_character_data( Option, ), ) { - server - .state - .update_character_data(entity, loaded_components); + server.state.update_character_data( + entity, + loaded_components, + &*server.world, + &server.index.as_index_ref(), + ); sys::subscription::initialize_region_subscription(server.state.ecs(), entity); } diff --git a/server/src/lib.rs b/server/src/lib.rs index 5622c5dd9e..b2e8cddbef 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -343,7 +343,7 @@ impl Server { }, }; - world.find_lowest_accessible_pos(index, spawn_chunk) + world.find_accessible_pos(index, TerrainChunkSize::center_wpos(spawn_chunk), false) }); #[cfg(not(feature = "worldgen"))] let spawn_point = SpawnPoint::default(); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 90432f4bfa..eea380aeff 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -97,7 +97,13 @@ pub trait StateExt { fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId); /// Update the components associated with the entity's current character. /// Performed after loading component data from the database - fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents); + fn update_character_data( + &mut self, + entity: EcsEntity, + components: PersistedComponents, + world: &world::World, + index: &world::IndexRef, + ); /// Iterates over registered clients and send each `ServerMsg` fn send_chat(&self, msg: comp::UnresolvedChatMsg); fn notify_players(&self, msg: ServerGeneral); @@ -480,7 +486,13 @@ impl StateExt for State { } } - fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) { + fn update_character_data( + &mut self, + entity: EcsEntity, + components: PersistedComponents, + world: &world::World, + index: &world::IndexRef, + ) { let (body, stats, skill_set, inventory, waypoint) = components; if let Some(player_uid) = self.read_component_copied::(entity) { @@ -525,8 +537,13 @@ impl StateExt for State { ); if let Some(waypoint) = waypoint { + // Avoid spawning the player in terrain by finding the highest solid point in + // their waypoint's column to spawn them at + let spawn_pos = + world.find_accessible_pos(*index, waypoint.get_pos().xy().as_::(), false); + self.write_component_ignore_entity_dead(entity, waypoint); - self.write_component_ignore_entity_dead(entity, comp::Pos(waypoint.get_pos())); + self.write_component_ignore_entity_dead(entity, comp::Pos(spawn_pos)); self.write_component_ignore_entity_dead(entity, comp::Vel(Vec3::zero())); self.write_component_ignore_entity_dead(entity, comp::ForceUpdate); } diff --git a/world/src/lib.rs b/world/src/lib.rs index dd7a5b9708..b59600521b 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -52,7 +52,9 @@ use common::{ assets, generation::{ChunkSupplement, EntityInfo}, resources::TimeOfDay, - terrain::{Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, + terrain::{ + Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize, TerrainGrid, + }, vol::{ReadVol, RectVolSize, WriteVol}, }; use common_net::msg::{world_msg, WorldMapMsg}; @@ -187,9 +189,13 @@ impl World { pub fn sample_blocks(&self) -> BlockGen { BlockGen::new(ColumnGen::new(&self.sim)) } - pub fn find_lowest_accessible_pos(&self, index: IndexRef, chunk_pos: Vec2) -> Vec3 { - // Calculate the middle of the chunk in the world - let spawn_wpos = TerrainChunkSize::center_wpos(chunk_pos); + pub fn find_accessible_pos( + &self, + index: IndexRef, + spawn_wpos: Vec2, + ascending: bool, + ) -> Vec3 { + let chunk_pos = TerrainGrid::chunk_key(spawn_wpos); // Unwrapping because generate_chunk only returns err when should_continue evals // to true @@ -199,9 +205,19 @@ impl World { let min_z = tc.get_min_z(); let max_z = tc.get_max_z(); - let pos = Vec3::new(spawn_wpos.x, spawn_wpos.y, min_z); + let pos = Vec3::new( + spawn_wpos.x, + spawn_wpos.y, + if ascending { min_z } else { max_z }, + ); (0..(max_z - min_z)) - .map(|z_diff| pos + Vec3::unit_z() * z_diff) + .map(|z_diff| { + if ascending { + pos + Vec3::unit_z() * z_diff + } else { + pos - Vec3::unit_z() * z_diff + } + }) .find(|test_pos| { let chunk_relative_xy = test_pos .xy()