ModernUO/dev-docs/runuo-migration-docs/11-api-reference.md
Kamron Batman e07416902a
feat: derive the Running bit from the step pace and fix step-pacing bursts (#2599)
Stacked on #2594. Fixes jerky creature movement (lich / Fast-bucket melee chases) by choosing the client animation flag from the actual step pace instead of a caller-supplied `run` argument, and fixes three step-pacing defects in the move budget found while verifying it with paired server/client traces.

### Why

The `Direction.Running` bit does nothing for creatures server-side (`Mobile.OnMove` reads it only for the player throttle and stealth reveal). Its whole effect is on the client, which animates each step over a fixed time selected by that bit: walk 400 ms / run 200 ms on foot, 200 / 100 ms mounted. ClassicUO queues up to 5 steps and *drops* the sixth, so a creature stepping every 300 ms while flagged as walking backs the queue up until it snaps forward — the observed jerk.

The `run` argument never carried the one fact that matters (the step interval). RunUO passed `true` in combat / `false` for pets and gated it on `dist > 5`; #2271 flipped every combat site to `false`; pets passed `currentDistance > 2`. None of that is a coherent signal.

### What

**Pace-derived run flag**
- `BaseAI.ShouldRun()`: run iff the effective step delay (move clock + badly-hurt inflation) is shorter than `Movement.WalkFootDelay` / `WalkMountDelay` (mounted or flying) — with a continuity rule: an *isolated* step (taken after standing at least a walk interval) goes out as a walk, because the client renders each step alone and a lone run-flagged step is a 200 ms dart. Only a continuing cadence flags run; a true sprinter (pace under the run interpolation) always runs, since a walk-rendered first step would flood the client's 5-step queue. This reproduces RunUO's close-in feel (its `dist > 5` gate) from first principles.
- `DoMoveImpl` stamps the bit; it is the single place the flag is set.
- `run` removed from `MoveTo`, `WalkMobileRange`, `ApproachTarget`, `MoveToPoint`, `MoveToWithGroup`, `MoveToWithCollisionAvoidance`, the move intent, and `PathFollower.Follow`. All 35 call sites updated. **API change** for custom scripts — documented in the RunUO migration docs (`09-items-mobiles-creatures.md`, `11-api-reference.md`) and `content-patterns.md` § Creature Speeds.

**Move-budget pacing fixes** (each confirmed by UTC-aligned server/client step traces)
- A stall no longer banks catch-up steps: the budget's snap-to-now released up to three steps in ~300 ms when a creature resumed chasing after standing beside its target — rendered as a teleport.
- Debt accrual removed entirely: a step landing sub-period late (think-grid vs budget misalignment during reactive mirroring) kept the remainder and fired a follow-up ~100 ms later — a dart pair. `ConsumeMoveBudget` now paces every step from when it was actually taken; in continuous pursuit the move-wake lands within wheel resolution of the deadline, so the cost is single-digit-ms drift.
- Net effect: a creature can never step faster than its pace, verified across a full chase session (zero sub-pace steps; metronomic 350 ms cadence for a 0.3 s lich).

- Test fixture now runs `Movement.Configure()` (the walk delays were 0 in tests).

### Accepted trade-off

Animal (LOW group) bodies without a run animation slide on their stand frames when flagged as running. Most are slow enough to stay flagged as walking; the client-side fallback is in ClassicUO/ClassicUO#1930.

### Tests

`RunFlagTests`: foot thresholds (0.3 / 0.125 run; 0.4 / 0.45 / 1.05 walk), flying uses the mount threshold, badly-hurt inflation flips a 0.35 s creature back to walk, a real `DoMove` stamps the bit, isolated steps drop to walk (sprinters keep running), a stall restarts the cadence with no banked steps, and a late step earns no quicker follow-up. Full suite: 837 Server + 747 UOContent green.
2026-08-30 16:48:52 -07:00

10 KiB

API Reference — RunUO to ModernUO Mapping

Quick Lookup

Alphabetical by RunUO API name. Use Ctrl+F / Cmd+F to search.

Attributes

RunUO ModernUO Notes
[Constructable] [Constructible] Spelling change
[CommandProperty(AccessLevel)] [SerializedCommandProperty(AccessLevel)] On [SerializableField] fields
[CommandProperty(AccessLevel)] [CommandProperty(AccessLevel)] On [SerializableProperty] properties (unchanged)
[CorpseName("name")] public override string CorpseName => "name"; Attribute → property override
[Constructable] [Constructible] Must use using ModernUO.Serialization;
[Usage("...")] [Usage("...")] Unchanged
[Description("...")] [Description("...")] Unchanged
N/A [Aliases("a1", "a2")] New in ModernUO
N/A [SerializationGenerator(ver, enc)] New — replaces manual Serialize/Deserialize
N/A [SerializableField(idx)] New — auto-generates property + serialization
N/A [SerializableProperty(idx)] New — custom property serialization
N/A [InvalidateProperties] New — auto-calls InvalidateProperties on set
N/A [AfterDeserialization] New — post-deserialize hook
N/A [DeltaDateTime] New — relative DateTime storage
N/A [EncodedInt] New — variable-length int encoding
N/A [InternString] New — string interning on load
N/A [Tidy] New — auto-clean null entries in collections
N/A [CanBeNull] New — nullable reference field
N/A [TypeAlias("old.name")] New — backward-compatible type mapping

Classes

RunUO ModernUO Notes
BinaryFileWriter IGenericWriter (via GenericPersistence) No manual file writing
BinaryFileReader IGenericReader (via GenericPersistence) No manual file reading
GenericWriter IGenericWriter Interface now
GenericReader IGenericReader Interface now
Gump DynamicGump or StaticGump<T> See 04-gumps.md
ObjectPropertyList (parameter) IPropertyList Interface for GetProperties
Packet (base class) Removed — use static methods See 05-packets.md
PacketWriter SpanWriter Ref struct
PacketReader SpanReader Ref struct
Timer (subclass pattern) Timer.StartTimer() + TimerExecutionToken See 03-timers.md
TextRelay Removed — use info.GetTextEntry(id) Returns string directly
RelayInfo RelayInfo (passed with in) in RelayInfo in OnResponse

Constructors

RunUO ModernUO Notes
MyItem(Serial serial) : base(serial) DELETE Auto-generated
BaseCreature(AI, Fight, 10, 1, 0.2, 0.4) BaseCreature(AI, Fight) Extra params have defaults

Events (EventSink)

RunUO ModernUO Signature
EventSink.Login EventSink.Connected LoginEventArgsAction<Mobile>
EventSink.Logout EventSink.Disconnected LogoutEventArgsAction<Mobile>
EventSink.WorldSave EventSink.WorldSave WorldSaveEventArgsAction
EventSink.WorldLoad EventSink.WorldLoad Action
EventSink.ServerStarted EventSink.ServerStarted Action
EventSink.Crashed EventSink.ServerCrashed Renamed
EventSink.Speech EventSink.Speech Action<SpeechEventArgs>
EventSink.Movement EventSink.Movement Action<MovementEventArgs>
EventSink.AggressiveAction EventSink.AggressiveAction Action<AggressiveActionEventArgs>
EventSink.AccountLogin EventSink.AccountLogin Action<AccountLoginEventArgs>
EventSink.SocketConnect EventSink.SocketConnect Action<SocketConnectEventArgs>
EventSink.Shutdown EventSink.Shutdown Action
EventSink.BeforeWorldSave Removed Use WorldSave
EventSink.PlayerDeath PlayerMobile.PlayerDeathEvent CodeGeneratedEvent
EventSink.CreatureDeath BaseCreature.CreatureDeathEvent CodeGeneratedEvent

Event Delegates

RunUO ModernUO
new WorldSaveEventHandler(Save) Save (direct method reference)
new WorldLoadEventHandler(Load) Load
new LoginEventHandler(OnLogin) OnConnected
new LogoutEventHandler(OnLogout) OnDisconnected
new SpeechEventHandler(OnSpeech) OnSpeech
new TimerCallback(Method) Method
new TimerStateCallback(Method) Method (typed overloads)
new OnPacketReceive(Handler) &Handler (function pointer)

Gump Methods

RunUO ModernUO Notes
AddPage(0) builder.AddPage() 0 is default
AddBackground(...) builder.AddBackground(...) On builder
AddAlphaRegion(...) builder.AddAlphaRegion(...) On builder
AddLabel(x, y, hue, text) builder.AddLabel(x, y, hue, text) On builder
AddHtml(x, y, w, h, text, bg, scroll) builder.AddHtml(x, y, w, h, text, background: bg, scrollbar: scroll) Named params
AddHtmlLocalized(...) builder.AddHtmlLocalized(...) On builder
AddButton(x, y, n, p, id, Reply, 0) builder.AddButton(x, y, n, p, id) Reply is default
AddTextEntry(...) builder.AddTextEntry(...) On builder
AddCheck(x, y, off, on, state, id) builder.AddCheckbox(x, y, off, on, state, id) Renamed
Closable = false builder.SetNoClose() Property → method
Dragable = false builder.SetNoMove() Renamed
Resizable = false builder.SetNoResize() Property → method
Disposable = false builder.SetNoDispose() Property → method
from.SendGump(new G(...)) G.DisplayTo(from, ...) Static entry point
from.CloseGump(typeof(G)) from.CloseGump<G>() Generic
from.HasGump(typeof(G)) from.HasGump<G>() Generic

Gump Response

RunUO ModernUO
OnResponse(NetState, RelayInfo) OnResponse(NetState, in RelayInfo)
info.TextEntries[i].Text info.GetTextEntry(id)
info.IsSwitched(id) info.IsSwitched(id) (same)
info.ButtonID info.ButtonID (same)

Methods

RunUO ModernUO Notes
DateTime.UtcNow Core.Now Server time source
Console.WriteLine(...) logger.Information(...) Structured logging
World.FindMobile(serial) reader.ReadEntity<Mobile>() In deserialization
World.FindItem(serial) reader.ReadEntity<Item>() In deserialization
reader.ReadMobile() reader.ReadEntity<Mobile>() Generic method
reader.ReadItem() reader.ReadEntity<Item>() Generic method
reader.ReadInt() reader.ReadInt() Same
writer.Write((int)value) writer.Write(value) No cast needed
writer.WriteEncodedInt(value) writer.WriteEncodedInt(value) Same
InvalidateProperties() InvalidateProperties() Same, or use [InvalidateProperties]
this.MarkDirty() this.MarkDirty() NEW — required in custom setters
MoveTo(m, run, range) MoveTo(m, range) run removed; the Running bit is derived from the step pace (BaseAI.ShouldRun)
WalkMobileRange(m, steps, run, min, max) WalkMobileRange(m, steps, min, max) Same
PathFollower.Follow(run, range) Follow(range) Same

Networking

RunUO ModernUO Notes
ns.Send(new MyPacket(...)) ns.SendMyPacket(...) Extension method
Packet.Compile() Not needed Stack-allocated
Packet.SetStatic() Not needed Reuse via buffer[0] != 0 guard
PacketHandlers.Register(...) IncomingPackets.Register(...) Function pointers
m_Stream.Write(value) writer.Write(value) SpanWriter
pvSrc.ReadInt32() reader.ReadInt32() SpanReader
pvSrc.ReadString() reader.ReadAsciiSafe() Explicit encoding
pvSrc.ReadUnicodeStringSafe() reader.ReadBigUniSafe() Explicit name

Persistence

RunUO ModernUO Notes
EventSink.WorldSave += Save class X : GenericPersistence Subclass
EventSink.WorldLoad += Load override Deserialize(IGenericReader) Method
new BinaryFileWriter(path, true) Handled by framework Automatic
BinaryFileReader / FileStream Handled by framework Automatic
Directory.CreateDirectory(...) Handled by framework Automatic

Properties

RunUO ModernUO Notes
Name = "text" (in constructor) public override string DefaultName => "text"; Property override
Name (custom text) DefaultName (property override) For fixed names
int Prop { get { return m_X; } } int Prop => _x; Expression-bodied
int Prop { get { return m_X; } set { m_X = value; } } Auto-generated by [SerializableField] Delete manual property

Timer

RunUO ModernUO Notes
new InternalTimer().Start() Timer.StartTimer(..., out token) Fire-and-forget
Timer.DelayCall(delay, callback) Timer.StartTimer(delay, callback) Similar
Timer.DelayCall(delay, stateCallback, state) Timer.DelayCall(delay, callback, state) Typed state
timer.Stop() token.Cancel() Struct, safe to call multiple times
timer.Running token.Running Same concept
TimerPriority.XXX Removed Timer wheel auto-schedules
Timer in Deserialize() [AfterDeserialization] Post-load hook
timer != null check token.Running check Value type, always valid

Threading / Performance

RunUO ModernUO Notes
lock (_obj) Remove Single-threaded
volatile Remove keyword Single-threaded
ConcurrentDictionary Dictionary Single-threaded
Task.Run(...) Use Timer.StartTimer() Game loop only
new Thread(...) Forbidden Game loop only
ArrayPool<T>.Shared STArrayPool<T>.Shared No-lock pool
new List<T>() (hot path) PooledRefList<T>.Create() Zero-alloc
World.Mobiles.Values iteration map.GetMobilesInRange<T>() Spatial query
World.Items.Values iteration map.GetItemsInRange<T>() Spatial query

Usings

RunUO ModernUO Notes
(implicit) using ModernUO.Serialization; For serialization attributes
(implicit) using Server.Logging; For logging
(implicit) using Server.Gumps; For gump extension methods
(implicit) using Server.Collections; For PooledRefList