Document the patrol and raid harness and its scope

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Ludovic Marie
2026-10-12 16:43:32 +02:00
parent cb0244ce4a
commit 70992eb815
2 changed files with 52 additions and 2 deletions
+2 -2
View File
@@ -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 |
+50
View File
@@ -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/<id>/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`.