Merge branch 'aweinstock/fix-waypoint' into 'master'

Spawn players aboveground when using `/site` or when their waypoint is underground.

See merge request veloren/veloren!2612
This commit is contained in:
Imbris 2021-07-16 23:18:33 +00:00
commit eb999b2821
8 changed files with 112 additions and 43 deletions

View File

@ -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

View File

@ -175,6 +175,48 @@ impl TerrainGrid {
}
}
impl TerrainChunk {
/// Find the highest or lowest accessible position within the chunk
pub fn find_accessible_pos(&self, spawn_wpos: Vec2<i32>, ascending: bool) -> Vec3<f32> {
let min_z = self.get_min_z();
let max_z = self.get_max_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| {
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()
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e.rem_euclid(sz as i32));
self.get(
Vec3::new(chunk_relative_xy.x, chunk_relative_xy.y, test_pos.z)
- Vec3::unit_z(),
)
.map_or(false, |b| b.is_filled())
&& (0..3).all(|z| {
self.get(
Vec3::new(chunk_relative_xy.x, chunk_relative_xy.y, test_pos.z)
+ Vec3::unit_z() * z,
)
.map_or(true, |b| !b.is_solid())
})
})
.unwrap_or(pos)
.map(|e| e as f32)
+ 0.5
}
}
// Terrain helper functions used across multiple crates.
/// Computes the position Vec2 of a SimChunk from an index, where the index was

View File

@ -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

View File

@ -50,7 +50,7 @@ use crate::{
connection_handler::ConnectionHandler,
data_dir::DataDir,
login_provider::LoginProvider,
presence::{Presence, RegionSubscription},
presence::{Presence, RegionSubscription, RepositionOnChunkLoad},
rtsim::RtSim,
state_ext::StateExt,
sys::sentinel::{DeletedEntities, TrackedComps},
@ -250,6 +250,7 @@ impl Server {
state.ecs_mut().register::<wiring::Circuit>();
state.ecs_mut().register::<comp::HomeChunk>();
state.ecs_mut().register::<login_provider::PendingLogin>();
state.ecs_mut().register::<RepositionOnChunkLoad>();
//Alias validator
let banned_words_paths = &settings.banned_words_files;
@ -343,7 +344,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();

View File

@ -1,7 +1,7 @@
use common_net::msg::PresenceKind;
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage};
use specs::{Component, DerefFlaggedStorage, NullStorage};
use specs_idvs::IdvStorage;
use vek::*;
@ -40,3 +40,10 @@ pub struct RegionSubscription {
impl Component for RegionSubscription {
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct RepositionOnChunkLoad;
impl Component for RepositionOnChunkLoad {
type Storage = NullStorage<Self>;
}

View File

@ -1,6 +1,10 @@
use crate::{
client::Client, persistence::PersistedComponents, presence::Presence, settings::Settings,
sys::sentinel::DeletedEntities, wiring, SpawnPoint,
client::Client,
persistence::PersistedComponents,
presence::{Presence, RepositionOnChunkLoad},
settings::Settings,
sys::sentinel::DeletedEntities,
wiring, SpawnPoint,
};
use common::{
character::CharacterId,
@ -525,6 +529,7 @@ impl StateExt for State {
);
if let Some(waypoint) = waypoint {
self.write_component_ignore_entity_dead(entity, RepositionOnChunkLoad);
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::Vel(Vec3::zero()));

View File

@ -1,9 +1,14 @@
use crate::{
chunk_generator::ChunkGenerator, client::Client, metrics::NetworkRequestMetrics,
presence::Presence, rtsim::RtSim, settings::Settings, SpawnPoint, Tick,
chunk_generator::ChunkGenerator,
client::Client,
metrics::NetworkRequestMetrics,
presence::{Presence, RepositionOnChunkLoad},
rtsim::RtSim,
settings::Settings,
SpawnPoint, Tick,
};
use common::{
comp::{self, agent, bird_medium, Alignment, BehaviorCapability, Pos},
comp::{self, agent, bird_medium, Alignment, BehaviorCapability, ForceUpdate, Pos},
event::{EventBus, ServerEvent},
generation::{get_npc_name, EntityInfo},
npc::NPC_NAMES,
@ -14,7 +19,7 @@ use common_ecs::{Job, Origin, Phase, System};
use common_net::msg::{SerializedTerrainChunk, ServerGeneral};
use common_state::TerrainChanges;
use comp::Behavior;
use specs::{Join, Read, ReadExpect, ReadStorage, Write, WriteExpect};
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write, WriteExpect, WriteStorage};
use std::sync::Arc;
use vek::*;
@ -93,9 +98,12 @@ impl<'a> System<'a> for Sys {
WriteExpect<'a, TerrainGrid>,
Write<'a, TerrainChanges>,
WriteExpect<'a, RtSim>,
ReadStorage<'a, Pos>,
WriteStorage<'a, Pos>,
ReadStorage<'a, Presence>,
ReadStorage<'a, Client>,
Entities<'a>,
WriteStorage<'a, RepositionOnChunkLoad>,
WriteStorage<'a, ForceUpdate>,
);
const NAME: &'static str = "terrain";
@ -114,9 +122,12 @@ impl<'a> System<'a> for Sys {
mut terrain,
mut terrain_changes,
mut rtsim,
positions,
mut positions,
presences,
clients,
entities,
mut reposition_on_load,
mut force_update,
): Self::SystemData,
) {
let mut server_emitter = server_event_bus.emitter();
@ -306,6 +317,24 @@ impl<'a> System<'a> for Sys {
}
}
let mut repositioned = Vec::new();
for (entity, pos, _) in (&entities, &mut positions, &reposition_on_load).join() {
// If an entity is marked as needing repositioning once the chunk loads (e.g.
// from having just logged in), reposition them.
let chunk_pos = terrain.pos_key(pos.0.map(|e| e as i32));
if let Some(chunk) = terrain.get_key(chunk_pos) {
pos.0 = chunk
.find_accessible_pos(pos.0.xy().as_::<i32>(), false)
.as_::<f32>();
repositioned.push(entity);
let _ = force_update.insert(entity, ForceUpdate);
}
}
for entity in repositioned {
reposition_on_load.remove(entity);
}
// Send the chunk to all nearby players.
use rayon::iter::{IntoParallelIterator, ParallelIterator};
new_chunks.into_par_iter().for_each(|(key, chunk)| {

View File

@ -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,41 +189,21 @@ 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<i32>) -> Vec3<f32> {
// 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<i32>,
ascending: bool,
) -> Vec3<f32> {
let chunk_pos = TerrainGrid::chunk_key(spawn_wpos);
// Unwrapping because generate_chunk only returns err when should_continue evals
// to true
let (tc, _cs) = self
.generate_chunk(index, chunk_pos, || false, None)
.unwrap();
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);
(0..(max_z - min_z))
.map(|z_diff| pos + Vec3::unit_z() * z_diff)
.find(|test_pos| {
let chunk_relative_xy = test_pos
.xy()
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e.rem_euclid(sz as i32));
tc.get(
Vec3::new(chunk_relative_xy.x, chunk_relative_xy.y, test_pos.z)
- Vec3::unit_z(),
)
.map_or(false, |b| b.is_filled())
&& (0..3).all(|z| {
tc.get(
Vec3::new(chunk_relative_xy.x, chunk_relative_xy.y, test_pos.z)
+ Vec3::unit_z() * z,
)
.map_or(true, |b| !b.is_solid())
})
})
.unwrap_or(pos)
.map(|e| e as f32)
+ 0.5
tc.find_accessible_pos(spawn_wpos, ascending)
}
#[allow(clippy::eval_order_dependence)]