mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Merge pull request #11697 from mvanhorn/fix/6763-memory-first-view-chunk-batch
🤖 Agent: the single-player memory connection sends every ready view column in one batch before PlayerLoaded (about 0.5 s saved at render distance 12), then the 64-per-tick cap comes back; socket guests keep vanilla pacing. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
This commit is contained in:
1 file changed
+119
-3
@@ -2839,7 +2839,10 @@ impl Hub {
|
||||
/// PlayerChunkSender.sendNextChunks' choice: ready chunks, closest first,
|
||||
/// as many as the batch quota the client's acknowledgements pace allows,
|
||||
/// with their places in the queue. What the view leaves of the quota goes
|
||||
/// to the far ring, as far as its brake lets it reach.
|
||||
/// to the far ring, as far as its brake lets it reach. A memory connection
|
||||
/// that has not loaded yet sends every ready view column in that one
|
||||
/// batch; the quota recorded for it covers them, so the tick after
|
||||
/// `PlayerLoaded` is capped again.
|
||||
fn next_batch<'w>(
|
||||
&mut self,
|
||||
i: usize,
|
||||
@@ -2855,6 +2858,10 @@ impl Hub {
|
||||
return None;
|
||||
}
|
||||
let budget = g.batch_quota.floor() as usize;
|
||||
// The integrated server's own player, still on the loading screen.
|
||||
// LAN guests and a client that has loaded keep the quota above.
|
||||
let burst = g.address == super::conn::MEMORY_ADDRESS && !g.loaded;
|
||||
let near_take = if burst { g.chunk_queue.len() } else { budget };
|
||||
// ChunkStatus.LIGHT needs its neighbours at INITIALIZE_LIGHT: a
|
||||
// column whose light layers would leave one out waits for it.
|
||||
let ready = |c: &Chunk| {
|
||||
@@ -2875,11 +2882,12 @@ impl Hub {
|
||||
.enumerate()
|
||||
.filter_map(|(idx, key)| Some((idx, &**world.chunks.get(key)?)))
|
||||
.filter(|(_, c)| ready(c))
|
||||
.take(budget)
|
||||
.take(near_take)
|
||||
.collect();
|
||||
let mut far = Vec::new();
|
||||
if let (Some(ring), Some((cx, cz))) = (self.far.as_mut(), g.center) {
|
||||
let mut left = budget - near.len();
|
||||
// What the view left of the vanilla quota. A first-view burst can take more than that.
|
||||
let mut left = budget.saturating_sub(near.len());
|
||||
for (idx, &(x, z)) in g.far_queue.iter().enumerate().take(super::far::LOOKS) {
|
||||
if left == 0 {
|
||||
break;
|
||||
@@ -2900,6 +2908,11 @@ impl Hub {
|
||||
far.push((idx, column));
|
||||
}
|
||||
}
|
||||
// `update_chunks` subtracts the batch. Covering the near columns keeps that from going
|
||||
// negative, so the next tick's `.min(chunks_per_tick)` cap holds again.
|
||||
if burst {
|
||||
g.batch_quota = g.batch_quota.max(near.len() as f32);
|
||||
}
|
||||
(!near.is_empty() || !far.is_empty()).then_some(Batch { near, far })
|
||||
}
|
||||
|
||||
@@ -6327,4 +6340,107 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single-player memory connection sends every ready view column before
|
||||
/// `PlayerLoaded`. After that, on a respawn, and for a socket guest, the
|
||||
/// batch is `chunks_per_tick` again (64). An unlit column stays out, and
|
||||
/// the gates that send nothing still do.
|
||||
#[test]
|
||||
fn memory_connection_first_view_sends_every_ready_column() {
|
||||
let (mut hub, _rx) = hub_with_a_guest();
|
||||
hub.ticks = 3;
|
||||
{
|
||||
let g = &mut hub.guests[0];
|
||||
g.chunks_per_tick = 64.0;
|
||||
g.batch_quota = 0.0;
|
||||
g.address = crate::lan::conn::MEMORY_ADDRESS;
|
||||
g.loaded = false;
|
||||
}
|
||||
// The unlit column first, then 80 lit ones (`floor_at`'s empty column, lit).
|
||||
let mut world = World::new(horizon_world::registry::BlockStateId(0), 0);
|
||||
let mut dark = crate::chunk::Chunk::empty(100, 100);
|
||||
dark.lit = false;
|
||||
world.chunks.insert((100, 100), std::sync::Arc::new(dark));
|
||||
hub.guests[0].chunk_queue.push((100, 100));
|
||||
for i in 0..80 {
|
||||
let (x, z) = (i % 16, i / 16);
|
||||
let mut chunk = crate::chunk::Chunk::empty(x, z);
|
||||
chunk.lit = true;
|
||||
world.chunks.insert((x, z), std::sync::Arc::new(chunk));
|
||||
hub.guests[0].chunk_queue.push((x, z));
|
||||
}
|
||||
|
||||
let batch = hub
|
||||
.next_batch(0, &world, None)
|
||||
.expect("the first view sends");
|
||||
assert_eq!(
|
||||
batch.near.len(),
|
||||
80,
|
||||
"every ready column leaves in one batch"
|
||||
);
|
||||
assert!(
|
||||
batch.near.iter().all(|(_, c)| c.lit),
|
||||
"an unlit column is not ready"
|
||||
);
|
||||
assert!(batch.far.is_empty());
|
||||
assert!(hub.guests[0].batch_quota >= 80.0);
|
||||
|
||||
hub.guests[0].loaded = true;
|
||||
let loaded = hub
|
||||
.next_batch(0, &world, None)
|
||||
.expect("a loaded client still receives chunks");
|
||||
assert_eq!(loaded.near.len(), 64, "PlayerLoaded puts the cap back");
|
||||
|
||||
hub.guests[0].address = "127.0.0.1:25565".parse().unwrap();
|
||||
hub.guests[0].loaded = false;
|
||||
hub.guests[0].batch_quota = 0.0;
|
||||
let socket = hub
|
||||
.next_batch(0, &world, None)
|
||||
.expect("a socket guest still receives chunks");
|
||||
assert_eq!(socket.near.len(), 64, "a socket guest keeps the quota");
|
||||
|
||||
hub.guests[0].address = crate::lan::conn::MEMORY_ADDRESS;
|
||||
hub.guests[0].loaded = true;
|
||||
hub.guests[0].loading = true;
|
||||
hub.guests[0].batch_quota = 0.0;
|
||||
let respawn = hub
|
||||
.next_batch(0, &world, None)
|
||||
.expect("a respawned player still receives chunks");
|
||||
assert_eq!(
|
||||
respawn.near.len(),
|
||||
64,
|
||||
"a respawn stays on the vanilla pace"
|
||||
);
|
||||
|
||||
// Seventy lit columns, the unlit one still queued: all seventy, in one batch.
|
||||
hub.guests[0].loaded = false;
|
||||
hub.guests[0].batch_quota = 0.0;
|
||||
hub.guests[0].chunk_queue.truncate(71);
|
||||
let seventy = hub
|
||||
.next_batch(0, &world, None)
|
||||
.expect("seventy ready columns send");
|
||||
assert_eq!(seventy.near.len(), 70);
|
||||
assert!(seventy.near.iter().all(|(_, c)| c.lit));
|
||||
|
||||
hub.guests[0].unacknowledged = hub.guests[0].max_unacknowledged;
|
||||
assert!(
|
||||
hub.next_batch(0, &world, None).is_none(),
|
||||
"too many batches still waiting sends nothing"
|
||||
);
|
||||
|
||||
hub.guests[0].unacknowledged = 0;
|
||||
hub.ticks = hub.guests[0].placed_at + 1;
|
||||
assert!(
|
||||
hub.next_batch(0, &world, None).is_none(),
|
||||
"the tick after placement still waits"
|
||||
);
|
||||
|
||||
hub.ticks = 3;
|
||||
hub.guests[0].chunk_queue.clear();
|
||||
hub.guests[0].far_queue.clear();
|
||||
assert!(
|
||||
hub.next_batch(0, &world, None).is_none(),
|
||||
"an empty queue sends nothing"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user