mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/small-fixes' into 'master'
Fixed chonk memory usage bug, added block manipulation See merge request veloren/veloren!278
This commit is contained in:
commit
3d2a6632ef
@ -5,7 +5,8 @@ use crate::{
|
|||||||
comp,
|
comp,
|
||||||
msg::{EcsCompPacket, EcsResPacket},
|
msg::{EcsCompPacket, EcsResPacket},
|
||||||
sys,
|
sys,
|
||||||
terrain::{TerrainChunk, TerrainMap},
|
terrain::{Block, TerrainChunk, TerrainMap},
|
||||||
|
vol::WriteVol,
|
||||||
};
|
};
|
||||||
use rayon::{ThreadPool, ThreadPoolBuilder};
|
use rayon::{ThreadPool, ThreadPoolBuilder};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
@ -15,7 +16,11 @@ use specs::{
|
|||||||
Component, DispatcherBuilder, Entity as EcsEntity,
|
Component, DispatcherBuilder, Entity as EcsEntity,
|
||||||
};
|
};
|
||||||
use sphynx;
|
use sphynx;
|
||||||
use std::{collections::HashSet, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// How much faster should an in-game day be compared to a real day?
|
/// How much faster should an in-game day be compared to a real day?
|
||||||
@ -40,24 +45,48 @@ pub struct DeltaTime(pub f32);
|
|||||||
/// lag. Ideally, we'd avoid such a situation.
|
/// lag. Ideally, we'd avoid such a situation.
|
||||||
const MAX_DELTA_TIME: f32 = 1.0;
|
const MAX_DELTA_TIME: f32 = 1.0;
|
||||||
|
|
||||||
pub struct Changes {
|
pub struct TerrainChange {
|
||||||
|
blocks: HashMap<Vec3<i32>, Block>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TerrainChange {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
blocks: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerrainChange {
|
||||||
|
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
||||||
|
self.blocks.insert(pos, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.blocks.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChunkChanges {
|
||||||
pub new_chunks: HashSet<Vec2<i32>>,
|
pub new_chunks: HashSet<Vec2<i32>>,
|
||||||
pub changed_chunks: HashSet<Vec2<i32>>,
|
pub modified_chunks: HashSet<Vec2<i32>>,
|
||||||
pub removed_chunks: HashSet<Vec2<i32>>,
|
pub removed_chunks: HashSet<Vec2<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Changes {
|
impl Default for ChunkChanges {
|
||||||
pub fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
new_chunks: HashSet::new(),
|
new_chunks: HashSet::new(),
|
||||||
changed_chunks: HashSet::new(),
|
modified_chunks: HashSet::new(),
|
||||||
removed_chunks: HashSet::new(),
|
removed_chunks: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cleanup(&mut self) {
|
impl ChunkChanges {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
self.new_chunks.clear();
|
self.new_chunks.clear();
|
||||||
self.changed_chunks.clear();
|
self.modified_chunks.clear();
|
||||||
self.removed_chunks.clear();
|
self.removed_chunks.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,7 +97,6 @@ pub struct State {
|
|||||||
ecs: sphynx::World<EcsCompPacket, EcsResPacket>,
|
ecs: sphynx::World<EcsCompPacket, EcsResPacket>,
|
||||||
// Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher
|
// Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher
|
||||||
thread_pool: Arc<ThreadPool>,
|
thread_pool: Arc<ThreadPool>,
|
||||||
changes: Changes,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
@ -77,7 +105,6 @@ impl State {
|
|||||||
Self {
|
Self {
|
||||||
ecs: sphynx::World::new(specs::World::new(), Self::setup_sphynx_world),
|
ecs: sphynx::World::new(specs::World::new(), Self::setup_sphynx_world),
|
||||||
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
|
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
|
||||||
changes: Changes::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +119,6 @@ impl State {
|
|||||||
state_package,
|
state_package,
|
||||||
),
|
),
|
||||||
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
|
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
|
||||||
changes: Changes::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +160,8 @@ impl State {
|
|||||||
ecs.add_resource(Time(0.0));
|
ecs.add_resource(Time(0.0));
|
||||||
ecs.add_resource(DeltaTime(0.0));
|
ecs.add_resource(DeltaTime(0.0));
|
||||||
ecs.add_resource(TerrainMap::new().unwrap());
|
ecs.add_resource(TerrainMap::new().unwrap());
|
||||||
|
ecs.add_resource(TerrainChange::default());
|
||||||
|
ecs.add_resource(ChunkChanges::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a component with the state's ECS.
|
/// Register a component with the state's ECS.
|
||||||
@ -172,8 +200,8 @@ impl State {
|
|||||||
|
|
||||||
/// Get a reference to the `Changes` structure of the state. This contains
|
/// Get a reference to the `Changes` structure of the state. This contains
|
||||||
/// information about state that has changed since the last game tick.
|
/// information about state that has changed since the last game tick.
|
||||||
pub fn changes(&self) -> &Changes {
|
pub fn chunk_changes(&self) -> Fetch<ChunkChanges> {
|
||||||
&self.changes
|
self.ecs.read_resource()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current in-game time of day.
|
/// Get the current in-game time of day.
|
||||||
@ -197,12 +225,12 @@ impl State {
|
|||||||
|
|
||||||
/// Get a reference to this state's terrain.
|
/// Get a reference to this state's terrain.
|
||||||
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
||||||
self.ecs.read_resource::<TerrainMap>()
|
self.ecs.read_resource()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a writable reference to this state's terrain.
|
/// Get a writable reference to this state's terrain.
|
||||||
pub fn terrain_mut(&self) -> FetchMut<TerrainMap> {
|
pub fn terrain_mut(&self) -> FetchMut<TerrainMap> {
|
||||||
self.ecs.write_resource::<TerrainMap>()
|
self.ecs.write_resource()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes every chunk of the terrain.
|
/// Removes every chunk of the terrain.
|
||||||
@ -226,9 +254,15 @@ impl State {
|
|||||||
.insert(key, Arc::new(chunk))
|
.insert(key, Arc::new(chunk))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
self.changes.changed_chunks.insert(key);
|
self.ecs
|
||||||
|
.write_resource::<ChunkChanges>()
|
||||||
|
.modified_chunks
|
||||||
|
.insert(key);
|
||||||
} else {
|
} else {
|
||||||
self.changes.new_chunks.insert(key);
|
self.ecs
|
||||||
|
.write_resource::<ChunkChanges>()
|
||||||
|
.new_chunks
|
||||||
|
.insert(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +274,10 @@ impl State {
|
|||||||
.remove(key)
|
.remove(key)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
self.changes.removed_chunks.insert(key);
|
self.ecs
|
||||||
|
.write_resource::<ChunkChanges>()
|
||||||
|
.removed_chunks
|
||||||
|
.insert(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,11 +299,27 @@ impl State {
|
|||||||
dispatch_builder.build().dispatch(&self.ecs.res);
|
dispatch_builder.build().dispatch(&self.ecs.res);
|
||||||
|
|
||||||
self.ecs.maintain();
|
self.ecs.maintain();
|
||||||
|
|
||||||
|
// Apply terrain changes
|
||||||
|
let mut terrain = self.ecs.write_resource::<TerrainMap>();
|
||||||
|
let mut chunk_changes = self.ecs.write_resource::<ChunkChanges>();
|
||||||
|
self.ecs
|
||||||
|
.write_resource::<TerrainChange>()
|
||||||
|
.blocks
|
||||||
|
.drain()
|
||||||
|
.for_each(|(pos, block)| {
|
||||||
|
if terrain.set(pos, block).is_ok() {
|
||||||
|
chunk_changes.modified_chunks.insert(terrain.pos_key(pos));
|
||||||
|
} else {
|
||||||
|
warn!("Tried to modify block outside of terrain at {:?}", pos);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clean up the state after a tick.
|
/// Clean up the state after a tick.
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
// Clean up data structures from the last tick.
|
// Clean up data structures from the last tick.
|
||||||
self.changes.cleanup();
|
self.ecs.write_resource::<TerrainChange>().clear();
|
||||||
|
self.ecs.write_resource::<ChunkChanges>().clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::{block::Block, TerrainChunkMeta, TerrainChunkSize};
|
use super::{block::Block, TerrainChunkMeta, TerrainChunkSize};
|
||||||
use crate::{
|
use crate::{
|
||||||
vol::{BaseVol, ReadVol, WriteVol},
|
vol::{BaseVol, ReadVol, VolSize, WriteVol},
|
||||||
volumes::chunk::{Chunk, ChunkErr},
|
volumes::chunk::{Chunk, ChunkErr},
|
||||||
};
|
};
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
@ -185,21 +185,24 @@ impl WriteVol for Chonk {
|
|||||||
self.sub_chunks[sub_chunk_idx] = SubChunk::Hash(*cblock, map);
|
self.sub_chunks[sub_chunk_idx] = SubChunk::Hash(*cblock, map);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SubChunk::Hash(cblock, _map) if block == *cblock => Ok(()),
|
SubChunk::Hash(cblock, map) if block == *cblock => {
|
||||||
SubChunk::Hash(_cblock, map) if map.len() < 4096 => {
|
map.remove(&rpos.map(|e| e as u8));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
SubChunk::Hash(_cblock, map) if map.len() <= 4096 => {
|
||||||
map.insert(rpos.map(|e| e as u8), block);
|
map.insert(rpos.map(|e| e as u8), block);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SubChunk::Hash(cblock, map) => {
|
SubChunk::Hash(cblock, map) => {
|
||||||
let mut new_chunk = Chunk::filled(*cblock, ());
|
let mut new_chunk = Chunk::filled(*cblock, ());
|
||||||
new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope)
|
|
||||||
|
|
||||||
for (map_pos, map_block) in map {
|
for (map_pos, map_block) in map {
|
||||||
new_chunk
|
new_chunk
|
||||||
.set(map_pos.map(|e| e as i32), *map_block)
|
.set(map_pos.map(|e| e as i32), *map_block)
|
||||||
.unwrap(); // Can't fail (I hope!)
|
.unwrap(); // Can't fail (I hope!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope)
|
||||||
|
|
||||||
self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk);
|
self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -222,11 +225,22 @@ impl WriteVol for Chonk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SubChunkSize;
|
||||||
|
|
||||||
|
impl VolSize for SubChunkSize {
|
||||||
|
const SIZE: Vec3<u32> = Vec3 {
|
||||||
|
x: TerrainChunkSize::SIZE.x,
|
||||||
|
y: TerrainChunkSize::SIZE.y,
|
||||||
|
z: SUB_CHUNK_HEIGHT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum SubChunk {
|
pub enum SubChunk {
|
||||||
Homogeneous(Block),
|
Homogeneous(Block),
|
||||||
Hash(Block, FxHashMap<Vec3<u8>, Block>),
|
Hash(Block, FxHashMap<Vec3<u8>, Block>),
|
||||||
Heterogeneous(Chunk<Block, TerrainChunkSize, ()>),
|
Heterogeneous(Chunk<Block, SubChunkSize, ()>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubChunk {
|
impl SubChunk {
|
||||||
|
@ -7,7 +7,9 @@ use common::{
|
|||||||
comp,
|
comp,
|
||||||
msg::ServerMsg,
|
msg::ServerMsg,
|
||||||
npc::{get_npc_name, NpcKind},
|
npc::{get_npc_name, NpcKind},
|
||||||
state::TimeOfDay,
|
state::{TerrainChange, TimeOfDay},
|
||||||
|
terrain::Block,
|
||||||
|
vol::Vox,
|
||||||
};
|
};
|
||||||
use specs::{Builder, Entity as EcsEntity, Join};
|
use specs::{Builder, Entity as EcsEntity, Join};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -104,6 +106,18 @@ lazy_static! {
|
|||||||
"/players : Show the online players list",
|
"/players : Show the online players list",
|
||||||
handle_players,
|
handle_players,
|
||||||
),
|
),
|
||||||
|
ChatCommand::new(
|
||||||
|
"solid",
|
||||||
|
"{}",
|
||||||
|
"/solid : Make the blocks around you solid",
|
||||||
|
handle_solid,
|
||||||
|
),
|
||||||
|
ChatCommand::new(
|
||||||
|
"empty",
|
||||||
|
"{}",
|
||||||
|
"/empty : Make the blocks around you empty",
|
||||||
|
handle_empty,
|
||||||
|
),
|
||||||
ChatCommand::new(
|
ChatCommand::new(
|
||||||
"help", "", "/help: Display this message", handle_help)
|
"help", "", "/help: Display this message", handle_help)
|
||||||
];
|
];
|
||||||
@ -122,7 +136,7 @@ fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
|
|||||||
}
|
}
|
||||||
None => server.clients.notify(
|
None => server.clients.notify(
|
||||||
entity,
|
entity,
|
||||||
ServerMsg::Chat(String::from("Command 'jump' invalid in current state.")),
|
ServerMsg::Chat(String::from("You have no position!")),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,10 +254,9 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
server.clients.notify(
|
server
|
||||||
entity,
|
.clients
|
||||||
ServerMsg::Chat(format!("You don't have any position!")),
|
.notify(entity, ServerMsg::Chat(format!("You have no position!")));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,6 +328,44 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_solid(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||||
|
match server.state.read_component_cloned::<comp::Pos>(entity) {
|
||||||
|
Some(current_pos) => {
|
||||||
|
server.state.ecs().write_resource::<TerrainChange>().set(
|
||||||
|
current_pos.0.map(|e| e.floor() as i32),
|
||||||
|
Block::new(1, Rgb::broadcast(255)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => server.clients.notify(
|
||||||
|
entity,
|
||||||
|
ServerMsg::Chat(String::from("You have no position!")),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_empty(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||||
|
match server.state.read_component_cloned::<comp::Pos>(entity) {
|
||||||
|
Some(current_pos) => {
|
||||||
|
let mut terrain_change = server.state.ecs().write_resource::<TerrainChange>();
|
||||||
|
|
||||||
|
for i in -1..2 {
|
||||||
|
for j in -1..2 {
|
||||||
|
for k in -2..1 {
|
||||||
|
terrain_change.set(
|
||||||
|
current_pos.0.map(|e| e.floor() as i32) + Vec3::new(i, j, k),
|
||||||
|
Block::empty(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => server.clients.notify(
|
||||||
|
entity,
|
||||||
|
ServerMsg::Chat(String::from("You have no position!")),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
|
fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
|
||||||
for cmd in CHAT_COMMANDS.iter() {
|
for cmd in CHAT_COMMANDS.iter() {
|
||||||
server
|
server
|
||||||
|
@ -17,7 +17,7 @@ use common::{
|
|||||||
msg::{ClientMsg, ClientState, RequestStateError, ServerInfo, ServerMsg},
|
msg::{ClientMsg, ClientState, RequestStateError, ServerInfo, ServerMsg},
|
||||||
net::PostOffice,
|
net::PostOffice,
|
||||||
state::{State, Uid},
|
state::{State, Uid},
|
||||||
terrain::{TerrainChunk, TerrainChunkSize},
|
terrain::{TerrainChunk, TerrainChunkSize, TerrainMap},
|
||||||
vol::VolSize,
|
vol::VolSize,
|
||||||
};
|
};
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
@ -246,9 +246,24 @@ impl Server {
|
|||||||
self.pending_chunks.remove(&key);
|
self.pending_chunks.remove(&key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn chunk_in_vd(
|
||||||
|
player_pos: Vec3<f32>,
|
||||||
|
chunk_pos: Vec2<i32>,
|
||||||
|
terrain: &TerrainMap,
|
||||||
|
vd: u32,
|
||||||
|
) -> bool {
|
||||||
|
let player_chunk_pos = terrain.pos_key(player_pos.map(|e| e as i32));
|
||||||
|
|
||||||
|
let adjusted_dist_sqr = Vec2::from(player_chunk_pos - chunk_pos)
|
||||||
|
.map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
|
||||||
|
.magnitude_squared();
|
||||||
|
|
||||||
|
adjusted_dist_sqr <= vd.pow(2)
|
||||||
|
}
|
||||||
|
|
||||||
// Remove chunks that are too far from players.
|
// Remove chunks that are too far from players.
|
||||||
let mut chunks_to_remove = Vec::new();
|
let mut chunks_to_remove = Vec::new();
|
||||||
self.state.terrain().iter().for_each(|(key, _)| {
|
self.state.terrain().iter().for_each(|(chunk_key, _)| {
|
||||||
let mut should_drop = true;
|
let mut should_drop = true;
|
||||||
|
|
||||||
// For each player with a position, calculate the distance.
|
// For each player with a position, calculate the distance.
|
||||||
@ -258,15 +273,9 @@ impl Server {
|
|||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
|
||||||
|
|
||||||
let adjusted_dist_sqr = Vec2::from(chunk_pos - key)
|
|
||||||
.map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
|
|
||||||
.magnitude_squared();
|
|
||||||
|
|
||||||
if player
|
if player
|
||||||
.view_distance
|
.view_distance
|
||||||
.map(|vd| adjusted_dist_sqr <= vd.pow(2))
|
.map(|vd| chunk_in_vd(pos.0, chunk_key, &self.state.terrain(), vd))
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
should_drop = false;
|
should_drop = false;
|
||||||
@ -275,7 +284,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if should_drop {
|
if should_drop {
|
||||||
chunks_to_remove.push(key);
|
chunks_to_remove.push(chunk_key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for key in chunks_to_remove {
|
for key in chunks_to_remove {
|
||||||
@ -285,6 +294,36 @@ impl Server {
|
|||||||
// 6) Synchronise clients with the new state of the world.
|
// 6) Synchronise clients with the new state of the world.
|
||||||
self.sync_clients();
|
self.sync_clients();
|
||||||
|
|
||||||
|
// Sync changed chunks
|
||||||
|
'chunk: for chunk_key in &self.state.chunk_changes().modified_chunks {
|
||||||
|
let terrain = self.state.terrain();
|
||||||
|
|
||||||
|
for (entity, player, pos) in (
|
||||||
|
&self.state.ecs().entities(),
|
||||||
|
&self.state.ecs().read_storage::<comp::Player>(),
|
||||||
|
&self.state.ecs().read_storage::<comp::Pos>(),
|
||||||
|
)
|
||||||
|
.join()
|
||||||
|
{
|
||||||
|
if player
|
||||||
|
.view_distance
|
||||||
|
.map(|vd| chunk_in_vd(pos.0, *chunk_key, &terrain, vd))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
self.clients.notify(
|
||||||
|
entity,
|
||||||
|
ServerMsg::TerrainChunkUpdate {
|
||||||
|
key: *chunk_key,
|
||||||
|
chunk: Box::new(match self.state.terrain().get_key(*chunk_key) {
|
||||||
|
Some(chunk) => chunk.clone(),
|
||||||
|
None => break 'chunk,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 7) Finish the tick, pass control back to the frontend.
|
// 7) Finish the tick, pass control back to the frontend.
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
|
@ -88,12 +88,20 @@ impl Terrain {
|
|||||||
let current_tick = client.get_tick();
|
let current_tick = client.get_tick();
|
||||||
|
|
||||||
// Add any recently created or changed chunks to the list of chunks to be meshed.
|
// Add any recently created or changed chunks to the list of chunks to be meshed.
|
||||||
for pos in client
|
for (modified, pos) in client
|
||||||
.state()
|
.state()
|
||||||
.changes()
|
.chunk_changes()
|
||||||
|
.modified_chunks
|
||||||
|
.iter()
|
||||||
|
.map(|c| (true, c))
|
||||||
|
.chain(
|
||||||
|
client
|
||||||
|
.state()
|
||||||
|
.chunk_changes()
|
||||||
.new_chunks
|
.new_chunks
|
||||||
.iter()
|
.iter()
|
||||||
.chain(client.state().changes().changed_chunks.iter())
|
.map(|c| (false, c)),
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// TODO: ANOTHER PROBLEM HERE!
|
// TODO: ANOTHER PROBLEM HERE!
|
||||||
// What happens if the block on the edge of a chunk gets modified? We need to spawn
|
// What happens if the block on the edge of a chunk gets modified? We need to spawn
|
||||||
@ -103,7 +111,7 @@ impl Terrain {
|
|||||||
for j in -1..2 {
|
for j in -1..2 {
|
||||||
let pos = pos + Vec2::new(i, j);
|
let pos = pos + Vec2::new(i, j);
|
||||||
|
|
||||||
if !self.chunks.contains_key(&pos) {
|
if !self.chunks.contains_key(&pos) || modified {
|
||||||
let mut neighbours = true;
|
let mut neighbours = true;
|
||||||
for i in -1..2 {
|
for i in -1..2 {
|
||||||
for j in -1..2 {
|
for j in -1..2 {
|
||||||
@ -116,18 +124,21 @@ impl Terrain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if neighbours {
|
if neighbours {
|
||||||
self.mesh_todo.entry(pos).or_insert(ChunkMeshState {
|
self.mesh_todo.insert(
|
||||||
|
pos,
|
||||||
|
ChunkMeshState {
|
||||||
pos,
|
pos,
|
||||||
started_tick: current_tick,
|
started_tick: current_tick,
|
||||||
active_worker: false,
|
active_worker: false,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove any models for chunks that have been recently removed.
|
// Remove any models for chunks that have been recently removed.
|
||||||
for pos in &client.state().changes().removed_chunks {
|
for pos in &client.state().chunk_changes().removed_chunks {
|
||||||
self.chunks.remove(pos);
|
self.chunks.remove(pos);
|
||||||
self.mesh_todo.remove(pos);
|
self.mesh_todo.remove(pos);
|
||||||
}
|
}
|
||||||
@ -218,6 +229,8 @@ impl Terrain {
|
|||||||
z_bounds: response.z_bounds,
|
z_bounds: response.z_bounds,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.mesh_todo.remove(&response.pos);
|
||||||
}
|
}
|
||||||
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
|
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
|
||||||
// since it's either out of date or no longer needed.
|
// since it's either out of date or no longer needed.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user