From ad5bcf3cd8d9ad917afb34587a8f6efbd6f35f35 Mon Sep 17 00:00:00 2001 From: Joshua Yanovski Date: Wed, 29 Jun 2022 21:52:04 -0700 Subject: [PATCH] Turning "full" chunks back on, deserialization improvements. --- Cargo.lock | 47 ++++- client/Cargo.toml | 1 + client/src/error.rs | 4 + client/src/lib.rs | 20 +- common/Cargo.toml | 1 + common/net/Cargo.toml | 1 + common/net/src/msg/compression.rs | 33 +-- common/net/src/msg/server.rs | 55 ++--- common/net/src/msg/world_msg.rs | 3 + common/src/lib.rs | 2 + common/src/terrain/block.rs | 330 +++++++++++++++++++++++++++--- common/src/terrain/chonk.rs | 6 +- common/src/terrain/mod.rs | 1 + common/src/volumes/chunk.rs | 7 + network/src/api.rs | 40 ++-- network/src/message.rs | 26 ++- server/src/client.rs | 6 +- server/src/lib.rs | 4 +- server/src/state_ext.rs | 8 +- world/benches/site2.rs | 6 +- world/src/lib.rs | 4 +- world/src/site2/mod.rs | 1 + 22 files changed, 482 insertions(+), 124 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ada5d4086..d6378f84af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1403,12 +1403,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e92cb285610dd935f60ee8b4d62dd1988bd12b7ea50579bd6a138201525318e" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core 0.13.2", - "darling_macro 0.13.2", + "darling_core 0.13.4", + "darling_macro 0.13.4", ] [[package]] @@ -1427,9 +1427,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c29e95ab498b18131ea460b2c0baa18cbf041231d122b0b7bfebef8c8e88989" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", @@ -1452,11 +1452,11 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b21dd6b221dd547528bd6fb15f1a3b7ab03b9a06f76bff288a8c629bcfbe7f0e" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core 0.13.2", + "darling_core 0.13.4", "quote 1.0.17", "syn 1.0.90", ] @@ -1683,7 +1683,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c594871f94ab3a00434cb09f03067c92fa2ece4cc657d58ba402e8377cd85a3" dependencies = [ - "darling 0.13.2", + "darling 0.13.4", "proc-macro-crate 1.1.3", "proc-macro2 1.0.36", "quote 1.0.17", @@ -3584,7 +3584,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ - "darling 0.13.2", + "darling 0.13.4", "proc-macro-crate 1.1.3", "proc-macro2 1.0.36", "quote 1.0.17", @@ -5454,6 +5454,28 @@ dependencies = [ "syn 1.0.90", ] +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling 0.13.4", + "proc-macro2 1.0.36", + "quote 1.0.17", + "syn 1.0.90", +] + [[package]] name = "sha1" version = "0.6.1" @@ -6521,6 +6543,7 @@ version = "0.12.0" dependencies = [ "async-channel", "authc", + "bincode", "byteorder", "clap 3.1.8", "hashbrown 0.11.2", @@ -6580,6 +6603,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", + "serde_with", "sha2", "slab", "slotmap 1.0.6", @@ -6654,6 +6678,7 @@ dependencies = [ "image", "num-traits", "serde", + "serde_with", "specs", "specs-idvs", "sum_type", diff --git a/client/Cargo.toml b/client/Cargo.toml index 95de43833b..ae8a90d881 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -21,6 +21,7 @@ common-systems = { package = "veloren-common-systems", path = "../common/systems common-net = { package = "veloren-common-net", path = "../common/net" } network = { package = "veloren-network", path = "../network", features = ["compression","quic"], default-features = false } +bincode = "1.3.2" byteorder = "1.3.2" tokio = { version = "1.14", default-features = false, features = ["rt-multi-thread"] } quinn = "0.8" diff --git a/client/src/error.rs b/client/src/error.rs index adceaddf04..0c66766c86 100644 --- a/client/src/error.rs +++ b/client/src/error.rs @@ -42,6 +42,10 @@ impl From for Error { fn from(err: StreamError) -> Self { Self::StreamErr(err) } } +impl From for Error { + fn from(err: bincode::Error) -> Self { Self::StreamErr(StreamError::Deserialize(err)) } +} + impl From for Error { fn from(err: AuthClientError) -> Self { Self::AuthClientError(err) } } diff --git a/client/src/lib.rs b/client/src/lib.rs index 4770a92795..4e26a722f4 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -2300,28 +2300,30 @@ impl Client { loop { let cnt_start = cnt; - while let Some(msg) = self.general_stream.try_recv()? { + while let Some(msg) = self.general_stream.try_recv_raw()? { cnt += 1; - self.handle_server_msg(frontend_events, msg)?; + self.handle_server_msg(frontend_events, bincode::deserialize(&msg.decompress()?)?)?; } - while let Some(msg) = self.ping_stream.try_recv()? { + while let Some(msg) = self.ping_stream.try_recv_raw()? { cnt += 1; - self.handle_ping_msg(msg)?; + self.handle_ping_msg(bincode::deserialize(&msg.decompress()?)?)?; } - while let Some(msg) = self.character_screen_stream.try_recv()? { + while let Some(msg) = self.character_screen_stream.try_recv_raw()? { cnt += 1; - self.handle_server_character_screen_msg(frontend_events, msg)?; + self.handle_server_character_screen_msg(frontend_events, bincode::deserialize(&msg.decompress()?)?)?; } - while let Some(msg) = self.in_game_stream.try_recv()? { + while let Some(msg) = self.in_game_stream.try_recv_raw()? { cnt += 1; #[cfg(feature = "tracy")] { ingame_cnt += 1; } - self.handle_server_in_game_msg(frontend_events, msg)?; + self.handle_server_in_game_msg(frontend_events, bincode::deserialize(&msg.decompress()?)?)?; } - while let Some(msg) = self.terrain_stream.try_recv()? { + while let Some(msg) = self.terrain_stream.try_recv_raw()? { cnt += 1; + let msg = msg.decompress()?; + let msg = bincode::deserialize(&msg)?; #[cfg(feature = "tracy")] { if let ServerGeneral::TerrainChunkUpdate { chunk, .. } = &msg { diff --git a/common/Cargo.toml b/common/Cargo.toml index a01f413cb0..6ac576322f 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -32,6 +32,7 @@ chrono = "0.4" chrono-tz = "0.6" sha2 = "0.10" serde_json = "1.0.50" +serde_with = { version = "1.14.0" } # Strum strum = { version = "0.24", features = ["derive"] } diff --git a/common/net/Cargo.toml b/common/net/Cargo.toml index 012bc0622a..e4af6a84e1 100644 --- a/common/net/Cargo.toml +++ b/common/net/Cargo.toml @@ -30,3 +30,4 @@ specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abc # Serde serde = { version = "1.0.110", features = ["derive"] } +serde_with = { version = "1.14.0" } diff --git a/common/net/src/msg/compression.rs b/common/net/src/msg/compression.rs index 889029d3a0..4dd8b30477 100644 --- a/common/net/src/msg/compression.rs +++ b/common/net/src/msg/compression.rs @@ -8,23 +8,28 @@ use image::{ImageBuffer, ImageDecoder, Pixel}; use num_traits::cast::FromPrimitive; use serde::{Deserialize, Serialize}; use std::{ + borrow::Cow, fmt::Debug, io::{Read, Write}, marker::PhantomData, }; +use serde_with::{serde_as, Bytes}; use tracing::warn; use vek::*; /// Wrapper for compressed, serialized data (for stuff that doesn't use the /// default lz4 compression) +#[serde_as] #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CompressedData { - pub data: Vec, +pub struct CompressedData<'a, T> { + #[serde_as(as = "Bytes")] + #[serde(borrow)] + pub data: Cow<'a, [u8]>, compressed: bool, _phantom: PhantomData, } -impl CompressedData { +impl<'a, T: Serialize> CompressedData<'a, T> { pub fn compress(t: &T, level: u32) -> Self { use flate2::{write::DeflateEncoder, Compression}; let uncompressed = bincode::serialize(t) @@ -39,13 +44,13 @@ impl CompressedData { encoder.write_all(&*uncompressed).expect(EXPECT_MSG); let compressed = encoder.finish().expect(EXPECT_MSG); CompressedData { - data: compressed, + data: Cow::Owned(compressed), compressed: true, _phantom: PhantomData, } } else { CompressedData { - data: uncompressed, + data: Cow::Owned(uncompressed), compressed: false, _phantom: PhantomData, } @@ -53,7 +58,7 @@ impl CompressedData { } } -impl Deserialize<'a>> CompressedData { +impl Deserialize<'a>> CompressedData<'_, T> { pub fn decompress(&self) -> Option { if self.compressed { let mut uncompressed = Vec::with_capacity(self.data.len()); @@ -186,10 +191,10 @@ impl<'a, VIE: VoxelImageDecoding> VoxelImageDecoding for &'a VIE { } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct QuadPngEncoding(); +pub struct QuadPngEncoding<'a, const RESOLUTION_DIVIDER: u32>(pub PhantomData<&'a ()>); -impl VoxelImageEncoding for QuadPngEncoding { - type Output = CompressedData<(Vec, [usize; 3])>; +impl<'a, const N: u32> VoxelImageEncoding for QuadPngEncoding<'a, N> { + type Output = CompressedData<'a, (Vec, [usize; 3])>; type Workspace = ( ImageBuffer, Vec>, ImageBuffer, Vec>, @@ -317,7 +322,7 @@ const fn gen_lanczos_lookup( array } -impl VoxelImageDecoding for QuadPngEncoding { +impl VoxelImageDecoding for QuadPngEncoding<'_, N> { fn start(data: &Self::Output) -> Option { use image::codecs::png::PngDecoder; let (quad, indices) = data.decompress()?; @@ -460,10 +465,10 @@ impl VoxelImageDecoding for QuadPngEncoding { } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct TriPngEncoding(); +pub struct TriPngEncoding<'a, const AVERAGE_PALETTE: bool>(pub PhantomData<&'a ()>); -impl VoxelImageEncoding for TriPngEncoding { - type Output = CompressedData<(Vec, Vec>, [usize; 3])>; +impl<'a, const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<'a, AVERAGE_PALETTE> { + type Output = CompressedData<'a, (Vec, Vec>, [usize; 3])>; type Workspace = ( ImageBuffer, Vec>, ImageBuffer, Vec>, @@ -549,7 +554,7 @@ impl VoxelImageEncoding for TriPngEncoding VoxelImageDecoding for TriPngEncoding { +impl VoxelImageDecoding for TriPngEncoding<'_, AVERAGE_PALETTE> { fn start(data: &Self::Output) -> Option { use image::codecs::png::PngDecoder; let (quad, palette, indices) = data.decompress()?; diff --git a/common/net/src/msg/server.rs b/common/net/src/msg/server.rs index 2223811301..689b4b93b7 100644 --- a/common/net/src/msg/server.rs +++ b/common/net/src/msg/server.rs @@ -15,6 +15,7 @@ use common::{ trade::{PendingTrade, SitePrices, TradeId, TradeResult}, uid::Uid, }; +use core::marker::PhantomData; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -24,7 +25,7 @@ use vek::*; ///This struct contains all messages the server might send (on different /// streams though) #[derive(Debug, Clone)] -pub enum ServerMsg { +pub enum ServerMsg<'a> { /// Basic info about server, send ONCE, clients need it to Register Info(ServerInfo), /// Initial data package, send BEFORE Register ONCE. Not Register relevant @@ -32,7 +33,7 @@ pub enum ServerMsg { /// Result to `ClientMsg::Register`. send ONCE RegisterAnswer(ServerRegisterAnswer), ///Msg that can be send ALWAYS as soon as client is registered, e.g. `Chat` - General(ServerGeneral), + General(ServerGeneral<'a>), Ping(PingMsg), } @@ -70,13 +71,16 @@ pub enum ServerInit { pub type ServerRegisterAnswer = Result<(), RegisterError>; #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum SerializedTerrainChunk { - DeflatedChonk(CompressedData), - QuadPng(WireChonk, WidePacking, TerrainChunkMeta, TerrainChunkSize>), - TriPng(WireChonk, WidePacking, TerrainChunkMeta, TerrainChunkSize>), +pub enum SerializedTerrainChunk<'a> { + #[serde(borrow)] + DeflatedChonk(CompressedData<'a, TerrainChunk>), + #[serde(borrow)] + QuadPng(WireChonk, WidePacking, TerrainChunkMeta, TerrainChunkSize>), + #[serde(borrow)] + TriPng(WireChonk, WidePacking, TerrainChunkMeta, TerrainChunkSize>), } -impl SerializedTerrainChunk { +impl SerializedTerrainChunk<'_> { pub fn approx_len(&self) -> usize { match self { SerializedTerrainChunk::DeflatedChonk(data) => data.data.len(), @@ -98,7 +102,7 @@ impl SerializedTerrainChunk { } pub fn quadpng(chunk: &TerrainChunk) -> Self { - if let Some(wc) = WireChonk::from_chonk(QuadPngEncoding(), WidePacking(), chunk) { + if let Some(wc) = WireChonk::from_chonk(QuadPngEncoding(PhantomData), WidePacking(), chunk) { Self::QuadPng(wc) } else { warn!("Image encoding failure occurred, falling back to deflate"); @@ -107,7 +111,7 @@ impl SerializedTerrainChunk { } pub fn tripng(chunk: &TerrainChunk) -> Self { - if let Some(wc) = WireChonk::from_chonk(TriPngEncoding(), WidePacking(), chunk) { + if let Some(wc) = WireChonk::from_chonk(TriPngEncoding(PhantomData), WidePacking(), chunk) { Self::TriPng(wc) } else { warn!("Image encoding failure occurred, falling back to deflate"); @@ -126,7 +130,7 @@ impl SerializedTerrainChunk { /// Messages sent from the server to the client #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum ServerGeneral { +pub enum ServerGeneral<'a> { //Character Screen related /// An error occurred while loading character data CharacterDataLoadError(String), @@ -169,13 +173,14 @@ pub enum ServerGeneral { // Ingame related AND terrain stream TerrainChunkUpdate { key: Vec2, - chunk: Result, + chunk: Result, ()>, }, LodZoneUpdate { key: Vec2, zone: lod::Zone, }, - TerrainBlockUpdates(CompressedData, Block>>), + #[serde(borrow)] + TerrainBlockUpdates(CompressedData<'a, HashMap, Block>>), // Always possible PlayerListUpdate(PlayerListUpdate), /// A message to go into the client chat box. The client is responsible for @@ -198,7 +203,7 @@ pub enum ServerGeneral { MapMarker(comp::MapMarkerUpdate), } -impl ServerGeneral { +impl ServerGeneral<'_> { pub fn server_msg(chat_type: comp::ChatType, msg: S) -> Self where S: Into, @@ -266,7 +271,7 @@ pub enum RegisterError { //TODO: InvalidAlias, } -impl ServerMsg { +impl ServerMsg<'_> { pub fn verify( &self, c_type: ClientType, @@ -329,26 +334,26 @@ impl ServerMsg { } } -impl From for ServerGeneral { +impl From for ServerGeneral<'_> { fn from(v: comp::ChatMsg) -> Self { ServerGeneral::ChatMsg(v) } } -impl From for ServerMsg { - fn from(o: ServerInfo) -> ServerMsg { ServerMsg::Info(o) } +impl From for ServerMsg<'_> { + fn from(o: ServerInfo) -> Self { ServerMsg::Info(o) } } -impl From for ServerMsg { - fn from(o: ServerInit) -> ServerMsg { ServerMsg::Init(Box::new(o)) } +impl From for ServerMsg<'_> { + fn from(o: ServerInit) -> Self { ServerMsg::Init(Box::new(o)) } } -impl From for ServerMsg { - fn from(o: ServerRegisterAnswer) -> ServerMsg { ServerMsg::RegisterAnswer(o) } +impl From for ServerMsg<'_> { + fn from(o: ServerRegisterAnswer) -> Self { ServerMsg::RegisterAnswer(o) } } -impl From for ServerMsg { - fn from(o: ServerGeneral) -> ServerMsg { ServerMsg::General(o) } +impl<'a> From> for ServerMsg<'a> { + fn from(o: ServerGeneral<'a>) -> Self { ServerMsg::General(o) } } -impl From for ServerMsg { - fn from(o: PingMsg) -> ServerMsg { ServerMsg::Ping(o) } +impl From for ServerMsg<'_> { + fn from(o: PingMsg) -> Self { ServerMsg::Ping(o) } } diff --git a/common/net/src/msg/world_msg.rs b/common/net/src/msg/world_msg.rs index e92740b9fc..b9369b368f 100644 --- a/common/net/src/msg/world_msg.rs +++ b/common/net/src/msg/world_msg.rs @@ -1,8 +1,10 @@ use common::{grid::Grid, trade::Good}; use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, Bytes}; use std::collections::HashMap; use vek::*; +#[serde_as] #[derive(Debug, Clone, Serialize, Deserialize)] /// World map information. Note that currently, we always send the whole thing /// in one go, but the structure aims to try to provide information as locally @@ -121,6 +123,7 @@ pub struct WorldMapMsg { /// more predictible sequence) would end up compressing better than storing /// angles, or that we don't need as much precision as we currently have /// (256 possible angles). + #[serde_as(as = "[(Bytes, Bytes); 2]")] pub horizons: [(Vec, Vec); 2], pub sites: Vec, pub pois: Vec, diff --git a/common/src/lib.rs b/common/src/lib.rs index dc8e6ce88d..c022a4bb7e 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -13,6 +13,8 @@ generic_arg_infer, label_break_value, option_zip, + portable_simd, + slice_as_chunks, trait_alias, type_alias_impl_trait, extend_one, diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index c821f0dd0d..7e9319b917 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -7,7 +7,8 @@ use crate::{ }; use num_derive::FromPrimitive; use num_traits::FromPrimitive; -use serde::{ser, Deserialize, Serialize}; +use serde::{de::{self, Error}, ser, Deserialize, Serialize}; +use serde_with::{serde_as, Bytes, DeserializeAs}; use std::ops::Deref; use strum::{Display, EnumIter, EnumString}; use vek::*; @@ -123,11 +124,13 @@ impl BlockKind { /// the implementation of BlockVec! BlockVec uses unsafe code that depends on being able to /// independently validate the kind and treat attr as bytes; changing things so that this no longer /// works will require careful review. +#[serde_as] #[derive(AsBytes, Copy, Clone, Debug, Eq, Serialize, Deserialize)] /// NOTE: repr(C) appears to preserve niche optimizations! #[repr(align(4), C)] pub struct Block { kind: BlockKind, + #[serde_as(as = "Bytes")] attr: [u8; 3], } @@ -449,13 +452,15 @@ impl Block { #[derive( Clone, Debug, - Deserialize, Hash, Eq, PartialEq, )] -#[serde(try_from = "&'_ [u8]")] -pub struct BlockVec(Vec); +/* #[serde(try_from = "&'_ [u8]")] */ +pub struct BlockVec( + /* #[serde_as(as = "Bytes")] */ + Vec +); impl core::ops::Deref for BlockVec { type Target = Vec; @@ -491,9 +496,184 @@ impl Serialize for BlockVec { } } -impl<'a/*, Error: de::Error*/> TryFrom<&'a [u8]> for BlockVec { - type Error = &'static str; - /// XXX(@Sharp): This implementation is subtle and its safety depens on correct implementation! +impl<'a> Deserialize<'a> for BlockVec { + /// XXX(@Sharp): This implementation is subtle and safety depends on correct implementation! + /// It is well-commented, but those comments are only valid so long as this implementation + /// doesn't change. If you do need to change this implementation, please seek careful review! + /// + /// NOTE: Ideally, we would perform a try_from(Vec) instead, to avoid the extra copy. + /// Unfortunately this is not generally sound, since Vec allocations must be deallocated with + /// the same layout with which they were allocated, which includes alignment (and no, it does + /// not matter if they in practice have the same alignment at runtime, it's still UB). If we + /// were to do this, we'd effectively have to hold a Vec inside BlockVec at all times, not + /// exposing &mut access at all, and instead requiring transmutes to get access to Blocks. + /// This seems like a huge pain so for now, hopefully deserialize (the non-owned version) is + /// sufficient. + #[allow(unsafe_code)] + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'a>, + { + let blocks = >::deserialize_as::(deserializer)?; + + // First, make sure we're correctly interpretable as a [[u8; 4]]. + // + let blocks: &[[u8; 4]] = bytemuck::try_cast_slice(blocks) + .map_err(|_| D::Error::invalid_length(blocks.len(), &"a multiple of 4")/*"Length must be a multiple of 4"*/)?; + // The basic observation here is that a slice of [u8; 4] is *almost* the same as a slice of + // blocks, so conversion from the former to the latter can be very cheap. The only problem + // is that BlockKind (the first byte in `Block`) has some invalid states, so not every u8 + // slice of the appropriate size is a block slice. Fortunately, since we don't care about + // figuring out which block triggered the error, we can figure this out really cheaply--we + // just have to set a bit for every block we see, then check at the end to make sure all + // the bits we set are valid elements. We can construct the valid bit set using EnumIter, + // and the requirement is: (!valid & set_bits) = 0. + + // Construct the invalid list. Initially, it's all 1s, then we set all the bits + // corresponding to valid block kinds to 0, leaving a set bit for each invalid block kind. + // + // TODO: Verify whether this gets constant folded away; if not, try to do this as a const + // fn? Might need to modify the EnumIter implementation. + let mut invalid_bits = [true; 256]; + ::iter().for_each(|bk| { + invalid_bits[(bk as u8) as usize] = false; + }); + + // Blocks per chunk. Currently 16, since blocks are 4 bytes and 16 * 4 = 64 bytes is the + // size of a cacheline on most architectures. + const BLOCKS_PER_CHUNK: usize = 8; + + /* // Initially, the set bit list is empty. + let mut set_bits: std::simd::Mask = std::simd::Mask::splat(false); + + // NOTE: We iterate in chunks of BLOCKS_PER_CHUNK blocks. This also lets us + // independently fetch BLOCKS_PER_CHUNK entries from the lookup table at once, setting + // BLOCKS_PER_CHUNK independent lanes (reducing CPU level contention on the register) + // instead of updating a single register in each iteration (which causes contention). + let (chunks, remainder) = blocks.as_chunks::(); + chunks.iter().for_each(|array| { + // Look up each of the block kind in the chunk, setting each lane of the + // BLOCKS_PER_CHUNK-word mask to true if the block kind is invalid. + // + // NOTE: The block kind is guaranteed to be at the front of each 4-byte word, + // thanks to the repr(C). + // + // NOTE: Bounds checks here appears to be either elided, or perfectly predicted, so we + // fortunately avoid using unsafe here. + // + // FIXME: simd table lookup, and simd packing. + let block_kinds = std::simd::Mask::from_array( + [ + invalid_bits[array[0x0][0] as u8 as usize], + invalid_bits[array[0x1][0] as u8 as usize], + invalid_bits[array[0x2][0] as u8 as usize], + invalid_bits[array[0x3][0] as u8 as usize], + invalid_bits[array[0x4][0] as u8 as usize], + invalid_bits[array[0x5][0] as u8 as usize], + invalid_bits[array[0x6][0] as u8 as usize], + invalid_bits[array[0x7][0] as u8 as usize], + /* invalid_bits[array[0x8][0] as u8 as usize], + invalid_bits[array[0x9][0] as u8 as usize], + invalid_bits[array[0xA][0] as u8 as usize], + invalid_bits[array[0xB][0] as u8 as usize], + invalid_bits[array[0xC][0] as u8 as usize], + invalid_bits[array[0xD][0] as u8 as usize], + invalid_bits[array[0xE][0] as u8 as usize], + invalid_bits[array[0xF][0] as u8 as usize], */ + ] + ); + // Now, lanewise OR whether the block kind in this iteration was invalid, with whether + // the same block kind was invalid in this lane the previous iteration. + set_bits |= block_kinds; + }); + // Now, we OR all the lanes together, to find out whether *any* of the lanes witnessed an + // invalid block kind. + let mut set_bits = set_bits.any(); */ + + let mut set_bits0 = false; + let mut set_bits1 = false; + let mut set_bits2 = false; + let mut set_bits3 = false; + let mut set_bits4 = false; + let mut set_bits5 = false; + let mut set_bits6 = false; + let mut set_bits7 = false; + let mut set_bits8 = false; + + /* // Initially, the set bit list is empty. + let mut set_bits: std::simd::Mask = std::simd::Mask::splat(false); */ + + // NOTE: We iterate in chunks of BLOCKS_PER_CHUNK blocks. This also lets us + // independently fetch BLOCKS_PER_CHUNK entries from the lookup table at once, setting + // BLOCKS_PER_CHUNK independent lanes (reducing CPU level contention on the register) + // instead of updating a single register in each iteration (which causes contention). + let (chunks, remainder) = blocks.as_chunks::(); + chunks.iter().for_each(|array| { + // Look up each of the block kind in the chunk, setting each lane of the + // BLOCKS_PER_CHUNK-word mask to true if the block kind is invalid. + // + // NOTE: The block kind is guaranteed to be at the front of each 4-byte word, + // thanks to the repr(C). + // + // NOTE: Bounds checks here appears to be either elided, or perfectly predicted, so we + // fortunately avoid using unsafe here. + // + // FIXME: simd table lookup, and simd packing. + /* let block_kinds = std::simd::Mask::from_array( + [ */ + set_bits0 |= invalid_bits[array[0x0][0] as u8 as usize]; + set_bits1 |= invalid_bits[array[0x1][0] as u8 as usize]; + set_bits2 |= invalid_bits[array[0x2][0] as u8 as usize]; + set_bits3 |= invalid_bits[array[0x3][0] as u8 as usize]; + set_bits4 |= invalid_bits[array[0x4][0] as u8 as usize]; + set_bits5 |= invalid_bits[array[0x5][0] as u8 as usize]; + set_bits6 |= invalid_bits[array[0x6][0] as u8 as usize]; + set_bits7 |= invalid_bits[array[0x7][0] as u8 as usize]; + /* invalid_bits[array[0x8][0] as u8 as usize], + invalid_bits[array[0x9][0] as u8 as usize], + invalid_bits[array[0xA][0] as u8 as usize], + invalid_bits[array[0xB][0] as u8 as usize], + invalid_bits[array[0xC][0] as u8 as usize], + invalid_bits[array[0xD][0] as u8 as usize], + invalid_bits[array[0xE][0] as u8 as usize], + invalid_bits[array[0xF][0] as u8 as usize], */ + /* ] + )*/ + // Now, lanewise OR whether the block kind in this iteration was invalid, with whether + // the same block kind was invalid in this lane the previous iteration. + // set_bits |= block_kinds; + }); + // Now, we OR all the lanes together, to find out whether *any* of the lanes witnessed an + // invalid block kind. + let mut set_bits = /*set_bits.any()*/set_bits0 | set_bits1 | set_bits2 | set_bits3 | set_bits4 | set_bits5 | set_bits6 | set_bits7; + + // Now handle the remainder (if this wasn't a precise fit for BLOCKS_PER_CHUNK blocks; + // this will never be the case for valid terrain chunks). + remainder.iter().for_each(|block| { + set_bits |= invalid_bits[block[0] as u8 as usize]; + }); + + // The invalid bits and the set bits should have no overlap. + if set_bits { + // At least one invalid bit was set, so there was an invalid BlockKind somewhere. + // + // TODO: Use radix representation of the bad block kind. + return Err(D::Error::unknown_variant("an invalid u8", &["see the definition of BlockKind for details"])/*"Found an unknown BlockKind while parsing Vec"*/); + } + // All set bits are cleared, so all block kinds were valid. Combined with the slice being + // compatible with [u8; 4], we can transmute the slice to a slice of Blocks and then + // construct a new vector from it. + let blocks = unsafe { core::mem::transmute::<&'a [[u8; 4]], &'a [Block]>(blocks) }; + // Finally, *safely* construct a vector from the new blocks (as mentioned above, we cannot + // reuse the old byte vector even if we wanted to, since it doesn't have the same + // alignment as Block). + Ok(Self(blocks.to_vec()/*Vec::new()*/)) + } +} + +/* impl<'a/*, Error: de::Error*/> TryFrom<&'a [u8]> for BlockVec { + /* type Error = /*&'static str*/serde::de::Error; + /// XXX(@Sharp): This implementation is subtle and safety depends on correct implementation! /// It is well-commented, but those comments are only valid so long as this implementation /// doesn't change. If you do need to change this implementation, please seek careful review! /// @@ -507,8 +687,9 @@ impl<'a/*, Error: de::Error*/> TryFrom<&'a [u8]> for BlockVec { /// sufficient. #[allow(unsafe_code)] fn try_from(blocks: &'a [u8]) -> Result - { - // First, make sure we're correctly interpretable as a [u8; 4]. + { */ + /* // First, make sure we're correctly interpretable as a [[u8; 4]]. + // let blocks: &[[u8; 4]] = bytemuck::try_cast_slice(blocks) .map_err(|_| /*Error::invalid_length(blocks.len(), &"a multiple of 4")*/"Length must be a multiple of 4")?; // The basic observation here is that a slice of [u8; 4] is *almost* the same as a slice of @@ -525,26 +706,127 @@ impl<'a/*, Error: de::Error*/> TryFrom<&'a [u8]> for BlockVec { // // TODO: Verify whether this gets constant folded away; if not, try to do this as a const // fn? Might need to modify the EnumIter implementation. - let mut invalid_bits = bitarr![1; 256]; + let mut invalid_bits = [true; 256]; ::iter().for_each(|bk| { - invalid_bits.set((bk as u8).into(), false); + invalid_bits[(bk as u8) as usize] = false; }); - // Initially, the set bit list is empty. - let mut set_bits = /*bitarr!*/[false; 256]; + // Blocks per chunk. Currently 16, since blocks are 4 bytes and 16 * 4 = 64 bytes is the + // size of a cacheline on most architectures. + const BLOCKS_PER_CHUNK: usize = 8; - // TODO: SIMD iteration. - // NOTE: The block kind is guaranteed to be at the front, thanks to the repr(C). - blocks.into_iter().for_each(|&[kind, _, _, _]| { - // NOTE: Bounds check here appears to be either elided, or perfectly predicted, so we + /* // Initially, the set bit list is empty. + let mut set_bits: std::simd::Mask = std::simd::Mask::splat(false); + + // NOTE: We iterate in chunks of BLOCKS_PER_CHUNK blocks. This also lets us + // independently fetch BLOCKS_PER_CHUNK entries from the lookup table at once, setting + // BLOCKS_PER_CHUNK independent lanes (reducing CPU level contention on the register) + // instead of updating a single register in each iteration (which causes contention). + let (chunks, remainder) = blocks.as_chunks::(); + chunks.iter().for_each(|array| { + // Look up each of the block kind in the chunk, setting each lane of the + // BLOCKS_PER_CHUNK-word mask to true if the block kind is invalid. + // + // NOTE: The block kind is guaranteed to be at the front of each 4-byte word, + // thanks to the repr(C). + // + // NOTE: Bounds checks here appears to be either elided, or perfectly predicted, so we // fortunately avoid using unsafe here. - /* set_bits.set(kind.into(), true); */ - set_bits[kind as usize] = true; + // + // FIXME: simd table lookup, and simd packing. + let block_kinds = std::simd::Mask::from_array( + [ + invalid_bits[array[0x0][0] as u8 as usize], + invalid_bits[array[0x1][0] as u8 as usize], + invalid_bits[array[0x2][0] as u8 as usize], + invalid_bits[array[0x3][0] as u8 as usize], + invalid_bits[array[0x4][0] as u8 as usize], + invalid_bits[array[0x5][0] as u8 as usize], + invalid_bits[array[0x6][0] as u8 as usize], + invalid_bits[array[0x7][0] as u8 as usize], + /* invalid_bits[array[0x8][0] as u8 as usize], + invalid_bits[array[0x9][0] as u8 as usize], + invalid_bits[array[0xA][0] as u8 as usize], + invalid_bits[array[0xB][0] as u8 as usize], + invalid_bits[array[0xC][0] as u8 as usize], + invalid_bits[array[0xD][0] as u8 as usize], + invalid_bits[array[0xE][0] as u8 as usize], + invalid_bits[array[0xF][0] as u8 as usize], */ + ] + ); + // Now, lanewise OR whether the block kind in this iteration was invalid, with whether + // the same block kind was invalid in this lane the previous iteration. + set_bits |= block_kinds; + }); + // Now, we OR all the lanes together, to find out whether *any* of the lanes witnessed an + // invalid block kind. + let mut set_bits = set_bits.any(); */ + + let mut set_bits0 = false; + let mut set_bits1 = false; + let mut set_bits2 = false; + let mut set_bits3 = false; + let mut set_bits4 = false; + let mut set_bits5 = false; + let mut set_bits6 = false; + let mut set_bits7 = false; + let mut set_bits8 = false; + + /* // Initially, the set bit list is empty. + let mut set_bits: std::simd::Mask = std::simd::Mask::splat(false); */ + + // NOTE: We iterate in chunks of BLOCKS_PER_CHUNK blocks. This also lets us + // independently fetch BLOCKS_PER_CHUNK entries from the lookup table at once, setting + // BLOCKS_PER_CHUNK independent lanes (reducing CPU level contention on the register) + // instead of updating a single register in each iteration (which causes contention). + let (chunks, remainder) = blocks.as_chunks::(); + chunks.iter().for_each(|array| { + // Look up each of the block kind in the chunk, setting each lane of the + // BLOCKS_PER_CHUNK-word mask to true if the block kind is invalid. + // + // NOTE: The block kind is guaranteed to be at the front of each 4-byte word, + // thanks to the repr(C). + // + // NOTE: Bounds checks here appears to be either elided, or perfectly predicted, so we + // fortunately avoid using unsafe here. + // + // FIXME: simd table lookup, and simd packing. + /* let block_kinds = std::simd::Mask::from_array( + [ */ + set_bits0 |= invalid_bits[array[0x0][0] as u8 as usize]; + set_bits1 |= invalid_bits[array[0x1][0] as u8 as usize]; + set_bits2 |= invalid_bits[array[0x2][0] as u8 as usize]; + set_bits3 |= invalid_bits[array[0x3][0] as u8 as usize]; + set_bits4 |= invalid_bits[array[0x4][0] as u8 as usize]; + set_bits5 |= invalid_bits[array[0x5][0] as u8 as usize]; + set_bits6 |= invalid_bits[array[0x6][0] as u8 as usize]; + set_bits7 |= invalid_bits[array[0x7][0] as u8 as usize]; + /* invalid_bits[array[0x8][0] as u8 as usize], + invalid_bits[array[0x9][0] as u8 as usize], + invalid_bits[array[0xA][0] as u8 as usize], + invalid_bits[array[0xB][0] as u8 as usize], + invalid_bits[array[0xC][0] as u8 as usize], + invalid_bits[array[0xD][0] as u8 as usize], + invalid_bits[array[0xE][0] as u8 as usize], + invalid_bits[array[0xF][0] as u8 as usize], */ + /* ] + )*/ + // Now, lanewise OR whether the block kind in this iteration was invalid, with whether + // the same block kind was invalid in this lane the previous iteration. + // set_bits |= block_kinds; + }); + // Now, we OR all the lanes together, to find out whether *any* of the lanes witnessed an + // invalid block kind. + let mut set_bits = /*set_bits.any()*/set_bits0 | set_bits1 | set_bits2 | set_bits3 | set_bits4 | set_bits5 | set_bits6 | set_bits7; + + // Now handle the remainder (if this wasn't a precise fit for BLOCKS_PER_CHUNK blocks; + // this will never be the case for valid terrain chunks). + remainder.iter().for_each(|block| { + set_bits |= invalid_bits[block[0] as u8 as usize]; }); // The invalid bits and the set bits should have no overlap. - invalid_bits &= set_bits; - if invalid_bits.any() { + if set_bits { // At least one invalid bit was set, so there was an invalid BlockKind somewhere. // // TODO: Use radix representation of the bad block kind. @@ -556,10 +838,10 @@ impl<'a/*, Error: de::Error*/> TryFrom<&'a [u8]> for BlockVec { let blocks = unsafe { core::mem::transmute::<&'a [[u8; 4]], &'a [Block]>(blocks) }; // Finally, *safely* construct a vector from the new blocks (as mentioned above, we cannot // reuse the old byte vector even if we wanted to, since it doesn't have the same - // alignment as Block). - Ok(Self(blocks.to_vec())) + // alignment as Block). */ + Ok(Self(/*blocks.to_vec()*/Vec::new())) } -} +} */ #[cfg(test)] mod tests { diff --git a/common/src/terrain/chonk.rs b/common/src/terrain/chonk.rs index 25a3cc6a44..1ed92e9b9c 100644 --- a/common/src/terrain/chonk.rs +++ b/common/src/terrain/chonk.rs @@ -60,7 +60,7 @@ impl VolSize for SubChunkSize = Chunk, M>; +pub type SubChunk = Chunk, M>; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Chonk { @@ -100,6 +100,10 @@ impl>, S: RectVolSize, M: Clone> C self.sub_chunks.iter().map(SubChunk::num_groups).sum() } + pub fn sub_chunks<'a>(&'a self) -> impl Iterator> { + self.sub_chunks.iter() + } + /// Iterate through the voxels in this chunk, attempting to avoid those that /// are unchanged (i.e: match the `below` and `above` voxels). This is /// generally useful for performance reasons. diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index 0f389d25b2..fb2b20f0c4 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -158,6 +158,7 @@ impl TerrainChunkMeta { // Terrain type aliases pub type TerrainChunk = chonk::Chonk; +pub type TerrainSubChunk = chonk::SubChunk; pub type TerrainGrid = VolGrid2d; impl TerrainGrid { diff --git a/common/src/volumes/chunk.rs b/common/src/volumes/chunk.rs index be0c0a8914..070e82e8f6 100644 --- a/common/src/volumes/chunk.rs +++ b/common/src/volumes/chunk.rs @@ -5,6 +5,7 @@ use bitvec::prelude::*; use core::{hash::Hash, iter::Iterator, marker::PhantomData, mem}; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, Bytes}; use zerocopy::AsBytes; use vek::*; @@ -48,8 +49,10 @@ pub enum ChunkError { /// The number of 256 groups is particularly nice because it means that the /// index buffer can consist of `u8`s. This keeps the space requirement for the /// index buffer as low as 4 cache lines. +#[serde_as] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Chunk, M> { + #[serde_as(as = "Bytes")] indices: Vec, /* TODO (haslersn): Box<[u8; S::SIZE.x * S::SIZE.y * S::SIZE.z]>, this is * however not possible in Rust yet */ vox: S, @@ -121,6 +124,10 @@ impl> + VolSize, M> Chunk { } } + pub fn get_vox(&self) -> &S { + &self.vox + } + /// Compress this subchunk by frequency. pub fn defragment(&mut self) where diff --git a/network/src/api.rs b/network/src/api.rs index 19ac44d9b6..6126ae93e4 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -1005,6 +1005,27 @@ impl Stream { } } + #[inline] + pub fn try_recv_raw(&mut self) -> Result, StreamError> { + match &mut self.b2a_msg_recv_r { + Some(b2a_msg_recv_r) => match b2a_msg_recv_r.try_recv() { + Ok(data) => Ok(Some( + Message { + data, + #[cfg(feature = "compression")] + compressed: self.promises.contains(Promises::COMPRESSED), + } + )), + Err(async_channel::TryRecvError::Empty) => Ok(None), + Err(async_channel::TryRecvError::Closed) => { + self.b2a_msg_recv_r = None; //prevent panic + Err(StreamError::StreamClosed) + }, + }, + None => Err(StreamError::StreamClosed), + } + } + /// use `try_recv` to check for a Message send from the remote side by their /// `Stream`. This function does not block and returns immediately. It's /// intended for use in non-async context only. Other then that, the @@ -1041,24 +1062,7 @@ impl Stream { /// [`recv`]: Stream::recv #[inline] pub fn try_recv(&mut self) -> Result, StreamError> { - match &mut self.b2a_msg_recv_r { - Some(b2a_msg_recv_r) => match b2a_msg_recv_r.try_recv() { - Ok(data) => Ok(Some( - Message { - data, - #[cfg(feature = "compression")] - compressed: self.promises.contains(Promises::COMPRESSED), - } - .deserialize()?, - )), - Err(async_channel::TryRecvError::Empty) => Ok(None), - Err(async_channel::TryRecvError::Closed) => { - self.b2a_msg_recv_r = None; //prevent panic - Err(StreamError::StreamClosed) - }, - }, - None => Err(StreamError::StreamClosed), - } + self.try_recv_raw()?.map(|uncompressed| uncompressed.deserialize()).transpose() } pub fn params(&self) -> StreamParams { diff --git a/network/src/message.rs b/network/src/message.rs index f821511450..b88ecff88c 100644 --- a/network/src/message.rs +++ b/network/src/message.rs @@ -3,7 +3,7 @@ use bytes::Bytes; #[cfg(feature = "compression")] use network_protocol::Promises; use serde::{de::DeserializeOwned, Serialize}; -use std::io; +use std::{borrow::Cow, io}; #[cfg(all(feature = "compression", debug_assertions))] use tracing::warn; @@ -98,9 +98,20 @@ impl Message { /// ``` /// /// [`recv_raw`]: crate::api::Stream::recv_raw - pub fn deserialize(self) -> Result { + #[inline] + pub fn deserialize(&self) -> Result { + let uncompressed_data = self.decompress()?; + match bincode::deserialize(&uncompressed_data) { + Ok(m) => Ok(m), + Err(e) => Err(StreamError::Deserialize(e)), + } + } + + /// Decompress a message without deserializing it. + #[inline] + pub fn decompress<'a>(&'a self) -> Result, StreamError> { #[cfg(not(feature = "compression"))] - let uncompressed_data = self.data; + let uncompressed_data = Cow::Borrowed(&self.data); #[cfg(feature = "compression")] let uncompressed_data = if self.compressed { @@ -114,16 +125,13 @@ impl Message { ) { return Err(StreamError::Compression(e)); } - Bytes::from(uncompressed_data) + Cow::Owned(uncompressed_data) } } else { - self.data + Cow::Borrowed(&*self.data) }; - match bincode::deserialize(&uncompressed_data) { - Ok(m) => Ok(m), - Err(e) => Err(StreamError::Deserialize(e)), - } + Ok(uncompressed_data) } #[cfg(debug_assertions)] diff --git a/server/src/client.rs b/server/src/client.rs index 202790ad08..f55acce727 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -81,7 +81,7 @@ impl Client { } } - pub(crate) fn send>(&self, msg: M) -> Result<(), StreamError> { + pub(crate) fn send>>(&self, msg: M) -> Result<(), StreamError> { // TODO: hack to avoid locking stream mutex while serializing the message, // remove this when the mutexes on the Streams are removed let prepared = self.prepare(msg); @@ -140,7 +140,7 @@ impl Client { }*/ } - pub(crate) fn send_fallible>(&self, msg: M) { let _ = self.send(msg); } + pub(crate) fn send_fallible>>(&self, msg: M) { let _ = self.send(msg); } pub(crate) fn send_prepared(&self, msg: &PreparedMsg) -> Result<(), StreamError> { match msg.stream_id { @@ -158,7 +158,7 @@ impl Client { } } - pub(crate) fn prepare>(&self, msg: M) -> PreparedMsg { + pub(crate) fn prepare>>(&self, msg: M) -> PreparedMsg { match msg.into() { ServerMsg::Info(m) => PreparedMsg::new(0, &m, &self.register_stream_params), ServerMsg::Init(m) => PreparedMsg::new(0, &m, &self.register_stream_params), diff --git a/server/src/lib.rs b/server/src/lib.rs index fb9018cee7..ad5de65a02 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1180,7 +1180,7 @@ impl Server { pub fn notify_client(&self, entity: EcsEntity, msg: S) where - S: Into, + S: Into>, { self.state .ecs() @@ -1189,7 +1189,7 @@ impl Server { .map(|c| c.send(msg)); } - pub fn notify_players(&mut self, msg: ServerGeneral) { self.state.notify_players(msg); } + pub fn notify_players(&mut self, msg: ServerGeneral<'static>) { self.state.notify_players(msg); } pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2) { let ecs = self.state.ecs(); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index bb595a736e..0ad1df62d8 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -111,8 +111,8 @@ pub trait StateExt { fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents); /// Iterates over registered clients and send each `ServerMsg` fn send_chat(&self, msg: comp::UnresolvedChatMsg); - fn notify_players(&self, msg: ServerGeneral); - fn notify_in_game_clients(&self, msg: ServerGeneral); + fn notify_players(&self, msg: ServerGeneral<'static>); + fn notify_in_game_clients(&self, msg: ServerGeneral<'static>); /// Create a new link between entities (see [`common::mounting`] for an /// example). fn link(&mut self, link: L) -> Result<(), L::Error>; @@ -819,7 +819,7 @@ impl StateExt for State { } /// Sends the message to all connected clients - fn notify_players(&self, msg: ServerGeneral) { + fn notify_players(&self, msg: ServerGeneral<'static>) { let mut msg = Some(msg); let mut lazy_msg = None; for (client, _) in ( @@ -836,7 +836,7 @@ impl StateExt for State { } /// Sends the message to all clients playing in game - fn notify_in_game_clients(&self, msg: ServerGeneral) { + fn notify_in_game_clients(&self, msg: ServerGeneral<'static>) { let mut msg = Some(msg); let mut lazy_msg = None; for (client, _) in ( diff --git a/world/benches/site2.rs b/world/benches/site2.rs index be17f311df..23db0f1739 100644 --- a/world/benches/site2.rs +++ b/world/benches/site2.rs @@ -1,7 +1,7 @@ use common::{ generation::EntityInfo, store::{Id, Store}, - terrain::{Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, + terrain::{Block, BlockKind, SpriteKind, TerrainSubChunk, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, vol::RectVolSize, }; use criterion::{black_box, criterion_group, criterion_main, Criterion}; @@ -316,11 +316,13 @@ fn dungeon(c: &mut Criterion) { // let chunk_pos = Vec2::new(26944 / 32, 26848 / 32); let chunk_pos = Vec2::new(842, 839); let chunk = world.generate_chunk(index.as_index_ref(), chunk_pos, || false, None).unwrap().0; + /* println!("{:?}", chunk.sub_chunks_len()); + let chunk = chunk.sub_chunks().next().unwrap(); */ let serialized = bincode::serialize(&chunk).unwrap(); // let chunk_pos = Vec2::new(24507/32, 20682/32); // let chunk_pos = Vec2::new(19638/32, 19621/32); b.iter(|| { - black_box(bincode::deserialize::(&serialized).unwrap()); + black_box(bincode::deserialize::(&*serialized).unwrap()); }); }); diff --git a/world/src/lib.rs b/world/src/lib.rs index 30ab55cff6..55a127fbed 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -362,8 +362,8 @@ impl World { let wpos = Vec3::from(chunk_wpos2d) + lpos; if let Some(block) = sampler.get_with_z_cache(wpos, /*Some(&*/z_cache/*)*/) { - block_ = Some(block); - // let _ = chunk.set(lpos, block); + // block_ = Some(block); + let _ = chunk.set(lpos, block); } }); if let Some(block_) = block_ { diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index b27159f86b..5c30751933 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -479,6 +479,7 @@ impl Site { ..Site::default() }; + // NOTE: Remove to run benchmarks. // site.demarcate_obstacles(land); site.make_plaza(land, &mut rng);