Respect entity-ticking loading status in execute loaded

Require completed saved-entity application and loading status without changing simulation or physical unload policy.

Co-Authored-By: Codex <[email protected]>
This commit is contained in:
Florian ERNSTandCodex committed 2026-11-04 22:07:56 +01:00
1 parent aef74cd095
commit 52c79cedb1
5 files changed
+72 -7

No files matched your search

@@ -949,6 +949,17 @@ impl Game<'_> {
let keep = results.iter().any(|r| r.is_some_and(|v| (v != 0) == want));
Ok((keep, 0))
}
/// `ExecuteCommand.isChunkLoaded`: an entity-ticking loading status and
/// completed entity loading, rather than merely a retained lit column.
fn command_chunk_loaded(&self, x: i32, z: i32) -> bool {
self.tickets.levels().loaded_entity_ticking(x, z)
&& self.world.chunks.contains_key(&(x, z))
&& self.streaming.is_lit(x, z)
// Streaming queues the saved tags before making the column resident;
// spawn_loaded_entities applies them before this chunk's entities run.
&& !self.world.loaded_entities.iter().any(|(key, _)| *key == (x, z))
}
fn condition_matches(&mut self, c: &Context) -> R<(bool, i32)> {
let flag = |b: bool| (b, i32::from(b));
if c.has("data") {
@@ -976,12 +987,10 @@ impl Game<'_> {
.coords(c, "pos")
.map(|co| co.block(self.source_position(), self.source_rotation()));
let dimension = self.command_source.map_or(self.dim, |s| s.dimension);
// ExecuteCommand.isChunkLoaded: ENTITY_TICKING, which is past lit.
let loaded = |p: [i32; 3]| {
let key = (p[0] >> 4, p[2] >> 4);
self.world.chunks.contains_key(&key) && self.streaming.is_lit(key.0, key.1)
};
return Ok(flag(dimension == self.dim && p.is_some_and(loaded)));
return Ok(flag(
dimension == self.dim
&& p.is_some_and(|p| self.command_chunk_loaded(p[0] >> 4, p[2] >> 4)),
));
}
if c.has("dimension") {
let dimension = self.command_source.map_or(self.dim, |s| s.dimension);
@@ -209,6 +209,12 @@ impl Levels {
pub fn random_tick_bounds(&self) -> Option<(i32, i32, i32, i32)> {
self.1
}
/// The loading track has `FullChunkStatus.ENTITY_TICKING`, independently
/// of whether the simulation tracker also permits entities to tick.
pub fn loaded_entity_ticking(&self, x: i32, z: i32) -> bool {
self.get(x, z)
.is_none_or(|(loading, _)| loading <= ENTITY_TICKING)
}
/// `ChunkHolder.getFullStatus` is block ticking or more: the chunk's
/// ticking future can complete (its saved ticks unpack then).
pub fn loaded_ticking(&self, x: i32, z: i32) -> bool {
@@ -475,6 +481,9 @@ mod tests {
assert!(l.random_ticking(-4, 4) && !l.random_ticking(0, 5));
assert!(l.block_ticking(5, 5) && !l.block_ticking(6, 0));
assert!(l.loaded_ticking(9, 9) && !l.loaded_ticking(10, 0));
// A command asks the loading status, not the smaller simulation area.
assert!(l.loaded_entity_ticking(8, 8));
assert!(!l.loaded_entity_ticking(9, 0));
}
#[test]
@@ -486,6 +495,8 @@ mod tests {
assert!(l.entity_ticking(4, 0) && !l.entity_ticking(5, 0));
assert!(l.random_ticking(5, 0) && !l.random_ticking(6, 0));
assert!(l.block_ticking(5, 5) && !l.block_ticking(6, 0));
assert!(l.loaded_entity_ticking(4, 0));
assert!(!l.loaded_entity_ticking(5, 0));
}
#[test]
+1 -1
View File
@@ -137,7 +137,7 @@ EnchantCommand.register port crates/horizon-command/src/vanilla.rs::registered e
net/minecraft/server/commands/ExecuteCommand#wrapStores port crates/horizon-command/src/tree_execute.rs::store execute store syntax
net/minecraft/server/commands/ExecuteCommand#storeValue port crates/horizon-client/src/game/commands/execute.rs::Game::store_value score and bossbar store targets
net/minecraft/server/commands/ExecuteCommand#storeData port crates/horizon-client/src/game/commands/execute.rs::Game::store_value data target store with int, float, short, long, double tags
net/minecraft/server/commands/ExecuteCommand#isChunkLoaded port crates/horizon-client/src/game/commands/execute.rs::Game::condition_matches gap: the loaded arm tests a chunk present with light (streaming.is_lit) and has no entity check, where vanilla wants the chunk at ENTITY_TICKING and level.areEntitiesLoaded
net/minecraft/server/commands/ExecuteCommand#isChunkLoaded port crates/horizon-client/src/game/commands/execute.rs::Game::command_chunk_loaded ENTITY_TICKING loading status, resident light-ready column and completed saved-entity application
net/minecraft/server/commands/ExecuteCommand#countItems port crates/horizon-client/src/game/commands/items.rs::Game::condition_items item count predicate
net/minecraft/server/commands/ExecuteCommand#checkScore port crates/horizon-client/src/game/commands/execute.rs::Game::condition_score score comparison, both score holders
net/minecraft/server/commands/ExecuteCommand#checkStopwatch port crates/horizon-client/src/game/commands/execute.rs::Game::condition_stopwatch stopwatch range check
Can't render this file because it contains an unexpected character in line 66 and column 104.
+11
View File
@@ -91,6 +91,9 @@ def main():
build.mkdir(parents=True, exist_ok=True)
report_path = build / 'report.json'
report_path.unlink(missing_ok=True)
if args.loaded_fixture:
for name in ['client_config.json', 'rust-client-config.json']:
(build / name).unlink(missing_ok=True)
if args.prepare:
lab.prepare()
lock, jars, libraries = base.verify_artifacts()
@@ -168,6 +171,14 @@ def main():
config = json.loads((build / name).read_text())
if config.get('view_distance') != 2 or config.get('simulation_distance') != 2:
sys.exit(f'actual login distance prerequisite failed: {name}: {config}')
java = json.loads((build / 'client_config.json').read_text())
if java.get('server_view') != 2 or java.get('server_simulation') != 2 or not java.get('client_loaded'):
sys.exit(f'Java server/client-loaded prerequisite failed: {java}')
for name in ['java.log', 'rust.log']:
waits = re.findall(r'harness paced wait: (\d+) ticks, (\d+) ms', (build / name).read_text())
if len(waits) != 2 or any(int(n) != ticks or not ticks * 50 <= int(ms) <= upper
for (n, ms), ticks, upper in zip(waits, [40, 120], [4000, 10000])):
sys.exit(f'bounded paced-wait prerequisite failed: {name}: {waits}')
native = json.loads((build / 'rust-client-config.json').read_text())
if native.get('render_setting') != 2 or native.get('simulation_setting') != 2 or not native.get('joined'):
sys.exit(f'native settings/join prerequisite failed: {native}')
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""Distance-two /execute loaded after forced-ticket removal.
The default90-record gate is strict. --block-diagnostic also retains30 FULL
block-access observations, whose separate failures are outside this task.
"""
import argparse
from pathlib import Path
import subprocess
import sys
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--binary', type=Path)
parser.add_argument('--prepare', action='store_true')
parser.add_argument('--block-diagnostic', action='store_true')
args = parser.parse_args()
root = Path(__file__).resolve().parents[1]
folder = 'loaded-block-diagnostic' if args.block_diagnostic else 'loaded-command-parity'
command = [sys.executable, str(root / 'tools/test_command_exec_parity.py'),
'--loaded-fixture', '--only', 'loaded ticket removal',
'--work-dir', str(root / '.local' / folder)]
if args.binary:
command += ['--binary', str(args.binary.resolve())]
if args.prepare:
command.append('--prepare')
if args.block_diagnostic:
command.append('--loaded-block-diagnostic')
return subprocess.run(command, cwd=root).returncode
if __name__ == '__main__':
sys.exit(main())