Repository navigation
EN UO Topic scripts runtime
Українська · Русский · 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;
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;
}
public object? RunScenario()
{
var stamp = UO.Date();
var month = MonthPart(stamp);
var day = stamp % 100;
var shortYear = stamp / 10000;
UO.Print(Convert.ToString(shortYear) + "/" + Convert.ToString(month) + "/" + Convert.ToString(day));
return null;
}
dynamic MonthPart(dynamic stamp)
{
return stamp / 100 % 100;
}
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: int / Integer.
During a call:
Internal implementation code; this is not a standalone script.
public int Date()
{
var now = timeSource.Now;
return (now.Year % 100) * 10000 + now.Month * 100 + now.Day;
}Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.Exec(object? parameters);Executes a text game command or runs a registered C# delegate or a loaded C# file.
-
parameters- command string: for example, "useskill Hiding", "exec Worker 42" or "exec Child.csx::Main 'north gate' 42". An expression C# like 2+3 inside a string is not automatically evaluated.
void: no result. Returning control does not mean the end of the launch or confirmation of the game action by the server.
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.
- The local registry RegisterRoutine is checked first. "Name args", "exec Name args" and "_exec Name args" are supported. The delegate receives string[]: the argument 42 remains the string "42", and '00123' retains leading zeros. Single or double quotes concatenate an argument with spaces; '' specifies an empty string. An unclosed quote causes an error. In this route, the backslash is kept literally; Do not use the C# escape syntax within pre-written command text.
- The local delegate runs asynchronously. To wait for it to finish, use FunRunning and WaitAsync. Restarting the active name does not create a copy and does not remove the pause. The helper error is passed to the main script at the checkpoint.
- Explicitly loaded file entries are started by the current manager with its own Args and lifetime. Just accepting the launch request does not mean that the file has finished working.
- Regular game commands are passed to the native dispatcher. The parameter contracts of the corresponding UO-command are stored there; Print/Msg/Say/ConsolePrint/TextPrint combine the rest of the line into text. For precise C# types and expressions, use a direct call like UO.UseSkill("Hiding"). An unknown command, invalid arguments, or quotes can cause an exception to be caught by try/catch.
- Console aliases: sayu → Say, closeuo → CloseHandle, CountOnGround → CountGround, clearignorelist → IgnoreReset, canceltarget → CancelTarget, cancelmove → MoveOff.
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.Exec("exec Hello");
while (UO.FunRunning("Hello") != 0) await WaitAsync(25);Parameters and execution flow:
- Hiding is the only argument to UseSkill. Upon entering the world, the client sends a request to use the skill; the success of hiding is determined by the server. Same operation by direct call: UO.UseSkill("Hiding").
UO.Exec("useskill Hiding");Parameters and execution flow:
- SelectSavedRoute is defined below and records the name of the selected route. The quotation marks around 00123 preserve leading zeros. Exec runs the registered delegate asynchronously. The loop with FunRunning and WaitAsync explicitly waits for it to finish, then the script returns the string 00123. This is a training name save, not a movement command.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
var registration = RegisterRoutine("SelectSavedRoute", (Action<string[]>)(arguments =>
{
if (arguments.Length == 1)
SelectSavedRoute(arguments[0]);
}));
UO.Exec("SelectSavedRoute '00123'");
while (UO.FunRunning("SelectSavedRoute") != 0)
await WaitAsync(25);
return UO.GetGlobal("manual", "savedRoute");
return null;
}
void SelectSavedRoute(dynamic name)
{
UO.SetGlobal("manual", "savedRoute", name);
}Parameters and execution flow:
- Run this C# script: RegisterRoutine associates the name WeightWatch with the declared function of the current execution. exec WeightWatch starts three weight measurements at intervals 1000 ms and does not wait for them to complete. The main script remains active while the local assistant is running; The assistant does not have a separate manager index. If the task is already paused, Exec again does not resume it.
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);
UO.Exec("exec WeightWatch");
while (UO.FunRunning("WeightWatch") != 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);
}
}
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);
}Parameters and execution flow:
- Replace the path with the existing C# file with your Main procedure. startscript loads the file and asks Main to run. The quotes belong to the text command; path backslashes are preserved. See IDE for startup errors, as Exec does not return an acknowledgment.
UO.Exec("startscript 'C:\\UO Scripts\\Gather.csx'");C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: parameters (String)
Result: void / Unit.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionRuntime.Exec. -
InjectionScript.Runtime.InjectionRuntime.Tokenize:Tokenize(parameters). -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.Exec:bridge.Exec(parts[1].Text). -
InjectionScript.Runtime.InjectionRuntime.TryHandleLegacyConsoleCommand:TryHandleLegacyConsoleCommand(command, rawArguments). -
InjectionScript.Runtime.InjectionRuntime.NormalizeConsoleAlias:NormalizeConsoleAlias(command). -
InjectionScript.Runtime.InjectionRuntime.IsRemainderTextCommand:IsRemainderTextCommand(command). -
InjectionScript.Runtime.InjectionRuntime.ToConsoleValue:ToConsoleValue(token.Text). -
InjectionScript.Runtime.InjectionRuntime.CallNativeSubrutine:CallNativeSubrutine(command, args).
Internal implementation code; this is not a standalone script.
case "Exec":
var words = CommandWords(Convert.ToString(arguments[0]) ?? "");
if (words.Length == 0) return false;
int offset = words[0].Equals("exec", StringComparison.OrdinalIgnoreCase) || words[0].Equals("_exec", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
if (words.Length > offset && Exists(words[offset]))
{ Start(words[offset], words.Skip(offset + 1).ToArray()); return true; }
if (words.Length > offset && events is ICSharpScriptHost scriptHost
&& (offset == 1 || scriptHost.HasCSharpScript(words[offset])))
{
string command = Convert.ToString(arguments[0]) ?? "";
if (words[0].Equals("_exec", StringComparison.OrdinalIgnoreCase)) command = "exec" + command.TrimStart()[5..];
scriptHost.ExecuteCSharpScript(command); return true;
}
return false;Internal implementation code; this is not a standalone script.
public void Exec(string parameters)
{
ConsoleToken[] parts = Tokenize(parameters).ToArray();
if (parts.Length == 0)
return;
string command = parts[0].Text;
if (command.Equals("exec", StringComparison.OrdinalIgnoreCase))
{
if (parts.Length != 2 || string.IsNullOrWhiteSpace(parts[1].Text))
throw new NativeSubrutineException("Usage: exec script_function");
bridge.Exec(parts[1].Text);
return;
}
ConsoleToken[] arguments = parts.Skip(1).ToArray();
string[] rawArguments = arguments.Select(token => token.Text).ToArray();
if (TryHandleLegacyConsoleCommand(command, rawArguments))
return;
command = NormalizeConsoleAlias(command);
if (IsRemainderTextCommand(command) && rawArguments.Length > 0)
arguments = new[] { new ConsoleToken(string.Join(" ", rawArguments), true) };
else if (command.Equals("ColorPrint", StringComparison.OrdinalIgnoreCase) && rawArguments.Length > 1)
arguments = new[] { arguments[0], new ConsoleToken(string.Join(" ", rawArguments.Skip(1)), true) };
else if (command.Equals("CharPrint", StringComparison.OrdinalIgnoreCase) && rawArguments.Length > 2)
arguments = new[] { arguments[0], arguments[1], new ConsoleToken(string.Join(" ", rawArguments.Skip(2)), true) };
InjectionValue[] args = arguments.Select(token => token.Quoted
? new InjectionValue(token.Text) : ToConsoleValue(token.Text)).ToArray();
CallNativeSubrutine(command, args);
}Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.FunRunning(object? subrutineName);Checks whether the registered C#-callback with the specified name is executed. Result 1 enables manual pause; a completed or canceled task gives 0.
-
subrutineNameis the string name given by RegisterRoutine. Whitespace around the edges is removed; Registered name searches are not case sensitive.
int: 1 - condition is met, 0 - not met. Save the result of one call if multiple checks need it.
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", async () => await WaitAsync(100));
UO.CallSub("Hello");
UO.Print("Running flag: " + UO.FunRunning("Hello"));
while (UO.FunRunning("Hello") != 0) await WaitAsync(25);Parameters and execution flow:
- First, RegisterRoutine associates the name with the C# function. FunRunning checks the task of this execution, including the pause. The main script remains active until the helper completes.
var registration = RegisterRoutine("WeightWatch", WatchWeight);
if (UO.FunRunning("WeightWatch") == 0) UO.CallSub("WeightWatch");
while (UO.FunRunning("WeightWatch") != 0) await WaitAsync(25);
async Task WatchWeight()
{
for (int sample = 0; sample < 3; sample++)
{
UO.Print($"Weight: {UO.Weight()}");
await WaitAsync(250);
}
}Parameters and execution flow:
- The async delegate changes the shared counter via
Interlocked. After FunRunning=0 the result is read throughVolatile. This is a local callback; it does not have a separate index GetScriptsList.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
int completed = 0;
var registration = RegisterRoutine("Worker", async () =>
{
await WaitAsync(150);
Interlocked.Exchange(ref completed, 1);
});
UO.CallSub("Worker");
UO.Print($"Worker active: {UO.FunRunning("Worker") != 0}");
while (UO.FunRunning("Worker") != 0)
await WaitAsync(25);
return Volatile.Read(ref completed) == 1;
return null;
}Parameters and execution flow:
- Terminate selects the registered task of the current execution. Waiting for WaitAsync to use her cancellation token.
finallyperforms cleaning; further game actions of the canceled task are not 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()
{
int cleanupDone = 0;
var registration = RegisterRoutine("Worker", async () =>
{
try
{
await WaitAsync(5000);
}
finally
{
Interlocked.Exchange(ref cleanupDone, 1);
}
});
UO.CallSub("Worker");
await WaitAsync(100);
if (UO.FunRunning("Worker") != 0)
UO.Terminate("Worker");
while (UO.FunRunning("Worker") != 0)
await WaitAsync(25);
while (Volatile.Read(ref cleanupDone) == 0)
await WaitAsync(25);
return true;
return null;
}
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: int / Integer.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionApiUO.FunRunning. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.FunRunning:bridge.FunRunning(subrutineName).
Internal implementation code; this is not a standalone script.
case "FunRunning":
string runningName = Convert.ToString(arguments[0]) ?? "";
result = IsRunning(runningName) || events is ICSharpScriptHost runningHost && runningHost.IsCSharpScriptRunning(runningName) ? 1 : 0;
return true;Internal implementation code; this is not a standalone script.
public int FunRunning(string subrutineName) => bridge.FunRunning(subrutineName) ? 1 : 0;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
dynamic UO.GetPauseScriptOnDisconnectStatus();Reads the script pause setting when the manager is disabled.
There are no parameters.
int 1 — PauseScriptsOnDisconnect enabled; 0 - disabled.
- Reads the manager flag, not the current pause of an individual script. The call itself does not pause or unpause.
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.
object? result = UO.GetPauseScriptOnDisconnectStatus();
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));Parameters and execution flow:
- The value describes the manager setting.
var enabled = UO.GetPauseScriptOnDisconnectStatus();
UO.Print(Convert.ToString(enabled));Parameters and execution flow:
- The configuration flag and connection are read independently.
return RunScenario();
object? RunScenario()
{
var pauseOnDisconnect = UO.GetPauseScriptOnDisconnectStatus();
var online = UO.Connected();
UO.Print(Convert.ToString(pauseOnDisconnect) + " " + Convert.ToString(online));
return null;
}Parameters and execution flow:
- The variable contains a snapshot of the setting at the time of reading.
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 saved = UO.GetPauseScriptOnDisconnectStatus();
await Delay(100);
UO.Print(Convert.ToString(saved));
return null;
}
Task Delay(object? milliseconds) => WaitAsync(checked((int)Convert.ToDouble(milliseconds)));
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: dynamic / Any.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.GetPauseScriptOnDisconnectStatus:bridge.GetPauseScriptOnDisconnectStatus().
Internal implementation code; this is not a standalone script.
case "getpausescriptondisconnectstatus": return new InjectionValue(bridge.GetPauseScriptOnDisconnectStatus());Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
string UO.GetScriptName(object? scriptIndex);Reads the display name of the active run.
-
scriptIndex- Mandatory ScriptIndex - integer, index from zero from fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
string: display name or "" if there is no index. An existing launch can also have its name explicitly set to the empty string.
- Running and paused launches are taken into account; those completed and having received a cancellation request are excluded. A running script usually takes into account itself. Simply loading the IDE tab is not considered a launch.
- Indexes are the current positions in startup order. Starting/stopping scripts can shift positions. Different calls do not form a common atomic snapshot; re-read the list before the next control command.
- GetScriptsList gives the indices, GetScriptsCount the quantity, GetScriptState the three-state code. Don't substitute one for the other or treat any non-null result as true.
- ScriptIndex=0 - the first current launch, not necessarily calling the script. SetScriptName changes the display name without renaming the file.
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.GetScriptName(0);
UO.Print("GetScriptName: " + Convert.ToString(result));Parameters and execution flow:
- Run the given C#-script. The number 0, where it is passed, denotes the index; empty parentheses mean no arguments. The text Print is an example message only.
- ScriptIndex=0 - the first current launch, not necessarily calling the script. SetScriptName changes the display name without renaming the file.
- string: display name or "" if there is no index. An existing launch can also have its name explicitly set to the empty string.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
var index = 0;
var name = UO.GetScriptName(index);
UO.Print(name);Parameters and execution flow:
- This is a specific example of command sharing. Array indexing starts from zero; Please check the length before handling. UO.Wait(250), where it is used, waits 250 milliseconds.
- ScriptIndex=0 - the first current launch, not necessarily calling the script. SetScriptName changes the display name without renaming the file.
- string: display name or "" if there is no index. An existing launch can also have its name explicitly set to the empty string.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
return RunScenario();
object? RunScenario()
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
var name = UO.GetScriptName(index);
UO.Print(Convert.ToString(index) + " = " + name);
}
return null;
}Parameters and execution flow:
- Below Main is the entire function. Its parameters and result are explained separately from the API-command it calls.
- DescribeScript checks the status and concatenates the name with the path. Pausing between calls is possible; "missing" is the value of our function, not the result of GetScriptName.
- string: display name or "" if there is no index. An existing launch can also have its name explicitly set to the empty string.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
UO.Print(DescribeScript(0));
return null;
}
dynamic DescribeScript(dynamic index)
{
if (Truth(UO.GetScriptState(index) == 0))
{
return "missing";
}
return UO.GetScriptName(index) + " | " + UO.GetScriptPath(index);
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: scriptIndex (Any)
Result: string / String.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.GetScriptName:bridge.GetScriptName(Arg(0)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Arg:Arg(0).
Internal implementation code; this is not a standalone script.
case "getscriptname": return new InjectionValue(bridge.GetScriptName(Arg(0)));Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
string UO.GetScriptPath(object? scriptIndex);Reads the path of the active startup source file.
-
scriptIndex- Mandatory ScriptIndex - integer, index from zero from fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
string: saved path of the source file or "" if there is no index. When running the file, this is usually the full path; The command or source may not have a regular file in memory.
- Running and paused launches are taken into account; those completed and having received a cancellation request are excluded. A running script usually takes into account itself. Simply loading the IDE tab is not considered a launch.
- Indexes are the current positions in startup order. Starting/stopping scripts can shift positions. Different calls do not form a common atomic snapshot; re-read the list before the next control command.
- GetScriptsList gives the indices, GetScriptsCount the quantity, GetScriptState the three-state code. Don't substitute one for the other or treat any non-null result as true.
- ScriptIndex=0 selects the first current run. The result indicates its source; the command does not open, save, or run this file.
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.GetScriptPath(0);
UO.Print("GetScriptPath: " + Convert.ToString(result));Parameters and execution flow:
- Run the given C#-script. The number 0, where it is passed, denotes the index; empty parentheses mean no arguments. The text Print is an example message only.
- ScriptIndex=0 selects the first current run. The result indicates its source; the command does not open, save, or run this file.
- string: saved path of the source file or "" if there is no index. When running the file, this is usually the full path; The command or source may not have a regular file in memory.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
var index = 0;
var path = UO.GetScriptPath(index);
if (Truth(path!=""))
{
UO.Print(path);
}
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:
- This is a specific example of command sharing. Array indexing starts from zero; Please check the length before handling. UO.Wait(250), where it is used, waits 250 milliseconds.
- ScriptIndex=0 selects the first current run. The result indicates its source; the command does not open, save, or run this file.
- string: saved path of the source file or "" if there is no index. When running the file, this is usually the full path; The command or source may not have a regular file in memory.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
return RunScenario();
object? RunScenario()
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
UO.Print(UO.GetScriptName(index) + " -> " + UO.GetScriptPath(index));
}
return null;
}Parameters and execution flow:
- Below Main is the entire function. Its parameters and result are explained separately from the API-command it calls.
- ReadScriptPath substitutes the fallback parameter only if the path is empty. This is a separate function and not a new GetScriptPath overload.
- string: saved path of the source file or "" if there is no index. When running the file, this is usually the full path; The command or source may not have a regular file in memory.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
UO.Print(ReadScriptPath(0, "path unavailable"));
return null;
}
dynamic ReadScriptPath(dynamic index, dynamic fallback)
{
var path = UO.GetScriptPath(index);
if (Truth(path == ""))
{
return fallback;
}
return path;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: scriptIndex (Any)
Result: string / String.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.GetScriptPath:bridge.GetScriptPath(Arg(0)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Arg:Arg(0).
Internal implementation code; this is not a standalone script.
case "getscriptpath": return new InjectionValue(bridge.GetScriptPath(Arg(0)));Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.GetScriptState(object? scriptIndex);Reads the execution status by index.
-
scriptIndex- Mandatory ScriptIndex - integer, index from zero from fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
int — status code: 0 = no/unknown, 1 = running, 2 = pause. This is not a bool: explicitly compare with 1 or 2.
- Running and paused launches are taken into account; those completed and having received a cancellation request are excluded. A running script usually takes into account itself. Simply loading the IDE tab is not considered a launch.
- Indexes are the current positions in startup order. Starting/stopping scripts can shift positions. Different calls do not form a common atomic snapshot; re-read the list before the next control command.
- GetScriptsList gives the indices, GetScriptsCount the quantity, GetScriptState the three-state code. Don't substitute one for the other or treat any non-null result as true.
- The manual/debug pause and the configured shutdown pause are taken into account. The 1 state does not guarantee that the script is currently consuming CPU or receiving server data.
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.GetScriptState(0);
UO.Print("GetScriptState: " + Convert.ToString(result));Parameters and execution flow:
- Run the given C#-script. The number 0, where it is passed, denotes the index; empty parentheses mean no arguments. The text Print is an example message only.
- The manual/debug pause and the configured shutdown pause are taken into account. The 1 state does not guarantee that the script is currently consuming CPU or receiving server data.
- int — status code: 0 = no/unknown, 1 = running, 2 = pause. This is not a bool: explicitly compare with 1 or 2.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
int state = UO.GetScriptState(0);
string text = state switch
{
1 => "running",
2 => "paused",
_ => "unknown"
};
UO.Print(text);Parameters and execution flow:
- This is a specific example of command sharing. Array indexing starts from zero; Please check the length before handling. UO.Wait(250), where it is used, waits 250 milliseconds.
- The manual/debug pause and the configured shutdown pause are taken into account. The 1 state does not guarantee that the script is currently consuming CPU or receiving server data.
- int — status code: 0 = no/unknown, 1 = running, 2 = pause. This is not a bool: explicitly compare with 1 or 2.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
return RunScenario();
object? RunScenario()
{
var paused = 0;
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(UO.GetScriptState(index) == 2))
{
paused += 1;
}
}
UO.Print(Convert.ToString(paused));
return null;
}
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:
- Below Main is the entire function. Its parameters and result are explained separately from the API-command it calls.
- IsScriptActive converts 1 and 2 to true and 0 to false. GetScriptState itself still returns a numeric status code.
- int — status code: 0 = no/unknown, 1 = running, 2 = pause. This is not a bool: explicitly compare with 1 or 2.
- Mandatory ScriptIndex is an integer, index from zero from the fresh GetScriptsList. For a negative or missing index, the described empty/unknown result is returned. Do not pass the subject serial, procedure name, or ID run from IDE.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
if (Truth(Truth(IsScriptActive(0)) == true))
{
UO.Print("running or paused");
}
else
{
UO.Print("not active");
}
return null;
}
dynamic IsScriptActive(dynamic index)
{
var state = UO.GetScriptState(index);
return Truth(state == 1) || Truth(state == 2);
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: scriptIndex (Any)
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.GetScriptState:bridge.GetScriptState(Arg(0)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Arg:Arg(0).
Internal implementation code; this is not a standalone script.
case "getscriptstate": return new InjectionValue(bridge.GetScriptState(Arg(0)));Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.GetScriptsCount();Counts active runs in this client.
There are no parameters.
int >= 0: number of active runs, including paused ones. This is a quantity, not a bool or a script index.
- Running and paused launches are taken into account; those completed and having received a cancellation request are excluded. A running script usually takes into account itself. Simply loading the IDE tab is not considered a launch.
- Indexes are the current positions in startup order. Starting/stopping scripts can shift positions. Different calls do not form a common atomic snapshot; re-read the list before the next control command.
- GetScriptsList gives the indices, GetScriptsCount the quantity, GetScriptState the three-state code. Don't substitute one for the other or treat any non-null result as true.
- There are no arguments. Counting does not sort the list and does not change the state of runs.
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.GetScriptsCount();
UO.Print("GetScriptsCount: " + Convert.ToString(result));Parameters and execution flow:
- Run the given C#-script. The number 0, where it is passed, denotes the index; empty parentheses mean no arguments. The text Print is an example message only.
- There are no arguments. Counting does not sort the list and does not change the state of runs.
- int >= 0: number of active runs, including paused ones. This is a quantity, not a bool or a script index.
var count = UO.GetScriptsCount();
UO.Print(Convert.ToString(count));Parameters and execution flow:
- This is a specific example of command sharing. Array indexing starts from zero; Please check the length before handling. UO.Wait(250), where it is used, waits 250 milliseconds.
- There are no arguments. Counting does not sort the list and does not change the state of runs.
- int >= 0: number of active runs, including paused ones. This is a quantity, not a bool or a script index.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
var before = UO.GetScriptsCount();
await Delay(250);
var after = UO.GetScriptsCount();
UO.Print(Convert.ToString(after - before));
return null;
}
Task Delay(object? milliseconds) => WaitAsync(checked((int)Convert.ToDouble(milliseconds)));Parameters and execution flow:
- Below Main is the entire function. Its parameters and result are explained separately from the API-command it calls.
- HasOtherScripts compares the count with 1: The calling script typically occupies one entry. Unlike GetScriptsCount, the helper function returns true/false.
- int >= 0: number of active runs, including paused ones. This is a quantity, not a bool or a script index.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
if (Truth(HasOtherScripts()))
{
UO.Print("Other executions are active");
}
else
{
UO.Print("No other active executions");
}
return null;
}
dynamic HasOtherScripts()
{
var count = UO.GetScriptsCount();
return count > 1;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.GetScriptsCount:bridge.GetScriptsCount().
Internal implementation code; this is not a standalone script.
case "getscriptscount": return new InjectionValue(bridge.GetScriptsCount());Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
dynamic UO.GetScriptsList();Returns the current numeric script indexes.
There are no parameters.
Array of int: indices 0..N-1 or an empty array. The elements are numbers, not names or text entries.
- Running and paused launches are taken into account; those completed and having received a cancellation request are excluded. A running script usually takes into account itself. Simply loading the IDE tab is not considered a launch.
- Indexes are the current positions in startup order. Starting/stopping scripts can shift positions. Different calls do not form a common atomic snapshot; re-read the list before the next control command.
- GetScriptsList gives the indices, GetScriptsCount the quantity, GetScriptState the three-state code. Don't substitute one for the other or treat any non-null result as true.
- There are no arguments. Pass each number to the corresponding getter of the name, path or state. The resulting array is a separate snapshot; changing it does not control 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.
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.
object? result = UO.GetScriptsList();
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));Parameters and execution flow:
- Run the given C#-script. The number 0, where it is passed, denotes the index; empty parentheses mean no arguments. The text Print is an example message only.
- There are no arguments. Pass each number to the corresponding getter of the name, path or state. The resulting array is a separate snapshot; changing it does not control scripts.
- Array of int: indices 0..N-1 or an empty array. The elements are numbers, not names or text entries.
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
UO.Print(Convert.ToString(index) + ": " + UO.GetScriptName(index));
}Parameters and execution flow:
- This is a specific example of command sharing. Array indexing starts from zero; Please check the length before handling. UO.Wait(250), where it is used, waits 250 milliseconds.
- There are no arguments. Pass each number to the corresponding getter of the name, path or state. The resulting array is a separate snapshot; changing it does not control scripts.
- Array of int: indices 0..N-1 or an empty array. The elements are numbers, not names or text entries.
return RunScenario();
object? RunScenario()
{
var indices = UO.GetScriptsList();
if (Truth(ArrayLength(indices) > 0))
{
var firstIndex = indices[0];
UO.Print(UO.GetScriptPath(firstIndex));
}
return null;
}
int ArrayLength(object? value) => value is System.Array array ? array.Length : 0;
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:
- Below Main is the entire function. Its parameters and result are explained separately from the API-command it calls.
- FindNamedScript iterates through the current indices and returns the first exact match of the display name or -1. Names may be repeated, indexes may move later.
- Array of int: indices 0..N-1 or an empty array. The elements are numbers, not names or text entries.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var index = FindNamedScript("Mining");
if (Truth(index >= 0))
{
UO.Print(Convert.ToString(index));
}
else
{
UO.Print("Name not found");
}
return null;
}
dynamic FindNamedScript(dynamic wanted)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(UO.GetScriptName(index) == wanted))
{
return index;
}
}
return -1;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: dynamic / Array.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.GetScriptsList:bridge.GetScriptsList().
Internal implementation code; this is not a standalone script.
case "getscriptslist": return new InjectionValue(bridge.GetScriptsList().Select(value => new InjectionValue(value)).ToArray());Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.Launch(object? file);
int UO.Launch(object? file, object? parameters);
int UO.Launch(object? file, object? parameters, object? workingDirectory);
int UO.Launch(object? file, object? parameters, object? workingDirectory, object? hidden);-
file— Mandatory path/name of the program or document; converted to text, outer spaces are removed. An empty value gives 0 and an error message. To select the exact program, use the full path; There is no need to add outer quotes to the file itself. -
workingDirectory— Optional working folder of the process. If there is a omission or an empty line, the folder of the current script is taken. If runtime does not have such a folder, bridge uses the client's current working folder. This is not always the game folder or the EXE client folder. -
hidden— Optional boolean value, default false. true disables opening through the OS shell and requests launch without a console window; you need an executable file. false allows document associations. The GUI-program itself can still show its window.
int: 1 (true) if Process.Start returned a process object; otherwise 0 (false). In C# check result == 1 or result != 0; int cannot be directly compared to true. This is not PID, an exit code, or confirmation of completion of work. If an exception occurs, an error is output and 0 is returned. When opening a document through an already running program, the OS sometimes does not return a new process: 0 in this case does not prove that the document was not opened.
- Only overloads of one to four arguments are allowed, in the order specified. To specify hidden and leave the default working folder, pass an empty string as the third argument. Conversions of file/parameters/workingDirectory to text do not check for the existence of the file in advance.
- The default folder is determined by the currently executing script. With hidden=true the relative program path is first checked against workingDirectory. The partial OS name lookup and associations are system specific; the full path eliminates ambiguity. If the working folder or program is not available, startup may fail.
- The external process runs independently. Launch does not wait for it to complete, does not read stdout/stderr, and does not terminate it when the script is stopped. true for hidden is a request for a launch method, and not a guarantee of the invisibility of any windows. The Process object is released after the request; the running program itself continues to run.
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.Launch("loot-report.txt");
UO.Print("Launch: " + Convert.ToString(result));Parameters and execution flow:
- Place the existing loot-report.txt next to the script. The OS opens it with the application assigned to the .txt. Main returns the result of the Launch request, not the contents of the report or confirmation that it was read.
var started = UO.Launch("loot-report.txt");
return started;Parameters and execution flow:
- The folder and report must exist; replace them with your own. Chr(34) encloses the full path of the report in double quotes inside arguments. notepad.exe must be accessible to the system. false enables the normal editor window.
return RunScenario();
object? RunScenario()
{
var report = "C:\\UO Scripts\\loot report.txt";
var arguments = Chr(34) + report + Chr(34);
return UO.Launch("notepad.exe", arguments, "C:\\UO Scripts", false);
return null;
}
string Chr(object? value) => ((char)Convert.ToInt32(value)).ToString();Parameters and execution flow:
- The Windows example runs the built-in cmd.exe. In the script folder, log-files.txt with a list of *.logs is created or overwritten. An empty third argument saves the script folder, true hides the console. true from Main means only launch: the file may not be ready yet, and the absence of *.log or a utility error does not change the already obtained result Launch.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var started = UO.Launch("cmd.exe", "/d /c dir /b *.log > log-files.txt", "", true);
if (Truth(started == 0))
{
return false;
}
return true;
return null;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: file (Any)
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.Register [Launch]. -
InjectionScript.Runtime.InjectionApiUO.CompactLaunch:CompactLaunch(values).
The call executes the stored handler delegate. The registration method creates this delegate while building the API.
Internal implementation code; this is not a standalone script.
values => new InjectionValue(CompactLaunch(values))Parameters: file (Any), parameters (Any)
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.Register [Launch]. -
InjectionScript.Runtime.InjectionApiUO.CompactLaunch:CompactLaunch(values).
The call executes the stored handler delegate. The registration method creates this delegate while building the API.
Internal implementation code; this is not a standalone script.
values => new InjectionValue(CompactLaunch(values))Parameters: file (Any), parameters (Any), workingDirectory (Any)
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.Register [Launch]. -
InjectionScript.Runtime.InjectionApiUO.CompactLaunch:CompactLaunch(values).
The call executes the stored handler delegate. The registration method creates this delegate while building the API.
Internal implementation code; this is not a standalone script.
values => new InjectionValue(CompactLaunch(values))Parameters: file (Any), parameters (Any), workingDirectory (Any), hidden (Any)
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.Register [Launch]. -
InjectionScript.Runtime.InjectionApiUO.CompactLaunch:CompactLaunch(values).
The call executes the stored handler delegate. The registration method creates this delegate while building the API.
Internal implementation code; this is not a standalone script.
values => new InjectionValue(CompactLaunch(values))Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.PauseResumeScript(object? scriptIndex);Toggles manual pause of one active run. Use to temporarily stop mining or another procedure without losing its current progress.
-
scriptIndex- Mandatory ScriptIndex - current launch index: 0 means the first one, then 1, 2... Get it through GetScriptsList; it is not serial, the file name and not the result StartScript. A negative/missing index doesn't change anything.
void - no return value. Don't compare the call to true/1. Check the condition by separately reading GetScriptState; the result of such a reading is 0 (no start), 1 (running), 2 (pause).
- Works only with runs of the current session. An open tab without execution is not an active launch. Stopping does not delete the original file or disconnect the connection to the server.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
- This is a switch, not an "always continue" command. Manual and automatic pauses are independent: switching to manual does not remove the connection break pause. The code 2 does not explain the reason for the pause. If you stop your own launch in this way, it will not be able to make the next call to continue it; resume from IDE/manager or other script.
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.
for (int index = 0; index < UO.GetScriptsCount(); index++)
if (UO.GetScriptName(index) == "Worker")
{ UO.PauseResumeScript(index); break; }Parameters and execution flow:
- MineOre is an example of the display name of an already running procedure. FindNamedRun returns the first matched index or -1. Call from another controller; calling again changes the manual pause back.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
var index = FindNamedRun("MineOre");
if (Truth(index >= 0))
{
UO.PauseResumeScript(index);
}
dynamic FindNamedRun(dynamic wanted)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(UO.GetScriptName(index) == wanted))
{
return index;
}
}
return -1;
}
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:
- Check == 1 limits the action to a running trigger. The value 2 is not used as a "clear manual pause" indication because it may indicate no communication.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
return RunScenario();
object? RunScenario()
{
var index = FindNamedRun("MineOre");
if (Truth(index >= 0))
{
if (Truth(UO.GetScriptState(index) == 1))
{
UO.PauseResumeScript(index);
}
}
return null;
}
dynamic FindNamedRun(dynamic wanted)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(UO.GetScriptName(index) == wanted))
{
return index;
}
}
return -1;
}
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:
- Replace the absolute path with yours. FindFileRun compares paths in a case-insensitive manner and returns the first run of this file; Several procedures can be executed in one file.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var wantedPath = "D:/Game/AutoLoad/Scripts/Worker.csx";
var index = FindFileRun(wantedPath);
if (Truth(index >= 0))
{
UO.PauseResumeScript(index);
}
return null;
}
dynamic FindFileRun(dynamic wantedPath)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(Convert.ToString(ReplaceText(UO.GetScriptPath(index), Chr(92), "/")) == Convert.ToString(ReplaceText(wantedPath, Chr(92), "/"))))
{
return index;
}
}
return -1;
}
string Chr(object? value) => ((char)Convert.ToInt32(value)).ToString();
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
string ReplaceText(object? value, object? oldValue, object? newValue) => (Convert.ToString(value) ?? "").Replace(Convert.ToString(oldValue) ?? "", Convert.ToString(newValue) ?? "");
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: scriptIndex (Any)
Result: void / Unit.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.PauseResumeScript:bridge.PauseResumeScript(Arg(0)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Arg:Arg(0).
Internal implementation code; this is not a standalone script.
case "pauseresumescript": bridge.PauseResumeScript(Arg(0)); return InjectionValue.Unit;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.SetEvent(object? name, object? stateValue);
void UO.SetEvent(object? name, object? stateValue, object? repeatOrHandler);
void UO.SetEvent(object? nameValue, object? stateValue, object? repeatValue, object? subNameValue);Enables or disables monitoring of changes in the character's state and allows you to select a one-time or repeating reaction. For example, you can cancel the remaining target once after death or receive warnings about low stamina. The handler is specified separately from the condition; the main script should continue to execute UO.Wait.
-
name— A string with one of five names: ChangeHitPoints, ChangeMana, ChangeStamina, Death, SeeNewPlayer. Case is not important; the prefix ev is acceptable, for example evChangeMana. Whitespace around the edges is removed. There is no numeric catalog of events; an arbitrary name does not create a new event. -
nameValue— Same event name as name; this is the name of the first parameter in a four-argument form, not an optional value. -
stateValue- true/1 turns on surveillance, false/0 unsubscribes. Pass boolean values without quotes. In a numeric conversion, any non-zero integer includes an observation. Disabling does not wait or stop an already running handler. -
repeatOrHandler— The third argument has two purposes. A non-numeric non-empty string, such as "OnManaChanged", specifies an auto-repeat handler. Number/bool sets repeat only: false/0 — one operation, true/1 — repeat; no handler is assigned in this form. The string "1" is also numeric and is not a procedure name. For a one-time handler, use four arguments. -
repeatValue- In the form with four arguments: false/0 - delete the subscription after the first detected transition, true/1 - leave it for the next changes. A one-time subscription is also canceled when the handler is not found or the manager rejects its launch; Do not confuse the detection of an event with the successful completion of a procedure.
void - no value. Call on a separate line, without var result = and without comparison with true/1. Completing registration does not confirm that the handler was found, started, or that the server took an action. If the procedure name is not found on the event, the call does not occur.
- Two arguments: false disables the event; true involves repeated observation without a handler. Any procedure with a matching name is not automatically substituted. This form does not return a finding and does not replace the HP/Mana/Dead test.
- The four arguments explicitly specify the event name, enable, repeat, and handler. Both boolean parameters are written without quotes. false/0 = one-shot: the subscription is unsubscribed after the first transition detected, not after the end of the handler.
- Re-registration of one event replaces its previous settings and remembers the current state as a new reference point. Subscriptions SetEvent and SetEventProc to one normalized name within one script use one record and do not accumulate.
- ChangeHitPoints compares the current HP self with the previous snapshot; triggers both when losing and restoring health. ChangeMana and ChangeStamina react similarly in both directions. It's not just damage, mana consumption or fatigue.
- Death is triggered when transitioning alive → dead. Registering an already dead character does not immediately call the handler; dying again after resurrection can trigger it again.
- SeeNewPlayer compares the set of downloaded mobiles excluding 0 and self. The name does not mean testing the actual player: this includes NPC and creatures. One or more new serial per check causes one startup attempt. The return of the departed mobile from the loaded set can also be considered a new appearance. ID is not passed to the handler - the necessary objects must be found separately.
- UO.Wait and WaitAsync divide the wait into intervals up to 25 ms and check for events after each; UO.Wait(0) also performs the check. This is polling the available state rather than intercepting every network packet: fast changes and returns between checks may be missed. There is no constant monitoring in a computation cycle without UO.Wait or after the main script has completed.
- The handler name is pre-associated with the C#-delegate via RegisterRoutine. Callback is executed asynchronously in this session; one task does not superimpose on itself. After the end of the main script, her further actions are prohibited.
- A subscription refers to an instance of a running script. Remove it in Main, which created it: the command of the same name inside a separately launched handler will change its own subscriptions. Unregistering does not stop an already running handler. Use try/
finallyfor normal exit or handleable error; force stop also stops further polling of this script. - An unknown non-empty event name is rejected with a system message when registering. An empty name does nothing. The presence of a handler is checked only when triggered; It is better to detect an error in its name in advance through SubExists and checking the script in IDE.
In C# the handler must be explicitly registered: RegisterRoutine("Handler", Handler). Changes to HP, mana and stamina come through the native event queue; Death and SeeNewPlayer are checked against snapshots of the local world. Direct subscriptions with arguments are available via SubscribeEvent("UO.JournalEntry", handler) and similar native names. All subscriptions are released when this execution is completed or canceled.
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("HealthChanged", () => UO.Print("HP changed"));
UO.SetEvent("ChangeHitPoints", true, false, "HealthChanged");
await WaitAsync(5000);Parameters and execution flow:
- The observation lasts eight seconds. Death of an already dead character does not call the handler immediately. The second death event after resurrection is no longer processed in this example.
- The handler checks the target's active cursor at execution time and only then calls CancelTarget. A prepared queue without an active cursor is not canceled in this example. The script does not resurrect the character, stop all scripts, and does not end Main. Don't run the example if you want to keep the target active.
var registrationOnFirstDeath = RegisterRoutine("OnFirstDeath", OnFirstDeath);
UO.SetEvent("Death", true, false, "OnFirstDeath");
try
{
UO.Wait(8000);
}
finally
{
UO.SetEvent("Death", false);
}
void OnFirstDeath()
{
if (Truth(UO.Targeting()))
{
UO.CancelTarget();
UO.Print("Death detected: active targeting cancelled");
}
else
{
UO.Print("Death detected: no active target");
}
}
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:
- 24 × 250 ms – six seconds. 10 is our threshold in the handler, not the additional argument SetEvent. The increase in endurance is also caused by the handler, but with a value from 10 the message is not displayed.
- true, "OnFatigue" is different from true, true: the latter option specifies repetition without procedure. The script does not increase endurance or change server speed.
return RunScenario();
object? RunScenario()
{
var registrationOnFatigue = RegisterRoutine("OnFatigue", OnFatigue);
UO.SetEvent("evChangeStamina", true, "OnFatigue");
try
{
for (int tick = (int)(1); tick <= 24; tick += (int)(1))
{
UO.Wait(250);
}
}
finally
{
UO.SetEvent("ChangeStamina", false);
}
return null;
}
void OnFatigue()
{
var staminaNow = UO.Stamina();
if (Truth(staminaNow < 10))
{
UO.Print("Stamina is " + Convert.ToString(staminaNow) + ": consider resting");
}
}
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:
- For the first two seconds, each detected change triggers OnManaLog. For the next three seconds, OnLowMana is used. Registering a second handler does not itself trigger it.
- The replacement does not interrupt the old procedure that is already running. These short handlers do not use Main local variables. If the imposition of procedures is prohibited, the manager can reject the launch - for this mode, check Mana directly in Main.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var registrationOnManaLog = RegisterRoutine("OnManaLog", OnManaLog);
var registrationOnLowMana = RegisterRoutine("OnLowMana", OnLowMana);
UO.SetEvent("ChangeMana", true, true, "OnManaLog");
try
{
UO.Wait(2000);
UO.SetEvent("ChangeMana", true, true, "OnLowMana");
UO.Wait(3000);
}
finally
{
UO.SetEvent("ChangeMana", false);
}
return null;
}
void OnManaLog()
{
UO.Print("Mana now: " + Convert.ToString(UO.Mana()));
}
void OnLowMana()
{
if (Truth(UO.Mana() < 20))
{
UO.Print("Mana below 20: check the cost before casting");
}
}
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: name (Any), stateValue (Any)
Result: void / Unit.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionApiUO.SetEvent. -
InjectionScript.Runtime.InjectionApiUO.SetEvent:SetEvent(name, stateValue, InjectionValue.True, new InjectionValue(string.Empty)).
Internal implementation code; this is not a standalone script.
case "SetEvent": SetStateEvent(arguments); return true;Internal implementation code; this is not a standalone script.
public void SetEvent(InjectionValue name, InjectionValue stateValue)
=> SetEvent(name, stateValue, InjectionValue.True, new InjectionValue(string.Empty));Parameters: name (Any), stateValue (Any), repeatOrHandler (Any)
Result: void / Unit.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionApiUO.SetEvent. -
InjectionScript.Runtime.InjectionValue.ToString:repeatOrHandler.ToString(). -
InjectionScript.Runtime.NumberConversions.TryStr2Int:NumberConversions.TryStr2Int(raw, out _). -
InjectionScript.Runtime.InjectionApiUO.SetEvent:SetEvent(name, stateValue, InjectionValue.True, repeatOrHandler).
Internal implementation code; this is not a standalone script.
case "SetEvent": SetStateEvent(arguments); return true;Internal implementation code; this is not a standalone script.
public void SetEvent(InjectionValue name, InjectionValue stateValue, InjectionValue repeatOrHandler)
{
string raw = repeatOrHandler.ToString() ?? string.Empty;
if (!string.IsNullOrWhiteSpace(raw) && !NumberConversions.TryStr2Int(raw, out _))
SetEvent(name, stateValue, InjectionValue.True, repeatOrHandler);
else
SetEvent(name, stateValue, repeatOrHandler, new InjectionValue(string.Empty));
}Parameters: nameValue (Any), stateValue (Any), repeatValue (Any), subNameValue (Any)
Result: void / Unit.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionApiUO.SetEvent. -
InjectionScript.Runtime.InjectionApiUO.NormalizeLegacyEventName:NormalizeLegacyEventName(nameValue.ToString()). -
InjectionScript.Runtime.InjectionValue.ToString:nameValue.ToString(). -
InjectionScript.Runtime.NumberConversions.ToInt:NumberConversions.ToInt(stateValue). -
InjectionScript.Runtime.InjectionApiUO.IsSupportedLegacyEventName:IsSupportedLegacyEventName(name). -
InjectionScript.Runtime.InjectionApiUO.SystemMessage:SystemMessage($"SetEvent: unsupported event '{name}'. Supported: SeeNewPlayer, Death, ChangeHitPoints, ChangeMana, ChangeStamina."). -
InjectionScript.Runtime.InjectionApiUO.NormalizeLegacyEventHandler:NormalizeLegacyEventHandler(subNameValue.ToString()). -
InjectionScript.Runtime.InjectionApiUO.CaptureLegacyEventSnapshot:CaptureLegacyEventSnapshot(subscription).
Internal implementation code; this is not a standalone script.
case "SetEvent": SetStateEvent(arguments); return true;Internal implementation code; this is not a standalone script.
public void SetEvent(InjectionValue nameValue, InjectionValue stateValue, InjectionValue repeatValue, InjectionValue subNameValue)
{
string name = NormalizeLegacyEventName(nameValue.ToString());
if (string.IsNullOrEmpty(name))
return;
bool enabled = NumberConversions.ToInt(stateValue) != 0;
eventStates[name] = enabled ? 1 : 0;
if (!enabled)
{
legacyEvents.Remove(name);
return;
}
if (!IsSupportedLegacyEventName(name))
{
eventStates[name] = 0;
legacyEvents.Remove(name);
SystemMessage($"SetEvent: unsupported event '{name}'. Supported: SeeNewPlayer, Death, ChangeHitPoints, ChangeMana, ChangeStamina.");
return;
}
var subscription = new LegacyEventSubscription
{
Name = name,
Repeat = NumberConversions.ToInt(repeatValue) != 0,
Handler = NormalizeLegacyEventHandler(subNameValue.ToString())
};
CaptureLegacyEventSnapshot(subscription);
legacyEvents[name] = subscription;
}Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.SetEventProc(object? eventname, object? procname);Designates a procedure that the client will run when an observed state changes: for example, when health is lost, mana changes, or a new mobile appears. Allows you to separate the reaction from the main cycle. Registration itself does not run anything: The main script must remain active; C#-control points, WaitAsync and UO.Wait handle events.
-
eventname— A string with one of five names: ChangeHitPoints, ChangeMana, ChangeStamina, Death, SeeNewPlayer. Case is not important; the prefix ev is acceptable, for example evChangeMana. Whitespace around the edges is removed. There is no numeric catalog of events; an arbitrary name does not create a new event.
void - no value. Call on a separate line, without var result = and without comparison with true/1. Completing registration does not confirm that the handler was found, started, or that the server took an action. If the procedure name is not found on the event, the call does not occur.
- Both arguments are required. Subscription is recurring: new detected changes cause new launch attempts. In one running script, one handler is assigned to one normalized event name; re-registration replaces it and updates the initial snapshot. SetEventProc(event, "") unsubscribes in the same script. For a one-time reaction, use SetEvent(event, true, false, "Handler").
- ChangeHitPoints compares the current HP self with the previous snapshot; triggers both when losing and restoring health. ChangeMana and ChangeStamina react similarly in both directions. It's not just damage, mana consumption or fatigue.
- Death is triggered when transitioning alive → dead. Registering an already dead character does not immediately call the handler; dying again after resurrection can trigger it again.
- SeeNewPlayer compares the set of downloaded mobiles excluding 0 and self. The name does not mean testing the actual player: this includes NPC and creatures. One or more new serial per check causes one startup attempt. The return of the departed mobile from the loaded set can also be considered a new appearance. ID is not passed to the handler - the necessary objects must be found separately.
- UO.Wait and WaitAsync divide the wait into intervals up to 25 ms and check for events after each; UO.Wait(0) also performs the check. This is polling the available state rather than intercepting every network packet: fast changes and returns between checks may be missed. There is no constant monitoring in a computation cycle without UO.Wait or after the main script has completed.
- The handler name is pre-associated with the C#-delegate via RegisterRoutine. Callback is executed asynchronously in this session; one task does not superimpose on itself. After the end of the main script, her further actions are prohibited.
- A subscription refers to an instance of a running script. Remove it in Main, which created it: the command of the same name inside a separately launched handler will change its own subscriptions. Unregistering does not stop an already running handler. Use try/
finallyfor normal exit or handleable error; force stop also stops further polling of this script. - An unknown non-empty event name is rejected with a system message when registering. An empty name does nothing. The presence of a handler is checked only when triggered; It is better to detect an error in its name in advance through SubExists and checking the script in IDE.
In C# the handler must be explicitly registered: RegisterRoutine("Handler", Handler). Changes to HP, mana and stamina come through the native event queue; Death and SeeNewPlayer are checked against snapshots of the local world. Direct subscriptions with arguments are available via SubscribeEvent("UO.JournalEntry", handler) and similar native names. All subscriptions are released when this execution is completed or canceled.
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("HealthChanged", () => UO.Print("HP changed"));
UO.SetEventProc("ChangeHitPoints", "HealthChanged");
await WaitAsync(5000);Parameters and execution flow:
- ChangeHitPoints observes HP of his character. OnHealthChanged - name of the method below/above Main in the same loaded file. UO.Wait handles events; an empty second argument disables repetition.
- The handler only warns and does not automatically select treatment. When changing HP 60 → 50 → 70 two reactions are performed; This is not a hit counter.
var registrationOnHealthChanged = RegisterRoutine("OnHealthChanged", OnHealthChanged);
UO.SetEventProc("ChangeHitPoints", "OnHealthChanged");
try
{
for (int tick = (int)(1); tick <= 40; tick += (int)(1))
{
UO.Wait(250);
}
}
finally
{
UO.SetEventProc("ChangeHitPoints", "");
}
void OnHealthChanged()
{
var hp = UO.HP();
if (Truth(hp < 40))
{
UO.Print("Low health: " + Convert.ToString(hp) + ". Check healing or retreat.");
}
else
{
UO.Print("Health changed: " + Convert.ToString(hp));
}
}
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:
- evChangeMana and ChangeMana are one event. There are two independent subscriptions and two different handlers, not two handlers under the same name. Thresholds 20 and 10 can be changed.
- The wait lasts 5 seconds. Resource recovery is also detected, but if does not issue a warning when the value is already above the threshold.
return RunScenario();
object? RunScenario()
{
var registrationOnManaChanged = RegisterRoutine("OnManaChanged", OnManaChanged);
var registrationOnStaminaChanged = RegisterRoutine("OnStaminaChanged", OnStaminaChanged);
UO.SetEventProc("evChangeMana", "OnManaChanged");
UO.SetEventProc("ChangeStamina", "OnStaminaChanged");
try
{
for (int tick = (int)(1); tick <= 20; tick += (int)(1))
{
UO.Wait(250);
}
}
finally
{
UO.SetEventProc("ChangeMana", "");
UO.SetEventProc("ChangeStamina", "");
}
return null;
}
void OnManaChanged()
{
if (Truth(UO.Mana() < 20))
{
UO.Print("Mana below 20: postpone expensive spells");
}
}
void OnStaminaChanged()
{
if (Truth(UO.Stamina() < 10))
{
UO.Print("Stamina below 10: consider a movement break");
}
}
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:
- SeeNewPlayer detects the occurrence of serial in the loaded set of mobiles, including NPC. Loading five creatures at once does not guarantee five summons.
- GetMobiles() - separate reading of the actual set, not ID from the event. To select an enemy requires explicit conditions; the example does not intentionally attack anyone.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var registrationOnMobileAppeared = RegisterRoutine("OnMobileAppeared", OnMobileAppeared);
UO.SetEventProc("SeeNewPlayer", "OnMobileAppeared");
try
{
UO.Wait(5000);
}
finally
{
UO.SetEventProc("SeeNewPlayer", "");
}
UO.Wait(1000);
return null;
}
void OnMobileAppeared()
{
var mobiles = UO.GetMobiles();
UO.Print("Loaded mobiles now: " + Convert.ToString(ArrayLength(mobiles)));
}
int ArrayLength(object? value) => value is System.Array array ? array.Length : 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: eventname (Any), procname (Any)
Result: void / Unit.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Text:Text(1). -
InjectionScript.Runtime.InjectionApiUO.SetEvent:SetEvent(arguments[0], InjectionValue.False). -
InjectionScript.Runtime.InjectionApiUO.SetEvent:SetEvent(arguments[0], InjectionValue.True, InjectionValue.True, arguments[1]).
Internal implementation code; this is not a standalone script.
case "SetEventProc":
SetStateEvent([arguments[0], !string.IsNullOrWhiteSpace(Convert.ToString(arguments[1])), true, arguments[1]]);
return true;Internal implementation code; this is not a standalone script.
case "seteventproc":
{
if (string.IsNullOrWhiteSpace(Text(1)))
SetEvent(arguments[0], InjectionValue.False);
else
SetEvent(arguments[0], InjectionValue.True, InjectionValue.True, arguments[1]);
return InjectionValue.Unit;
}Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.SetPauseScriptOnDisconnectStatus(object? value);Changes the rule for scripts to pause when the connection is lost.
-
value- 0 turns off; any non-zero integer includes.
void - no value. Call on a separate line; completion does not confirm the server's action.
- SetPauseScriptOnDisconnectStatus sets _manager.PauseScriptsOnDisconnect = enabled != 0. Already running scripts do not receive the PauseResumeScript command here.
- Changes the client setting. The value is not the result of a connection test and does not confirm processing by the server.
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 command is a separate statement. The message before it marks the start of the call; it does not confirm a server action.
UO.Print("Calling UO.SetPauseScriptOnDisconnectStatus");
UO.SetPauseScriptOnDisconnectStatus(1);Parameters and execution flow:
- Changes the rule for scripts to pause when the connection is lost.
UO.SetPauseScriptOnDisconnectStatus(1);Parameters and execution flow:
- SetPauseScriptOnDisconnectStatus sets _manager.PauseScriptsOnDisconnect = enabled != 0. Already running scripts do not receive the PauseResumeScript command here.
return RunScenario();
object? RunScenario()
{
UO.SetPauseScriptOnDisconnectStatus(0);
return null;
}Parameters and execution flow:
- Changes the client setting. The value is not the result of a connection test and does not confirm processing by the server.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
ApplyMode(1);
return null;
}
void ApplyMode(dynamic mode)
{
UO.SetPauseScriptOnDisconnectStatus(mode);
}
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: value (Any)
Result: void / Unit.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.SetPauseScriptOnDisconnectStatus:bridge.SetPauseScriptOnDisconnectStatus(Arg(0)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Arg:Arg(0).
Internal implementation code; this is not a standalone script.
case "setpausescriptondisconnectstatus": bridge.SetPauseScriptOnDisconnectStatus(Arg(0)); return InjectionValue.Unit;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.SetScriptName(object? scriptIndex, object? value);Changes the display name of one active run. Helps distinguish between running tasks in the list and through GetScriptName.
-
scriptIndex- Mandatory ScriptIndex - current launch index: 0 means the first one, then 1, 2... Get it through GetScriptsList; it is not serial, the file name and not the result StartScript. A negative/missing index doesn't change anything. -
value- Required Value - new name, converted to a string. Spaces are allowed; an empty string is also acceptable. This is the signature of the current run, and not the new name of the file or procedure in the source.
void - no return value. Don't compare the call to true/1. Check the condition by separately reading GetScriptState; the result of such a reading is 0 (no start), 1 (running), 2 (pause).
- Works only with runs of the current session. An open tab without execution is not an active launch. Stopping does not delete the original file or disconnect the connection to the server.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
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.
for (int index = 0; index < UO.GetScriptsCount(); index++)
if (UO.GetScriptName(index) == "Worker")
{ UO.SetScriptName(index, "Worker renamed"); break; }Parameters and execution flow:
- MineOre is searched for in active runs, Mining is the new signature. The names inside the quotes are user-created and can be replaced.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
var index = FindNamedRun("MineOre");
if (Truth(index >= 0))
{
UO.SetScriptName(index, "Mining");
}
dynamic FindNamedRun(dynamic wanted)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(UO.GetScriptName(index) == wanted))
{
return index;
}
}
return -1;
}
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:
- Replace wantedPath with your absolute path. The signature of the first active launch of this file changes, but the file on disk remains in the same place.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
return RunScenario();
object? RunScenario()
{
var wantedPath = "D:/Game/AutoLoad/Scripts/Worker.csx";
var index = FindFileRun(wantedPath);
if (Truth(index >= 0))
{
UO.SetScriptName(index, "Worker A");
}
return null;
}
dynamic FindFileRun(dynamic wantedPath)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(Convert.ToString(ReplaceText(UO.GetScriptPath(index), Chr(92), "/")) == Convert.ToString(ReplaceText(wantedPath, Chr(92), "/"))))
{
return index;
}
}
return -1;
}
string Chr(object? value) => ((char)Convert.ToInt32(value)).ToString();
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
string ReplaceText(object? value, object? oldValue, object? newValue) => (Convert.ToString(value) ?? "").Replace(Convert.ToString(oldValue) ?? "", Convert.ToString(newValue) ?? "");Parameters and execution flow:
- GetScriptState = 2 adds the text paused to the name. This is a one-time signature: it is not automatically updated after continuing and does not control the pause itself.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var index = FindNamedRun("Mining");
if (Truth(index >= 0))
{
var newName = "Mining";
if (Truth(UO.GetScriptState(index) == 2))
{
newName = "Mining (paused)";
}
UO.SetScriptName(index, newName);
}
return null;
}
dynamic FindNamedRun(dynamic wanted)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(UO.GetScriptName(index) == wanted))
{
return index;
}
}
return -1;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: scriptIndex (Any), value (Any)
Result: void / Unit.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.SetScriptName:bridge.SetScriptName(Arg(0), Text(1)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Arg:Arg(0). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Text:Text(1).
Internal implementation code; this is not a standalone script.
case "setscriptname": bridge.SetScriptName(Arg(0), Text(1)); return InjectionValue.Unit;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.StartScript(object? scriptPath);int: number of active runs since the accepted run; 65535 (0xFFFF) = startup error. This is not the index of the new script, nor is it a bool, nor is it the result of completion.
- Running and paused launches are taken into account; those completed and having received a cancellation request are excluded. A running script usually takes into account itself. Simply loading the IDE tab is not considered a launch.
- Indexes are the current positions in startup order. Starting/stopping scripts can shift positions. Different calls do not form a common atomic snapshot; re-read the list before the next control command.
- GetScriptsList gives the indices, GetScriptsCount the quantity, GetScriptState the three-state code. Don't substitute one for the other or treat any non-null result as true.
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.StartScript("Scripts/Worker.csx");
UO.Print("StartScript: " + Convert.ToString(result));Parameters and execution flow:
- Run the given C#-script. The number 0, where it is passed, denotes the index; empty parentheses mean no arguments. The text Print is an example message only.
- Before the examples, separately create the specified Worker.csx files. An invalid/inaccessible path, an inappropriate Main, a disabled C#, or a parallel run disabled will result in a failure. If the previous run is still stopped, a delayed retry is possible; accepting the start does not mean completion. A short script may finish before reading the returned quantity.
- int: number of active runs since the accepted run; 65535 (0xFFFF) = startup error. This is not the index of the new script, nor is it a bool, nor is it the result of completion.
- Mandatory ScriptPath is a string. The relative path starts in the AutoLoad folder of this client; absolute path is acceptable. Enclose paths with spaces in quotes. You need .csx with a top-level code or a regular .cs/.csproj with a supported entry point C#.
var count = UO.StartScript("Scripts/Worker.csx");
if (Truth(count==65535))
{
UO.Print("launch failed");
}
else
{
UO.Print("Active executions: " + Convert.ToString(count));
}
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:
- This is a specific example of command sharing. Array indexing starts from zero; Please check the length before handling. UO.Wait(250), where it is used, waits 250 milliseconds.
- Before the examples, separately create the specified Worker.csx files. An invalid/inaccessible path, an inappropriate Main, a disabled C#, or a parallel run disabled will result in a failure. If the previous run is still stopped, a delayed retry is possible; accepting the start does not mean completion. A short script may finish before reading the returned quantity.
- int: number of active runs since the accepted run; 65535 (0xFFFF) = startup error. This is not the index of the new script, nor is it a bool, nor is it the result of completion.
- Mandatory ScriptPath is a string. The relative path starts in the AutoLoad folder of this client; absolute path is acceptable. Enclose paths with spaces in quotes. You need .csx with a top-level code or a regular .cs/.csproj with a supported entry point C#.
return RunScenario();
object? RunScenario()
{
var count = UO.StartScript("Scripts/My Worker.csx");
if (Truth(count != 65535))
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
UO.Print(Convert.ToString(index) + ": " + UO.GetScriptPath(index));
}
}
return null;
}
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:
- Below Main is the entire function. Its parameters and result are explained separately from the API-command it calls.
- TryStartCSharp compares the result with 65535 and returns true/false. It does not wait for completion and does not turn the quantity into an index.
- int: number of active runs since the accepted run; 65535 (0xFFFF) = startup error. This is not the index of the new script, nor is it a bool, nor is it the result of completion.
- Mandatory ScriptPath is a string. The relative path starts in the AutoLoad folder of this client; absolute path is acceptable. Enclose paths with spaces in quotes. You need .csx with a top-level code or a regular .cs/.csproj with a supported entry point C#.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
if (Truth(TryStartCSharp("Scripts/Worker.csx")))
{
UO.Print("launch accepted");
}
else
{
UO.Print("check file, Main and execution settings");
}
return null;
}
dynamic TryStartCSharp(dynamic fileName)
{
var count = UO.StartScript(fileName);
return Truth(count != 65535);
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: scriptPath (Any)
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.StartScript:bridge.StartScript(Text(0)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Text:Text(0).
Internal implementation code; this is not a standalone script.
case "startscript": return new InjectionValue(bridge.StartScript(Text(0)));Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.StopAllScripts();There are no parameters.
void - no return value. Don't compare the call to true/1. Check the condition by separately reading GetScriptState; the result of such a reading is 0 (no start), 1 (running), 2 (pause).
- Works only with runs of the current session. An open tab without execution is not an active launch. Stopping does not delete the original file or disconnect the connection to the server.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
- There are no arguments. Running and suspended procedures, pending queries, and the rerun queue are canceled. The calling script also gets a cancellation: do not put the required save/report after this command. Other profiles and processes are not affected.
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 command is a separate statement. The message before it marks the start of the call; it does not confirm a server action.
UO.Print("Calling UO.StopAllScripts");
UO.StopAllScripts();Parameters and execution flow:
- The command has empty parentheses. Perform the desired save before calling. The controller itself will also be stopped.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
UO.StopAllScripts();Parameters and execution flow:
- Connected checks the entrance to the world, Dead checks death. StopAllScripts is called only when both conditions are met; the example checks them once.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
return RunScenario();
object? RunScenario()
{
if (Truth(UO.Connected()))
{
if (Truth(UO.Dead()))
{
UO.StopAllScripts();
}
}
return null;
}
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:
- 5000 is the milliseconds in UO.Wait, not the StopAllScripts parameter. Manual or automatic controller pause may delay execution; This is not an independent timer.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var delayMs = 5000;
UO.Wait(delayMs);
UO.StopAllScripts();
return null;
}
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: void / Unit.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.StopAllScripts:bridge.StopAllScripts().
Internal implementation code; this is not a standalone script.
case "stopallscripts": bridge.StopAllScripts(); return InjectionValue.Unit;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.StopScript(object? scriptIndex);Requests the cancellation of one active launch, including one that is paused. Use when this run needs to be completed rather than continued later.
-
scriptIndex- Mandatory ScriptIndex - current launch index: 0 means the first one, then 1, 2... Get it through GetScriptsList; it is not serial, the file name and not the result StartScript. A negative/missing index doesn't change anything.
void - no return value. Don't compare the call to true/1. Check the condition by separately reading GetScriptState; the result of such a reading is 0 (no start), 1 (running), 2 (pause).
- Works only with runs of the current session. An open tab without execution is not an active launch. Stopping does not delete the original file or disconnect the connection to the server.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
- Cancellation is cooperative: waits for a pause and is checked by the engine; Actions already sent to the server are not cancelled. A canceled launch is immediately removed from the active list, although resource release may still continue. Do not check completion against the previous index: there may already be another run under it.
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.
for (int index = 0; index < UO.GetScriptsCount(); index++)
if (UO.GetScriptName(index) == "Worker")
{ UO.StopScript(index); break; }Parameters and execution flow:
- MineOre — launch name. -1 is not transferred to StopScript. After stopping, the saved index is no longer used.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
var index = FindNamedRun("MineOre");
if (Truth(index >= 0))
{
UO.StopScript(index);
}
dynamic FindNamedRun(dynamic wanted)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(UO.GetScriptName(index) == wanted))
{
return index;
}
}
return -1;
}
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:
- Code 2 selects a paused run. StopScript ends it rather than unpauses it; other tasks continue.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
return RunScenario();
object? RunScenario()
{
var index = FindNamedRun("MineOre");
if (Truth(index >= 0))
{
if (Truth(UO.GetScriptState(index) == 2))
{
UO.StopScript(index);
}
}
return null;
}
dynamic FindNamedRun(dynamic wanted)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(UO.GetScriptName(index) == wanted))
{
return index;
}
}
return -1;
}
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 absolute path selects the first active run of the file. This is not deleting a file or stopping all procedures from that file.
- Indexes are dynamic: positions may shift after another run is completed. Multiple API calls are not an atomic operation. The examples are designed for the absence of parallel changes to the list; look for the desired launch again before the action and do not store the index via UO.Wait.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var wantedPath = "D:/Game/AutoLoad/Scripts/Worker.csx";
var index = FindFileRun(wantedPath);
if (Truth(index >= 0))
{
UO.StopScript(index);
}
return null;
}
dynamic FindFileRun(dynamic wantedPath)
{
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
if (Truth(Convert.ToString(ReplaceText(UO.GetScriptPath(index), Chr(92), "/")) == Convert.ToString(ReplaceText(wantedPath, Chr(92), "/"))))
{
return index;
}
}
return -1;
}
string Chr(object? value) => ((char)Convert.ToInt32(value)).ToString();
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
string ReplaceText(object? value, object? oldValue, object? newValue) => (Convert.ToString(value) ?? "").Replace(Convert.ToString(oldValue) ?? "", Convert.ToString(newValue) ?? "");
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: scriptIndex (Any)
Result: void / Unit.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.StopScript:bridge.StopScript(Arg(0)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Arg:Arg(0).
Internal implementation code; this is not a standalone script.
case "stopscript": bridge.StopScript(Arg(0)); return InjectionValue.Unit;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.SubExists(object? subrutineName);Checks whether the C# delegate with the specified name is registered with the current execution. This is a registry of callback functions for a given session, without automatically searching for arbitrary methods through reflection; explicitly loaded file inputs are also accessible by key.
-
subrutineNameis the string name given by RegisterRoutine. Whitespace around the edges is removed; Registered name searches are not case sensitive.
int: 1 - condition is met, 0 - not met. Save the result of one call if multiple checks need it.
- 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"));
UO.Print("Registered flag: " + UO.SubExists("Hello"));Parameters and execution flow:
- The command returns int: in C# check result != 0 or result == 1; direct comparison with true is not acceptable. The definition of WeightWatch is given below and is registered via RegisterRoutine before the command is called.
- 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);
if (Truth(Truth(UO.SubExists("WeightWatch")) == true))
{
UO.CallSub("WeightWatch");
}
while (UO.FunRunning("WeightWatch") != 0) await WaitAsync(25);
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:
- WeightWatch can be loaded from another file. If it is not present, the ManaWatch declared here is selected. If the primary is found but cannot start, there will be no automatic transition to the secondary.
- 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 registrationManaWatch = RegisterRoutine("ManaWatch", ManaWatch);
if (Truth(UO.SubExists("WeightWatch") == 1))
{
UO.CallSub("WeightWatch");
}
else if (Truth(UO.SubExists("ManaWatch")))
{
UO.CallSub("ManaWatch");
}
while (UO.FunRunning("ManaWatch") != 0)
await WaitAsync(25);
return null;
}
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;Parameters and execution flow:
- HaveMonitors tests two names using &&. If at least one is missing, Main does not call any task. A set of definitions is tested, not a guarantee of successful simultaneous execution.
- 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);
if (Truth(HaveMonitors()))
{
UO.CallSub("WeightWatch");
UO.CallSub("ManaWatch");
}
while (UO.FunRunning("WeightWatch") != 0 || UO.FunRunning("ManaWatch") != 0)
await WaitAsync(25);
return null;
}
dynamic HaveMonitors()
{
return UO.SubExists("WeightWatch") != 0 && UO.SubExists("ManaWatch") != 0;
}
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: int / Integer.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionApiUO.SubExists. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.SubExists:bridge.SubExists(subrutineName).
Internal implementation code; this is not a standalone script.
case "SubExists":
string requested = Convert.ToString(arguments[0]) ?? "";
result = Exists(requested) || events is ICSharpScriptHost known && known.HasCSharpScript(requested) ? 1 : 0;
return true;Internal implementation code; this is not a standalone script.
public int SubExists(string subrutineName) => bridge.SubExists(subrutineName) ? 1 : 0;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.Terminate();
void UO.Terminate(object? subrutineName);-
subrutineName— Optional string. No argument or an empty string selects the most recently started file; "all" requests cancellation of all runs. For C# files, use an exact catalogue key such asharvest.csx::Main. A local callback name such asHarvestworks only after explicit registration withRegisterRoutine. A file has oneMainentry; individual class methods do not automatically become separately launchable procedures.
void: The command does not return a number and does not wait for the worker thread to terminate. She immediately requests a cancellation. Execution stops at the next cancellation check; pauses, breakpoints, and built-in waits are interrupted. Server actions already dispatched before cancellation are not rolled back.
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.
- UO.Terminate() selects procedures by run order rather than by name, editor line, last action time, or display order. A paused procedure is also considered running.
- The local target belonging to this run is cleared and the remaining automatic route is canceled. Motion cancellation is performed on the game thread; if the route has already been replaced by another run or user, the previous owner does not stop it. Steps sent to the server are not recalled.
- The command remains available when the setting for one simultaneous launch is enabled. A canceled procedure frees up space only after its worker thread has been cleared; Before this, the new launch is skipped.
- Stopping by name also cancels the corresponding pending starts. Completed procedures are not changed. The client reports the absence of a suitable launch in the status. A stuck call to a third-party native plugin that does not support cancellation cannot be safely interrupted by forcefully killing the thread.
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.
For C# files, use an exact catalogue key such as harvest.csx::Main. A local callback name such as Harvest works only after explicit registration with RegisterRoutine. A file has one Main entry; individual class methods do not automatically become separately launchable procedures.
Run this code as a separate .csx file. Arguments appear directly in the call; replace demonstration names, serials and addresses with your own.
UO.Terminate("harvest.csx::Main");
UO.Print("Cancellation requested for harvest.csx::Main");This requests cancellation of the already running harvest.csx::Main. The message does not confirm its finally has finished or that game actions have been rolled back.
var procedureName = "harvest.csx::Main";
UO.Terminate(procedureName);
UO.Print("Cancellation requested for harvest.csx::Main");The exact key harvest.csx::Main differs from heal.csx::Main. Replace it with the key of your loaded file. Arbitrary suffixes such as #2 do not create another entry.
return RunScenario();
object? RunScenario()
{
var procedureKey = "harvest.csx::Main";
UO.Terminate(procedureKey);
UO.Print("Cancellation requested: " + procedureKey);
return null;
}The string terminate harvest.csx::Main passes the same key through UO.Exec. A key containing spaces must be quoted inside the command string.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var command = "terminate harvest.csx::Main";
UO.Exec(command);
UO.Print("Stop command sent");
return null;
}
}Parameters and execution flow:
- The form without arguments selects the last running script, including the paused one. This is a policy by example, not a mandatory response to poor health; a message is displayed before the cancellation request.
if (UO.GetHP() <= 30)
{
UO.Print("Low health: requesting cancellation of the most recent run");
UO.Terminate();
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: void / Unit.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.Terminate. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.Terminate:bridge.Terminate(string.Empty).
Internal implementation code; this is not a standalone script.
public void Terminate() => bridge.Terminate(string.Empty);Parameters: subrutineName (String)
Result: void / Unit.
During a call:
-
ClassicUO.CSharp.CSharpCallbacks.TryInvoke. -
InjectionScript.Runtime.InjectionApiUO.Terminate. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.Terminate:bridge.Terminate(subrutineName).
Internal implementation code; this is not a standalone script.
case "Terminate" when arguments.Length == 1:
string routine = Convert.ToString(arguments[0]) ?? "";
if (Exists(routine)) { Cancel(routine); return true; }
if (Cancel(routine)) return true;
return false;Internal implementation code; this is not a standalone script.
public void Terminate(string subrutineName) => bridge.Terminate(subrutineName);Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.TerminateAll();There are no parameters.
void: no result. The command requests the cancellation of all current runs and returns control to the calling code if it has not been canceled itself. Worker threads are cleaned up after their cancellation checks are triggered.
- The exact syntax is UO.TerminateAll(), with no space inside the name and no arguments. UO.Terminate("all") is supported as a compatible alternative notation.
- Stops running procedures, manually paused and breakpointed procedures, built-in waits, and registered delayed runs. Calling back when there is no work is safe.
- Removing local sights and automatic routes takes into account their owners. The remaining route of the stopped script is canceled on the game thread; the user's new route that replaced it is saved. Already sent motion packages are not cancelled.
- Cancel does not roll back actions already sent to the server. Third-party native plugin code without cancellation support cannot be safely destroyed along with the stream. New explicit starts after command execution are allowed; when the checkbox is enabled, they wait for space to become free in the sense that the attempt is skipped rather than queued until the cleanup is completed.
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 command is a separate statement. The message before it marks the start of the call; it does not confirm a server action.
UO.Print("Calling UO.TerminateAll");
UO.TerminateAll();Parameters and execution flow:
- TerminateAll has no parameters. Condition UO.Dead() checks for the character's death before the next iteration.
- When the condition is met, both the current Main and the remaining runs C# are canceled. The Print message is located before the stop because after it the code for this procedure stops.
- WaitAsync(250) specifies the pause 250 milliseconds between checks in this example. The stop command does not change the length of normal script pauses.
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;Parameters and execution flow:
- This alternative notation uses UO.Terminate with one parameter subrutineName="all". This is the equivalent of UO.TerminateAll().
- The string all is a special value, not the name of the procedure that needs to be declared. EmergencyStop is also cancelled.
- For the emergency game key, use the one-line command from the first example: the separate procedure EmergencyStop is itself subject to the prohibition of parallel runs.
return RunScenario();
object? RunScenario()
{
UO.Print("Stopping all C# scripts");
UO.Terminate("all");
return null;
}Parameters and execution flow:
- GetSerial stores the serial of the required object, here its own backpack. Exists=0 means that this object is currently unknown to the client.
- TerminateAll is called without arguments only when the selected condition is violated. This policy stops all procedures, not just work on a given backpack.
- First, the reason for the stop is displayed. Once a cancellation request is made, the current procedure is also terminated; the next action cannot be counted on.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var bag = UO.GetSerial("backpack");
if (Truth(UO.Exists(bag) == 0))
{
UO.Print("Required backpack is unavailable: stopping all scripts");
UO.TerminateAll();
}
return null;
}
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:
- If there is no connection, all starts are stopped. The command does not return the number of canceled scripts and also cancels the current run.
if (UO.Connected() == 0)
{
UO.Print("Disconnected: requesting cancellation of every active script");
UO.TerminateAll();
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: void / Unit.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.TerminateAll. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.Terminate:bridge.Terminate("all").
Internal implementation code; this is not a standalone script.
public void TerminateAll() => bridge.Terminate("all");Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.Time();Reads the computer's local time of day using the number HHMMSS. Convenient for selecting the allowed operating time, not for measuring duration.
There are no parameters.
int HHMMSS, 0..235959 with valid minutes and seconds. 07:29:35 -> 72935; midnight -> 0. Zero is a valid time, not an error or a logical flag 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.
- Leading zeros are not preserved, milliseconds are not preserved. Numerically, 13:00:00 minus 12:59:59 gives 4041, although one second has passed. At midnight the value becomes zero again.
- 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.Time();
UO.Print("Time: " + Convert.ToString(result));Parameters and execution flow:
- stamp is read once so as not to mix different seconds. Int(stamp/10000) — hours; Int(stamp/100) MOD 100 — minutes; MOD 100 – seconds. A simple string does not pad fields with zeros.
var stamp = UO.Time();
var hour = stamp / 10000;
var minute = stamp / 100 % 100;
var second = stamp % 100;
UO.Print(Convert.ToString(hour) + ":" + Convert.ToString(minute) + ":" + Convert.ToString(second));Parameters and execution flow:
- 180000 - 18:00:00 inclusive; 220000 - 22:00:00 not inclusive. One check decides whether to continue Main. The schedule is not registered and the series does not start automatically.
return RunScenario();
object? RunScenario()
{
var stamp = UO.Time();
if (Truth(stamp >= 180000) && Truth(stamp < 220000))
{
UO.Print("Evening batch permitted by local schedule");
}
else
{
UO.Print("Outside the local schedule");
return null;
}
return null;
}
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:
- ClockInWindow(stamp,first,last) accepts valid HHMMSS. 230000..20000 means from 23:00 inclusive to 02:00 not inclusive: OR takes midnight into account. Equal boundaries mean empty interval. During the quiet period, Main ends; The function is given in its entirety.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
if (Truth(ClockInWindow(UO.Time(), 230000, 20000)))
{
UO.Print("Local quiet period; do not start a new batch");
return null;
}
UO.Print("Outside the local quiet period");
return null;
}
dynamic ClockInWindow(dynamic stamp, dynamic first, dynamic last)
{
if (Truth(first == last))
{
return false;
}
if (Truth(first < last))
{
return Truth(stamp >= first) && Truth(stamp < last);
}
return Truth(stamp >= first) || Truth(stamp < last);
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: int / Integer.
During a call:
Internal implementation code; this is not a standalone script.
public int Time()
{
var now = timeSource.Now;
return now.Hour * 10000 + now.Minute * 100 + now.Second;
}Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.Timer();Reads the running time of the current runtime in tenths of a second. Suitable for your own delays and action marks.
There are no parameters.
int 0..2147483647; unit is 100 ms. At 399 ms 3 will be returned. This is the clock reading, not true/false and not the recharge remaining.
- 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.
- The origin belongs to this runtime instance. A new running procedure may receive a new clock; You cannot compare values from different runs or store them as an absolute date. During UO.Wait and a script pause, real time continues to tick.
- The fractional part of the tenths is discarded. After about 2485.5 days the number reaches 2147483647 and remains there without overflowing.
- This is your delay, not a server cooldown of a skill/spell or a confirmation of success. The command does not allow you to bypass server restrictions on movement, spells or 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.
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.Timer();
UO.Print("Timer: " + Convert.ToString(result));Parameters and execution flow:
- WaitAsync(250) requests 250 ms; actual delay may be longer. The difference between the two marks is divided by 10.0 to obtain the seconds. Indication step UO.Timer - 100 ms.
var started = UO.Timer();
await Delay(250);
var elapsed = UO.Timer() - started;
var seconds = elapsed / 10.0;
UO.Print("Elapsed seconds: " + Convert.ToString(seconds));
Task Delay(object? milliseconds) => WaitAsync(checked((int)Convert.ToDouble(milliseconds)));Parameters and execution flow:
- The limit 30 is given in units of this function. UO.Wait(50) releases execution between checks. UO.Targeting reads the presence of a sight; example does not cast or target. If the sight does not appear, the cycle ends.
- An additional upper bound check ends the wait even if the int counter stops at the limit. The target cursor status is read repeatedly and may change between readings.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
var started = UO.Timer();
while (Truth(UO.Timer() - started < 30) && !(Truth(UO.Targeting())))
{
if (Truth(UO.Timer() >= 2147483647))
{
break;
}
await Delay(50);
}
if (Truth(UO.Targeting()))
{
UO.Print("Target cursor available");
}
else
{
UO.Print("Target wait ended");
}
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:
- IntervalPassed(started,current,interval) is completely defined under Main. interval=5 — half a second in units of this clock. Negative interval, inverse, and int on the upper bound yield false; reaching the interval exactly gives true.
- This is your delay, not a server cooldown of a skill/spell or a confirmation of success. The command does not allow you to bypass server restrictions on movement, spells or scripts.
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 started = UO.Timer();
await Delay(500);
var ready = IntervalPassed(started, UO.Timer(), 5);
if (Truth(ready))
{
UO.Print("Local retry interval passed");
}
return null;
}
dynamic IntervalPassed(dynamic started, dynamic current, dynamic interval)
{
if (Truth(interval < 0) || Truth(current < started))
{
return false;
}
if (Truth(current >= 2147483647))
{
return false;
}
return current - started >= interval;
}
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;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: int / Integer.
During a call:
Internal implementation code; this is not a standalone script.
public int Timer() => (int)Math.Max(0d, Math.Min(int.MaxValue, timeSource.SinceStart.TotalMilliseconds / 100d));Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.Wait(object? waitTimeMS);Pauses the current script for the specified number of milliseconds. Useful between repeated status checks to prevent the script from running in a continuous loop.
-
waitTimeMS— Integer time in milliseconds: 1000 = one second. Use 0..2147483647. This is not the number of steps, not the seconds, and not the server confirmation timeout.
void - no result. Call on a separate line. The end of the delay does not mean that the object has moved, the spell has completed, or the connection has been established: check the desired state separately.
- Zero creates no real delay and is not suitable as a pause in a fast loop. Negative time does not mean infinity: UO.Wait reduces it to 0; UO.Wait/sleep can reject it with active callbacks, and in the client bridge without them it will not wait. For a portable script, use only non-negative times.
UO.Wait(100);
// For an asynchronous script, use the host helper:
await WaitAsync(100);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 command is a separate statement. The message before it marks the start of the call; it does not confirm a server action.
UO.Print("Calling UO.Wait");
UO.Wait(250);Parameters and execution flow:
- 250 - quarter of a second. The next line is executed after waiting; The Delay complete message is the text of the example, not the returned result.
UO.Wait(250);
UO.Print("Delay complete");Parameters and execution flow:
- WaitForOnline is fully declared in this script. The argument 20 means a maximum of twenty pauses of 250 ms. The connection is checked before pauses and again after the last one. The helper returns the state Connected (1/0); The wait command itself does not return anything and does not connect to the server.
return RunScenario();
object? RunScenario()
{
return WaitForOnline(20);
return null;
}
dynamic WaitForOnline(dynamic checks)
{
while (Truth(checks > 0))
{
if (Truth(UO.Connected()))
{
return 1;
}
UO.Wait(250);
checks = checks - 1;
}
return UO.Connected();
}
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 loop makes exactly three weight readings. Between readings there are two pauses of 500 ms, after the last there is no pause. A message is printed the first time it is read and when the weight changes. The previous variable stores the value to compare, rather than adjusting the game weight.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
var previous = -1;
var checks = 0;
while (Truth(checks < 3))
{
var weight = UO.Weight();
if (Truth(weight != previous))
{
UO.Print("Weight: " + Convert.ToString(weight));
previous = weight;
}
checks = checks + 1;
if (Truth(checks < 3))
{
UO.Wait(500);
}
}
return null;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: waitTimeMS (Any)
Result: void / Unit.
During a call:
-
ClassicUO.CSharp.CSharpScriptGlobals.WaitAsync. -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
InjectionScript.Runtime.InjectionApiUO.WaitWithEvents:WaitWithEvents(Arg(0)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Arg:Arg(0).
Internal implementation code; this is not a standalone script.
public async Task WaitAsync(int milliseconds)
{
ArgumentOutOfRangeException.ThrowIfNegative(milliseconds);
Checkpoint();
// Short slices allow Pause to suspend further script progress promptly.
int remaining = milliseconds;
while (remaining > 0)
{
int slice = Math.Min(remaining, 25);
await Task.Delay(slice, Cancellation).ConfigureAwait(false);
Checkpoint(); remaining -= slice;
}
}Internal implementation code; this is not a standalone script.
case "wait": WaitWithEvents(Arg(0)); return InjectionValue.Unit;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.Waiting();Checks whether prepared responses remain in the target queue.
There are no parameters.
int 1 - there is an uncanceled deferred target of an object, tile or cancellation of the next sight; 0 - queue is empty. It's not having an open cursor; for the cursor use TargetPresent/Targeting.
The result of the command is a numeric flag: 1 means a fulfilled condition, 0 means an unfulfilled one. After var result = command(...) check if (result != 0) or if (result == 0). Save the result of one call: a repeated call can perform the action again or read the state that has already changed.
- The entire client's queue is read. If several procedures are running simultaneously, the responses of each of them are taken into account.
- Zero only means there is no response pending. It does not confirm the successful use of a skill or spell on the server.
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.Waiting();
UO.Print("Waiting: " + Convert.ToString(result));Parameters and execution flow:
- After WaitTargetSelf and before the server target 1 is expected.
- CancelWaitTarget clears the queue; the next test gives 0.
UO.WaitTargetSelf();
UO.Print(Convert.ToString(UO.Waiting()));
UO.CancelWaitTarget();Parameters and execution flow:
- The check has no parameters. UO.Wait=50 ms, maximum 20 expectations.
- If the answer remains, it is cancelled. Add a check for the result of the server action separately.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
var attempts = 0;
while (Truth(UO.Waiting()) && Truth(attempts < 20))
{
await Delay(50);
attempts = attempts + 1;
}
if (Truth(UO.Waiting()))
{
UO.CancelWaitTarget();
}
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:
- The first line shows the deferred responses, the second - the state of the sight.
- A queue without a cursor and a cursor without a queue are possible; these values are not interchangeable.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
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;
}
public object? RunScenario()
{
UO.Print("Queued: " + Convert.ToString(UO.Waiting()));
UO.Print("Cursor: " + Convert.ToString(UO.TargetPresent()));
return null;
}
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: No parameters.
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApiUO.Waiting. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.GetWaitTargetQueue:bridge.GetWaitTargetQueue().
Internal implementation code; this is not a standalone script.
public int Waiting() => bridge.GetWaitTargetQueue().Length > 0 ? 1 : 0;ClassicUO — Bad Newbie & C# Scripts IDE