ModernUO/dev-docs/claude-skills/modernuo-events.md
Kamron Batman 1391c563fe
chore: Adds AI instructions and SKILLs for ModernUO codebase (#2347)
Summary

  - Adds CLAUDE.md at repo root with 14 terse code audit rules (always loaded, low token cost)
  - Adds pointer files for other AI tools: AGENTS.md (Codex), GEMINI.md, .github/COPILOT-INSTRUCTIONS.md (Copilot), .cursorrules (Cursor) — all redirect to CLAUDE.md as single source of truth
  - Gitignores /.claude so personal AI config isn't distributed
  - Moves Claude skills to dev-docs/claude-skills/ (opt-in, not auto-loaded)
  - Adds 14 dev-docs covering codebase conventions

  Code Audit Rules (in CLAUDE.md)

  1. LINQ tiered rules (Tier 1 free, Tier 2 warm, Tier 3 forbidden)
  2. No Console.WriteLine — use LogFactory.GetLogger()
  3. No concurrency primitives in game code
  4. No World.Mobiles/World.Items iteration
  5. Clean up refs in OnDelete()/OnAfterDelete()
  6. Cancel timers in OnDelete()/OnAfterDelete()
  7. STArrayPool<T>.Shared not ArrayPool<T>.Shared
  8. PooledRefList<T> not new List<T>() on hot paths
  9. Serialization: partial class, [Constructible], no serialized TimerExecutionToken
  10. No Task.Run/new Thread() in game code
  11. Never assume era — ask which expansion
  12. _camelCase fields, PascalCase properties/methods
  13. No empty gumps — use DisplayTo() pattern
  14. PropertyList string literals must be {} holes, cliloc-as-argument uses :#
2026-03-01 11:42:19 -08:00

5.5 KiB

name description
modernuo-events Trigger when subscribing to or creating game events, working with EventSink or generated events. When hooking into player login, death, speech, or other game events.

ModernUO Events System

When This Activates

  • Subscribing to game events (login, logout, death, speech)
  • Creating new events
  • Working with EventSink static events
  • Using [GeneratedEvent] / [OnEvent] attributes

Key Rules

  1. Subscribe to events in Configure() static method
  2. EventSink events are static event Action<T> -- subscribe with +=
  3. Event handlers must match the delegate signature
  4. Unsubscribe if your system can be disabled (prevent leaks)

EventSink Events

Subscribe in Configure():

public static void Configure()
{
    EventSink.Connected += OnPlayerConnected;
    EventSink.Disconnected += OnPlayerDisconnected;
    EventSink.Logout += OnLogout;
    EventSink.ServerStarted += OnServerStarted;
}

Available Events

Core Lifecycle

EventSink.ServerStarted      // Action              -- Server fully started
EventSink.Shutdown            // Action              -- Server shutting down
EventSink.WorldLoad           // Action              -- World loaded from saves
EventSink.WorldSave           // Action              -- World save triggered
EventSink.WorldSavePostSnapshot // Action<WorldSavePostSnapshotEventArgs>

Player Connection

EventSink.Connected           // Action<Mobile>      -- Player connected
EventSink.BeforeDisconnected  // Action<Mobile>      -- About to disconnect
EventSink.Disconnected        // Action<Mobile>      -- Player disconnected
EventSink.Logout              // Action<Mobile>      -- Player logged out

Account

EventSink.AccountLogin        // Action<AccountLoginEventArgs>
// AccountLoginEventArgs: .State (NetState), .Username, .Password, .Accepted (set), .RejectReason (set)

Communication

EventSink.Speech              // Action<SpeechEventArgs>
// SpeechEventArgs: .Mobile, .Speech, .Type, .Hue, .Keywords, .Handled (set), .Blocked (set)

Combat

EventSink.AggressiveAction    // Action<AggressiveActionEventArgs>
// AggressiveActionEventArgs: .Aggressed, .Aggressor, .Criminal

Movement

EventSink.Movement            // Action<MovementEventArgs>
// MovementEventArgs: .Mobile, .Direction, .Blocked (set)

Network

EventSink.SocketConnect       // Action<SocketConnectEventArgs>
// SocketConnectEventArgs: .Address, .AllowConnection (set)

EventSink.ServerCrashed       // Action<ServerCrashedEventArgs>
// ServerCrashedEventArgs: .Exception, .Close (set)

UI

EventSink.PaperdollRequest    // Action<Mobile, Mobile>  -- (beholder, beheld)

Event Handler Pattern

public static class MyEventSystem
{
    public static void Configure()
    {
        EventSink.Connected += OnConnected;
        EventSink.Speech += OnSpeech;
    }

    private static void OnConnected(Mobile m)
    {
        if (m is PlayerMobile pm)
        {
            pm.SendMessage("Welcome back!");
        }
    }

    private static void OnSpeech(SpeechEventArgs e)
    {
        if (e.Speech.InsensitiveContains("help"))
        {
            e.Mobile.SendMessage("How can I help you?");
            e.Handled = true;  // Prevent further processing
        }
    }
}

Generated Events (Code-Generated)

For custom events on game entities, use the CodeGeneratedEvents package:

Defining Events

// On the class that fires the event:
[GeneratedEvent(nameof(PlayerLoginEvent))]
public static partial void PlayerLoginEvent(PlayerMobile player);

Subscribing to Events

// On the handler class:
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
public static void HandleLogin(PlayerMobile player)
{
    // Handle the event
}

Known Generated Events

  • PlayerMobile.PlayerLoginEvent
  • PlayerMobile.PlayerDeathEvent
  • BaseCreature.CreatureDeathEvent

External reference: https://github.com/modernuo/CodeGeneratedEvents

EventArgs Pool Pattern

Some EventArgs use object pooling to avoid allocation:

// Movement uses pooling:
var args = MovementEventArgs.Create(mobile, dir);
EventSink.InvokeMovement(args);
args.Free();  // Return to pool

// AggressiveAction uses pooling:
var args = AggressiveActionEventArgs.Create(aggressed, aggressor, criminal);
EventSink.InvokeAggressiveAction(args);
args.Free();

Anti-Patterns

  • Subscribing outside Configure(): Won't be called during startup
  • Not checking player type: EventSink.Connected fires for all mobiles, cast to PlayerMobile if needed
  • Blocking in event handlers: Event handlers run on the game loop -- keep them fast
  • Not unsubscribing: If system can be disabled, unsubscribe to prevent leaks

Real Examples

  • EventSink core: Projects/Server/Events/EventSink.cs
  • Speech events: Projects/Server/Events/SpeechEvent.cs
  • Movement events: Projects/Server/Events/MovementEvent.cs
  • Account events: Projects/Server/Events/AccountLoginEvent.cs
  • World events: Projects/Server/Events/EventSink.cs (WorldLoad, WorldSave, ServerStarted, Shutdown)
  • Connection events: Projects/Server/Events/SocketConnectionEvent.cs
  • GumpSystem subscription: Projects/UOContent/Gumps/Base/GumpSystem.cs

See Also

  • dev-docs/events.md - Complete events documentation
  • dev-docs/claude-skills/modernuo-content-patterns.md - Content hooks
  • dev-docs/claude-skills/modernuo-configuration.md - Configure() pattern