docs: update serialization docs and skills for generator v4 (#2588)

## Summary

Brings every serialization-related doc, skill, and the CLAUDE.md rule in line with generator **v4** (adopted in #2586/#2587). No code changes.

**Updated surface, everywhere it was referenced:**
- `[SerializableFieldSaveFlag(order)]` / `[SerializableFieldDefault(order)]` → `[SaveFlag(nameof(Should), nameof(Default))]` on the field (second method optional).
- `[TimerDrift]` + `[DeserializeTimerField(order)]` → `[DeserializeTimer(nameof(Method), wallClock)]` on the field, with the anchored-time semantics spelled out: drifting by default (downtime preserves the remaining delay, idle saves byte-stable), `wallClock: true` for absolute deadlines, restart method invoked **only when a timer was running** (no sentinel), and the timer `MigrateFrom` pattern (`XxxNext`/`XxxDelay`) for wire-format changes.
- New `[SerializableField]` documentation: the real signature (the documented `saveIf` parameter never existed) plus the setter hooks — `allowFieldChange` (`bool Method(ref T value)`: coerce/veto before assignment) and `fieldChanged` (`void Method(T oldValue, T newValue)` after) — with the generated pipeline and the SG3015/SG3018 guardrails.
- `[SerializableProperty]` guidance narrowed to its remaining purpose: custom getters and setter semantics the hooks cannot express.
- `[AnchoredDateTime]` documented alongside `[DeltaDateTime]` (now marked legacy, with the version-bump warning for converting between them).

**Files:** `dev-docs/serialization.md`, `dev-docs/timers.md`, `dev-docs/claude-skills/modernuo-serialization.md`, `dev-docs/claude-skills/modernuo-timers.md`, `dev-docs/runuo-migration-docs/02-serialization.md`, `dev-docs/runuo-migration-docs/03-timers.md`, and a condensed v4 addition to CLAUDE.md rule 9.

**Example refresh:** the skill's `BagOfSending` "custom properties" example was itself converted in #2587 — it is now quoted in its real post-conversion form as the canonical hooks example; the real-examples list points at `BaseWeapon.cs` for custom getters and `BaseLight.cs` for the drifting-timer + `MigrateFrom` pattern.

Verified by grep: zero references to the removed v3 attribute names remain anywhere in `dev-docs/` or `CLAUDE.md`.
This commit is contained in:
Kamron Batman 2026-08-22 18:29:27 -07:00 committed by GitHub
parent b042edcf0b
commit b992c7b955
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 219 additions and 63 deletions

View file

@ -147,18 +147,27 @@ public partial class DecayingItem : Item
}
```
### [DeserializeTimerField] Pattern (for Timer fields)
### [DeserializeTimer] Pattern (for Timer fields)
Required on every serializable `Timer` member. Drifting by default: the next tick is stored
as anchored time, so server downtime does not consume the remaining delay. Use
`wallClock: true` for absolute deadlines (delay is negative if it passed during downtime).
The method is invoked **only when a timer was running at save** — no sentinel to check.
```csharp
[SerializableField(0, setter: "private")]
[DeserializeTimer(nameof(DeserializeEvaluateTimer), wallClock: true)]
private Timer _evaluateTimer;
[DeserializeTimerField(0)]
private void DeserializeEvaluateTimer(TimeSpan delay)
{
_evaluateTimer = Timer.DelayCall(delay, EvaluationInterval, Evaluate);
}
```
Switching an existing timer between drifting and `wallClock` changes the wire format — bump
the class's `[SerializationGenerator]` version and add a `MigrateFrom` (the old content
struct exposes `XxxDelay`, `TimeSpan.MinValue` when no timer ran).
### Custom Timer Class (When You Need Complex Logic)
```csharp
private class DecayTimer : Timer