EN UO Topic scripts runtime - LIHACHTETAN/ClassicUO-Bad-Newbie-CSharp-Scripts-IDE GitHub Wiki

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

Scripts & Runtime

Home · C# · IDE · UO API · A–Z · Examples · Languages

Start, pause, stop scripts and procedures, instance information, events and wait. Terminate and StopScript operate at different execution levels.

Sections

Topic Commands and explanations
StartScript UO.StartScript
StopScript UO.StopScript
StopAllScripts UO.StopAllScripts
PauseResumeScript UO.PauseResumeScript
Script State UO.GetScriptState
Script List UO.GetScriptsList · UO.GetScriptsCount
Script Name UO.GetScriptName · UO.SetScriptName
Script Path UO.GetScriptPath
CallSub UO.CallSub
SubExists UO.SubExists
Events UO.SetEvent · UO.SetEventProc
Timers UO.Timer · UO.Wait
Disconnect Behaviour UO.GetPauseScriptOnDisconnectStatus · UO.SetPauseScriptOnDisconnectStatus

All theme commands

Script Lifecycle

UO.StartScript · UO.StopScript · UO.StopAllScripts · UO.PauseResumeScript · UO.GetScriptName · UO.SetScriptName · UO.GetScriptPath · UO.GetScriptsCount · UO.GetScriptsList · UO.GetScriptState · UO.Exec · UO.Launch

Procedures / Events

UO.CallSub · UO.SubExists · UO.FunRunning · UO.Terminate · UO.TerminateAll · UO.SetEvent · UO.SetEventProc

Timers / Waiting

UO.Wait · UO.Waiting · UO.Timer · UO.Time · UO.Date

Disconnect Behaviour

UO.GetPauseScriptOnDisconnectStatus · UO.SetPauseScriptOnDisconnectStatus

Examples on the topic

Each command card has several independent scenarios: direct code, functions and classes. Below are selected short scenarios; restrictions and full analysis are in the cards.

UO.GetScriptsList

Parameters, result and all scenarios

var indices = UO.GetScriptsList();
foreach (var index in indices)
{
    UO.Print(Convert.ToString(index) + ": " + UO.GetScriptName(index));
}

UO.TerminateAll

Parameters, result and all scenarios

for (int cycle = (int)(1); cycle <= 10; cycle += (int)(1))
{
    if (Truth(UO.Dead()))
    {
        UO.Print("Character is dead: stopping all C# scripts");
        UO.TerminateAll();
    }
    await Delay(250);
}

Task Delay(object? milliseconds) => WaitAsync(checked((int)Convert.ToDouble(milliseconds)));
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;

Complete command reference


UO.CallSub

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

C# signatures

void UO.CallSub(object? subrutineName);

Launches the C# delegate previously registered in the current execution via RegisterRoutine(name, handler). The call returns control before the helper completes; One registered task cannot be imposed on itself.

Parameters

  • subrutineName is the string name given by RegisterRoutine. Whitespace around the edges is removed; Registered name searches are not case sensitive.

Return value

void - no result; an unknown name CallSub causes an error, and re-running an already active name is skipped.

Behavior

UO.FunRunning(name) == 0 can also occur immediately after cancellation is requested. It does not mean the previous callback has finished its finally block. Another CallSub or Exec for the same local name is skipped until the previous worker actually exits.

  • Registration and tasks belong to the same execution. Another character, profile or subsequent run has its own registry.
  • RegisterRoutine accepts Action, Func<Task>, or a delegate with an array of string arguments to Exec. Removing the registration does not run the code and prevents further searches for the name.
  • The main script must remain active while its helpers are needed: after its completion, tasks are canceled and registrations and subscriptions are released.
  • FunRunning is not a manager index. GetScriptsList and GetScriptState describe independent file launches; the local callback does not receive a separate index.
  • The callback error is passed to the main script at the next check. WaitAsync, UO.Wait and instrumented C# loops support pause, cancel and event queue processing.

Uploaded C#-files

In addition to local delegates, the current manager supports explicitly loaded C#-entries with a key like Child.csx::Main. SubExists and FunRunning accept such a key; CallSub runs the file without Args, and Exec("exec Child.csx::Main 'north gate' 42") passes Args. The key with spaces is enclosed in quotes inside Exec. A unique short name is acceptable, an ambiguous name does not pick a random file. Local registration takes precedence. A file launch gets its own context and lifetime in the current world/manager; local delegates belong to the execution that registered them.

Standalone C# scripts

Each example can be saved as a separate file .csx and run in IDE. The UO object and wait functions provide the host of the current game session. Replace demo serial names, coordinates and addresses with your own.

Simple example

Run this code as a separate .csx file. Arguments appear directly in the call; replace demonstration names, serials and addresses with your own.

RegisterRoutine("Hello", () => UO.Print("Hello from a routine"));
UO.CallSub("Hello");
while (UO.FunRunning("Hello") != 0) await WaitAsync(25);

1. Start observer and continue Main - direct script

Parameters and execution flow:

  • WeightWatch is the name of the procedure below. Main returns 77 without waiting for three samples; 77 is the result of Main, not CallSub.
  • Save the example as a separate file, load it into IDE/manager and run Main so that the rest of the procedures end up in the directory. WeightWatch/ManaWatch - training procedures fully defined here: three weight/mana measurements with an interval of UO.Wait(1000), that is, 1000 ms, output only when entering the world. This is not continuous monitoring; a pause can prolong time. For the example with two tasks, simultaneous launch of procedures must be allowed.
var registrationWeightWatch = RegisterRoutine("WeightWatch", WeightWatch);
UO.CallSub("WeightWatch");
while (UO.FunRunning("WeightWatch") != 0) await WaitAsync(25);
return 77;

void WeightWatch()
{
    dynamic sample = 0;
    for (sample = (int)(1); sample <= 3; sample += (int)(1))
    {
        if (Truth(UO.Connected()))
        {
            UO.Print("Weight: " + Convert.ToString(UO.Weight()));
        }
        UO.Wait(1000);
    }
}

bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;

2. Do not restart an active or suspended task - script function

Parameters and execution flow:

  • StartIfIdle checks the definition first, then the activity. false means the request was not sent. true means only the request CallSub was sent, not confirmation of start or completion.
  • Save the example as a separate file, load it into IDE/manager and run Main so that the rest of the procedures end up in the directory. WeightWatch/ManaWatch - training procedures fully defined here: three weight/mana measurements with an interval of UO.Wait(1000), that is, 1000 ms, output only when entering the world. This is not continuous monitoring; a pause can prolong time. For the example with two tasks, simultaneous launch of procedures must be allowed.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
    var registrationWeightWatch = RegisterRoutine("WeightWatch", WeightWatch);
    var launched = StartIfIdle("WeightWatch");
    while (UO.FunRunning("WeightWatch") != 0)
        await WaitAsync(25);
    return launched;
    return null;
}

dynamic StartIfIdle(dynamic name)
{
    if (!(Truth(UO.SubExists(name))))
    {
        return false;
    }

    if (Truth(UO.FunRunning(name)))
    {
        return false;
    }

    UO.CallSub(name);
    return true;
}

void WeightWatch()
{
    dynamic sample = 0;
    for (sample = (int)(1); sample <= 3; sample += (int)(1))
    {
        if (Truth(UO.Connected()))
        {
            UO.Print("Weight: " + Convert.ToString(UO.Weight()));
        }

        UO.Wait(1000);
    }
}

bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;

3. Two Independent Observers - Scenario Class

Parameters and execution flow:

  • The first CallSub starts WeightWatch, the second - ManaWatch. The runs are independent and do not exchange local variables; the order of their output is not guaranteed.
  • Save the example as a separate file, load it into IDE/manager and run Main so that the rest of the procedures end up in the directory. WeightWatch/ManaWatch - training procedures fully defined here: three weight/mana measurements with an interval of UO.Wait(1000), that is, 1000 ms, output only when entering the world. This is not continuous monitoring; a pause can prolong time. For the example with two tasks, simultaneous launch of procedures must be allowed.
return await new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenarioAsync();
class ScriptScenario
{
    private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
    private readonly Func<int, Task> WaitAsync;
    private readonly string[] Args;
    private readonly CancellationToken Cancellation;
    private readonly Action Checkpoint;
    public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
    {
        UO = api;
        WaitAsync = wait;
        Args = args;
        Cancellation = cancellation;
        Checkpoint = checkpoint;
    }

    async public Task<object?> RunScenarioAsync()
    {
        var registrationWeightWatch = RegisterRoutine("WeightWatch", WeightWatch);
        var registrationManaWatch = RegisterRoutine("ManaWatch", ManaWatch);
        UO.CallSub("WeightWatch");
        UO.CallSub("ManaWatch");
        while (UO.FunRunning("WeightWatch") != 0 || UO.FunRunning("ManaWatch") != 0)
            await WaitAsync(25);
        return null;
    }

    void WeightWatch()
    {
        dynamic sample = 0;
        for (sample = (int)(1); sample <= 3; sample += (int)(1))
        {
            if (Truth(UO.Connected()))
            {
                UO.Print("Weight: " + Convert.ToString(UO.Weight()));
            }

            UO.Wait(1000);
        }
    }

    void ManaWatch()
    {
        dynamic sample = 0;
        for (sample = (int)(1); sample <= 3; sample += (int)(1))
        {
            if (Truth(UO.Connected()))
            {
                UO.Print("Mana: " + Convert.ToString(UO.Mana()));
            }

            UO.Wait(1000);
        }
    }

    bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
    private IDisposable RegisterRoutine(string name, Action handler) => global::ClassicUO.CSharp.CSharpScriptContext.Current.RegisterRoutine(name, handler);
    private IDisposable RegisterRoutine(string name, Func<Task> handler) => global::ClassicUO.CSharp.CSharpScriptContext.Current.RegisterRoutine(name, handler);
    private IDisposable RegisterRoutine(string name, Action<string[]> handler) => global::ClassicUO.CSharp.CSharpScriptContext.Current.RegisterRoutine(name, handler);
    private IDisposable RegisterRoutine(string name, Func<string[], Task> handler) => global::ClassicUO.CSharp.CSharpScriptContext.Current.RegisterRoutine(name, handler);
}

Command implementation

C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.

UO.CallSub(subrutineName) — UO.CallSub(String)

Parameters: subrutineName (String)

Result: void / Unit.

During a call:

Internal implementation code; this is not a standalone script.

case "CallSub":
                string called = Convert.ToString(arguments[0]) ?? "";
                if (Exists(called)) Start(called);
                else if (events is ICSharpScriptHost host && host.HasCSharpScript(called)) host.ExecuteCSharpScript("exec \"" + called + "\"");
                else throw new ArgumentException($"Register the C# routine or load a C# script entry named '{called}' before calling it.");
                return true;

Internal implementation code; this is not a standalone script.

public void CallSub(string subrutineName) => bridge.Exec(subrutineName);

UO.Date

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

C# signatures

int UO.Date();

Reads the local calendar date of the computer with the number YYMMDD. Useful for naming a daily series of activities or detecting date changes.

Parameters

There are no parameters.

Return value

int YYMMDD: 2026-09-30 -> 260930. There are no delimiters or leading zeros stored. It is not duration, not Unix time, and not true/false.

Behavior

  • There are no parameters, no forms with arguments. Empty parentheses only read the time: the call does not wait, start a timer, or send packets to the game.
  • RealTimeSource.Now reads DateTime.Now: the computer's local date/time, not UTC, not the shard time, and not the game clock. Time zone, daylight saving time and manual clock change affect the results. Each call captures a different moment.
  • Only the last two digits of the year are stored: both 2000-01-01 and 2100-01-01 give 101. Different centuries are no different. Individual Date and Time calls may fall on opposite sides of midnight.
  • Do not subtract calendar codes to calculate delay. Use Timer() and two marks of one run. The calendar clock may go back.

Standalone C# scripts

Each example can be saved as a separate file .csx and run in IDE. The UO object and wait functions provide the host of the current game session. Replace demo serial names, coordinates and addresses with your own.

Simple example

Run this code as a separate .csx file. Arguments appear directly in the call; replace demonstration names, serials and addresses with your own.

The result of one call is printed in the journal. A number is not converted to bool; an array is displayed as its element count.

var result = UO.Date();
UO.Print("Date: " + Convert.ToString(result));

1. Name the daily resource collection series - direct script

Parameters and execution flow:

  • The result of the call without parameters is saved once. sessionLabel is a string like harvest-260930. Print only shows the name: the file is not created, resource collection does not start.
var dateStamp = UO.Date();
var sessionLabel = "harvest-" + Convert.ToString(dateStamp);
UO.Print(sessionLabel);

2. End series when local day changes - script function

Parameters and execution flow:

  • originalDate stores the original date. Timer limits observation to 60 seconds, UO.Wait(1000) checks approximately once per second. Changing the date ends Main. The example does not wait indefinitely for midnight and does not claim that the day has changed on the server.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
    var originalDate = UO.Date();
    var started = Environment.TickCount;
    while (Truth(Environment.TickCount - started < 60.0))
    {
        if (Truth(UO.Date() != originalDate))
        {
            UO.Print("Local date changed; end this batch");
            return null;
        }

        await Delay(1000);
    }

    UO.Print("No date change observed within one minute");
    return null;
}

Task Delay(object? milliseconds) => WaitAsync(checked((int)Convert.ToDouble(milliseconds)));
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;

3. Select year, month and day - script class

Parameters and execution flow:

  • MonthPart(stamp) is shown in its entirety: division rounded down and MOD 100 extracts the month. day — last two digits; shortYear—incomplete year. For 260930 we get 26, 9, 30.
return  new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
    private readonly global::ClassicUO.CSharp.CSharpGameApi UO