EN UO Topic scripts runtime - LIHACHTETAN/ClassicUO-Bad-Newbie-CSharp-Scripts-IDE GitHub Wiki
Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
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.
| 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 |
UO.StartScript · UO.StopScript · UO.StopAllScripts · UO.PauseResumeScript · UO.GetScriptName · UO.SetScriptName · UO.GetScriptPath · UO.GetScriptsCount · UO.GetScriptsList · UO.GetScriptState · UO.Exec · UO.Launch
UO.CallSub · UO.SubExists · UO.FunRunning · UO.Terminate · UO.TerminateAll · UO.SetEvent · UO.SetEventProc
UO.Wait · UO.Waiting · UO.Timer · UO.Time · UO.Date
UO.GetPauseScriptOnDisconnectStatus · UO.SetPauseScriptOnDisconnectStatus
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.
Parameters, result and all scenarios
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
UO.Print(Convert.ToString(index) + ": " + UO.GetScriptName(index));
}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;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
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.
-
subrutineNameis the string name given by RegisterRoutine. Whitespace around the edges is removed; Registered name searches are not case sensitive.
void - no result; an unknown name CallSub causes an error, and re-running an already active name is skipped.
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.
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.
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.
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);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;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;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);
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: subrutineName (String)
Result: void / Unit.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionApiUO.CallSub. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.Exec:bridge.Exec(subrutineName).
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);Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
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.
There are no parameters.
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.
- 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.
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.
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));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);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;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