Skip to content

Commit cb14cfa

Browse files
authored
Chunk Rework (#402)
## Description Iterate on the design of `valence_instance` and `valence_anvil`. This has been a blocker for #193 and #342. - Removed the `LOADED` const param from `Chunk` and split it into separate `LoadedChunk` and `UnloadedChunk` types. Common operations between the two are part of the new `Chunk` trait. - Reworked the chunk interface and made it more convenient. - Removed the confusing `ChunkCell` mechanism and flattened its data into `LoadedChunk`. - As a consequence of the previous point, `LoadedChunk` now implements `WritePacket`. - Added support for the new biome change packet introduced circa 1.19.4. - Resolved some confusion around the client's respawn position/compass position. Fixes #312. - Improvements to `valence_anvil`. Now handles chunks with unusual `min_y` correctly. - Simplified `PacketWriter` interface. No longer takes a mut ref to a `Vec<u8>`. - Fixed a bug where block entities were not properly removed when truncating a chunk. - Added more unit tests for chunk mutations and chunk loading/unloading. - Loaded chunks now use viewership information for optimization. Only chunks in view of clients will record changes. For instance, writing a packet to a chunk out of view is a no-op. - Entities are always despawned on the client when the entity is moved to an unloaded chunk position. - Reorganized `valence_instance`. - Fix player list systems not being added to the correct system set. - New utilities for unit tests. There was initially some indecision around whether chunks and block entities should be entities in the ECS. After some consideration, I decided to abandon that idea.
1 parent 8535034 commit cb14cfa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2691
-1967
lines changed

Diff for: Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ license = "MIT"
9292

9393
[workspace.dependencies]
9494
aes = "0.8.2"
95-
anyhow = "1.0.70"
95+
anyhow = { version = "1.0.70", features = ["backtrace"] }
9696
approx = "0.5.1"
9797
arrayvec = "0.7.2"
9898
async-trait = "0.1.60"

Diff for: benches/idle.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn setup(
2929

3030
for z in -5..5 {
3131
for x in -5..5 {
32-
instance.insert_chunk([x, z], Chunk::default());
32+
instance.insert_chunk([x, z], UnloadedChunk::new());
3333
}
3434
}
3535

Diff for: benches/packet.rs

+8-15
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ use valence::prelude::*;
77
use valence::protocol::array::LengthPrefixedArray;
88
use valence::protocol::byte_angle::ByteAngle;
99
use valence::protocol::decode::PacketDecoder;
10-
use valence::protocol::encode::{encode_packet, encode_packet_compressed, PacketEncoder};
10+
use valence::protocol::encode::PacketEncoder;
1111
use valence::protocol::var_int::VarInt;
1212
use valence::text::TextFormat;
13+
use valence_core::protocol::encode::{PacketWriter, WritePacket};
1314
use valence_entity::packet::EntitySpawnS2c;
1415
use valence_instance::packet::ChunkDataS2c;
1516
use valence_player_list::packet::PlayerListHeaderS2c;
@@ -129,7 +130,7 @@ pub fn packet(c: &mut Criterion) {
129130
let mut decoder = PacketDecoder::new();
130131
let mut packet_buf = vec![];
131132

132-
encode_packet(&mut packet_buf, &chunk_data_packet).unwrap();
133+
PacketWriter::new(&mut packet_buf, None).write_packet(&chunk_data_packet);
133134

134135
c.bench_function("decode_chunk_data", |b| {
135136
b.iter(|| {
@@ -148,7 +149,7 @@ pub fn packet(c: &mut Criterion) {
148149
});
149150

150151
packet_buf.clear();
151-
encode_packet(&mut packet_buf, &player_list_header_packet).unwrap();
152+
PacketWriter::new(&mut packet_buf, None).write_packet(&player_list_header_packet);
152153

153154
c.bench_function("decode_player_list_header", |b| {
154155
b.iter(|| {
@@ -167,7 +168,7 @@ pub fn packet(c: &mut Criterion) {
167168
});
168169

169170
packet_buf.clear();
170-
encode_packet(&mut packet_buf, &spawn_entity_packet).unwrap();
171+
PacketWriter::new(&mut packet_buf, None).write_packet(&spawn_entity_packet);
171172

172173
c.bench_function("decode_entity_spawn", |b| {
173174
b.iter(|| {
@@ -187,10 +188,8 @@ pub fn packet(c: &mut Criterion) {
187188

188189
decoder.set_compression(Some(256));
189190

190-
let mut scratch = vec![];
191-
192191
packet_buf.clear();
193-
encode_packet_compressed(&mut packet_buf, &chunk_data_packet, 256, &mut scratch).unwrap();
192+
PacketWriter::new(&mut packet_buf, Some(256)).write_packet(&chunk_data_packet);
194193

195194
c.bench_function("decode_chunk_data_compressed", |b| {
196195
b.iter(|| {
@@ -209,13 +208,7 @@ pub fn packet(c: &mut Criterion) {
209208
});
210209

211210
packet_buf.clear();
212-
encode_packet_compressed(
213-
&mut packet_buf,
214-
&player_list_header_packet,
215-
256,
216-
&mut scratch,
217-
)
218-
.unwrap();
211+
PacketWriter::new(&mut packet_buf, Some(256)).write_packet(&player_list_header_packet);
219212

220213
c.bench_function("decode_player_list_header_compressed", |b| {
221214
b.iter(|| {
@@ -234,7 +227,7 @@ pub fn packet(c: &mut Criterion) {
234227
});
235228

236229
packet_buf.clear();
237-
encode_packet_compressed(&mut packet_buf, &spawn_entity_packet, 256, &mut scratch).unwrap();
230+
PacketWriter::new(&mut packet_buf, Some(256)).write_packet(&spawn_entity_packet);
238231

239232
c.bench_function("decode_spawn_entity_compressed", |b| {
240233
b.iter(|| {

Diff for: crates/valence_anvil/src/lib.rs

+11-24
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ use valence_client::{Client, OldView, UpdateClientsSet, View};
3838
use valence_core::chunk_pos::ChunkPos;
3939
use valence_core::ident::Ident;
4040
use valence_entity::{Location, OldLocation};
41-
use valence_instance::{Chunk, Instance};
41+
use valence_instance::chunk::UnloadedChunk;
42+
use valence_instance::Instance;
4243
use valence_nbt::Compound;
4344

4445
mod parse_chunk;
@@ -61,7 +62,7 @@ pub struct AnvilLevel {
6162
receiver: Receiver<(ChunkPos, WorkerResult)>,
6263
}
6364

64-
type WorkerResult = anyhow::Result<Option<(Chunk, AnvilChunk)>>;
65+
type WorkerResult = anyhow::Result<Option<(UnloadedChunk, u32)>>;
6566

6667
impl AnvilLevel {
6768
pub fn new(world_root: impl Into<PathBuf>, biomes: &BiomeRegistry) -> Self {
@@ -82,7 +83,6 @@ impl AnvilLevel {
8283
.iter()
8384
.map(|(id, name, _)| (name.to_string_ident(), id))
8485
.collect(),
85-
section_count: 0, // Assigned later.
8686
}),
8787
ignored_chunks: HashSet::new(),
8888
pending: HashMap::new(),
@@ -137,8 +137,6 @@ struct ChunkWorkerState {
137137
decompress_buf: Vec<u8>,
138138
/// Mapping of biome names to their biome ID.
139139
biome_to_id: BTreeMap<Ident<String>, BiomeId>,
140-
/// Number of chunk sections in the instance.
141-
section_count: usize,
142140
}
143141

144142
impl ChunkWorkerState {
@@ -287,10 +285,9 @@ impl Plugin for AnvilPlugin {
287285
}
288286
}
289287

290-
fn init_anvil(mut query: Query<(&mut AnvilLevel, &Instance), Added<AnvilLevel>>) {
291-
for (mut level, inst) in &mut query {
292-
if let Some(mut state) = level.worker_state.take() {
293-
state.section_count = inst.section_count();
288+
fn init_anvil(mut query: Query<&mut AnvilLevel, (Added<AnvilLevel>, With<Instance>)>) {
289+
for mut level in &mut query {
290+
if let Some(state) = level.worker_state.take() {
294291
thread::spawn(move || anvil_worker(state));
295292
}
296293
}
@@ -374,9 +371,9 @@ fn send_recv_chunks(
374371
anvil.pending.remove(&pos);
375372

376373
let status = match res {
377-
Ok(Some((chunk, AnvilChunk { data, timestamp }))) => {
374+
Ok(Some((chunk, timestamp))) => {
378375
inst.insert_chunk(pos, chunk);
379-
ChunkLoadStatus::Success { data, timestamp }
376+
ChunkLoadStatus::Success { timestamp }
380377
}
381378
Ok(None) => ChunkLoadStatus::Empty,
382379
Err(e) => ChunkLoadStatus::Failed(e),
@@ -418,17 +415,9 @@ fn anvil_worker(mut state: ChunkWorkerState) {
418415
return Ok(None);
419416
};
420417

421-
let mut chunk = Chunk::new(state.section_count);
422-
// TODO: account for min_y correctly.
423-
parse_chunk::parse_chunk(&anvil_chunk.data, &mut chunk, 4, |biome| {
424-
state
425-
.biome_to_id
426-
.get(biome.as_str())
427-
.copied()
428-
.unwrap_or_default()
429-
})?;
430-
431-
Ok(Some((chunk, anvil_chunk)))
418+
let chunk = parse_chunk::parse_chunk(anvil_chunk.data, &state.biome_to_id)?;
419+
420+
Ok(Some((chunk, anvil_chunk.timestamp)))
432421
}
433422
}
434423

@@ -446,8 +435,6 @@ pub struct ChunkLoadEvent {
446435
pub enum ChunkLoadStatus {
447436
/// A new chunk was successfully loaded and inserted into the instance.
448437
Success {
449-
/// The raw chunk data of the new chunk.
450-
data: Compound,
451438
/// The time this chunk was last modified, measured in seconds since the
452439
/// epoch.
453440
timestamp: u32,

0 commit comments

Comments
 (0)