ModernUO/dev-docs/claude-skills/modernuo-configuration.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-configuration Trigger when adding server settings, reading config values, or working with modernuo.json or JsonConfig.

ModernUO Configuration System

When This Activates

  • Adding new server settings
  • Reading config values with ServerConfiguration
  • Using JsonConfig.Serialize/Deserialize<T>
  • Working with modernuo.json or custom config files

Key Rules

  1. Read settings in Configure() static method
  2. Use GetOrUpdateSetting() when you want the setting created with a default if missing
  3. Use GetSetting() for read-only access (won't write default to file)
  4. Custom config files use JsonConfig.Serialize/Deserialize<T>()
  5. Config path base: Distribution/Configuration/

ServerConfiguration

GetOrUpdateSetting (Creates Default If Missing)

// In Configure() method:
var maxAccounts = ServerConfiguration.GetOrUpdateSetting("accountHandler.maxAccountsPerIP", 1);
var saveDelay = ServerConfiguration.GetOrUpdateSetting("autosave.saveDelay", TimeSpan.FromMinutes(5));
var enabled = ServerConfiguration.GetOrUpdateSetting("mySystem.enabled", true);

If the key doesn't exist in modernuo.json, it writes the default value and returns it.

GetSetting (Read-Only)

var statMax = ServerConfiguration.GetSetting("stats.statMax", 100);
var usePub45 = ServerConfiguration.GetSetting("stats.usePub45StatGain", false);

Returns the default if key is missing but does NOT write to the config file.

Supported Types

ServerConfiguration.GetSetting(string key, int defaultValue)
ServerConfiguration.GetSetting(string key, bool defaultValue)
ServerConfiguration.GetSetting(string key, double defaultValue)
ServerConfiguration.GetSetting(string key, TimeSpan defaultValue)
ServerConfiguration.GetSetting<T>(string key, T defaultValue) where T : struct, Enum

SetSetting

ServerConfiguration.SetSetting("mySystem.customValue", "42");
// Immediately persisted to modernuo.json

Configuration Pattern

namespace Server.Custom;

public static class MySystem
{
    private static bool _enabled;
    private static int _maxItems;
    private static TimeSpan _cooldown;

    public static void Configure()
    {
        _enabled = ServerConfiguration.GetOrUpdateSetting("mySystem.enabled", true);
        _maxItems = ServerConfiguration.GetOrUpdateSetting("mySystem.maxItems", 100);
        _cooldown = ServerConfiguration.GetOrUpdateSetting("mySystem.cooldown", TimeSpan.FromMinutes(5));
    }

    // System logic uses _enabled, _maxItems, _cooldown...
}

Custom Config Files with JsonConfig

For complex configuration that doesn't fit in modernuo.json:

using Server.Json;

public static class MyComplexSystem
{
    private static MyConfig _config;
    private static readonly string ConfigPath =
        Path.Combine(Core.BaseDirectory, "Configuration/MySystem/config.json");

    public static void Configure()
    {
        _config = JsonConfig.Deserialize<MyConfig>(ConfigPath)
            ?? new MyConfig();
    }

    public static void SaveConfig()
    {
        JsonConfig.Serialize(ConfigPath, _config);
    }
}

public class MyConfig
{
    public bool Enabled { get; set; } = true;
    public int MaxItems { get; set; } = 100;
    public List<string> BlockedNames { get; set; } = new();
    public Dictionary<string, int> Scores { get; set; } = new();
}

JsonConfig Options

  • Pretty-printed (WriteIndented = true)
  • Comments allowed (ReadCommentHandling = Skip)
  • Trailing commas allowed
  • Null values omitted (WhenWritingNull)
  • Enums serialized as strings (JsonStringEnumConverter)
  • Built-in converters for: ClientVersion, Guid, Map, Point3D, Rectangle3D, TimeSpan, IPEndPoint, Type, WorldLocation, TextDefinition

modernuo.json Structure

Location: Distribution/Configuration/modernuo.json

{
  "assemblyDirectories": ["./Assemblies"],
  "dataDirectories": ["C:\\Ultima Online Classic"],
  "listeners": ["0.0.0.0:2593"],
  "settings": {
    "accountHandler.enableAutoAccountCreation": "True",
    "accountHandler.maxAccountsPerIP": "1",
    "autosave.enabled": "True",
    "autosave.saveDelay": "00:05:00",
    "world.savePath": "Saves",
    "stats.statMax": "100",
    "timer.initialPoolCapacity": "1024",
    "mySystem.enabled": "True"
  }
}

All settings are stored as strings in the settings dictionary.

Naming Convention for Keys

Use dot-separated hierarchical keys:

systemName.settingName
systemName.subSystem.settingName

Examples:

  • accountHandler.maxAccountsPerIP
  • stats.statMax
  • movement.delay.walkFoot
  • autosave.saveDelay

Anti-Patterns

  • Reading config in constructors: Use Configure() static method instead
  • Hardcoding values: Use ServerConfiguration.GetOrUpdateSetting() for tunable values
  • Complex objects in modernuo.json: Use JsonConfig with separate files instead
  • Not providing defaults: Always pass a sensible default value

Real Examples

  • ServerConfiguration: Projects/Server/Configuration/ServerConfiguration.cs
  • JsonConfig: Projects/Server/Json/JsonConfig.cs
  • Config usage: Projects/UOContent/Skills/SkillCheck.cs (Configure method)
  • Timer pool config: Projects/Server/Timer/Timer.Pool.cs
  • Main config: Distribution/Configuration/modernuo.json

See Also

  • dev-docs/configuration.md - Complete configuration documentation
  • dev-docs/claude-skills/modernuo-era-expansion.md - Expansion configuration
  • dev-docs/claude-skills/modernuo-events.md - Configure() pattern