From 70992eb81583017a609a2b9c062cd8a418b99fa8 Mon Sep 17 00:00:00 2001 From: Ludovic Marie <136255825+ludovic111@users.noreply.github.com> Date: Mon, 12 Oct 2026 16:43:32 +0200 Subject: [PATCH] Document the patrol and raid harness and its scope Co-Authored-By: Claude Opus 5.5 --- README.md | 4 ++-- docs/rust-parity.md | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c8ae1d65c..fdbf321c0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Horizon is a Rust rewrite of Minecraft Java **26.3** targeting one-to-one vanill - shields, maps, books, fireworks, beacons, advancements and statistics; - Anvil saves. -Parity is **not** complete. Still missing: the warden and the sculk family, the breeze, sniffer, creaking, happy ghast, llamas and a few other mobs, raids and pillager patrols, villager breeding and gossip, the loom, crafter and recipe book, scoreboards, subtitles, and spawned mob gear enchantments. Multiplayer joins vanilla servers but Horizon cannot host one. See [Rust rewrite status](docs/rust-parity.md). +Parity is **not** complete. Still missing: the warden and the sculk family, the breeze, sniffer, creaking, happy ghast, llamas and a few other mobs, villager breeding and gossip, the loom, crafter and recipe book, scoreboards, subtitles, and spawned mob gear enchantments. Multiplayer joins vanilla servers but Horizon cannot host one. See [Rust rewrite status](docs/rust-parity.md). ### Java parity: 84% @@ -38,7 +38,7 @@ round-trip harness has not been rerun. | Lighting | 1 | None: saved and relit light matches vanilla, slabs and stairs included | | World generation | 1 | None: 2,352 whole chunks over 225 structure sites, in all three dimensions, identical to vanilla | | Region files and saves | 0.75 | Saves round-trip with vanilla, and worlds from 1.18.2 to 26.2 upgrade as vanilla's DataFixers do over 22 sample worlds; worlds older than 1.18.2 are not upgraded | -| Entities and physics | 0.75 | Attributes, dimensions, death loot, spawn rules and natural spawning (15,500 NaturalSpawner cases) match; AI not compared; drowned, villagers, traders and the wither unverified | +| Entities and physics | 0.75 | Attributes, dimensions, death loot, spawn rules, natural spawning (15,500 NaturalSpawner cases), pillager patrols and raids (1,900 PatrolSpawner, Raid and raids.dat cases) match; AI not compared; drowned, villagers, traders and the wither unverified | | Interaction and inventory | 1 | None: menu clicks match over 6,000 click sequences | | Menus, HUD and chat | 0.75 | Commands parse as vanilla over 39,685 inputs, but /item modify, /spectate, /waypoint and /dialog do not run; no tab list or world border rendering | | Audio | 0.75 | Sound engine, music and ambience not compared with vanilla | diff --git a/docs/rust-parity.md b/docs/rust-parity.md index 807b875cb..8a1ad6781 100644 --- a/docs/rust-parity.md +++ b/docs/rust-parity.md @@ -150,6 +150,7 @@ python3 tools/test_biome_source_parity.py python3 tools/test_anchor_parity.py python3 tools/test_biome_temperature_parity.py python3 tools/test_float_log_parity.py +python3 tools/test_raid_parity.py ``` `--prepare` regenerates the local reference artifacts from the existing lock; @@ -1288,3 +1289,52 @@ binding. Rendering, transport, full entity lifecycle integration and playable Linux gameplay remain unfinished. In this pinned version, `LivingEntity.updateDataBeforeSync` invokes dirty effect synchronization; it is a separate phase from `tickEffects`. + +## Pillager patrols and raids + +`horizon_client::patrol` ports `PatrolSpawner` and `horizon_client::raid` ports +`Raid` and `Raids`; the game runs both through them. `tools/test_raid_parity.py` +runs vanilla's own classes in `VanillaRaidOracle`, on a stand-in `ServerLevel` +built from boxes of blocks and light, a biome, village sections, village points +of interest, unloaded and non-ticking chunks, the overworld clock, local +difficulty inputs and one player, and compares every case with +`raid_fixture` line by line. The last run matched **1,900 cases at zero +differences**: 1,000 patrol, 400 raid and 500 codec cases, with 474 patrol +pillagers, 1,364 raid waves, 11,399 raiders, 149 victories and 21 losses. + +- **Patrols**: the countdown, `isBrightOutside`, the one in five draw, the + random player, spectators, the village distance, `hasChunksAt`, the + `can_pillager_patrol_spawn` attribute (mushroom fields and the early game + timeline), the group size from the local difficulty, each member's + `MOTION_BLOCKING_NO_LEAVES` height, empty spawn block and spawn rules, the + leader's banner and patrol target, and the level random's next draw. +- **Raids**: `createOrExtendRaid` (spectators, the `raids` rule, + `can_start_raid`, the mean of the village points of interest, the omen + absorbed, the trigger stat), the pre-raid countdown and its cached spawn + position, `findRandomSpawnPos` with Mth's sine table, wave composition by + difficulty and the bonus wave, leaders and riders, `addWaveMob`, + `removeFromRaid` and `updateBossbar` (progress bits with Java's `clamp`, the + title, visibility, the player in the bar), the horn's seed and position, + `updateRaiders` (far, gone, removed and idle raiders), joining, loaded and + hurt raiders, victory with Hero of the Village, loss, the celebration, + peaceful, the `raids` rule, unloaded chunks, `/raid setomen` and `stop`, and + the 48,000 tick timeout. +- **raids.dat**: `Raids.CODEC` with `resultOrPartial` over generated and + mutated data: numbers of any NBT type converted as Java does, partial + results (`Util.fixedSize` streams, lists with bad elements, a non-list + `raids`), raids dropped on missing fields, a fresh `Raids` when `next_id` or + `tick` is missing, the heroes in `HashSet` order and the raids in the order + of fastutil's entry-set stream (the tick iterates the map the other way). + Each dimension keeps its own `dimensions//data/minecraft/raids.dat`. + +Scope and remaining gaps: `finalizeSpawn` runs with a fixed random in the +oracle, so its own draws are not compared, and raid buffs and patrol targets +come from unseeded entity randoms. The omens act inside the effect tick +(`BadOmenMobEffect`, `RaidOmenMobEffect`), including the +`ConcurrentModificationException` vanilla swallows when Raid Omen is first +added; this has unit tests but no oracle, since the effect map's order follows +identity hashes. Raider goals (pathfinding to the raid, celebrating, fetching +the banner, villagers' raid activities) and ravager riding are not compared: +they belong to the mob AI harness. The game's village distance comes from the +points of interest its villagers claim rather than a `PoiManager`. +