mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Defer repositioning until after chunks are generated normally, to reduce latency and avoid duplicate work.
This commit is contained in:
parent
7555be0e25
commit
c417e8c5b9
@ -45,3 +45,10 @@ impl Component for WaypointArea {
|
|||||||
impl Default for WaypointArea {
|
impl Default for WaypointArea {
|
||||||
fn default() -> Self { Self(5.0) }
|
fn default() -> Self { Self(5.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RepositionOnChunkLoad;
|
||||||
|
|
||||||
|
impl Component for RepositionOnChunkLoad {
|
||||||
|
type Storage = IdvStorage<Self>;
|
||||||
|
}
|
||||||
|
@ -80,7 +80,7 @@ pub use self::{
|
|||||||
slot, Inventory, InventoryUpdate, InventoryUpdateEvent,
|
slot, Inventory, InventoryUpdate, InventoryUpdateEvent,
|
||||||
},
|
},
|
||||||
last::Last,
|
last::Last,
|
||||||
location::{Waypoint, WaypointArea},
|
location::{RepositionOnChunkLoad, Waypoint, WaypointArea},
|
||||||
misc::Object,
|
misc::Object,
|
||||||
ori::Ori,
|
ori::Ori,
|
||||||
phys::{
|
phys::{
|
||||||
|
@ -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.
|
// Terrain helper functions used across multiple crates.
|
||||||
|
|
||||||
/// Computes the position Vec2 of a SimChunk from an index, where the index was
|
/// Computes the position Vec2 of a SimChunk from an index, where the index was
|
||||||
|
@ -182,6 +182,7 @@ impl State {
|
|||||||
ecs.register::<comp::Last<comp::Ori>>();
|
ecs.register::<comp::Last<comp::Ori>>();
|
||||||
ecs.register::<comp::Alignment>();
|
ecs.register::<comp::Alignment>();
|
||||||
ecs.register::<comp::Agent>();
|
ecs.register::<comp::Agent>();
|
||||||
|
ecs.register::<comp::RepositionOnChunkLoad>();
|
||||||
ecs.register::<comp::WaypointArea>();
|
ecs.register::<comp::WaypointArea>();
|
||||||
ecs.register::<comp::ForceUpdate>();
|
ecs.register::<comp::ForceUpdate>();
|
||||||
ecs.register::<comp::InventoryUpdate>();
|
ecs.register::<comp::InventoryUpdate>();
|
||||||
|
@ -41,12 +41,9 @@ pub fn handle_loaded_character_data(
|
|||||||
Option<comp::Waypoint>,
|
Option<comp::Waypoint>,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
server.state.update_character_data(
|
server
|
||||||
entity,
|
.state
|
||||||
loaded_components,
|
.update_character_data(entity, loaded_components);
|
||||||
&*server.world,
|
|
||||||
&server.index.as_index_ref(),
|
|
||||||
);
|
|
||||||
sys::subscription::initialize_region_subscription(server.state.ecs(), entity);
|
sys::subscription::initialize_region_subscription(server.state.ecs(), entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,13 +97,7 @@ pub trait StateExt {
|
|||||||
fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId);
|
fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId);
|
||||||
/// Update the components associated with the entity's current character.
|
/// Update the components associated with the entity's current character.
|
||||||
/// Performed after loading component data from the database
|
/// Performed after loading component data from the database
|
||||||
fn update_character_data(
|
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents);
|
||||||
&mut self,
|
|
||||||
entity: EcsEntity,
|
|
||||||
components: PersistedComponents,
|
|
||||||
world: &world::World,
|
|
||||||
index: &world::IndexRef,
|
|
||||||
);
|
|
||||||
/// Iterates over registered clients and send each `ServerMsg`
|
/// Iterates over registered clients and send each `ServerMsg`
|
||||||
fn send_chat(&self, msg: comp::UnresolvedChatMsg);
|
fn send_chat(&self, msg: comp::UnresolvedChatMsg);
|
||||||
fn notify_players(&self, msg: ServerGeneral);
|
fn notify_players(&self, msg: ServerGeneral);
|
||||||
@ -486,13 +480,7 @@ impl StateExt for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_character_data(
|
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) {
|
||||||
&mut self,
|
|
||||||
entity: EcsEntity,
|
|
||||||
components: PersistedComponents,
|
|
||||||
world: &world::World,
|
|
||||||
index: &world::IndexRef,
|
|
||||||
) {
|
|
||||||
let (body, stats, skill_set, inventory, waypoint) = components;
|
let (body, stats, skill_set, inventory, waypoint) = components;
|
||||||
|
|
||||||
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
||||||
@ -537,13 +525,9 @@ impl StateExt for State {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if let Some(waypoint) = waypoint {
|
if let Some(waypoint) = waypoint {
|
||||||
// Avoid spawning the player in terrain by finding the highest solid point in
|
self.write_component_ignore_entity_dead(entity, comp::RepositionOnChunkLoad);
|
||||||
// their waypoint's column to spawn them at
|
|
||||||
let spawn_pos =
|
|
||||||
world.find_accessible_pos(*index, waypoint.get_pos().xy().as_::<i32>(), false);
|
|
||||||
|
|
||||||
self.write_component_ignore_entity_dead(entity, waypoint);
|
self.write_component_ignore_entity_dead(entity, waypoint);
|
||||||
self.write_component_ignore_entity_dead(entity, comp::Pos(spawn_pos));
|
self.write_component_ignore_entity_dead(entity, comp::Pos(waypoint.get_pos()));
|
||||||
self.write_component_ignore_entity_dead(entity, comp::Vel(Vec3::zero()));
|
self.write_component_ignore_entity_dead(entity, comp::Vel(Vec3::zero()));
|
||||||
self.write_component_ignore_entity_dead(entity, comp::ForceUpdate);
|
self.write_component_ignore_entity_dead(entity, comp::ForceUpdate);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
presence::Presence, rtsim::RtSim, settings::Settings, SpawnPoint, Tick,
|
presence::Presence, rtsim::RtSim, settings::Settings, SpawnPoint, Tick,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{self, agent, bird_medium, Alignment, BehaviorCapability, Pos},
|
comp::{self, agent, bird_medium, Alignment, BehaviorCapability, Pos, RepositionOnChunkLoad},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
generation::{get_npc_name, EntityInfo},
|
generation::{get_npc_name, EntityInfo},
|
||||||
npc::NPC_NAMES,
|
npc::NPC_NAMES,
|
||||||
@ -14,7 +14,7 @@ use common_ecs::{Job, Origin, Phase, System};
|
|||||||
use common_net::msg::{SerializedTerrainChunk, ServerGeneral};
|
use common_net::msg::{SerializedTerrainChunk, ServerGeneral};
|
||||||
use common_state::TerrainChanges;
|
use common_state::TerrainChanges;
|
||||||
use comp::Behavior;
|
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 std::sync::Arc;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -93,9 +93,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteExpect<'a, TerrainGrid>,
|
WriteExpect<'a, TerrainGrid>,
|
||||||
Write<'a, TerrainChanges>,
|
Write<'a, TerrainChanges>,
|
||||||
WriteExpect<'a, RtSim>,
|
WriteExpect<'a, RtSim>,
|
||||||
ReadStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Presence>,
|
ReadStorage<'a, Presence>,
|
||||||
ReadStorage<'a, Client>,
|
ReadStorage<'a, Client>,
|
||||||
|
Entities<'a>,
|
||||||
|
WriteStorage<'a, RepositionOnChunkLoad>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "terrain";
|
const NAME: &'static str = "terrain";
|
||||||
@ -114,9 +116,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut terrain,
|
mut terrain,
|
||||||
mut terrain_changes,
|
mut terrain_changes,
|
||||||
mut rtsim,
|
mut rtsim,
|
||||||
positions,
|
mut positions,
|
||||||
presences,
|
presences,
|
||||||
clients,
|
clients,
|
||||||
|
entities,
|
||||||
|
mut reposition_on_load,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut server_emitter = server_event_bus.emitter();
|
let mut server_emitter = server_event_bus.emitter();
|
||||||
@ -306,6 +310,20 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (entity, pos) in (&entities, &mut positions).join() {
|
||||||
|
if reposition_on_load.get(entity).is_some() {
|
||||||
|
// If the player is in the new chunk and 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)) = new_chunks.iter().find(|(key, _)| *key == chunk_pos) {
|
||||||
|
pos.0 = chunk
|
||||||
|
.find_accessible_pos(pos.0.xy().as_::<i32>(), false)
|
||||||
|
.as_::<f32>();
|
||||||
|
reposition_on_load.remove(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send the chunk to all nearby players.
|
// Send the chunk to all nearby players.
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
new_chunks.into_par_iter().for_each(|(key, chunk)| {
|
new_chunks.into_par_iter().for_each(|(key, chunk)| {
|
||||||
|
@ -202,42 +202,8 @@ impl World {
|
|||||||
let (tc, _cs) = self
|
let (tc, _cs) = self
|
||||||
.generate_chunk(index, chunk_pos, || false, None)
|
.generate_chunk(index, chunk_pos, || false, None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let min_z = tc.get_min_z();
|
|
||||||
let max_z = tc.get_max_z();
|
|
||||||
|
|
||||||
let pos = Vec3::new(
|
tc.find_accessible_pos(spawn_wpos, ascending)
|
||||||
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));
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::eval_order_dependence)]
|
#[allow(clippy::eval_order_dependence)]
|
||||||
|
Loading…
Reference in New Issue
Block a user