ModernUO/dev-docs/claude-skills/migrate-from-runuo/migrate-timers.md
Kamron Batman 4f9bc1d9f6
feat: Adds AI skills to migrate from RunUO (#2366)
## Summary

Adds comprehensive RunUO → ModernUO migration documentation and Claude AI skills to help shard owners and script authors convert RunUO 2.7 code to ModernUO.

- **10 migration skills** (`dev-docs/claude-skills/migrate-from-runuo/`) — system-by-system conversion guides (foundation, serialization, timers, gumps, packets, property lists, commands/events, persistence, items/mobiles, systems/engines)
- **12 reference docs** (`dev-docs/runuo-migration-docs/`) — deep-reference with before/after examples, API mapping tables, edge cases, and gotchas
- **Updated existing skills** — `modernuo-timers`, `modernuo-serialization`, and `modernuo-threading` now document that `Serialize()` runs on background threads and timers are not thread-safe
- **Updated `CLAUDE.md`** — added migration skill lookup table

### Key migration patterns covered
- Manual `Serialize()`/`Deserialize()` → source-generated `[SerializableField]`
- `Packet` class hierarchy → static `SpanWriter`/`SpanReader` methods
- `Timer` subclasses → `TimerExecutionToken` fire-and-forget
- `Gump` → `StaticGump<T>`/`DynamicGump` with builders
- `EventSink.WorldSave` → `GenericPersistence`
- `ObjectPropertyList` → `IPropertyList` with string hole rules
- Universal changes: naming (`m_` → `_`), `[Constructable]` → `[Constructible]`, logging, spatial queries
2026-03-13 00:33:45 -07:00

2 KiB

name description
migrate-timers Trigger: when converting RunUO Timer subclasses, Timer.DelayCall patterns, or TimerPriority usage to ModernUO fire-and-forget timers. Covers: Timer subclass elimination, TimerExecutionToken, callback patterns, timer restoration.

RunUO -> ModernUO Timer Migration

When This Activates

  • Converting nested Timer subclasses with OnTick()
  • Converting Timer.DelayCall() patterns
  • Removing TimerPriority usage
  • Restoring timers after deserialization

Conversion Steps

  1. Move OnTick() logic to a method on the parent class
  2. Replace new InternalTimer(this).Start() with Timer.StartTimer(..., callback, out _token)
  3. Add private TimerExecutionToken _token; (NOT serialized)
  4. Cancel in OnAfterDelete(): _token.Cancel();
  5. Restore in [AfterDeserialization]: re-call Timer.StartTimer(...)
  6. Delete the nested Timer class entirely
  7. Remove all TimerPriority references

Quick Mapping

RunUO ModernUO
new Timer(delay).Start() Timer.StartTimer(delay, callback)
new Timer(delay, interval).Start() Timer.StartTimer(delay, interval, callback, out token)
timer.Stop() token.Cancel()
Timer.DelayCall(delay, callback) Timer.StartTimer(delay, callback)
Timer.DelayCall(delay, stateCallback, state) Timer.DelayCall(delay, callback, state)
TimerPriority.XXX Remove -- timer wheel auto-schedules
Timer started in Deserialize() [AfterDeserialization] method

Anti-Patterns

  • Serializing TimerExecutionToken -- it's a struct tracking a pooled timer
  • Starting timers in Deserialize() -- world isn't loaded yet, use [AfterDeserialization]
  • Lambda closures on hot paths -- use Timer.DelayCall with state parameters instead

See Also

  • dev-docs/runuo-migration-docs/03-timers.md -- detailed migration reference
  • dev-docs/timers.md -- complete ModernUO timer system
  • dev-docs/claude-skills/modernuo-timers.md -- ModernUO timer skill