mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sharp/cancel-chunk-gen' into 'master'
Allow canceling chunk generation. See merge request veloren/veloren!499
This commit is contained in:
commit
7e6fbda34a
@ -488,7 +488,9 @@ impl Client {
|
||||
self.state.write_component(self.entity, inventory)
|
||||
}
|
||||
ServerMsg::TerrainChunkUpdate { key, chunk } => {
|
||||
self.state.insert_chunk(key, *chunk);
|
||||
if let Ok(chunk) = chunk {
|
||||
self.state.insert_chunk(key, *chunk);
|
||||
}
|
||||
self.pending_chunks.remove(&key);
|
||||
}
|
||||
ServerMsg::TerrainBlockUpdates(mut blocks) => blocks
|
||||
|
@ -58,7 +58,7 @@ pub enum ServerMsg {
|
||||
InventoryUpdate(comp::Inventory),
|
||||
TerrainChunkUpdate {
|
||||
key: Vec2<i32>,
|
||||
chunk: Box<TerrainChunk>,
|
||||
chunk: Result<Box<TerrainChunk>, ()>,
|
||||
},
|
||||
TerrainBlockUpdates(HashMap<Vec3<i32>, Block>),
|
||||
Error(ServerError),
|
||||
|
@ -27,7 +27,7 @@ use common::{
|
||||
vol::{ReadVol, RectVolSize, Vox},
|
||||
};
|
||||
use crossbeam::channel;
|
||||
use hashbrown::HashSet;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use log::debug;
|
||||
use metrics::ServerMetrics;
|
||||
use rand::Rng;
|
||||
@ -35,7 +35,10 @@ use specs::{join::Join, world::EntityBuilder as EcsEntityBuilder, Builder, Entit
|
||||
use std::{
|
||||
i32,
|
||||
net::SocketAddr,
|
||||
sync::Arc,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use uvth::{ThreadPool, ThreadPoolBuilder};
|
||||
@ -68,9 +71,15 @@ pub struct Server {
|
||||
clients: Clients,
|
||||
|
||||
thread_pool: ThreadPool,
|
||||
chunk_tx: channel::Sender<(Vec2<i32>, (TerrainChunk, ChunkSupplement))>,
|
||||
chunk_rx: channel::Receiver<(Vec2<i32>, (TerrainChunk, ChunkSupplement))>,
|
||||
pending_chunks: HashSet<Vec2<i32>>,
|
||||
chunk_tx: channel::Sender<(
|
||||
Vec2<i32>,
|
||||
Result<(TerrainChunk, ChunkSupplement), EcsEntity>,
|
||||
)>,
|
||||
chunk_rx: channel::Receiver<(
|
||||
Vec2<i32>,
|
||||
Result<(TerrainChunk, ChunkSupplement), EcsEntity>,
|
||||
)>,
|
||||
pending_chunks: HashMap<Vec2<i32>, Arc<AtomicBool>>,
|
||||
|
||||
server_settings: ServerSettings,
|
||||
server_info: ServerInfo,
|
||||
@ -113,7 +122,7 @@ impl Server {
|
||||
.build(),
|
||||
chunk_tx,
|
||||
chunk_rx,
|
||||
pending_chunks: HashSet::new(),
|
||||
pending_chunks: HashMap::new(),
|
||||
|
||||
server_info: ServerInfo {
|
||||
name: settings.server_name.clone(),
|
||||
@ -463,7 +472,20 @@ impl Server {
|
||||
let before_tick_5 = Instant::now();
|
||||
// 5) Fetch any generated `TerrainChunk`s and insert them into the terrain.
|
||||
// Also, send the chunk data to anybody that is close by.
|
||||
if let Ok((key, (chunk, supplement))) = self.chunk_rx.try_recv() {
|
||||
'insert_terrain_chunks: while let Ok((key, res)) = self.chunk_rx.try_recv() {
|
||||
let (chunk, supplement) = match res {
|
||||
Ok((chunk, supplement)) => (chunk, supplement),
|
||||
Err(entity) => {
|
||||
self.clients.notify(
|
||||
entity,
|
||||
ServerMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Err(()),
|
||||
},
|
||||
);
|
||||
continue 'insert_terrain_chunks;
|
||||
}
|
||||
};
|
||||
// Send the chunk to all nearby players.
|
||||
for (entity, view_distance, pos) in (
|
||||
&self.state.ecs().entities(),
|
||||
@ -485,7 +507,7 @@ impl Server {
|
||||
entity,
|
||||
ServerMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Box::new(chunk.clone()),
|
||||
chunk: Ok(Box::new(chunk.clone())),
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -552,32 +574,40 @@ impl Server {
|
||||
|
||||
// Remove chunks that are too far from players.
|
||||
let mut chunks_to_remove = Vec::new();
|
||||
self.state.terrain().iter().for_each(|(chunk_key, _)| {
|
||||
let mut should_drop = true;
|
||||
self.state
|
||||
.terrain()
|
||||
.iter()
|
||||
.map(|(k, _)| k)
|
||||
.chain(self.pending_chunks.keys().cloned())
|
||||
.for_each(|chunk_key| {
|
||||
let mut should_drop = true;
|
||||
|
||||
// For each player with a position, calculate the distance.
|
||||
for (player, pos) in (
|
||||
&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, &self.state.terrain(), vd))
|
||||
.unwrap_or(false)
|
||||
// For each player with a position, calculate the distance.
|
||||
for (player, pos) in (
|
||||
&self.state.ecs().read_storage::<comp::Player>(),
|
||||
&self.state.ecs().read_storage::<comp::Pos>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
should_drop = false;
|
||||
break;
|
||||
if player
|
||||
.view_distance
|
||||
.map(|vd| chunk_in_vd(pos.0, chunk_key, &self.state.terrain(), vd))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
should_drop = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if should_drop {
|
||||
chunks_to_remove.push(chunk_key);
|
||||
}
|
||||
});
|
||||
if should_drop {
|
||||
chunks_to_remove.push(chunk_key);
|
||||
}
|
||||
});
|
||||
for key in chunks_to_remove {
|
||||
self.state.remove_chunk(key);
|
||||
if let Some(cancel) = self.pending_chunks.remove(&key) {
|
||||
cancel.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
let before_tick_6 = Instant::now();
|
||||
@ -604,10 +634,10 @@ impl Server {
|
||||
entity,
|
||||
ServerMsg::TerrainChunkUpdate {
|
||||
key: *chunk_key,
|
||||
chunk: Box::new(match self.state.terrain().get_key(*chunk_key) {
|
||||
chunk: Ok(Box::new(match self.state.terrain().get_key(*chunk_key) {
|
||||
Some(chunk) => chunk.clone(),
|
||||
None => break 'chunk,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -997,10 +1027,10 @@ impl Server {
|
||||
Some(chunk) => {
|
||||
client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Box::new(chunk.clone()),
|
||||
chunk: Ok(Box::new(chunk.clone())),
|
||||
})
|
||||
}
|
||||
None => requested_chunks.push(key),
|
||||
None => requested_chunks.push((entity, key)),
|
||||
}
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
@ -1083,8 +1113,8 @@ impl Server {
|
||||
}
|
||||
|
||||
// Generate requested chunks.
|
||||
for key in requested_chunks {
|
||||
self.generate_chunk(key);
|
||||
for (entity, key) in requested_chunks {
|
||||
self.generate_chunk(entity, key);
|
||||
}
|
||||
|
||||
for (pos, block) in modified_blocks {
|
||||
@ -1298,14 +1328,22 @@ impl Server {
|
||||
.clear();
|
||||
}
|
||||
|
||||
pub fn generate_chunk(&mut self, key: Vec2<i32>) {
|
||||
if self.pending_chunks.insert(key) {
|
||||
let chunk_tx = self.chunk_tx.clone();
|
||||
let world = self.world.clone();
|
||||
self.thread_pool.execute(move || {
|
||||
let _ = chunk_tx.send((key, world.generate_chunk(key)));
|
||||
});
|
||||
}
|
||||
pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2<i32>) {
|
||||
let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) {
|
||||
v
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let cancel = Arc::new(AtomicBool::new(false));
|
||||
v.insert(Arc::clone(&cancel));
|
||||
let chunk_tx = self.chunk_tx.clone();
|
||||
let world = self.world.clone();
|
||||
self.thread_pool.execute(move || {
|
||||
let payload = world
|
||||
.generate_chunk(key, || cancel.load(Ordering::Relaxed))
|
||||
.map_err(|_| entity);
|
||||
let _ = chunk_tx.send((key, payload));
|
||||
});
|
||||
}
|
||||
|
||||
fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) {
|
||||
|
@ -65,7 +65,11 @@ impl World {
|
||||
BlockGen::new(self, ColumnGen::new(&self.sim))
|
||||
}
|
||||
|
||||
pub fn generate_chunk(&self, chunk_pos: Vec2<i32>) -> (TerrainChunk, ChunkSupplement) {
|
||||
pub fn generate_chunk(
|
||||
&self,
|
||||
chunk_pos: Vec2<i32>,
|
||||
mut should_continue: impl FnMut() -> bool,
|
||||
) -> Result<(TerrainChunk, ChunkSupplement), ()> {
|
||||
let air = Block::empty();
|
||||
let stone = Block::new(BlockKind::Dense, Rgb::new(200, 220, 255));
|
||||
let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190));
|
||||
@ -81,7 +85,7 @@ impl World {
|
||||
{
|
||||
Some((base_z, sim_chunk)) => (base_z as i32, sim_chunk),
|
||||
None => {
|
||||
return (
|
||||
return Ok((
|
||||
TerrainChunk::new(
|
||||
CONFIG.sea_level as i32,
|
||||
water,
|
||||
@ -89,7 +93,7 @@ impl World {
|
||||
TerrainChunkMeta::void(),
|
||||
),
|
||||
ChunkSupplement::default(),
|
||||
)
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
@ -101,6 +105,9 @@ impl World {
|
||||
let mut chunk = TerrainChunk::new(base_z, stone, air, meta);
|
||||
for x in 0..TerrainChunkSize::RECT_SIZE.x as i32 {
|
||||
for y in 0..TerrainChunkSize::RECT_SIZE.y as i32 {
|
||||
if should_continue() {
|
||||
return Err(());
|
||||
};
|
||||
let wpos2d = Vec2::new(x, y)
|
||||
+ Vec2::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
||||
|
||||
@ -151,7 +158,7 @@ impl World {
|
||||
},
|
||||
};
|
||||
|
||||
(chunk, supplement)
|
||||
Ok((chunk, supplement))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user