Skip to content

data: протокол 26.2 (776) и спеки поверх - #1

Merged
Titlehhhh merged 9 commits into
mainfrom
data-26.2
Aug 20, 2026
Merged

data: протокол 26.2 (776) и спеки поверх#1
Titlehhhh merged 9 commits into
mainfrom
data-26.2

Conversation

@Titlehhhh

@Titlehhhh Titlehhhh commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Ветка под цель «спеки на 26.2». Мультиверсия остаётся: слои дописываются поверх, ничего старого не режем.

Сделано

  1. Данные. Подмодуль переставлен с релиза 3.102.3 (28.12.2025) на голову ветки pc_26_2 (коммит 4dd8762a, открытый запрос 🎈 Add Minecraft pc 26.2 data PrismarineJS/minecraft-data#1219). Разом приехали протоколы 773–776, диапазон загрузчика поднят до 776.
  2. Поверхность версий в фактах. Не было способа спросить «какой версии какой номер протокола». Добавлено: GetVersions(), команда versions, endpoint GET /api/versions, инструмент get_protocol_versions.
  3. Манифест и покрытие. Spec/protocol-ids.json пересобран (286 записей), таблица версий в Coverage.fs доведена до 776.
  4. Слои на существующих спеках — 15 пакетов, плюс новые типы RespawnData, GlobalPos, ClockUpdate, ExplosionParticleEntry, ExplosionParticleInfo.
  5. LpVec3 в рантайме песочницы — новый вектор скорости с 1.21.9. В protodef это «родной» тип без структуры, поэтому разметка взята из внешних источников. Спеком не выражается: 1 байт на ноль, иначе 48 бит (2 бита масштаба, флаг продолжения, три компоненты по 15 бит) и хвостовой varint. Проверен побайтно на эталонных векторах из документации плюс 2000 случайных.

Версии

Подтверждено по minecraft.wiki, ViaVersion и ProtocolLib: 773 = 1.21.9 и 1.21.10, 774 = 1.21.11, 775 = 26.1–26.1.2, 776 = 26.2.

Покрытие

pv версия пакетов покрыто
772 1.21.7–1.21.8 245 202
776 26.2 257 210

Тринадцать пакетов на 776 новые, один убран (play.toServer.debug_sample_subscription).

Где спеки сознательно расходятся с данными

На слое 774 minecraft-data типизирует chat_command_signed.checksum как i8, а enchant_item.enchantment как i8, хотя по обе стороны стоят u8 и varint. Внешние источники говорят, что это брак данных, а не изменение протокола: MCProtocolLib на коммите с protocolVersion(774) читает обычный байт и varint, ревизия вики, обрамляющая 774, даёт Byte и VarInt, а ViaVersion в переходе 773→774 объявляет серверный набор пакетов неизменным. Сами PrismarineJS называют это багом в своих запросах #1188 и #1201. Слой 774 в обоих спеках убран.

Что осталось

  • Семь пакетов без спека упираются в кодоген объединений (задача 3 очереди): player_info, use_entity, четыре debug_* и debug_subscription_request — им нужны типы DebugSubscriptionUpdate/Event/DataType со switch.
  • LpVec3 нужен в самом McProtoNet до доставки генерата — сейчас он только в песочнице.
  • Именование: DeathLocation и GlobalPos по проводу одинаковы, на 773 в SpawnInfo данные переименовывают поле в GlobalPos. Слой для этого не заведён — модель не умеет менять тип поля между версиями (задача 9 очереди).

Риски

  • Данные 26.2 сгенерированы ботом и в мастер не влиты. Пока ветка pc_26_2 жива, клон подмодуля работает; удалят после мержа — пин перевести на мастер.
  • Открыт запрос Store PC protocols as deltas from the previous version PrismarineJS/minecraft-data#1231 «хранить протоколы дельтами от предыдущей версии». Вольют — загрузчик фактов придётся править.

Titlehhhh and others added 7 commits August 20, 2026 14:28
The data submodule moves from Release 3.102.3 (2025-12-28) to the upstream
`pc_26_2` branch head 4dd8762a ("fix: restore 26.2 login protocol fields",
open PR PrismarineJS/minecraft-data#1219). The branch is master plus the 26.2
boilerplate, so protocols 773-776 arrive together: 1.21.9-1.21.11, 26.1, 26.2.
26.2 data is bot-generated and not merged upstream yet.

The loader range follows: ProtocolDataOptions default ToProtocol 772 -> 776,
McpServer end 772 -> 776. Nothing else in the loader is version-name aware -
ProtocolLoader keys off the integer in each version.json, so "26.1"/"26.2"
folder names need no parser change.

Checked: `tools\mcproto-facts.cmd stats` loads the full range, 286 packets,
new ids among them (configuration.toClient.code_of_conduct,
configuration.toServer.accept_code_of_conduct). `ids --pv 776 --ns play
--direction toClient` returns 141 packet ids against 134 at --pv 772.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing exposed the mapping between a protocol number and the Minecraft
releases that speak it, so extending the coverage table to 773-776 had no
lawful source. ProtocolMap already carries it (ProtocolInfo.MinecraftVersions,
filled from each version.json); the repository just never published it.

IProtocolRepository gains GetVersions() returning ProtocolVersionEntry
(protocol number + release names), implemented in ProtocolRepository off the
existing map. ProtocolQueryService.GetVersions() orders by protocol number and
adds a joined Display string. Wired into all three surfaces: CLI `versions`,
REST `GET /api/versions`, stdio MCP `get_protocol_versions`. Command and
endpoint lists in AI_CONTEXT.md and README.md follow.

Checked: `tools\mcproto-facts.cmd versions --format toon` prints 735-776;
the new numbers read 773=1.21.9, 774=1.21.11, 775=26.1, 776=26.2. McpServer
builds clean (built to a scratch output path because the running instance
holds the normal one).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Spec/protocol-ids.json regenerated from the updated facts server: 286 packet
entries against 245 before, because the data now reaches 26.2. The packet
universe at 776 is 257 ids - 13 more than at 772 (code_of_conduct,
accept_code_of_conduct, the five debug_* packets, game_test_highlight_pos,
game_rule_values, low_disk_space_warning, set_game_rule, attack,
spectate_entity, debug_subscription_request) and one gone
(play.toServer.debug_sample_subscription).

Coverage.knownVersions gains 773 "1.21.9", 774 "1.21.11", 775 "26.1",
776 "26.2" from the new facts versions surface, so the coverage report stops
cutting off at 772.

Checked: `dotnet run -- coverage` reports 201/257 covered on pv 776 against
202/245 on pv 772; wire gaps stay at 3 and stubs at 6. Of the 31 packets whose
shape changes above 772, 15 already have a spec and need a new wire layer;
16 have no spec at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fifteen packets change shape above protocol 772; each got a new wire layer,
with the previous newest layer closed at the right boundary. Notable ones:

- explosion (773): x/y/z collapse into center vec3f64, radius f32 and an i32
  block count are inserted after it, and a weighted particle list is appended.
  Two named types carry the tail: ExplosionParticleEntry (data, weight) and
  ExplosionParticleInfo (particle, scaling, speed).
- spawn_position (773): the body becomes RespawnData (GlobalPos, yaw, pitch),
  so a dimension identifier now precedes the block position and a pitch
  follows the old angle. RespawnData and GlobalPos are new type specs.
- update_time (775): time and tickDayTime give way to a varint-counted list of
  clock updates (id, totalTicks as varlong, partialTick, rate) - new type
  ClockUpdate.
- spawn_entity and entity_velocity (773): the i16 velocity triple becomes the
  quantized LpVec3, and in spawn_entity it also moves ahead of pitch/yaw.
- login.toClient.success (776): trailing sessionId UUID - one of the two
  packets that change on 26.2 itself.
- cookie_response in all three states (773): the value goes back to optional.
  Facts show it required on 772 only, so that single version keeps its own
  layer.
- player_rotation (773): relativeYaw and relativePitch booleans after each
  angle. debug_sample_subscription: support now ends at 772, the packet is
  gone from 773.

Two specs deliberately differ from the data. minecraft-data types the chat
command checksum as i8 and the enchant_item button id as i8 on 774 only, with
u8 and varint on both sides of it. External sources agree those are upstream
defects, not protocol changes: MCProtocolLib pinned at 774 reads a plain byte
and a varint respectively, the wiki revision bracketing 774 says Byte and
VarInt, ViaVersion's 773->774 protocol declares the serverbound packet set
identical, and PrismarineJS themselves call the i8 checksum a bug in
minecraft-data#1188 and the i8 button id a bug in #1201. So the specs keep u8
from 770 on and varint from 767 on, with no 774 layer.

Coverage.knownVersions display names follow the confirmed mapping: 773 covers
1.21.9 and 1.21.10, 775 covers 26.1 through 26.1.2.

Checked: dotnet build clean; `dotnet run -- gen` emits no new TODO markers, only
the six allowlisted stubs; `dotnet run -- coverage` reports 210/257 on pv 776.
F# formatted with Fantomas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Protocol 773 replaced the i16 velocity triple with lpVec3, which protodef
declares as a native - McProtoFacts has no structure for it, so the shape came
from external sources (MCProtocolLib's readLpVec3, the protocol wiki's data
type page, node-minecraft-protocol's codec, Botcraft, decompiled vanilla).
It is not spec-able: the encoding is 1 byte for zero, otherwise 48 bits holding
a 2-bit scale, a continuation flag and three 15-bit components, with the rest
of the scale trailing as a varint. So it joins position and the other
hand-written runtime primitives.

Runtime.cs: LpVec3 as a record struct of three doubles. Read assembles the
48 bits from an unsigned byte pair plus a big-endian uint32; write picks
scale = ceil(chebyshev norm), packs each component with round-half-up (C#
banker's rounding would shift bytes), and sets the continuation flag when the
scale needs more than two bits. MinecraftVersion.LatestProtocol moves 772 ->
776.

Console: LpVec3 is checked against the two sample vectors from the protocol
documentation byte for byte (F1FF0000FFFF and F6FF4001051F02), the zero vector
must be a single 0x00, and 2000 random vectors across nine magnitude decades
must round-trip within one quantization step. UpdateTimePacket and
SpawnPositionPacket cases now cover their new layers as well (776 alongside the
older versions), since both packets lost their common fields to per-layer
groups.

The csproj excludes two more generated files that need the unmodelled Particle
type, and LoginPacket, which needs NBT.

Checked: sandbox console runs clean, every assertion passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nine packets that arrived above protocol 772 had no spec at all; they need
nothing the codegen is missing, so they are modelled now:

- configuration.toClient.code_of_conduct (contents string) and
  configuration.toServer.accept_code_of_conduct (empty), both 773+.
- play.toClient.game_test_highlight_pos (two positions, 773+) and
  play.toClient.low_disk_space_warning (empty, 775+).
- play.toServer.attack and play.toServer.spectate_entity (entity id varint,
  775+) - two of the three packets the old use_entity union split into on 26.1.
- play.toClient.game_rule_values and play.toServer.set_game_rule (775+), with
  the new named type GameRule (name, value).
- play.toClient.login, which had no spec despite being the join packet. All
  nine wire layers from 736 to 776 are modelled; on 776 onlineMode is inserted
  before enforcesSecureChat, which three independent sources confirm against
  the bot-generated data.

The module in Login.fs is named LoginPlay so it does not shadow the DSL's
ProtocolState.Login case, which every spec has in scope.

SpawnInfo is deliberately left alone. Facts rename its death field's type from
DeathLocation to GlobalPos at 773, but the two types are byte-identical
(dimension identifier plus position), so the rename has no wire effect - and a
layer for it would not compile, because an api field cannot change its named
type between versions.

Checked: dotnet build clean; `gen` emits no new TODO markers; the sandbox
console runs every assertion clean; `coverage` now reports 210/257 on pv 776
against 201 before this batch. F# formatted with Fantomas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The union gap is closed on both sides. A union spec now renders to
Unions/<Name>.cs as a [Union] partial record whose cases are nested partial
records, and the two container paths that consume one are generated instead of
stubbed: a layout's readUnion becomes Union.Read(ref reader, protocolVersion,
disc), and on the write side the wire-only discriminator it pairs with is
derived from the model through Union.Discriminator(protocolVersion), followed
by Union.Write. Read branches on protocol version first, then switches on the
discriminator; Write switches on the case; a case written at a version whose
layer does not carry it throws.

Case naming keeps the spec's name while the parameter list is identical in
every layer that carries it, and suffixes the layer label otherwise, reusing
the packet layer-label helper. TeamAction therefore has CreatedVUntil764 and
CreatedV771_Last side by side, with Removed shared.

Case records shadowed the same-named types they carry - partial record
Rotations(Rotations Value) bound the parameter to the case, not the type - so
arm type references are namespace-qualified before rendering.

TeamsPacket and EntityMetadataEntry generate completely now and left the
allowlist; six stubs remain, none of them union-shaped: inlineUnion
(EncryptionResponse), SentinelArray, map columns, FixedBytes and array-of-array.

Around the codegen:

- TeamAction's 771+ arms read the team flags byte into the new TeamFlags
  bitflags spec instead of discarding it. With a real write path, a discarded
  byte meant every team this library sends carried friendly fire and
  see-friendly-invisible as off.
- The sandbox runtime gained a minimal NBT model, because without NbtTag no
  union compiles there at all. It unblocked more than unions: the csproj
  exclusion list drops from 30 files to 10.
- Spec/Unions/Codegen/UnionShapeProbe.fs is a codegen fixture, not protocol: it
  reproduces the three shapes EntityMetadataValue is built from (cases named
  after C# keywords, a case carrying a same-named type, a case carrying an
  array of a named type) so the sandbox actually binds them. It is never
  delivered.
- deliver-to-mcprotonet.ps1 refuses to deliver a set whose files reference an
  excluded type, listing every offender, before it touches the target
  directory. It refuses today: the Flow aggregates reference TeamsPacket while
  the unions are held back, and they stay held back until McProtoNet takes a
  plain Dunet reference.

Checked: dotnet build clean; gen emits the six allowlisted stubs and nothing
else; the sandbox console runs every assertion clean, including Teams
round-tripping at 764 and 772 with a byte-identical re-write, the flags byte
travelling as one u8, and the probe union round-tripping all five cases.
Fantomas reformatted parts of CSharp.fs that were not formatted at HEAD, so the
diff there is larger than the union work alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Titlehhhh

Copy link
Copy Markdown
Owner Author

Объединения на dunet (коммит 5938bf3)

Дыра кодогена закрыта. Спек объединения рендерится в Unions/<Имя>.cs как [Union] partial record с вложенными случаями; readUnion в раскладке превращается в Union.Read(ref reader, protocolVersion, disc), а на записи служебный дискриминатор выводится из модели через Union.Discriminator(protocolVersion).

Имена случаев: имя из спека сохраняется, пока форма одинакова во всех слоях, иначе к нему приписывается метка слоя — у TeamAction рядом живут CreatedVUntil764 и CreatedV771_Last, а Removed общий.

TeamsPacket и EntityMetadataEntry теперь генерируются целиком и вышли из списка заглушек. Осталось шесть, ни одна не про объединения: inlineUnion, SentinelArray, колонки карты, FixedBytes, массив массивов.

Попутно:

  • у TeamAction на 771+ байт флагов команды больше не выбрасывается, а читается в новый тип TeamFlags. С живой записью это была тихая потеря данных: дружественный огонь и видимость невидимок всегда уходили выключенными;
  • в рантайм песочницы добавлен минимальный NBT — без него там не собиралось ни одно объединение. Заодно список исключений песочницы усох с 30 файлов до 10;
  • скрипт доставки теперь отказывается доставлять набор, где доставляемые файлы ссылаются на исключённый тип, и печатает список. Сейчас он отказывается: агрегаты Flow ссылаются на TeamsPacket, а объединения придержаны.

Проверка: сборка чистая, генерат даёт шесть разрешённых заглушек и ничего сверх, консоль песочницы проходит все утверждения — Teams читается и пишется байт в байт на 764 и 772, флаги едут одним u8, пробный тип объединения гоняется по всем пяти случаям.

Решение владельца: берёт ли McProtoNet обычную ссылку на Dunet (тогда объединения едут в доставку), или ждём родных объединений C# 15. PrivateAssets="all" не подходит — с версии 1.11 пакет несёт настоящую сборку с атрибутом, и отражение по типу падает в рантайме.

Titlehhhh and others added 2 commits August 20, 2026 16:46
play.toServer.use_entity had no spec. Up to 774 the payload is a union behind
the mouse discriminator - interact carries a hand, attack carries nothing,
interact_at carries a hit position and a hand - with the sneaking flag after
it. From 775 (26.1) the union is gone: the server takes target, hand, an
LpVec3 target offset and sneaking, and the three modes became separate packets
(attack and spectate_entity already have specs).

New union spec InteractAction covers the pre-775 arms; the packet spec carries
both layers. It generates completely - no stub - so the union backend now has
a second, independent user besides Teams.

Sandbox: both eras round-trip. At 774 an interact_at goes out with
discriminator 2, reads back as InteractAt with its three floats and hand, and
re-writes byte-identical. At 776 the flat form round-trips with the quantized
offset landing inside one quantization step.

Coverage on pv 776: 211/257.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TeamsPacket and TeamAction leave the exclusion list: McProtoNet references
Dunet and gates versions through its own source generator, so the union
compiles there. ExplosionParticleEntry and ExplosionParticleInfo join the list
instead — they carry Particle, which McProtoNet does not model, the same reason
ExplosionPacket has always been held back.

Checked: a real delivery run into McProtoNet copied 267 files, the dangling-
reference guard stayed quiet, and McProtoNet.Protocol builds clean on net10.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Titlehhhh
Titlehhhh merged commit e8c083f into main Aug 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant