Skip to content

fix: size migration content SaveFlag enums like the live writer (v4.1.1) - #59

Merged
kamronbatman merged 1 commit into
mainfrom
kb/migration-saveflag-width
Sep 24, 2026
Merged

kamronbatman merged 1 commit into
mainfrom
kb/migration-saveflag-width

Conversation

@kamronbatman

@kamronbatman kamronbatman commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Problem

GenerateMigrationContentStruct emitted a single V{N}SaveFlag enum with no underlying type (so int), whatever the flag count. The live generator that wrote vN sizes its SaveFlag enums from the count: int up to 32 flags, ulong for 33-64, then one extra ulong enum (SaveFlag2, ...) per 64 flags.

  • 33-64 flags: bit 31 emits 0x80000000 into an int enum, which fails with CS0266: Cannot implicitly convert type 'uint' to 'int'. This blocks ModernUO #2670, which needs to migrate from BaseCreature v23 (54 save-flagged fields).
  • 65+ flags: the content struct reads one enum where the writer wrote several, so every field after the flags is misaligned.

Fix

The content struct now uses the same partitioning as the live side (Class.cs):

  • V{N}SaveFlag, V{N}SaveFlag2, ... are : ulong once there are more than 32 flags, with values from GenerateEnumValueLong
  • the constructor reads every flag enum before any field, as the writer does
  • each field is tested against its own enum and variable

Output for 32 or fewer flags is byte-identical, and every existing snapshot still matches.

Readonly fields can't shift the bit numbering: Class.cs already leaves them out of the migration schema, the same way the live side skips them for flag bits.

Tests

  • MigrationSaveFlagTests: a real round trip at 20, 40 and 70 flags. It compiles a v0 entity, serializes it with the live generator through a binary writer that sizes enums by their underlying type (as ModernUO does), then deserializes the bytes into v1 through MigrateFrom(V0Content). It checks every value (present values around bits 31/32 and 63/64, unset fields as null, a flagged bool) and that the whole stream was consumed. Before the fix, the 40 and 70 cases failed with the CS0266 above.
  • Snapshots/MigrationSaveFlagsMultiEnum (66 flags) pins the emitted V0SaveFlag : ulong / V0SaveFlag2.
  • Adds SourceGeneratorTestHelper.CompileAndLoad so tests can execute generated code. It shares its compile code with RunGeneratorAllOutputs.

dotnet test: 119 passed, 0 failed.

Release

Bumps Annotations, Generator and SchemaGenerator to 4.1.1.

The V{N}Content struct emitted one int-backed V{N}SaveFlag regardless of
flag count. The live generator that wrote vN uses ulong past 32 flags and
one extra ulong enum per 64 past 64, so migrating from a version with 33+
save-flagged fields failed with CS0266, and 65+ would misread the stream.

The content struct now partitions flags with the same rule, reads every
flag enum before any field, and tests each field against its own enum.
Output for 32 or fewer flags is unchanged.

Adds a round-trip test (20/40/70 flags) that serializes with the live
generator and migrates through MigrateFrom(V0Content), plus a
MigrationSaveFlagsMultiEnum snapshot.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@kamronbatman
kamronbatman merged commit 8caa147 into main Sep 24, 2026
2 checks 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