modernuo/dev-docs/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

7.8 KiB

ModernUO Configuration System

This document covers ModernUO's configuration system, including ServerConfiguration for global settings, JsonConfig for custom config files, and best practices.

Overview

ModernUO has two configuration mechanisms:

  1. ServerConfiguration: Global key-value settings stored in modernuo.json
  2. JsonConfig: Custom JSON configuration files for complex data structures

ServerConfiguration

Defined in Projects/Server/Configuration/ServerConfiguration.cs.

Reading Settings

GetSetting (Read-Only)

Returns the configured value or the default. Does NOT write the default to the config file.

int statMax = ServerConfiguration.GetSetting("stats.statMax", 100);
bool enabled = ServerConfiguration.GetSetting("mySystem.enabled", true);
TimeSpan delay = ServerConfiguration.GetSetting("autosave.saveDelay", TimeSpan.FromMinutes(5));
double rate = ServerConfiguration.GetSetting("stats.gainChanceMultiplier", 1.0);
Expansion exp = ServerConfiguration.GetSetting("core.expansion", Expansion.ML);

Supported types:

  • int
  • bool
  • double
  • TimeSpan
  • T where T : struct, Enum

GetOrUpdateSetting (Read-Write)

Returns the configured value. If the key doesn't exist, writes the default to the config file and returns it.

int maxAccounts = ServerConfiguration.GetOrUpdateSetting("accountHandler.maxAccountsPerIP", 1);
bool autoCreate = ServerConfiguration.GetOrUpdateSetting("accountHandler.enableAutoAccountCreation", true);
int poolSize = ServerConfiguration.GetOrUpdateSetting("timer.initialPoolCapacity", 1024);

Use this when you want new settings to appear in modernuo.json automatically with sensible defaults.

SetSetting

Directly sets a value and immediately persists to disk:

ServerConfiguration.SetSetting("mySystem.enabled", "true");
ServerConfiguration.SetSetting("mySystem.maxItems", "100");

Configuration Pattern

Read settings in your Configure() static method:

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));
    }

    public static void Initialize()
    {
        if (!_enabled)
            return;

        // System initialization that depends on config values
    }
}

Key Naming Convention

Use dot-separated hierarchical keys:

systemName.settingName
systemName.subSystem.settingName

Examples from the codebase:

accountHandler.enableAutoAccountCreation
accountHandler.enablePlayerPasswordCommand
accountHandler.maxAccountsPerIP
autosave.enabled
autosave.saveDelay
world.savePath
world.useMultithreadedSaves
movement.delay.walkFoot
movement.delay.runFoot
stats.statMax
stats.gainChanceMultiplier
stats.primaryStatGainChance
stats.gainDelay
stats.petGainDelay
stats.usePub45StatGain
timer.initialPoolCapacity
timer.maxPoolCapacity
core.enableIdleCPU

modernuo.json Structure

Located at 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"
  }
}

Key points:

  • All settings are stored as strings in the settings dictionary
  • Top-level fields (assemblyDirectories, dataDirectories, listeners) are structural
  • GetOrUpdateSetting adds new entries to settings automatically

JsonConfig

For complex configuration that doesn't fit in flat key-value pairs, use JsonConfig.

Defined in Projects/Server/Json/JsonConfig.cs.

API

// Deserialize from file (returns default if file doesn't exist)
T config = JsonConfig.Deserialize<T>(filePath);
T config = JsonConfig.Deserialize<T>(filePath, customOptions);

// Serialize to file (creates directory if needed)
JsonConfig.Serialize(filePath, config);
JsonConfig.Serialize(filePath, config, customOptions);

// Default options (available for customization)
JsonSerializerOptions options = JsonConfig.DefaultOptions;

Default JSON Options

WriteIndented = true                           // Pretty-printed
AllowTrailingCommas = true                     // Forgiving parser
ReadCommentHandling = JsonCommentHandling.Skip // Comments allowed
DefaultIgnoreCondition = WhenWritingNull       // Null values omitted
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping

Built-in Converters

JsonConfig includes converters for ModernUO types:

  • ClientVersion
  • Guid
  • Map
  • Point3D
  • Rectangle3D
  • TimeSpan
  • IPEndPoint
  • Type
  • WorldLocation
  • TextDefinition
  • All enums (as strings via JsonStringEnumConverter)

Custom Config File Pattern

using Server.Json;

namespace Server.Custom;

public class MySystemConfig
{
    public bool Enabled { get; set; } = true;
    public int MaxItems { get; set; } = 100;
    public TimeSpan Cooldown { get; set; } = TimeSpan.FromMinutes(5);
    public List<string> BlockedNames { get; set; } = new();
    public Dictionary<string, int> Scores { get; set; } = new();
}

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

    public static void Configure()
    {
        _config = JsonConfig.Deserialize<MySystemConfig>(ConfigPath);

        if (_config == null)
        {
            _config = new MySystemConfig();
            JsonConfig.Serialize(ConfigPath, _config);
        }
    }

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

This creates a config file like:

{
  "Enabled": true,
  "MaxItems": 100,
  "Cooldown": "00:05:00",
  "BlockedNames": [],
  "Scores": {}
}

Custom Converters

Add custom converters via JsonConfig.GetOptions():

var options = JsonConfig.GetOptions(new MyCustomConverterFactory());
var data = JsonConfig.Deserialize<MyType>(path, options);

Configuration File Locations

File Purpose
Distribution/Configuration/modernuo.json Main server settings
Distribution/Configuration/expansion.json Target expansion
Distribution/Data/expansions.json Expansion metadata
Distribution/Configuration/ Custom config directory

Custom config files should be placed under Distribution/Configuration/ in a subdirectory named after your system.

Best Practices

  1. Read in Configure() -- called before Initialize(), ensures settings are available early
  2. Use GetOrUpdateSetting for new features -- ensures defaults appear in config file
  3. Use GetSetting for optional/advanced settings -- doesn't clutter config file
  4. Use JsonConfig for complex data -- lists, dictionaries, nested objects
  5. Provide sensible defaults -- system should work without manual configuration
  6. Use era-aware defaults -- Core.LBR ? 125 : 100 for values that vary by expansion
  7. Document key names -- use clear hierarchical naming

Key File References

File Description
Projects/Server/Configuration/ServerConfiguration.cs ServerConfiguration class
Projects/Server/Json/JsonConfig.cs JsonConfig utility
Distribution/Configuration/modernuo.json Main config file
Projects/UOContent/Skills/SkillCheck.cs Config usage example
Projects/Server/Timer/Timer.Pool.cs Config usage example