120ac32311
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>
70 lines
2.8 KiB
Python
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()
|