docs: update serialization docs and skills for generator v4
Replaces order-based linkage ([SerializableFieldSaveFlag]/[SerializableFieldDefault]) with field-side [SaveFlag(nameof(...))], [TimerDrift]/[DeserializeTimerField] with [DeserializeTimer(nameof(Method), wallClock)], and documents the [SerializableField] setter hooks (allowFieldChange/fieldChanged), the anchored-time semantics for drifting timers, [AnchoredDateTime], and the timer MigrateFrom pattern. Narrows [SerializableProperty] guidance to custom getters. Also fixes the documented [SerializableField] signature (the saveIf parameter never existed) and refreshes real-code examples that were converted in the v4 migration PRs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b042edcf0b
commit
76bbcd88c4
7 changed files with 219 additions and 63 deletions
|
|
@ -457,16 +457,24 @@ set
|
|||
```
|
||||
Without this, changes won't be saved.
|
||||
|
||||
Most RunUO custom setters only clamp the value or run side effects after assignment. Those
|
||||
convert to a plain `[SerializableField]` with the `allowFieldChange`/`fieldChanged` hooks,
|
||||
which handle the equality check and `MarkDirty()` for you -- reserve `[SerializableProperty]`
|
||||
for custom getters (see `dev-docs/serialization.md`).
|
||||
|
||||
### 3. Field Ordering
|
||||
The `[SerializableField(N)]` index determines serialization order. Choose a logical order and don't change it after the first save — or increment the version.
|
||||
|
||||
### 4. Conditional Serialization
|
||||
Use `[SerializableFieldSaveFlag]` and `[SerializableFieldDefault]` to skip default values:
|
||||
Use `[SaveFlag]` on the serializable field to skip default values (the second method is
|
||||
optional -- omit it and the field keeps its default at load):
|
||||
```csharp
|
||||
[SerializableFieldSaveFlag(0)]
|
||||
[SerializableField(0)]
|
||||
[SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))]
|
||||
private int _maxItems;
|
||||
|
||||
private bool ShouldSerializeMaxItems() => _maxItems != -1;
|
||||
|
||||
[SerializableFieldDefault(0)]
|
||||
private int MaxItemsDefaultValue() => -1;
|
||||
```
|
||||
|
||||
|
|
@ -479,12 +487,15 @@ private List<Mobile> _followers;
|
|||
```
|
||||
|
||||
### 6. DateTime Fields
|
||||
Use `[DeltaDateTime]` to survive server restarts:
|
||||
Use `[AnchoredDateTime]` to survive server restarts -- the value is shifted by downtime at
|
||||
load, so the remaining time is preserved and idle saves stay byte-stable:
|
||||
```csharp
|
||||
[DeltaDateTime]
|
||||
[AnchoredDateTime]
|
||||
[SerializableField(0)]
|
||||
private DateTime _expireTime;
|
||||
```
|
||||
(`[DeltaDateTime]` is the legacy equivalent; it rewrites bytes on every save. Converting an
|
||||
existing field between the two changes the wire format and requires a version bump.)
|
||||
|
||||
### 7. Keeping Manual Serialization (Rare)
|
||||
Some edge cases still need manual serialization. If a type has complex conditional logic that can't be expressed with attributes, you can implement `ISerializable` manually. But this is rare — try attributes first.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue