Files
horizon-airdef/tools/generate_entity_tracking.py
T
Ludovic Marie 120ac32311 Test LAN hosting against the vanilla client and track entities as ServerEntity does
Add a decoding capture proxy (tools/net_capture.py), an offline vanilla
client launcher (tools/vanilla_client.py) and a vanilla codec oracle that
decodes every captured clientbound packet with vanilla's StreamCodecs.
Fix the entity data serializer ids (vector3 onwards were off by one), the
sheep wool and piglin baby indices, send mob variants, villager data and
equipment, and send entity movement at each type's update interval and
tracking range with position syncs, like ServerEntity.sendChanges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:47:11 +02:00

70 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Generate crates/horizon-client/src/lan/tracking.rs: every entity type's
tracking settings (EntityType.Builder clientTrackingRange, updateInterval and
trackDeltas, which ChunkMap.TrackedEntity and ServerEntity use to decide who
sees an entity and how often its movement is sent), read from the decompiled
EntityTypes.java.
generate_entity_tracking.py [--sources .local/minecraft/26.3/sources/client]
"""
import argparse
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
INT_MAX = 2147483647
def parse(sources):
text = (sources / "net/minecraft/world/entity/EntityTypes.java").read_text()
out = []
for m in re.finditer(r"public static final EntityType<[\w.]+> (\w+) = register\((.*?)\n \);", text, re.S):
name, body = m.group(1).lower(), m.group(2)
range_ = re.findall(r"\.clientTrackingRange\((\d+)\)", body)
interval = re.findall(r"\.updateInterval\((\w+(?:\.\w+)?)\)", body)
track = ".dontTrackDeltas()" not in body
r = int(range_[-1]) if range_ else 5
if ".noUpdateInterval()" in body or (interval and interval[-1] == "Integer.MAX_VALUE"):
i = 0
else:
i = int(interval[-1]) if interval else 3
out.append((f"minecraft:{name}", r, i, track))
return sorted(out)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--sources", type=Path, default=ROOT / ".local/minecraft/26.3/sources/client")
parser.add_argument("--out", type=Path, default=ROOT / "crates/horizon-client/src/lan/tracking.rs")
args = parser.parse_args()
rows = parse(args.sources)
if len(rows) < 100:
raise SystemExit(f"only {len(rows)} entity types found")
lines = [
"//! Entity tracking settings per entity type (EntityType.Builder",
"//! clientTrackingRange in chunks, updateInterval in ticks with 0 for never,",
"//! trackDeltas). Generated by tools/generate_entity_tracking.py from the",
"//! decompiled EntityTypes.java; do not edit by hand.",
"",
"/// (entity type, tracking range in chunks, update interval, track deltas), sorted.",
"pub const TRACKING: &[(&str, u8, u32, bool)] = &[",
]
lines += [f' ("{n}", {r}, {i}, {str(t).lower()}),' for n, r, i, t in rows]
lines += [
"];",
"",
"/// The settings of one entity type; unknown types get the builder defaults.",
"pub fn settings(kind: &str) -> (u8, u32, bool) {",
" TRACKING",
" .binary_search_by(|row| row.0.cmp(kind))",
" .map_or((5, 3, true), |i| (TRACKING[i].1, TRACKING[i].2, TRACKING[i].3))",
"}",
"",
]
args.out.write_text("\n".join(lines))
print(f"wrote {len(rows)} entity types to {args.out}")
if __name__ == "__main__":
main()