mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Deserialize chunks off the main thread.
This commit is contained in:
parent
6eb26ba6d8
commit
3ba53b3e4c
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6470,6 +6470,7 @@ dependencies = [
|
|||||||
"bincode",
|
"bincode",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap 3.1.8",
|
"clap 3.1.8",
|
||||||
|
"crossbeam-channel",
|
||||||
"hashbrown 0.12.0",
|
"hashbrown 0.12.0",
|
||||||
"image",
|
"image",
|
||||||
"num 0.4.0",
|
"num 0.4.0",
|
||||||
|
@ -23,6 +23,7 @@ network = { package = "veloren-network", path = "../network", features = ["compr
|
|||||||
|
|
||||||
bincode = "1.3.2"
|
bincode = "1.3.2"
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
|
crossbeam-channel = "0.5"
|
||||||
tokio = { version = "1.14", default-features = false, features = ["rt-multi-thread"] }
|
tokio = { version = "1.14", default-features = false, features = ["rt-multi-thread"] }
|
||||||
quinn = "0.8"
|
quinn = "0.8"
|
||||||
image = { version = "0.24", default-features = false, features = ["png"] }
|
image = { version = "0.24", default-features = false, features = ["png"] }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use authc::AuthClientError;
|
use authc::AuthClientError;
|
||||||
|
use common_net::msg::DecodeError;
|
||||||
pub use network::{InitProtocolError, NetworkConnectError, NetworkError};
|
pub use network::{InitProtocolError, NetworkConnectError, NetworkError};
|
||||||
use network::{ParticipantError, StreamError};
|
use network::{ParticipantError, StreamError};
|
||||||
use specs::error::Error as SpecsError;
|
use specs::error::Error as SpecsError;
|
||||||
@ -24,6 +25,7 @@ pub enum Error {
|
|||||||
//TODO: InvalidAlias,
|
//TODO: InvalidAlias,
|
||||||
Other(String),
|
Other(String),
|
||||||
SpecsErr(SpecsError),
|
SpecsErr(SpecsError),
|
||||||
|
CompressionError(DecodeError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SpecsError> for Error {
|
impl From<SpecsError> for Error {
|
||||||
@ -46,6 +48,15 @@ impl From<bincode::Error> for Error {
|
|||||||
fn from(err: bincode::Error) -> Self { Self::StreamErr(StreamError::Deserialize(err)) }
|
fn from(err: bincode::Error) -> Self { Self::StreamErr(StreamError::Deserialize(err)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<DecodeError> for Error {
|
||||||
|
fn from(err: DecodeError) -> Self {
|
||||||
|
match err {
|
||||||
|
DecodeError::Deserialize(err) => Self::StreamErr(StreamError::Deserialize(err)),
|
||||||
|
_ => Self::CompressionError(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<AuthClientError> for Error {
|
impl From<AuthClientError> for Error {
|
||||||
fn from(err: AuthClientError) -> Self { Self::AuthClientError(err) }
|
fn from(err: AuthClientError) -> Self { Self::AuthClientError(err) }
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ use common::{
|
|||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe::{ComponentRecipeBook, RecipeBook},
|
recipe::{ComponentRecipeBook, RecipeBook},
|
||||||
resources::{PlayerEntity, TimeOfDay},
|
resources::{PlayerEntity, TimeOfDay},
|
||||||
|
slowjob::SlowJobPool,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
terrain::{
|
terrain::{
|
||||||
block::Block, map::MapConfig, neighbors, BiomeKind, SitesKind, SpriteKind, TerrainChunk,
|
block::Block, map::MapConfig, neighbors, BiomeKind, SitesKind, SpriteKind, TerrainChunk,
|
||||||
@ -66,6 +67,7 @@ use common_net::{
|
|||||||
use common_state::State;
|
use common_state::State;
|
||||||
use common_systems::add_local_systems;
|
use common_systems::add_local_systems;
|
||||||
use comp::BuffKind;
|
use comp::BuffKind;
|
||||||
|
use crossbeam_channel as mpsc;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use network::{ConnectAddr, Network, Participant, Pid, Stream};
|
use network::{ConnectAddr, Network, Participant, Pid, Stream};
|
||||||
@ -199,6 +201,19 @@ impl Default for WeatherLerp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deserialized and decompressed terrain updates.
|
||||||
|
enum TerrainUpdate {
|
||||||
|
Chunk {
|
||||||
|
key: Vec2<i32>,
|
||||||
|
chunk: Option<Arc<TerrainChunk>>,
|
||||||
|
},
|
||||||
|
LodZone {
|
||||||
|
key: Vec2<i32>,
|
||||||
|
zone: lod::Zone,
|
||||||
|
},
|
||||||
|
Block(HashMap<Vec3<i32>, Block>),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
registered: bool,
|
registered: bool,
|
||||||
presence: Option<PresenceKind>,
|
presence: Option<PresenceKind>,
|
||||||
@ -251,6 +266,8 @@ pub struct Client {
|
|||||||
// TODO: move into voxygen
|
// TODO: move into voxygen
|
||||||
loaded_distance: f32,
|
loaded_distance: f32,
|
||||||
|
|
||||||
|
terrain_tx: mpsc::Sender<Result<TerrainUpdate, Error>>,
|
||||||
|
terrain_rx: mpsc::Receiver<Result<TerrainUpdate, Error>>,
|
||||||
pending_chunks: HashMap<Vec2<i32>, Instant>,
|
pending_chunks: HashMap<Vec2<i32>, Instant>,
|
||||||
target_time_of_day: Option<TimeOfDay>,
|
target_time_of_day: Option<TimeOfDay>,
|
||||||
}
|
}
|
||||||
@ -263,6 +280,8 @@ pub struct CharacterList {
|
|||||||
pub loading: bool,
|
pub loading: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TOTAL_PENDING_CHUNKS_LIMIT: usize = 1024;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
addr: ConnectionArgs,
|
addr: ConnectionArgs,
|
||||||
@ -355,6 +374,8 @@ impl Client {
|
|||||||
let mut state = State::client();
|
let mut state = State::client();
|
||||||
// Client-only components
|
// Client-only components
|
||||||
state.ecs_mut().register::<comp::Last<CharacterState>>();
|
state.ecs_mut().register::<comp::Last<CharacterState>>();
|
||||||
|
state.ecs_mut().write_resource::<SlowJobPool>()
|
||||||
|
.configure("TERRAIN_DESERIALIZING", |n| n / 2);
|
||||||
|
|
||||||
let entity = state.ecs_mut().apply_entity_package(entity_package);
|
let entity = state.ecs_mut().apply_entity_package(entity_package);
|
||||||
*state.ecs_mut().write_resource() = time_of_day;
|
*state.ecs_mut().write_resource() = time_of_day;
|
||||||
@ -646,6 +667,8 @@ impl Client {
|
|||||||
|
|
||||||
debug!("Initial sync done");
|
debug!("Initial sync done");
|
||||||
|
|
||||||
|
let (terrain_tx, terrain_rx) = mpsc::bounded(TOTAL_PENDING_CHUNKS_LIMIT);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
registered: false,
|
registered: false,
|
||||||
presence: None,
|
presence: None,
|
||||||
@ -707,6 +730,8 @@ impl Client {
|
|||||||
lod_distance: 4.0,
|
lod_distance: 4.0,
|
||||||
loaded_distance: 0.0,
|
loaded_distance: 0.0,
|
||||||
|
|
||||||
|
terrain_tx,
|
||||||
|
terrain_rx,
|
||||||
pending_chunks: HashMap::new(),
|
pending_chunks: HashMap::new(),
|
||||||
target_time_of_day: None,
|
target_time_of_day: None,
|
||||||
})
|
})
|
||||||
@ -1827,7 +1852,6 @@ impl Client {
|
|||||||
for key in keys.iter() {
|
for key in keys.iter() {
|
||||||
if self.state.terrain().get_key(*key).is_none() {
|
if self.state.terrain().get_key(*key).is_none() {
|
||||||
if !skip_mode && !self.pending_chunks.contains_key(key) {
|
if !skip_mode && !self.pending_chunks.contains_key(key) {
|
||||||
const TOTAL_PENDING_CHUNKS_LIMIT: usize = 1024;
|
|
||||||
const CURRENT_TICK_PENDING_CHUNKS_LIMIT: usize = 8 * 4;
|
const CURRENT_TICK_PENDING_CHUNKS_LIMIT: usize = 8 * 4;
|
||||||
if self.pending_chunks.len() < TOTAL_PENDING_CHUNKS_LIMIT
|
if self.pending_chunks.len() < TOTAL_PENDING_CHUNKS_LIMIT
|
||||||
&& current_tick_send_chunk_requests
|
&& current_tick_send_chunk_requests
|
||||||
@ -2261,29 +2285,47 @@ impl Client {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_server_terrain_msg(&mut self, msg: ServerGeneral) -> Result<(), Error> {
|
fn handle_server_terrain_msg(msg: ServerGeneral) -> Result<TerrainUpdate, Error> {
|
||||||
prof_span!("handle_server_terrain_mgs");
|
prof_span!("handle_server_terrain_mgs");
|
||||||
match msg {
|
match msg {
|
||||||
ServerGeneral::TerrainChunkUpdate { key, chunk } => {
|
ServerGeneral::TerrainChunkUpdate { key, chunk } => {
|
||||||
if let Some(chunk) = chunk.ok().and_then(|c| c.to_chunk()) {
|
let chunk = chunk
|
||||||
self.state.insert_chunk(key, Arc::new(chunk));
|
.ok()
|
||||||
|
.map(|c| c.to_chunk())
|
||||||
|
.transpose()?
|
||||||
|
.map(Arc::new);
|
||||||
|
return Ok(TerrainUpdate::Chunk { key, chunk });
|
||||||
|
},
|
||||||
|
ServerGeneral::LodZoneUpdate { key, zone } => {
|
||||||
|
return Ok(TerrainUpdate::LodZone { key, zone });
|
||||||
|
},
|
||||||
|
ServerGeneral::TerrainBlockUpdates(blocks) => {
|
||||||
|
let blocks = blocks.decompress()?;
|
||||||
|
return Ok(TerrainUpdate::Block(blocks));
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
return Err(Error::Other("Invalid terrain message".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_terrain_msg(&mut self, msg: TerrainUpdate) {
|
||||||
|
match msg {
|
||||||
|
TerrainUpdate::Chunk { key, chunk } => {
|
||||||
|
if let Some(chunk) = chunk {
|
||||||
|
self.state.insert_chunk(key, chunk);
|
||||||
}
|
}
|
||||||
self.pending_chunks.remove(&key);
|
self.pending_chunks.remove(&key);
|
||||||
},
|
},
|
||||||
ServerGeneral::LodZoneUpdate { key, zone } => {
|
TerrainUpdate::LodZone { key, zone } => {
|
||||||
self.lod_zones.insert(key, zone);
|
self.lod_zones.insert(key, zone);
|
||||||
self.lod_last_requested = None;
|
self.lod_last_requested = None;
|
||||||
},
|
},
|
||||||
ServerGeneral::TerrainBlockUpdates(blocks) => {
|
TerrainUpdate::Block(blocks) => {
|
||||||
if let Some(mut blocks) = blocks.decompress() {
|
blocks.into_iter().for_each(|(pos, block)| {
|
||||||
blocks.drain().for_each(|(pos, block)| {
|
self.state.set_block(pos, block);
|
||||||
self.state.set_block(pos, block);
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => unreachable!("Not a terrain message"),
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_server_character_screen_msg(
|
fn handle_server_character_screen_msg(
|
||||||
@ -2375,15 +2417,28 @@ impl Client {
|
|||||||
}
|
}
|
||||||
while let Some(msg) = self.terrain_stream.try_recv_raw()? {
|
while let Some(msg) = self.terrain_stream.try_recv_raw()? {
|
||||||
cnt += 1;
|
cnt += 1;
|
||||||
let msg = msg.decompress()?;
|
let terrain_tx = self.terrain_tx.clone();
|
||||||
let msg = bincode::deserialize(&msg)?;
|
self
|
||||||
|
.state
|
||||||
|
.slow_job_pool()
|
||||||
|
.spawn("TERRAIN_DESERIALIZING", move || {
|
||||||
|
let handle_msg = || {
|
||||||
|
let msg = msg.decompress()?;
|
||||||
|
let msg = bincode::deserialize(&msg)?;
|
||||||
|
Self::handle_server_terrain_msg(msg)
|
||||||
|
};
|
||||||
|
terrain_tx.send(handle_msg());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
while let Ok(msg) = self.terrain_rx.try_recv() {
|
||||||
|
let msg = msg?;
|
||||||
#[cfg(feature = "tracy")]
|
#[cfg(feature = "tracy")]
|
||||||
{
|
{
|
||||||
if let ServerGeneral::TerrainChunkUpdate { chunk, .. } = &msg {
|
if let TerrainUpdate::Chunk { chunk, .. } = &msg {
|
||||||
terrain_cnt += chunk.as_ref().map(|x| x.approx_len()).unwrap_or(0);
|
terrain_cnt += chunk.as_ref().map(|x| x.approx_len()).unwrap_or(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.handle_server_terrain_msg(msg)?;
|
self.handle_terrain_msg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if cnt_start == cnt {
|
if cnt_start == cnt {
|
||||||
|
@ -4,19 +4,55 @@ use common::{
|
|||||||
volumes::vol_grid_2d::VolGrid2d,
|
volumes::vol_grid_2d::VolGrid2d,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use image::{ImageBuffer, ImageDecoder, ImageEncoder, Pixel};
|
use image::{ImageBuffer, ImageDecoder, ImageEncoder, ImageError, Pixel};
|
||||||
use num_traits::cast::FromPrimitive;
|
use num_traits::cast::FromPrimitive;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
io::{Read, Write},
|
io::{Error as IoError, Read, Write},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
use serde_with::{serde_as, Bytes};
|
use serde_with::{serde_as, Bytes};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
/// Errors that can occur during decoding.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DecodeError {
|
||||||
|
Deserialize(bincode::Error),
|
||||||
|
Image(ImageError),
|
||||||
|
Io(IoError)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for DecodeError {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Deserialize(error) => error.to_string(),
|
||||||
|
Self::Image(error) => error.to_string(),
|
||||||
|
Self::Io(error) => error.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ImageError> for DecodeError {
|
||||||
|
fn from(error: ImageError) -> Self {
|
||||||
|
Self::Image(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IoError> for DecodeError {
|
||||||
|
fn from(error: IoError) -> Self {
|
||||||
|
Self::Io(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bincode::Error> for DecodeError {
|
||||||
|
fn from(error: bincode::Error) -> Self {
|
||||||
|
Self::Deserialize(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper for compressed, serialized data (for stuff that doesn't use the
|
/// Wrapper for compressed, serialized data (for stuff that doesn't use the
|
||||||
/// default lz4 compression)
|
/// default lz4 compression)
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
@ -59,15 +95,14 @@ impl<'a, T: Serialize> CompressedData<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: for<'a> Deserialize<'a>> CompressedData<'_, T> {
|
impl<T: for<'a> Deserialize<'a>> CompressedData<'_, T> {
|
||||||
pub fn decompress(&self) -> Option<T> {
|
pub fn decompress(&self) -> Result<T, DecodeError> {
|
||||||
if self.compressed {
|
if self.compressed {
|
||||||
let mut uncompressed = Vec::with_capacity(self.data.len());
|
let mut uncompressed = Vec::with_capacity(self.data.len());
|
||||||
flate2::read::DeflateDecoder::new(&*self.data)
|
flate2::read::DeflateDecoder::new(&*self.data)
|
||||||
.read_to_end(&mut uncompressed)
|
.read_to_end(&mut uncompressed)?;
|
||||||
.ok()?;
|
Ok(bincode::deserialize(&*uncompressed)?)
|
||||||
bincode::deserialize(&*uncompressed).ok()
|
|
||||||
} else {
|
} else {
|
||||||
bincode::deserialize(&*self.data).ok()
|
Ok(bincode::deserialize(&*self.data)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,6 +164,8 @@ impl PackingFormula for GridLtrPacking {
|
|||||||
pub trait VoxelImageEncoding {
|
pub trait VoxelImageEncoding {
|
||||||
type Workspace;
|
type Workspace;
|
||||||
type Output;
|
type Output;
|
||||||
|
type EncodeError;
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace;
|
fn create(width: u32, height: u32) -> Self::Workspace;
|
||||||
fn put_solid(&self, ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>);
|
fn put_solid(&self, ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>);
|
||||||
fn put_sprite(
|
fn put_sprite(
|
||||||
@ -140,25 +177,31 @@ pub trait VoxelImageEncoding {
|
|||||||
sprite: SpriteKind,
|
sprite: SpriteKind,
|
||||||
ori: Option<u8>,
|
ori: Option<u8>,
|
||||||
);
|
);
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output>;
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VoxelImageDecoding: VoxelImageEncoding {
|
pub trait VoxelImageDecoding: VoxelImageEncoding {
|
||||||
fn start(ws: &Self::Output) -> Option<Self::Workspace>;
|
fn start(ws: &Self::Output) -> Result<Self::Workspace, DecodeError>;
|
||||||
fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block;
|
fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn image_from_bytes<'a, I: ImageDecoder<'a>, P: 'static + Pixel<Subpixel = u8>>(
|
pub fn image_from_bytes<'a, I: ImageDecoder<'a>, P: 'static + Pixel<Subpixel = u8>>(
|
||||||
decoder: I,
|
decoder: I,
|
||||||
) -> Option<ImageBuffer<P, Vec<u8>>> {
|
) -> Result<ImageBuffer<P, Vec<u8>>, ImageError> {
|
||||||
let (w, h) = decoder.dimensions();
|
let (w, h) = decoder.dimensions();
|
||||||
let mut buf = vec![0; decoder.total_bytes() as usize];
|
let mut buf = vec![0; decoder.total_bytes() as usize];
|
||||||
decoder.read_image(&mut buf).ok()?;
|
decoder.read_image(&mut buf)?;
|
||||||
ImageBuffer::from_raw(w, h, buf)
|
Ok(ImageBuffer::from_raw(w, h, buf).ok_or_else(|| {
|
||||||
|
IoError::new(
|
||||||
|
std::io::ErrorKind::WriteZero,
|
||||||
|
"Could not write full image to buffer; maybe out of memory?"
|
||||||
|
)
|
||||||
|
})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, VIE: VoxelImageEncoding> VoxelImageEncoding for &'a VIE {
|
impl<'a, VIE: VoxelImageEncoding> VoxelImageEncoding for &'a VIE {
|
||||||
type Output = VIE::Output;
|
type Output = VIE::Output;
|
||||||
|
type EncodeError = VIE::EncodeError;
|
||||||
type Workspace = VIE::Workspace;
|
type Workspace = VIE::Workspace;
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace { VIE::create(width, height) }
|
fn create(width: u32, height: u32) -> Self::Workspace { VIE::create(width, height) }
|
||||||
@ -179,11 +222,11 @@ impl<'a, VIE: VoxelImageEncoding> VoxelImageEncoding for &'a VIE {
|
|||||||
(*self).put_sprite(ws, x, y, kind, sprite, ori)
|
(*self).put_sprite(ws, x, y, kind, sprite, ori)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> { VIE::finish(ws) }
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, VIE::EncodeError> { VIE::finish(ws) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, VIE: VoxelImageDecoding> VoxelImageDecoding for &'a VIE {
|
impl<'a, VIE: VoxelImageDecoding> VoxelImageDecoding for &'a VIE {
|
||||||
fn start(ws: &Self::Output) -> Option<Self::Workspace> { VIE::start(ws) }
|
fn start(ws: &Self::Output) -> Result<Self::Workspace, DecodeError> { VIE::start(ws) }
|
||||||
|
|
||||||
fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block {
|
fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block {
|
||||||
VIE::get_block(ws, x, y, is_border)
|
VIE::get_block(ws, x, y, is_border)
|
||||||
@ -195,6 +238,7 @@ pub struct QuadPngEncoding<'a, const RESOLUTION_DIVIDER: u32>(pub PhantomData<&'
|
|||||||
|
|
||||||
impl<'a, const N: u32> VoxelImageEncoding for QuadPngEncoding<'a, N> {
|
impl<'a, const N: u32> VoxelImageEncoding for QuadPngEncoding<'a, N> {
|
||||||
type Output = CompressedData<'a, (Vec<u8>, [usize; 3])>;
|
type Output = CompressedData<'a, (Vec<u8>, [usize; 3])>;
|
||||||
|
type EncodeError = ImageError;
|
||||||
type Workspace = (
|
type Workspace = (
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
@ -232,20 +276,19 @@ impl<'a, const N: u32> VoxelImageEncoding for QuadPngEncoding<'a, N> {
|
|||||||
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let mut indices = [0; 3];
|
let mut indices = [0; 3];
|
||||||
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
|
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| -> Result<_, Self::EncodeError> {
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
CompressionType::Rle,
|
CompressionType::Rle,
|
||||||
FilterType::Up,
|
FilterType::Up,
|
||||||
);
|
);
|
||||||
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)
|
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)?;
|
||||||
.ok()?;
|
|
||||||
indices[i] = buf.len();
|
indices[i] = buf.len();
|
||||||
Some(())
|
Ok(())
|
||||||
};
|
};
|
||||||
f(&ws.0, 0)?;
|
f(&ws.0, 0)?;
|
||||||
f(&ws.1, 1)?;
|
f(&ws.1, 1)?;
|
||||||
@ -262,11 +305,10 @@ impl<'a, const N: u32> VoxelImageEncoding for QuadPngEncoding<'a, N> {
|
|||||||
ws.3.width(),
|
ws.3.width(),
|
||||||
ws.3.height(),
|
ws.3.height(),
|
||||||
image::ColorType::Rgb8,
|
image::ColorType::Rgb8,
|
||||||
)
|
)?;
|
||||||
.ok()?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(CompressedData::compress(&(buf, indices), 4))
|
Ok(CompressedData::compress(&(buf, indices), 4))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +365,7 @@ const fn gen_lanczos_lookup<const N: u32, const R: u32>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: u32> VoxelImageDecoding for QuadPngEncoding<'_, N> {
|
impl<const N: u32> VoxelImageDecoding for QuadPngEncoding<'_, N> {
|
||||||
fn start(data: &Self::Output) -> Option<Self::Workspace> {
|
fn start(data: &Self::Output) -> Result<Self::Workspace, DecodeError> {
|
||||||
use image::codecs::png::PngDecoder;
|
use image::codecs::png::PngDecoder;
|
||||||
let (quad, indices) = data.decompress()?;
|
let (quad, indices) = data.decompress()?;
|
||||||
let ranges: [_; 4] = [
|
let ranges: [_; 4] = [
|
||||||
@ -332,11 +374,11 @@ impl<const N: u32> VoxelImageDecoding for QuadPngEncoding<'_, N> {
|
|||||||
indices[1]..indices[2],
|
indices[1]..indices[2],
|
||||||
indices[2]..quad.len(),
|
indices[2]..quad.len(),
|
||||||
];
|
];
|
||||||
let a = image_from_bytes(PngDecoder::new(&quad[ranges[0].clone()]).ok()?)?;
|
let a = image_from_bytes(PngDecoder::new(&quad[ranges[0].clone()])?)?;
|
||||||
let b = image_from_bytes(PngDecoder::new(&quad[ranges[1].clone()]).ok()?)?;
|
let b = image_from_bytes(PngDecoder::new(&quad[ranges[1].clone()])?)?;
|
||||||
let c = image_from_bytes(PngDecoder::new(&quad[ranges[2].clone()]).ok()?)?;
|
let c = image_from_bytes(PngDecoder::new(&quad[ranges[2].clone()])?)?;
|
||||||
let d = image_from_bytes(PngDecoder::new(&quad[ranges[3].clone()]).ok()?)?;
|
let d = image_from_bytes(PngDecoder::new(&quad[ranges[3].clone()])?)?;
|
||||||
Some((a, b, c, d))
|
Ok((a, b, c, d))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block {
|
fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block {
|
||||||
@ -469,6 +511,7 @@ pub struct TriPngEncoding<'a, const AVERAGE_PALETTE: bool>(pub PhantomData<&'a (
|
|||||||
|
|
||||||
impl<'a, const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<'a, AVERAGE_PALETTE> {
|
impl<'a, const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<'a, AVERAGE_PALETTE> {
|
||||||
type Output = CompressedData<'a, (Vec<u8>, Vec<Rgb<u8>>, [usize; 3])>;
|
type Output = CompressedData<'a, (Vec<u8>, Vec<Rgb<u8>>, [usize; 3])>;
|
||||||
|
type EncodeError = ImageError;
|
||||||
type Workspace = (
|
type Workspace = (
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
@ -508,20 +551,19 @@ impl<'a, const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<'a,
|
|||||||
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let mut indices = [0; 3];
|
let mut indices = [0; 3];
|
||||||
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
|
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| -> Result<_, Self::EncodeError> {
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
CompressionType::Rle,
|
CompressionType::Rle,
|
||||||
FilterType::Up,
|
FilterType::Up,
|
||||||
);
|
);
|
||||||
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)
|
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)?;
|
||||||
.ok()?;
|
|
||||||
indices[i] = buf.len();
|
indices[i] = buf.len();
|
||||||
Some(())
|
Ok(())
|
||||||
};
|
};
|
||||||
f(&ws.0, 0)?;
|
f(&ws.0, 0)?;
|
||||||
f(&ws.1, 1)?;
|
f(&ws.1, 1)?;
|
||||||
@ -550,12 +592,12 @@ impl<'a, const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<'a,
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(CompressedData::compress(&(buf, palette, indices), 4))
|
Ok(CompressedData::compress(&(buf, palette, indices), 4))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<'_, AVERAGE_PALETTE> {
|
impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<'_, AVERAGE_PALETTE> {
|
||||||
fn start(data: &Self::Output) -> Option<Self::Workspace> {
|
fn start(data: &Self::Output) -> Result<Self::Workspace, DecodeError> {
|
||||||
use image::codecs::png::PngDecoder;
|
use image::codecs::png::PngDecoder;
|
||||||
let (quad, palette, indices) = data.decompress()?;
|
let (quad, palette, indices) = data.decompress()?;
|
||||||
let ranges: [_; 3] = [
|
let ranges: [_; 3] = [
|
||||||
@ -563,9 +605,9 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<'_, AVER
|
|||||||
indices[0]..indices[1],
|
indices[0]..indices[1],
|
||||||
indices[1]..indices[2],
|
indices[1]..indices[2],
|
||||||
];
|
];
|
||||||
let a = image_from_bytes(PngDecoder::new(&quad[ranges[0].clone()]).ok()?)?;
|
let a = image_from_bytes(PngDecoder::new(&quad[ranges[0].clone()])?)?;
|
||||||
let b = image_from_bytes(PngDecoder::new(&quad[ranges[1].clone()]).ok()?)?;
|
let b = image_from_bytes(PngDecoder::new(&quad[ranges[1].clone()])?)?;
|
||||||
let c = image_from_bytes(PngDecoder::new(&quad[ranges[2].clone()]).ok()?)?;
|
let c = image_from_bytes(PngDecoder::new(&quad[ranges[2].clone()])?)?;
|
||||||
let mut d: HashMap<_, HashMap<_, _>> = HashMap::new();
|
let mut d: HashMap<_, HashMap<_, _>> = HashMap::new();
|
||||||
if AVERAGE_PALETTE {
|
if AVERAGE_PALETTE {
|
||||||
for i in 0..=255 {
|
for i in 0..=255 {
|
||||||
@ -578,7 +620,7 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<'_, AVER
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((a, b, c, d))
|
Ok((a, b, c, d))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block(ws: &Self::Workspace, x: u32, y: u32, _: bool) -> Block {
|
fn get_block(ws: &Self::Workspace, x: u32, y: u32, _: bool) -> Block {
|
||||||
@ -681,7 +723,7 @@ pub fn image_terrain_chonk<S: RectVolSize, Storage: core::ops::DerefMut<Target=V
|
|||||||
vie: &VIE,
|
vie: &VIE,
|
||||||
packing: P,
|
packing: P,
|
||||||
chonk: &Chonk<Block, Storage, S, M>,
|
chonk: &Chonk<Block, Storage, S, M>,
|
||||||
) -> Option<VIE::Output> {
|
) -> Result<VIE::Output, VIE::EncodeError> {
|
||||||
image_terrain(
|
image_terrain(
|
||||||
vie,
|
vie,
|
||||||
packing,
|
packing,
|
||||||
@ -701,7 +743,7 @@ pub fn image_terrain_volgrid<
|
|||||||
vie: &VIE,
|
vie: &VIE,
|
||||||
packing: P,
|
packing: P,
|
||||||
volgrid: &VolGrid2d<Chonk<Block, Storage, S, M>>,
|
volgrid: &VolGrid2d<Chonk<Block, Storage, S, M>>,
|
||||||
) -> Option<VIE::Output> {
|
) -> Result<VIE::Output, VIE::EncodeError> {
|
||||||
let mut lo = Vec3::broadcast(i32::MAX);
|
let mut lo = Vec3::broadcast(i32::MAX);
|
||||||
let mut hi = Vec3::broadcast(i32::MIN);
|
let mut hi = Vec3::broadcast(i32::MIN);
|
||||||
for (pos, chonk) in volgrid.iter() {
|
for (pos, chonk) in volgrid.iter() {
|
||||||
@ -727,7 +769,7 @@ pub fn image_terrain<
|
|||||||
vol: &V,
|
vol: &V,
|
||||||
lo: Vec3<u32>,
|
lo: Vec3<u32>,
|
||||||
hi: Vec3<u32>,
|
hi: Vec3<u32>,
|
||||||
) -> Option<VIE::Output> {
|
) -> Result<VIE::Output, VIE::EncodeError> {
|
||||||
let dims = Vec3::new(
|
let dims = Vec3::new(
|
||||||
hi.x.wrapping_sub(lo.x),
|
hi.x.wrapping_sub(lo.x),
|
||||||
hi.y.wrapping_sub(lo.y),
|
hi.y.wrapping_sub(lo.y),
|
||||||
@ -782,7 +824,7 @@ pub fn write_image_terrain<
|
|||||||
data: &VIE::Output,
|
data: &VIE::Output,
|
||||||
lo: Vec3<u32>,
|
lo: Vec3<u32>,
|
||||||
hi: Vec3<u32>,
|
hi: Vec3<u32>,
|
||||||
) -> Option<()> {
|
) -> Result<(), DecodeError> {
|
||||||
let ws = VIE::start(data)?;
|
let ws = VIE::start(data)?;
|
||||||
let dims = Vec3::new(
|
let dims = Vec3::new(
|
||||||
hi.x.wrapping_sub(lo.x),
|
hi.x.wrapping_sub(lo.x),
|
||||||
@ -805,7 +847,7 @@ pub fn write_image_terrain<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -824,16 +866,14 @@ pub struct WireChonk<VIE: VoxelImageEncoding, P: PackingFormula, M: Clone, S: Re
|
|||||||
impl<VIE: VoxelImageEncoding + VoxelImageDecoding, P: PackingFormula, M: Clone, S: RectVolSize>
|
impl<VIE: VoxelImageEncoding + VoxelImageDecoding, P: PackingFormula, M: Clone, S: RectVolSize>
|
||||||
WireChonk<VIE, P, M, S>
|
WireChonk<VIE, P, M, S>
|
||||||
{
|
{
|
||||||
pub fn from_chonk<Storage: core::ops::DerefMut<Target=Vec<Block>>>(vie: VIE, packing: P, chonk: &Chonk<Block, Storage, S, M>) -> Option<Self> {
|
pub fn from_chonk<Storage: core::ops::DerefMut<Target=Vec<Block>>>(vie: VIE, packing: P, chonk: &Chonk<Block, Storage, S, M>) -> Result<Self, VIE::EncodeError> {
|
||||||
let data = image_terrain_chonk(&vie, packing, chonk)?;
|
let data = image_terrain_chonk(&vie, packing, chonk)?;
|
||||||
Some(Self {
|
Ok(Self {
|
||||||
zmin: chonk.get_min_z(),
|
zmin: chonk.get_min_z(),
|
||||||
zmax: chonk.get_max_z(),
|
zmax: chonk.get_max_z(),
|
||||||
data,
|
data,
|
||||||
below: *chonk
|
below: *chonk.below(),
|
||||||
.get(Vec3::new(0, 0, chonk.get_min_z().saturating_sub(1)))
|
above: *chonk.above(),
|
||||||
.ok()?,
|
|
||||||
above: *chonk.get(Vec3::new(0, 0, chonk.get_max_z() + 1)).ok()?,
|
|
||||||
meta: chonk.meta().clone(),
|
meta: chonk.meta().clone(),
|
||||||
vie,
|
vie,
|
||||||
packing,
|
packing,
|
||||||
@ -841,7 +881,7 @@ impl<VIE: VoxelImageEncoding + VoxelImageDecoding, P: PackingFormula, M: Clone,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_chonk<Storage: Clone + core::ops::DerefMut<Target=Vec<Block>> + From<Vec<Block>>>(&self) -> Option<Chonk<Block, Storage, S, M>> {
|
pub fn to_chonk<Storage: Clone + core::ops::DerefMut<Target=Vec<Block>> + From<Vec<Block>>>(&self) -> Result<Chonk<Block, Storage, S, M>, DecodeError> {
|
||||||
let mut chonk = Chonk::new(self.zmin, self.below, self.above, self.meta.clone());
|
let mut chonk = Chonk::new(self.zmin, self.below, self.above, self.meta.clone());
|
||||||
write_image_terrain(
|
write_image_terrain(
|
||||||
&self.vie,
|
&self.vie,
|
||||||
@ -851,6 +891,6 @@ impl<VIE: VoxelImageEncoding + VoxelImageDecoding, P: PackingFormula, M: Clone,
|
|||||||
Vec3::new(0, 0, self.zmin as u32),
|
Vec3::new(0, 0, self.zmin as u32),
|
||||||
Vec3::new(S::RECT_SIZE.x, S::RECT_SIZE.y, self.zmax as u32),
|
Vec3::new(S::RECT_SIZE.x, S::RECT_SIZE.y, self.zmax as u32),
|
||||||
)?;
|
)?;
|
||||||
Some(chonk)
|
Ok(chonk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ pub mod world_msg;
|
|||||||
pub use self::{
|
pub use self::{
|
||||||
client::{ClientGeneral, ClientMsg, ClientRegister, ClientType},
|
client::{ClientGeneral, ClientMsg, ClientRegister, ClientType},
|
||||||
compression::{
|
compression::{
|
||||||
CompressedData, GridLtrPacking, PackingFormula, QuadPngEncoding, TriPngEncoding,
|
CompressedData, DecodeError, GridLtrPacking, PackingFormula, QuadPngEncoding, TriPngEncoding,
|
||||||
VoxelImageEncoding, WidePacking, WireChonk,
|
VoxelImageEncoding, WidePacking, WireChonk,
|
||||||
},
|
},
|
||||||
ecs_packet::EcsCompPacket,
|
ecs_packet::EcsCompPacket,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{
|
use super::{
|
||||||
world_msg::EconomyInfo, ClientType, CompressedData, EcsCompPacket, PingMsg, QuadPngEncoding,
|
world_msg::EconomyInfo, ClientType, CompressedData, DecodeError, EcsCompPacket, PingMsg, QuadPngEncoding,
|
||||||
TriPngEncoding, WidePacking, WireChonk,
|
TriPngEncoding, WidePacking, WireChonk,
|
||||||
};
|
};
|
||||||
use crate::sync;
|
use crate::sync;
|
||||||
@ -104,24 +104,26 @@ impl SerializedTerrainChunk<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn quadpng(chunk: &TerrainChunk) -> Self {
|
pub fn quadpng(chunk: &TerrainChunk) -> Self {
|
||||||
if let Some(wc) = WireChonk::from_chonk(QuadPngEncoding(PhantomData), WidePacking(), chunk) {
|
match WireChonk::from_chonk(QuadPngEncoding(PhantomData), WidePacking(), chunk) {
|
||||||
Self::QuadPng(wc)
|
Ok(wc) => Self::QuadPng(wc),
|
||||||
} else {
|
Err(err) => {
|
||||||
warn!("Image encoding failure occurred, falling back to deflate");
|
warn!("Image encoding failure occurred, falling back to deflate: {}", err);
|
||||||
Self::deflate(chunk)
|
Self::deflate(chunk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tripng(chunk: &TerrainChunk) -> Self {
|
pub fn tripng(chunk: &TerrainChunk) -> Self {
|
||||||
if let Some(wc) = WireChonk::from_chonk(TriPngEncoding(PhantomData), WidePacking(), chunk) {
|
match WireChonk::from_chonk(TriPngEncoding(PhantomData), WidePacking(), chunk) {
|
||||||
Self::TriPng(wc)
|
Ok(wc) => Self::TriPng(wc),
|
||||||
} else {
|
Err(err) => {
|
||||||
warn!("Image encoding failure occurred, falling back to deflate");
|
warn!("Image encoding failure occurred, falling back to deflate: {}", err);
|
||||||
Self::deflate(chunk)
|
Self::deflate(chunk)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_chunk(&self) -> Option<TerrainChunk> {
|
pub fn to_chunk(&self) -> Result<TerrainChunk, DecodeError> {
|
||||||
match self {
|
match self {
|
||||||
Self::DeflatedChonk(chonk) => chonk.decompress(),
|
Self::DeflatedChonk(chonk) => chonk.decompress(),
|
||||||
Self::QuadPng(wc) => wc.to_chonk(),
|
Self::QuadPng(wc) => wc.to_chonk(),
|
||||||
|
@ -84,6 +84,10 @@ impl<V, Storage: core::ops::DerefMut<Target=Vec<V>>, S: RectVolSize, M: Clone> C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn below(&self) -> &V { &self.below }
|
||||||
|
|
||||||
|
pub fn above(&self) -> &V { &self.above }
|
||||||
|
|
||||||
pub fn meta(&self) -> &M { &self.meta }
|
pub fn meta(&self) -> &M { &self.meta }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -432,6 +432,7 @@ fn get_client_msg_error(
|
|||||||
localization.get("main.login.authentication_error"),
|
localization.get("main.login.authentication_error"),
|
||||||
e
|
e
|
||||||
),
|
),
|
||||||
|
Error::CompressionError(e) => net_error(e.to_string(), mismatched_server_info),
|
||||||
Error::Kicked(e) => e,
|
Error::Kicked(e) => e,
|
||||||
Error::TooManyPlayers => localization.get("main.login.server_full").into(),
|
Error::TooManyPlayers => localization.get("main.login.server_full").into(),
|
||||||
Error::AuthServerNotTrusted => {
|
Error::AuthServerNotTrusted => {
|
||||||
|
@ -9,13 +9,13 @@ use common::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use common_net::msg::compression::{
|
use common_net::msg::compression::{
|
||||||
image_from_bytes, image_terrain_chonk, image_terrain_volgrid, CompressedData, GridLtrPacking,
|
image_from_bytes, image_terrain_chonk, image_terrain_volgrid, CompressedData, DecodeError, GridLtrPacking,
|
||||||
PackingFormula, QuadPngEncoding, TriPngEncoding, VoxelImageDecoding, VoxelImageEncoding,
|
PackingFormula, QuadPngEncoding, TriPngEncoding, VoxelImageDecoding, VoxelImageEncoding,
|
||||||
WidePacking,
|
WidePacking,
|
||||||
};
|
};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use image::{ImageBuffer, ImageEncoder};
|
use image::{ImageBuffer, ImageEncoder, ImageError};
|
||||||
use num_traits::cast::FromPrimitive;
|
use num_traits::cast::FromPrimitive;
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -166,6 +166,7 @@ pub struct PngEncoding;
|
|||||||
|
|
||||||
impl VoxelImageEncoding for PngEncoding {
|
impl VoxelImageEncoding for PngEncoding {
|
||||||
type Output = Vec<u8>;
|
type Output = Vec<u8>;
|
||||||
|
type EncodeError = ImageError;
|
||||||
type Workspace = ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
type Workspace = ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace {
|
fn create(width: u32, height: u32) -> Self::Workspace {
|
||||||
@ -193,7 +194,7 @@ impl VoxelImageEncoding for PngEncoding {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError> {
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
@ -206,9 +207,8 @@ impl VoxelImageEncoding for PngEncoding {
|
|||||||
ws.width(),
|
ws.width(),
|
||||||
ws.height(),
|
ws.height(),
|
||||||
image::ColorType::Rgba8,
|
image::ColorType::Rgba8,
|
||||||
)
|
)?;
|
||||||
.ok()?;
|
Ok(buf)
|
||||||
Some(buf)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +217,7 @@ pub struct JpegEncoding;
|
|||||||
|
|
||||||
impl VoxelImageEncoding for JpegEncoding {
|
impl VoxelImageEncoding for JpegEncoding {
|
||||||
type Output = Vec<u8>;
|
type Output = Vec<u8>;
|
||||||
|
type EncodeError = ImageError;
|
||||||
type Workspace = ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
type Workspace = ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace {
|
fn create(width: u32, height: u32) -> Self::Workspace {
|
||||||
@ -240,11 +241,11 @@ impl VoxelImageEncoding for JpegEncoding {
|
|||||||
ws.put_pixel(x, y, image::Rgba([kind as u8, sprite as u8, 255, 255]));
|
ws.put_pixel(x, y, image::Rgba([kind as u8, sprite as u8, 255, 255]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
||||||
jpeg.encode_image(ws).ok()?;
|
jpeg.encode_image(ws)?;
|
||||||
Some(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +254,7 @@ pub struct MixedEncoding;
|
|||||||
|
|
||||||
impl VoxelImageEncoding for MixedEncoding {
|
impl VoxelImageEncoding for MixedEncoding {
|
||||||
type Output = (Vec<u8>, [usize; 3]);
|
type Output = (Vec<u8>, [usize; 3]);
|
||||||
|
type EncodeError = ImageError;
|
||||||
type Workspace = (
|
type Workspace = (
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
@ -291,33 +293,32 @@ impl VoxelImageEncoding for MixedEncoding {
|
|||||||
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let mut indices = [0; 3];
|
let mut indices = [0; 3];
|
||||||
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
|
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| -> Result<_, Self::EncodeError> {
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
CompressionType::Rle,
|
CompressionType::Rle,
|
||||||
FilterType::Up,
|
FilterType::Up,
|
||||||
);
|
);
|
||||||
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)
|
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)?;
|
||||||
.ok()?;
|
|
||||||
indices[i] = buf.len();
|
indices[i] = buf.len();
|
||||||
Some(())
|
Ok(())
|
||||||
};
|
};
|
||||||
f(&ws.0, 0)?;
|
f(&ws.0, 0)?;
|
||||||
f(&ws.1, 1)?;
|
f(&ws.1, 1)?;
|
||||||
f(&ws.2, 2)?;
|
f(&ws.2, 2)?;
|
||||||
|
|
||||||
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 10);
|
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 10);
|
||||||
jpeg.encode_image(&ws.3).ok()?;
|
jpeg.encode_image(&ws.3)?;
|
||||||
Some((buf, indices))
|
Ok((buf, indices))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VoxelImageDecoding for MixedEncoding {
|
impl VoxelImageDecoding for MixedEncoding {
|
||||||
fn start((quad, indices): &Self::Output) -> Option<Self::Workspace> {
|
fn start((quad, indices): &Self::Output) -> Result<Self::Workspace, DecodeError> {
|
||||||
use image::codecs::{jpeg::JpegDecoder, png::PngDecoder};
|
use image::codecs::{jpeg::JpegDecoder, png::PngDecoder};
|
||||||
let ranges: [_; 4] = [
|
let ranges: [_; 4] = [
|
||||||
0..indices[0],
|
0..indices[0],
|
||||||
@ -325,11 +326,11 @@ impl VoxelImageDecoding for MixedEncoding {
|
|||||||
indices[1]..indices[2],
|
indices[1]..indices[2],
|
||||||
indices[2]..quad.len(),
|
indices[2]..quad.len(),
|
||||||
];
|
];
|
||||||
let a = image_from_bytes(PngDecoder::new(&quad[ranges[0].clone()]).ok()?)?;
|
let a = image_from_bytes(PngDecoder::new(&quad[ranges[0].clone()])?)?;
|
||||||
let b = image_from_bytes(PngDecoder::new(&quad[ranges[1].clone()]).ok()?)?;
|
let b = image_from_bytes(PngDecoder::new(&quad[ranges[1].clone()])?)?;
|
||||||
let c = image_from_bytes(PngDecoder::new(&quad[ranges[2].clone()]).ok()?)?;
|
let c = image_from_bytes(PngDecoder::new(&quad[ranges[2].clone()])?)?;
|
||||||
let d = image_from_bytes(JpegDecoder::new(&quad[ranges[3].clone()]).ok()?)?;
|
let d = image_from_bytes(JpegDecoder::new(&quad[ranges[3].clone()])?)?;
|
||||||
Some((a, b, c, d))
|
Ok((a, b, c, d))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block(ws: &Self::Workspace, x: u32, y: u32, _: bool) -> Block {
|
fn get_block(ws: &Self::Workspace, x: u32, y: u32, _: bool) -> Block {
|
||||||
@ -366,6 +367,7 @@ impl VoxelImageEncoding for MixedEncodingSparseSprites {
|
|||||||
usize,
|
usize,
|
||||||
CompressedData<'static, HashMap<Vec2<u32>, (SpriteKind, u8)>>,
|
CompressedData<'static, HashMap<Vec2<u32>, (SpriteKind, u8)>>,
|
||||||
);
|
);
|
||||||
|
type EncodeError = ImageError;
|
||||||
type Workspace = (
|
type Workspace = (
|
||||||
image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
image::ImageBuffer<image::Rgb<u8>, Vec<u8>>,
|
image::ImageBuffer<image::Rgb<u8>, Vec<u8>>,
|
||||||
@ -399,7 +401,7 @@ impl VoxelImageEncoding for MixedEncodingSparseSprites {
|
|||||||
ws.2.insert(Vec2::new(x, y), (sprite, ori.unwrap_or(0)));
|
ws.2.insert(Vec2::new(x, y), (sprite, ori.unwrap_or(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
@ -412,12 +414,11 @@ impl VoxelImageEncoding for MixedEncodingSparseSprites {
|
|||||||
ws.0.width(),
|
ws.0.width(),
|
||||||
ws.0.height(),
|
ws.0.height(),
|
||||||
image::ColorType::L8,
|
image::ColorType::L8,
|
||||||
)
|
)?;
|
||||||
.ok()?;
|
|
||||||
let index = buf.len();
|
let index = buf.len();
|
||||||
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
||||||
jpeg.encode_image(&ws.1).ok()?;
|
jpeg.encode_image(&ws.1)?;
|
||||||
Some((buf, index, CompressedData::compress(&ws.2, 4)))
|
Ok((buf, index, CompressedData::compress(&ws.2, 4)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,6 +427,7 @@ pub struct MixedEncodingDenseSprites;
|
|||||||
|
|
||||||
impl VoxelImageEncoding for MixedEncodingDenseSprites {
|
impl VoxelImageEncoding for MixedEncodingDenseSprites {
|
||||||
type Output = (Vec<u8>, [usize; 3]);
|
type Output = (Vec<u8>, [usize; 3]);
|
||||||
|
type EncodeError = ImageError;
|
||||||
type Workspace = (
|
type Workspace = (
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
Vec<u8>,
|
Vec<u8>,
|
||||||
@ -462,20 +464,19 @@ impl VoxelImageEncoding for MixedEncodingDenseSprites {
|
|||||||
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let mut indices = [0; 3];
|
let mut indices = [0; 3];
|
||||||
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
|
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| -> Result<_, Self::EncodeError> {
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
CompressionType::Fast,
|
CompressionType::Fast,
|
||||||
FilterType::Up,
|
FilterType::Up,
|
||||||
);
|
);
|
||||||
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)
|
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)?;
|
||||||
.ok()?;
|
|
||||||
indices[i] = buf.len();
|
indices[i] = buf.len();
|
||||||
Some(())
|
Ok(())
|
||||||
};
|
};
|
||||||
f(&ws.0, 0)?;
|
f(&ws.0, 0)?;
|
||||||
let mut g = |x: &[u8], i| {
|
let mut g = |x: &[u8], i| {
|
||||||
@ -487,8 +488,8 @@ impl VoxelImageEncoding for MixedEncodingDenseSprites {
|
|||||||
g(&ws.2, 2);
|
g(&ws.2, 2);
|
||||||
|
|
||||||
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
||||||
jpeg.encode_image(&ws.3).ok()?;
|
jpeg.encode_image(&ws.3)?;
|
||||||
Some((buf, indices))
|
Ok((buf, indices))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,6 +593,7 @@ pub struct PaletteEncoding<'a, NN: NearestNeighbor, const N: u32>(&'a HashMap<Bl
|
|||||||
|
|
||||||
impl<'a, NN: NearestNeighbor, const N: u32> VoxelImageEncoding for PaletteEncoding<'a, NN, N> {
|
impl<'a, NN: NearestNeighbor, const N: u32> VoxelImageEncoding for PaletteEncoding<'a, NN, N> {
|
||||||
type Output = CompressedData<'a, (Vec<u8>, [usize; 4])>;
|
type Output = CompressedData<'a, (Vec<u8>, [usize; 4])>;
|
||||||
|
type EncodeError = ImageError;
|
||||||
type Workspace = (
|
type Workspace = (
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
@ -628,27 +630,26 @@ impl<'a, NN: NearestNeighbor, const N: u32> VoxelImageEncoding for PaletteEncodi
|
|||||||
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
fn finish(ws: &Self::Workspace) -> Result<Self::Output, Self::EncodeError> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let mut indices = [0; 4];
|
let mut indices = [0; 4];
|
||||||
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
|
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| -> Result<_, Self::EncodeError> {
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
CompressionType::Rle,
|
CompressionType::Rle,
|
||||||
FilterType::Up,
|
FilterType::Up,
|
||||||
);
|
);
|
||||||
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)
|
png.write_image(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)?;
|
||||||
.ok()?;
|
|
||||||
indices[i] = buf.len();
|
indices[i] = buf.len();
|
||||||
Some(())
|
Ok(())
|
||||||
};
|
};
|
||||||
f(&ws.0, 0)?;
|
f(&ws.0, 0)?;
|
||||||
f(&ws.1, 1)?;
|
f(&ws.1, 1)?;
|
||||||
f(&ws.2, 2)?;
|
f(&ws.2, 2)?;
|
||||||
f(&ws.3, 3)?;
|
f(&ws.3, 3)?;
|
||||||
|
|
||||||
Some(CompressedData::compress(&(buf, indices), 1))
|
Ok(CompressedData::compress(&(buf, indices), 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1347,7 +1347,7 @@ impl SiteKind {
|
|||||||
},
|
},
|
||||||
SiteKind::Citadel => (-0.3..0.7).contains(&chunk.temp) && chunk.tree_density < 0.4,
|
SiteKind::Citadel => (-0.3..0.7).contains(&chunk.temp) && chunk.tree_density < 0.4,
|
||||||
SiteKind::GiantTree/* | SiteKind::Tree*/ => {
|
SiteKind::GiantTree/* | SiteKind::Tree*/ => {
|
||||||
chunk.tree_density > 0.4 && (-0.3..0.4).contains(&chunk.temp)
|
chunk.tree_density > 0.4/* && (-0.3..0.4).contains(&chunk.temp) */
|
||||||
},
|
},
|
||||||
SiteKind::CliffTown => {
|
SiteKind::CliffTown => {
|
||||||
(-0.6..0.4).contains(&chunk.temp)
|
(-0.6..0.4).contains(&chunk.temp)
|
||||||
|
Loading…
Reference in New Issue
Block a user