feat: Nullable<T> fields, null-safe collection helpers, migration timer fix (v4.2.0) - #60
Merged
Merged
Conversation
…er fix (v4.2.0)
- Fix: a migration content struct with a save-flagged Timer no longer emits
`{Name} = default;` for an absent flag (CS0120/CS0103). Rules now own the
absent assignment (ISerializableMigrationRule.GenerateMigrationAbsentAssignment);
TimerMigrationRule sets Next/Delay to DateTime.MinValue/TimeSpan.MinValue.
- Fix: nullable reference annotations (string?, Dictionary<int, string?>,
string?[]) crashed the generator (SG0001). Serialized type names now drop
reference-type `?` and keep Nullable<T>.
- Feature: NullableMigrationRule serializes Nullable<T> fields (int?, enums,
Point3D?, [SerializationGenerator] structs, and nested in collections) as a
HasValue bool followed by the value through T's own rule.
- Collection helpers mutate the backing field, lazily create a null collection
on add/insert/replace (keeping [SortedSetComparer]), tolerate null on
remove/clear, and mark dirty only on an actual change (set Add, dictionary
TryAdd, Remove results, non-empty Clear).
- Bump Generator, SchemaGenerator and Annotations to 4.2.0.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problems
Timer. For an absent flag,ContentStruct.csemitted{property.Name} = default;itself.TimerMigrationRuledeclares{Name}Next/{Name}Delayon the struct, not{Name}, so the line bound to the entity's instance member (CS0120) or to nothing (CS0103). Found while bumping ModernUO'sBaseCreaturefrom v23 to v24 (PendingDeleteTimer).string?,Dictionary<int, string?>orstring?[]reached the rules as"string?".PrimitiveTypeMigrationRulethrewSG0001(InvalidOperationException).Nullable<T>support.int?,Point3D?or a nullable generated struct failed withSG3007(no migration rule).AddToX/RemoveFromX/ClearXcalled straight through the property. They threw NullReferenceException on a lazily-null collection and calledMarkDirtyeven when nothing changed.Changes
ISerializableMigrationRule.GenerateMigrationAbsentAssignment.MigrationRulekeeps{Name} = default;, so other rules' output is byte-identical.TimerMigrationRuleemitsNext = DateTime.MinValue; Delay = TimeSpan.MinValue;, the same "no timer was running" values the present branch produces.ToSerializedTypeName()drops reference-type?and keepsNullable<T>asint?. The rules engine and every collection/KVP rule use it for type names and rule arguments.NullableMigrationRule, registered first. It writes aHasValuebool, then the value through T's own rule, so it composes with enums, UO types,[EncodedInt], generated structs, list elements and dictionary values. Schema:"rule": "NullableMigrationRule", "ruleArguments": [T, T's rule, ...T's arguments].fieldChanged, andallowFieldChangecould veto the new collection.AddToX/InsertIntoX/ReplaceInXcreate the collection when null, keeping[SortedSetComparer]. Types without an accessible parameterless ctor keep the old behavior.RemoveFromX/RemoveFromXAt/ClearXdo nothing when the collection is null.MarkDirtyonly fires on an actual change: setAddreturns true, dictionaryTryAdd(orContainsKey+Addfor otherIDictionarytypes),Removereturns true,Clearon a non-empty collection.Behavior changes for callers
AddToXon a dictionary no longer throws on a duplicate key; it does nothing.ClearXon an empty collection no longer marks dirty.virtualproperty override.Tests
Snapshots/MigrationSaveFlagTimer: save-flagged anchored, wall-clock and legacy drift timers in a v0 schema.MigrationSaveFlagTests.MigrateFrom_SeesTimerSentinelsWhenTheSaveFlagIsAbsent: writes v0 with the live generator (timers running or absent, anchored and wall-clock), then migrates. It asserts a positiveDelaywhen running,MinValuesentinels when absent, and that the stream stays aligned.Snapshots/NullableCollectionsandDataStructureMethodTests: nullable-annotated fields, plus a runtime run of every helper, on null and populated collections, checking theMarkDirtycount after each step.Snapshots/NullableValueTypesandNullableValueTypeTests: a live round trip of everyNullable<T>shape, populated and null. Also a v0 to v1 migration whose schema comes from schema mode via a newGenerateMigrationSchemashelper, not hand-written JSON.CS0103, and the nullable fixtures fail on main withSG0001/SG3007.After merge
Run the
Build and Publish NuGet Packagesworkflow. Then ModernUO #2670 can bump to 4.2.0 and regenerateBaseCreature.v24.json.