Skip to content

RU UO Topic scripts runtime

Codex edited this page Oct 12, 2026 · 4 revisions

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

Скрипты и среда выполнения

Главная · C# · IDE · UO API · A–Z · Примеры · Языки

Запуск, пауза, остановка скриптов и процедур, сведения об экземплярах, события и ожидание. Terminate и StopScript работают на разных уровнях выполнения.

Разделы

Тема Команды и пояснения
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

Таймеры / Waiting

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

Disconnect Поведение

UO.GetPauseScriptOnDisconnectStatus · UO.SetPauseScriptOnDisconnectStatus

Примеры по теме

В каждой карточке команды есть несколько самостоятельных сценариев: прямой код, функции и классы. Ниже приведены отдельные короткие сценарии; ограничения и полный разбор находятся в карточках.

UO.GetScriptsList

Параметры, результат и все сценарии

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

UO.TerminateAll

Параметры, результат и все сценарии

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;

Полное описание команд


UO.CallSub

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

C#-сигнатуры

void UO.CallSub(object? subrutineName);

Запускает C#-делегат, заранее зарегистрированный в текущем выполнении через RegisterRoutine(name, handler). Вызов возвращает управление до завершения помощника; одну зарегистрированную задачу нельзя накладывать саму на себя.

Параметры

  • subrutineName — строковое имя, заданное RegisterRoutine. Пробелы по краям удаляются; поиск зарегистрированного имени не зависит от регистра.

Возвращает

void — результата нет; неизвестное имя CallSub вызывает ошибку, а повторный запуск уже активного имени пропускается.

Поведение

Значение UO.FunRunning(name) == 0 возможно также сразу после запроса отмены. Оно не означает, что блок finally предыдущего callback уже завершился. Повторный CallSub или Exec того же локального имени пропускается, пока прежний обработчик фактически не завершит работу.

  • Регистрация и задачи принадлежат одному выполнению. Другой персонаж, профиль или последующий запуск имеет собственный реестр.
  • RegisterRoutine принимает Action, Func<Task> либо делегат с массивом строковых аргументов для Exec. Удаление регистрации не запускает код и исключает дальнейший поиск имени.
  • Основной скрипт должен оставаться активным, пока нужны его помощники: после его окончания задачи отменяются, а регистрации и подписки освобождаются.
  • FunRunning не является индексом менеджера. GetScriptsList и GetScriptState описывают самостоятельные запуски файлов; локальный callback отдельного индекса не получает.
  • Ошибка callback передаётся основному скрипту на следующей проверке. WaitAsync, UO.Wait и инструментированные C#-циклы поддерживают паузу, отмену и обработку очереди событий.

Загруженные C#-файлы

Помимо локальных делегатов, текущий менеджер поддерживает явно загруженные C#-входы с ключом вроде Child.csx::Main. SubExists и FunRunning принимают такой ключ; CallSub запускает файл без Args, а Exec("exec Child.csx::Main 'north gate' 42") передаёт Args. Ключ с пробелами заключается в кавычки внутри Exec. Уникальное короткое имя допустимо, неоднозначное имя не выбирает случайный файл. Локальная регистрация имеет приоритет. Файловый запуск получает собственный контекст и время жизни в текущем мире/менеджере; локальные делегаты принадлежат выполнению, которое их зарегистрировало.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

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

1. Запустить наблюдателя и продолжить Main — прямой скрипт

Параметры и ход выполнения:

  • WeightWatch — имя процедуры ниже. Main возвращает 77, не ожидая трёх замеров; 77 — результат Main, не CallSub.
  • Сохраните пример как отдельный файл, загрузите его в IDE/менеджер и запускайте Main, чтобы остальные процедуры попали в каталог. WeightWatch/ManaWatch — полностью определённые здесь учебные процедуры: три замера веса/маны с интервалом UO.Wait(1000), то есть 1000 мс, вывод только при входе в мир. Это не постоянный мониторинг; пауза может растянуть время. Для примера с двумя задачами должен быть разрешён одновременный запуск процедур.
var registrationWeightWatch = RegisterRoutine("WeightWatch", WeightWatch);
UO.CallSub("WeightWatch");
while (UO.FunRunning("WeightWatch") != 0) await WaitAsync(25);
return 77;

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

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

2. Не перезапускать активную или приостановленную задачу — функция сценария

Параметры и ход выполнения:

  • StartIfIdle сначала проверяет определение, затем активность. false означает, что запрос не отправлен. true означает только отправленный запрос CallSub, а не подтверждение запуска или завершения.
  • Сохраните пример как отдельный файл, загрузите его в IDE/менеджер и запускайте Main, чтобы остальные процедуры попали в каталог. WeightWatch/ManaWatch — полностью определённые здесь учебные процедуры: три замера веса/маны с интервалом UO.Wait(1000), то есть 1000 мс, вывод только при входе в мир. Это не постоянный мониторинг; пауза может растянуть время. Для примера с двумя задачами должен быть разрешён одновременный запуск процедур.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
    var registrationWeightWatch = RegisterRoutine("WeightWatch", WeightWatch);
    var launched = StartIfIdle("WeightWatch");
    while (UO.FunRunning("WeightWatch") != 0)
        await WaitAsync(25);
    return launched;
    return null;
}

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

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

    UO.CallSub(name);
    return true;
}

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

        UO.Wait(1000);
    }
}

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

3. Два независимых наблюдателя — класс сценария

Параметры и ход выполнения:

  • Первый CallSub запускает WeightWatch, второй — ManaWatch. Запуски независимы и не обмениваются локальными переменными; порядок их вывода не гарантирован.
  • Сохраните пример как отдельный файл, загрузите его в IDE/менеджер и запускайте Main, чтобы остальные процедуры попали в каталог. WeightWatch/ManaWatch — полностью определённые здесь учебные процедуры: три замера веса/маны с интервалом UO.Wait(1000), то есть 1000 мс, вывод только при входе в мир. Это не постоянный мониторинг; пауза может растянуть время. Для примера с двумя задачами должен быть разрешён одновременный запуск процедур.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

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

Параметры: subrutineName (String)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

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;

Внутренний код реализации, не самостоятельный скрипт.

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

UO.Date

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

C#-сигнатуры

int UO.Date();

Читает местную календарную дату компьютера числом YYMMDD. Удобна для названия дневной серии действий или обнаружения смены даты.

Параметры

Параметров нет.

Возвращает

int YYMMDD: 2026-09-30 -> 260930. Разделителей и сохраняемых начальных нулей нет. Это не длительность, не Unix-время и не true/false.

Поведение

  • Параметров нет, форм с аргументами нет. Пустые скобки только читают время: вызов не ждёт, не запускает таймер и не отправляет пакеты в игру.
  • RealTimeSource.Now читает DateTime.Now: местную дату/время компьютера, не UTC, не время шарда и не игровые часы. Часовой пояс, летнее время и ручной перевод часов влияют на результат. Каждый вызов фиксирует отдельный момент.
  • Сохраняются только две последние цифры года: и 2000-01-01, и 2100-01-01 дают 101. Разные столетия не различаются. Отдельные вызовы Date и Time могут попасть по разные стороны полуночи.
  • Не вычитайте календарные коды для расчёта задержки. Используйте Timer() и две отметки одного запуска. Календарные часы могут перевестись назад.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

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

1. Назвать дневную серию сбора ресурсов — прямой скрипт

Параметры и ход выполнения:

  • Результат вызова без параметров сохраняется один раз. sessionLabel — строка вроде harvest-260930. Print только показывает название: файл не создаётся, сбор ресурсов не запускается.
var dateStamp = UO.Date();
var sessionLabel = "harvest-" + Convert.ToString(dateStamp);
UO.Print(sessionLabel);

2. Завершить серию при смене местного дня — функция сценария

Параметры и ход выполнения:

  • originalDate хранит исходную дату. Timer ограничивает наблюдение 60 секундами, UO.Wait(1000) даёт проверку примерно раз в секунду. Смена даты завершает Main. Пример не ждёт полуночи бесконечно и не утверждает, что сменился день на сервере.
return await RunScenarioAsync();
async Task<object?> RunScenarioAsync()
{
    var originalDate = UO.Date();
    var started = Environment.TickCount;
    while (Truth(Environment.TickCount - started < 60.0))
    {
        if (Truth(UO.Date() != originalDate))
        {
            UO.Print("Local date changed; end this batch");
            return null;
        }

        await Delay(1000);
    }

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

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

3. Выделить год, месяц и день — класс сценария

Параметры и ход выполнения:

  • MonthPart(stamp) показана целиком: деление с округлением вниз и MOD 100 извлекают месяц. day — две последние цифры; shortYear — неполный год. Для 260930 получаются 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.Date() — UO.Date()

Параметры: Параметров нет.

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

public int Date()

        {

            var now = timeSource.Now;

            return (now.Year % 100) * 10000 + now.Month * 100 + now.Day;

        }

UO.Exec

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

C#-сигнатуры

void UO.Exec(object? parameters);

Выполняет текстовую игровую команду либо запускает зарегистрированный C#-делегат или загруженный C#-файл.

Параметры

  • parameters — строка команды: например "useskill Hiding", "exec Worker 42" или "exec Child.csx::Main 'north gate' 42". Выражение C# вроде 2+3 внутри строки автоматически не вычисляется.

Возвращает

void: результата нет. Возврат управления не означает окончания запуска или подтверждения игрового действия сервером.

Поведение

Значение UO.FunRunning(name) == 0 возможно также сразу после запроса отмены. Оно не означает, что блок finally предыдущего callback уже завершился. Повторный CallSub или Exec того же локального имени пропускается, пока прежний обработчик фактически не завершит работу.

  • Сначала проверяется локальный реестр RegisterRoutine. Поддерживаются "Name args", "exec Name args" и "_exec Name args". Делегат получает string[]: аргумент 42 остаётся строкой "42", а '00123' сохраняет ведущие нули. Одинарные либо двойные кавычки объединяют аргумент с пробелами; '' задаёт пустую строку. Незакрытая кавычка вызывает ошибку. В этом маршруте обратный слеш сохраняется буквально; не используйте синтаксис экранирования C# внутри уже готового текста команды.
  • Локальный делегат запускается асинхронно. Для ожидания его окончания используйте FunRunning и WaitAsync. Повторный запуск активного имени не создаёт копию и не снимает паузу. Ошибка помощника передаётся основному скрипту на контрольной точке.
  • Явно загруженные файловые входы запускаются текущим менеджером с собственным Args и временем жизни. Само принятие запроса запуска ещё не означает, что файл закончил работу.
  • Обычные игровые команды передаются native-диспетчеру. Там сохраняются контракты параметров соответствующей UO-команды; Print/Msg/Say/ConsolePrint/TextPrint объединяют остаток строки в текст. Для точных C#-типов и выражений используйте прямой вызов вроде UO.UseSkill("Hiding"). Неизвестная команда, неподходящие аргументы или кавычки могут вызвать исключение, перехватываемое try/catch.
  • Консольные алиасы: sayu → Say, closeuo → CloseHandle, CountOnGround → CountGround, clearignorelist → IgnoreReset, canceltarget → CancelTarget, cancelmove → MoveOff.

Загруженные C#-файлы

Помимо локальных делегатов, текущий менеджер поддерживает явно загруженные C#-входы с ключом вроде Child.csx::Main. SubExists и FunRunning принимают такой ключ; CallSub запускает файл без Args, а Exec("exec Child.csx::Main 'north gate' 42") передаёт Args. Ключ с пробелами заключается в кавычки внутри Exec. Уникальное короткое имя допустимо, неоднозначное имя не выбирает случайный файл. Локальная регистрация имеет приоритет. Файловый запуск получает собственный контекст и время жизни в текущем мире/менеджере; локальные делегаты принадлежат выполнению, которое их зарегистрировало.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

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

1. Использовать выбранный навык — прямой скрипт

Параметры и ход выполнения:

  • Hiding — единственный аргумент UseSkill. При входе в мир клиент отправляет запрос использования навыка; успех скрытия определяет сервер. Та же операция прямым вызовом: UO.UseSkill("Hiding").
UO.Exec("useskill Hiding");

2. Передать строковую метку локальной процедуре — функция сценария

Параметры и ход выполнения:

  • SelectSavedRoute определена ниже и записывает название выбранного маршрута. Кавычки вокруг 00123 сохраняют ведущие нули. Exec запускает зарегистрированный делегат асинхронно. Цикл с FunRunning и WaitAsync явно ожидает его окончания, затем сценарий возвращает строку 00123. Это учебное сохранение имени, а не команда движения.
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);
}

3. Запустить отдельного наблюдателя — класс сценария

Параметры и ход выполнения:

  • Запускайте этот C#-скрипт: RegisterRoutine связывает имя WeightWatch с объявленной функцией текущего выполнения. exec WeightWatch запускает три замера веса с интервалом 1000 мс и не ждёт их завершения. Основной скрипт остаётся активным, пока работает локальный помощник; у помощника нет отдельного индекса менеджера. Если задача уже на паузе, повторный Exec её не возобновляет.
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);
}

4. Загрузить скрипт из папки с пробелами — прямой скрипт

Параметры и ход выполнения:

  • Замените путь существующим C#-файлом со своей процедурой Main. startscript загружает файл и запрашивает запуск Main. Кавычки принадлежат текстовой команде; обратные слеши пути сохраняются. Ошибки запуска смотрите в IDE, так как Exec не возвращает подтверждения.
UO.Exec("startscript 'C:\\UO Scripts\\Gather.csx'");

Реализация команды

C# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.Exec(parameters) — UO.Exec(String)

Параметры: parameters (String)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

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;

Внутренний код реализации, не самостоятельный скрипт.

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

UO.FunRunning

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

C#-сигнатуры

int UO.FunRunning(object? subrutineName);

Проверяет, выполняется ли зарегистрированный C#-callback с указанным именем. Результат 1 включает ручную паузу; завершённая либо отменяющаяся задача даёт 0.

Параметры

  • subrutineName — строковое имя, заданное RegisterRoutine. Пробелы по краям удаляются; поиск зарегистрированного имени не зависит от регистра.

Возвращает

int: 1 — условие выполнено, 0 — не выполнено. Сохраните результат одного вызова, если он нужен нескольким проверкам.

Поведение

Значение UO.FunRunning(name) == 0 возможно также сразу после запроса отмены. Оно не означает, что блок finally предыдущего callback уже завершился. Повторный CallSub или Exec того же локального имени пропускается, пока прежний обработчик фактически не завершит работу.

  • Регистрация и задачи принадлежат одному выполнению. Другой персонаж, профиль или последующий запуск имеет собственный реестр.
  • RegisterRoutine принимает Action, Func<Task> либо делегат с массивом строковых аргументов для Exec. Удаление регистрации не запускает код и исключает дальнейший поиск имени.
  • Основной скрипт должен оставаться активным, пока нужны его помощники: после его окончания задачи отменяются, а регистрации и подписки освобождаются.
  • FunRunning не является индексом менеджера. GetScriptsList и GetScriptState описывают самостоятельные запуски файлов; локальный callback отдельного индекса не получает.
  • Ошибка callback передаётся основному скрипту на следующей проверке. WaitAsync, UO.Wait и инструментированные C#-циклы поддерживают паузу, отмену и обработку очереди событий.

Загруженные C#-файлы

Помимо локальных делегатов, текущий менеджер поддерживает явно загруженные C#-входы с ключом вроде Child.csx::Main. SubExists и FunRunning принимают такой ключ; CallSub запускает файл без Args, а Exec("exec Child.csx::Main 'north gate' 42") передаёт Args. Ключ с пробелами заключается в кавычки внутри Exec. Уникальное короткое имя допустимо, неоднозначное имя не выбирает случайный файл. Локальная регистрация имеет приоритет. Файловый запуск получает собственный контекст и время жизни в текущем мире/менеджере; локальные делегаты принадлежат выполнению, которое их зарегистрировало.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

RegisterRoutine("Hello", async () => await WaitAsync(100));
UO.CallSub("Hello");
UO.Print("Running flag: " + UO.FunRunning("Hello"));
while (UO.FunRunning("Hello") != 0) await WaitAsync(25);

1. Запустить зарегистрированный наблюдатель без наложения — прямой скрипт

Параметры и ход выполнения:

  • Сначала RegisterRoutine связывает имя с C#-функцией. FunRunning проверяет задачу этого выполнения, включая паузу. Основной скрипт остаётся активным до завершения помощника.
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);
    }
}

2. Дождаться завершения и прочитать результат фоновой работы — функция сценария

Параметры и ход выполнения:

  • Async-делегат меняет общий счётчик через Interlocked. После FunRunning=0 результат читается через Volatile. Это локальный callback; отдельного индекса 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;
}

3. Отменить зарегистрированный async-помощник — класс сценария

Параметры и ход выполнения:

  • Terminate выбирает зарегистрированную задачу текущего выполнения. Ожидание WaitAsync использует её токен отмены. finally выполняет очистку; дальнейшие игровые действия отменённой задачи не допускаются.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.FunRunning(subrutineName) — UO.FunRunning(String)

Параметры: subrutineName (String)

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "FunRunning":
                string runningName = Convert.ToString(arguments[0]) ?? "";
                result = IsRunning(runningName) || events is ICSharpScriptHost runningHost && runningHost.IsCSharpScriptRunning(runningName) ? 1 : 0;
                return true;

Внутренний код реализации, не самостоятельный скрипт.

public int FunRunning(string subrutineName) => bridge.FunRunning(subrutineName) ? 1 : 0;

UO.GetPauseScriptOnDisconnectStatus

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

C#-сигнатуры

dynamic UO.GetPauseScriptOnDisconnectStatus();

Читает настройку паузы скриптов при отключении менеджера.

Параметры

Параметров нет.

Возвращает

int 1 — PauseScriptsOnDisconnect включён; 0 — выключен.

Поведение

  • Читает флаг менеджера, не текущую паузу отдельного скрипта. Сам вызов не ставит и не снимает паузу.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

object? result = UO.GetPauseScriptOnDisconnectStatus();
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));

1. Прочитать настройку — прямой скрипт

Параметры и ход выполнения:

  • Значение описывает настройку менеджера.
var enabled = UO.GetPauseScriptOnDisconnectStatus();
UO.Print(Convert.ToString(enabled));

2. Отдельно проверить соединение — функция сценария

Параметры и ход выполнения:

  • Флаг настройки и соединение читаются независимо.
return  RunScenario();
 object? RunScenario()
{
    var pauseOnDisconnect = UO.GetPauseScriptOnDisconnectStatus();
    var online = UO.Connected();
    UO.Print(Convert.ToString(pauseOnDisconnect) + " " + Convert.ToString(online));
    return null;
}

3. Сохранить значение — класс сценария

Параметры и ход выполнения:

  • Переменная содержит снимок настройки на момент чтения.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.GetPauseScriptOnDisconnectStatus() — UO.GetPauseScriptOnDisconnectStatus()

Параметры: Параметров нет.

Результат: dynamic / Any.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "getpausescriptondisconnectstatus": return new InjectionValue(bridge.GetPauseScriptOnDisconnectStatus());

UO.GetScriptName

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

C#-сигнатуры

string UO.GetScriptName(object? scriptIndex);

Читает отображаемое имя активного запуска.

Параметры

  • scriptIndex — Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из IDE.

Возвращает

string: отображаемое имя либо "", если индекса нет. У существующего запуска имя тоже может быть явно установлено в пустую строку.

Поведение

  • Учитываются выполняющиеся и приостановленные запуски; завершённые и получившие запрос отмены исключаются. Работающий скрипт обычно учитывает и себя. Просто загруженная вкладка IDE запуском не считается.
  • Индексы — текущие позиции в порядке запуска. Запуск/остановка скриптов могут сдвинуть позиции. Разные вызовы не образуют общий атомарный снимок; перед последующей командой управления перечитайте список.
  • GetScriptsList даёт индексы, GetScriptsCount — количество, GetScriptState — код из трёх состояний. Не подменяйте одно другим и не считайте любой ненулевой результат значением true.
  • ScriptIndex=0 — первый текущий запуск, не обязательно вызывающий скрипт. SetScriptName меняет отображаемое имя, не переименовывая файл.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

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

1. Первый вызов и результат — прямой скрипт

Параметры и ход выполнения:

  • Запускайте приведённый C#-скрипт. Число 0, где оно передано, обозначает индекс; пустые скобки означают отсутствие аргументов. Текст Print — только сообщение примера.
  • ScriptIndex=0 — первый текущий запуск, не обязательно вызывающий скрипт. SetScriptName меняет отображаемое имя, не переименовывая файл.
  • string: отображаемое имя либо "", если индекса нет. У существующего запуска имя тоже может быть явно установлено в пустую строку.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из IDE.
var index = 0;
var name = UO.GetScriptName(index);
UO.Print(name);

2. Использование в цикле или условии — функция сценария

Параметры и ход выполнения:

  • Это отдельный пример совместного использования команд. Индексация массива начинается с нуля; перед обращением проверяйте длину. UO.Wait(250), где он используется, ждёт 250 миллисекунд.
  • ScriptIndex=0 — первый текущий запуск, не обязательно вызывающий скрипт. SetScriptName меняет отображаемое имя, не переименовывая файл.
  • string: отображаемое имя либо "", если индекса нет. У существующего запуска имя тоже может быть явно установлено в пустую строку.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из 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;
}

3. Полная вспомогательная функция — класс сценария

Параметры и ход выполнения:

  • Под Main приведена функция целиком. Её параметры и результат объясняются отдельно от API-команды, которую она вызывает.
  • DescribeScript проверяет состояние и объединяет имя с путём. Между вызовами возможна остановка; "missing" — значение нашей функции, а не результат GetScriptName.
  • string: отображаемое имя либо "", если индекса нет. У существующего запуска имя тоже может быть явно установлено в пустую строку.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.GetScriptName(scriptIndex) — UO.GetScriptName(Any)

Параметры: scriptIndex (Any)

Результат: string / String.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "getscriptname": return new InjectionValue(bridge.GetScriptName(Arg(0)));

UO.GetScriptPath

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

C#-сигнатуры

string UO.GetScriptPath(object? scriptIndex);

Читает путь исходного файла активного запуска.

Параметры

  • scriptIndex — Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из IDE.

Возвращает

string: сохранённый путь исходного файла либо "", если индекса нет. При запуске файла это обычно полный путь; у команды или исходника в памяти обычного файла может не быть.

Поведение

  • Учитываются выполняющиеся и приостановленные запуски; завершённые и получившие запрос отмены исключаются. Работающий скрипт обычно учитывает и себя. Просто загруженная вкладка IDE запуском не считается.
  • Индексы — текущие позиции в порядке запуска. Запуск/остановка скриптов могут сдвинуть позиции. Разные вызовы не образуют общий атомарный снимок; перед последующей командой управления перечитайте список.
  • GetScriptsList даёт индексы, GetScriptsCount — количество, GetScriptState — код из трёх состояний. Не подменяйте одно другим и не считайте любой ненулевой результат значением true.
  • ScriptIndex=0 выбирает первый текущий запуск. Результат указывает его исходник; команда не открывает, не сохраняет и не запускает этот файл.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

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

1. Первый вызов и результат — прямой скрипт

Параметры и ход выполнения:

  • Запускайте приведённый C#-скрипт. Число 0, где оно передано, обозначает индекс; пустые скобки означают отсутствие аргументов. Текст Print — только сообщение примера.
  • ScriptIndex=0 выбирает первый текущий запуск. Результат указывает его исходник; команда не открывает, не сохраняет и не запускает этот файл.
  • string: сохранённый путь исходного файла либо "", если индекса нет. При запуске файла это обычно полный путь; у команды или исходника в памяти обычного файла может не быть.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из 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;

2. Использование в цикле или условии — функция сценария

Параметры и ход выполнения:

  • Это отдельный пример совместного использования команд. Индексация массива начинается с нуля; перед обращением проверяйте длину. UO.Wait(250), где он используется, ждёт 250 миллисекунд.
  • ScriptIndex=0 выбирает первый текущий запуск. Результат указывает его исходник; команда не открывает, не сохраняет и не запускает этот файл.
  • string: сохранённый путь исходного файла либо "", если индекса нет. При запуске файла это обычно полный путь; у команды или исходника в памяти обычного файла может не быть.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из IDE.
return  RunScenario();
 object? RunScenario()
{
    var indices = UO.GetScriptsList();
    foreach (var index in indices)
    {
        UO.Print(UO.GetScriptName(index) + " -> " + UO.GetScriptPath(index));
    }

    return null;
}

3. Полная вспомогательная функция — класс сценария

Параметры и ход выполнения:

  • Под Main приведена функция целиком. Её параметры и результат объясняются отдельно от API-команды, которую она вызывает.
  • ReadScriptPath подставляет параметр fallback только при пустом пути. Это отдельная функция, а не новая перегрузка GetScriptPath.
  • string: сохранённый путь исходного файла либо "", если индекса нет. При запуске файла это обычно полный путь; у команды или исходника в памяти обычного файла может не быть.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.GetScriptPath(scriptIndex) — UO.GetScriptPath(Any)

Параметры: scriptIndex (Any)

Результат: string / String.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "getscriptpath": return new InjectionValue(bridge.GetScriptPath(Arg(0)));

UO.GetScriptState

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

C#-сигнатуры

int UO.GetScriptState(object? scriptIndex);

Читает состояние выполнения по индексу.

Параметры

  • scriptIndex — Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из IDE.

Возвращает

int — код состояния: 0 = нет/неизвестно, 1 = выполняется, 2 = пауза. Это не bool: явно сравнивайте с 1 или 2.

Поведение

  • Учитываются выполняющиеся и приостановленные запуски; завершённые и получившие запрос отмены исключаются. Работающий скрипт обычно учитывает и себя. Просто загруженная вкладка IDE запуском не считается.
  • Индексы — текущие позиции в порядке запуска. Запуск/остановка скриптов могут сдвинуть позиции. Разные вызовы не образуют общий атомарный снимок; перед последующей командой управления перечитайте список.
  • GetScriptsList даёт индексы, GetScriptsCount — количество, GetScriptState — код из трёх состояний. Не подменяйте одно другим и не считайте любой ненулевой результат значением true.
  • Учитывается ручная/отладочная пауза и настроенная пауза при отключении. Состояние 1 не гарантирует, что скрипт прямо сейчас расходует процессор или получает данные сервера.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

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

1. Первый вызов и результат — прямой скрипт

Параметры и ход выполнения:

  • Запускайте приведённый C#-скрипт. Число 0, где оно передано, обозначает индекс; пустые скобки означают отсутствие аргументов. Текст Print — только сообщение примера.
  • Учитывается ручная/отладочная пауза и настроенная пауза при отключении. Состояние 1 не гарантирует, что скрипт прямо сейчас расходует процессор или получает данные сервера.
  • int — код состояния: 0 = нет/неизвестно, 1 = выполняется, 2 = пауза. Это не bool: явно сравнивайте с 1 или 2.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из IDE.
int state = UO.GetScriptState(0);
string text = state switch
{
    1 => "running",
    2 => "paused",
    _ => "unknown"
};
UO.Print(text);

2. Использование в цикле или условии — функция сценария

Параметры и ход выполнения:

  • Это отдельный пример совместного использования команд. Индексация массива начинается с нуля; перед обращением проверяйте длину. UO.Wait(250), где он используется, ждёт 250 миллисекунд.
  • Учитывается ручная/отладочная пауза и настроенная пауза при отключении. Состояние 1 не гарантирует, что скрипт прямо сейчас расходует процессор или получает данные сервера.
  • int — код состояния: 0 = нет/неизвестно, 1 = выполняется, 2 = пауза. Это не bool: явно сравнивайте с 1 или 2.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из 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;

3. Полная вспомогательная функция — класс сценария

Параметры и ход выполнения:

  • Под Main приведена функция целиком. Её параметры и результат объясняются отдельно от API-команды, которую она вызывает.
  • IsScriptActive преобразует 1 и 2 в true, а 0 в false. Сама GetScriptState по-прежнему возвращает числовой код состояния.
  • int — код состояния: 0 = нет/неизвестно, 1 = выполняется, 2 = пауза. Это не bool: явно сравнивайте с 1 или 2.
  • Обязательный ScriptIndex — целое число, индекс с нуля из свежего GetScriptsList. Для отрицательного или отсутствующего индекса возвращается описанный пустой/неизвестный результат. Не передавайте serial предмета, имя процедуры или ID запуска из 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.GetScriptState(scriptIndex) — UO.GetScriptState(Any)

Параметры: scriptIndex (Any)

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "getscriptstate": return new InjectionValue(bridge.GetScriptState(Arg(0)));

UO.GetScriptsCount

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

C#-сигнатуры

int UO.GetScriptsCount();

Считает активные запуски в этом клиенте.

Параметры

Параметров нет.

Возвращает

int >= 0: число активных запусков, включая приостановленные. Это количество, а не bool и не индекс скрипта.

Поведение

  • Учитываются выполняющиеся и приостановленные запуски; завершённые и получившие запрос отмены исключаются. Работающий скрипт обычно учитывает и себя. Просто загруженная вкладка IDE запуском не считается.
  • Индексы — текущие позиции в порядке запуска. Запуск/остановка скриптов могут сдвинуть позиции. Разные вызовы не образуют общий атомарный снимок; перед последующей командой управления перечитайте список.
  • GetScriptsList даёт индексы, GetScriptsCount — количество, GetScriptState — код из трёх состояний. Не подменяйте одно другим и не считайте любой ненулевой результат значением true.
  • Аргументов нет. Подсчёт не сортирует список и не меняет состояние запусков.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

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

1. Первый вызов и результат — прямой скрипт

Параметры и ход выполнения:

  • Запускайте приведённый C#-скрипт. Число 0, где оно передано, обозначает индекс; пустые скобки означают отсутствие аргументов. Текст Print — только сообщение примера.
  • Аргументов нет. Подсчёт не сортирует список и не меняет состояние запусков.
  • int >= 0: число активных запусков, включая приостановленные. Это количество, а не bool и не индекс скрипта.
var count = UO.GetScriptsCount();
UO.Print(Convert.ToString(count));

2. Использование в цикле или условии — функция сценария

Параметры и ход выполнения:

  • Это отдельный пример совместного использования команд. Индексация массива начинается с нуля; перед обращением проверяйте длину. UO.Wait(250), где он используется, ждёт 250 миллисекунд.
  • Аргументов нет. Подсчёт не сортирует список и не меняет состояние запусков.
  • int >= 0: число активных запусков, включая приостановленные. Это количество, а не bool и не индекс скрипта.
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)));

3. Полная вспомогательная функция — класс сценария

Параметры и ход выполнения:

  • Под Main приведена функция целиком. Её параметры и результат объясняются отдельно от API-команды, которую она вызывает.
  • HasOtherScripts сравнивает количество с 1: вызывающий скрипт обычно занимает одну запись. В отличие от GetScriptsCount, вспомогательная функция возвращает true/false.
  • int >= 0: число активных запусков, включая приостановленные. Это количество, а не bool и не индекс скрипта.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.GetScriptsCount() — UO.GetScriptsCount()

Параметры: Параметров нет.

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "getscriptscount": return new InjectionValue(bridge.GetScriptsCount());

UO.GetScriptsList

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

C#-сигнатуры

dynamic UO.GetScriptsList();

Возвращает текущие числовые индексы скриптов.

Параметры

Параметров нет.

Возвращает

Array из int: индексы 0..N-1 либо пустой массив. Элементы — числа, а не имена и не текстовые записи.

Поведение

  • Учитываются выполняющиеся и приостановленные запуски; завершённые и получившие запрос отмены исключаются. Работающий скрипт обычно учитывает и себя. Просто загруженная вкладка IDE запуском не считается.
  • Индексы — текущие позиции в порядке запуска. Запуск/остановка скриптов могут сдвинуть позиции. Разные вызовы не образуют общий атомарный снимок; перед последующей командой управления перечитайте список.
  • GetScriptsList даёт индексы, GetScriptsCount — количество, GetScriptState — код из трёх состояний. Не подменяйте одно другим и не считайте любой ненулевой результат значением true.
  • Аргументов нет. Каждое число передавайте в соответствующий getter имени, пути или состояния. Полученный массив — отдельный снимок; его изменение не управляет скриптами.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

object? result = UO.GetScriptsList();
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));

1. Первый вызов и результат — прямой скрипт

Параметры и ход выполнения:

  • Запускайте приведённый C#-скрипт. Число 0, где оно передано, обозначает индекс; пустые скобки означают отсутствие аргументов. Текст Print — только сообщение примера.
  • Аргументов нет. Каждое число передавайте в соответствующий getter имени, пути или состояния. Полученный массив — отдельный снимок; его изменение не управляет скриптами.
  • Array из int: индексы 0..N-1 либо пустой массив. Элементы — числа, а не имена и не текстовые записи.
var indices = UO.GetScriptsList();
foreach (var index in indices)
{
    UO.Print(Convert.ToString(index) + ": " + UO.GetScriptName(index));
}

2. Использование в цикле или условии — функция сценария

Параметры и ход выполнения:

  • Это отдельный пример совместного использования команд. Индексация массива начинается с нуля; перед обращением проверяйте длину. UO.Wait(250), где он используется, ждёт 250 миллисекунд.
  • Аргументов нет. Каждое число передавайте в соответствующий getter имени, пути или состояния. Полученный массив — отдельный снимок; его изменение не управляет скриптами.
  • Array из int: индексы 0..N-1 либо пустой массив. Элементы — числа, а не имена и не текстовые записи.
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;

3. Полная вспомогательная функция — класс сценария

Параметры и ход выполнения:

  • Под Main приведена функция целиком. Её параметры и результат объясняются отдельно от API-команды, которую она вызывает.
  • FindNamedScript перебирает текущие индексы и возвращает первое точное совпадение отображаемого имени либо -1. Имена могут повторяться, индексы могут позже сдвинуться.
  • Array из int: индексы 0..N-1 либо пустой массив. Элементы — числа, а не имена и не текстовые записи.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.GetScriptsList() — UO.GetScriptsList()

Параметры: Параметров нет.

Результат: dynamic / Array.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "getscriptslist": return new InjectionValue(bridge.GetScriptsList().Select(value => new InjectionValue(value)).ToArray());

UO.Launch

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

C#-сигнатуры

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 — Обязательный путь/имя программы или документа; преобразуется в текст, крайние пробелы удаляются. Пустое значение даёт 0 и сообщение об ошибке. Для точного выбора программы используйте полный путь; внешние кавычки к самому file добавлять не нужно.
  • workingDirectory — Необязательная рабочая папка процесса. При пропуске или пустой строке берётся папка текущего скрипта. Если у runtime нет такой папки, bridge использует текущую рабочую папку клиента. Это не всегда папка игры или EXE клиента.
  • hidden — Необязательное логическое значение, по умолчанию false. true отключает открытие через оболочку ОС и запрашивает запуск без окна консоли; нужен исполняемый файл. false разрешает ассоциации документов. Сама GUI-программа всё равно может показать своё окно.

Возвращает

int: 1 (true), если Process.Start вернул объект процесса; иначе 0 (false). В C# проверяйте result == 1 либо result != 0; int нельзя напрямую сравнивать с true. Это не PID, не код завершения и не подтверждение выполнения работы. При исключении выводится ошибка и возвращается 0. При открытии документа через уже работающую программу ОС иногда не возвращает новый процесс: 0 в таком случае не доказывает, что документ не открылся.

Поведение

  • Допустимы только перегрузки от одного до четырёх аргументов в указанном порядке. Чтобы задать hidden и оставить рабочую папку по умолчанию, передайте пустую строку третьим аргументом. Преобразования file/parameters/workingDirectory в текст не проверяют существование файла заранее.
  • Папка по умолчанию определяется текущим исполняемым скриптом. При hidden=true относительный путь программы сначала проверяется относительно workingDirectory. Поиск неполного имени ОС и ассоциации зависят от системы; полный путь устраняет неоднозначность. Если рабочая папка или программа недоступны, запуск может завершиться ошибкой.
  • Внешний процесс работает независимо. Launch не ждёт его завершения, не читает stdout/stderr и не завершает его при остановке скрипта. true для hidden — запрос способа запуска, а не гарантия невидимости любых окон. Объект Process после запроса освобождается; сама запущенная программа продолжает работать.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

var result = UO.Launch("loot-report.txt");
UO.Print("Launch: " + Convert.ToString(result));

1. Открыть готовый отчёт о добыче — прямой скрипт

Параметры и ход выполнения:

  • Положите существующий loot-report.txt рядом со скриптом. ОС открывает его приложением, назначенным для .txt. Main возвращает результат запроса Launch, а не содержимое отчёта и не подтверждение его прочтения.
var started = UO.Launch("loot-report.txt");
return started;

2. Передать редактору путь с пробелами — функция сценария

Параметры и ход выполнения:

  • Папка и отчёт должны существовать; замените их своими. Chr(34) заключает полный путь отчёта в двойные кавычки внутри arguments. notepad.exe должен быть доступен системе. false разрешает обычное окно редактора.
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();

3. Подготовить список локальных журналов без окна консоли — класс сценария

Параметры и ход выполнения:

  • Пример для Windows запускает встроенную cmd.exe. В папке скрипта создаётся или перезаписывается log-files.txt со списком *.log. Пустой третий аргумент сохраняет папку скрипта, true скрывает консоль. true из Main означает только запуск: файл может быть ещё не готов, а отсутствие *.log или ошибка утилиты не меняют уже полученный результат 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.Launch(file) — UO.Launch(Any)

Параметры: file (Any)

Результат: int / Integer.

При вызове:

Вызов выполняет сохранённый delegate обработчика. Метод регистрации создаёт этот delegate при построении API.

Внутренний код реализации, не самостоятельный скрипт.

values => new InjectionValue(CompactLaunch(values))

UO.Launch(file, parameters) — UO.Launch(Any,Any)

Параметры: file (Any), parameters (Any)

Результат: int / Integer.

При вызове:

Вызов выполняет сохранённый delegate обработчика. Метод регистрации создаёт этот delegate при построении API.

Внутренний код реализации, не самостоятельный скрипт.

values => new InjectionValue(CompactLaunch(values))

UO.Launch(file, parameters, workingDirectory) — UO.Launch(Any,Any,Any)

Параметры: file (Any), parameters (Any), workingDirectory (Any)

Результат: int / Integer.

При вызове:

Вызов выполняет сохранённый delegate обработчика. Метод регистрации создаёт этот delegate при построении API.

Внутренний код реализации, не самостоятельный скрипт.

values => new InjectionValue(CompactLaunch(values))

UO.Launch(file, parameters, workingDirectory, hidden) — UO.Launch(Any,Any,Any,Any)

Параметры: file (Any), parameters (Any), workingDirectory (Any), hidden (Any)

Результат: int / Integer.

При вызове:

Вызов выполняет сохранённый delegate обработчика. Метод регистрации создаёт этот delegate при построении API.

Внутренний код реализации, не самостоятельный скрипт.

values => new InjectionValue(CompactLaunch(values))

UO.PauseResumeScript

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

C#-сигнатуры

void UO.PauseResumeScript(object? scriptIndex);

Переключает ручную паузу одного активного запуска. Используйте для временной остановки добычи или другой процедуры без потери её текущего выполнения.

Параметры

  • scriptIndex — Обязательный ScriptIndex — текущий индекс запуска: 0 обозначает первый, далее 1, 2… Получайте его через GetScriptsList; это не serial, имя файла и не результат StartScript. Отрицательный/отсутствующий индекс ничего не меняет.

Возвращает

void — возвращаемого значения нет. Не сравнивайте вызов с true/1. Состояние проверяйте отдельным чтением GetScriptState; результат такого чтения — 0 (нет запуска), 1 (работает), 2 (пауза).

Поведение

  • Работает только с запусками текущего сеанса. Открытая вкладка без выполнения не является активным запуском. Остановка не удаляет исходный файл и не отключает соединение с сервером.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через UO.Wait.
  • Это переключатель, а не команда «всегда продолжить». Ручная и автоматическая паузы независимы: переключение ручной не снимает паузу разрыва связи. Код 2 не объясняет причину паузы. Если остановить таким образом собственный запуск, следующий вызов для его продолжения он сам выполнить не сможет; возобновляйте из IDE/менеджера или другого скрипта.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

for (int index = 0; index < UO.GetScriptsCount(); index++)
    if (UO.GetScriptName(index) == "Worker")
    { UO.PauseResumeScript(index); break; }

1. Переключить паузу по имени — прямой скрипт

Параметры и ход выполнения:

  • MineOre — пример отображаемого имени уже работающей процедуры. FindNamedRun возвращает первый совпавший индекс или -1. Вызывайте из другого контроллера; повторный вызов меняет ручную паузу обратно.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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;

2. Поставить работающий запуск на паузу — функция сценария

Параметры и ход выполнения:

  • Проверка == 1 ограничивает действие работающим запуском. Значение 2 не используется как указание «снять ручную паузу», потому что оно может означать отсутствие связи.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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;

3. Выбрать запуск по файлу — класс сценария

Параметры и ход выполнения:

  • Замените абсолютный путь своим. FindFileRun сравнивает пути без учёта регистра и возвращает первый запуск этого файла; в одном файле могут выполняться несколько процедур.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.PauseResumeScript(scriptIndex) — UO.PauseResumeScript(Any)

Параметры: scriptIndex (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "pauseresumescript": bridge.PauseResumeScript(Arg(0)); return InjectionValue.Unit;

UO.SetEvent

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

C#-сигнатуры

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

Включает или выключает наблюдение за изменениями состояния персонажа и позволяет выбрать одноразовую либо повторяющуюся реакцию. Например, можно один раз отменить оставшийся target после смерти или получать предупреждения о низкой выносливости. Обработчик задаётся отдельно от условия; основной скрипт должен продолжать выполнять UO.Wait.

Параметры

  • name — Строка с одним из пяти имён: ChangeHitPoints, ChangeMana, ChangeStamina, Death, SeeNewPlayer. Регистр не важен; допустим префикс ev, например evChangeMana. Пробелы по краям удаляются. Числового каталога событий нет; произвольное имя не создаёт новое событие.
  • nameValue — То же имя события, что name; это название первого параметра в форме с четырьмя аргументами, не дополнительное значение.
  • stateValue — true/1 включает наблюдение, false/0 снимает подписку. Передавайте логические значения без кавычек. При числовом преобразовании любое ненулевое целое включает наблюдение. Отключение не ждёт и не останавливает уже запущенный обработчик.
  • repeatOrHandler — Третий аргумент имеет два назначения. Нечисловая непустая строка, например "OnManaChanged", задаёт обработчик с автоматическим повторением. Число/bool задаёт только повтор: false/0 — одно срабатывание, true/1 — повторять; обработчик в этой форме не назначается. Строка "1" тоже числовая и не является именем процедуры. Для одноразового обработчика используйте четыре аргумента.
  • repeatValue — В форме с четырьмя аргументами: false/0 — удалить подписку после первого обнаруженного перехода, true/1 — оставить её для следующих изменений. Одноразовая подписка снимается и тогда, когда обработчик не найден либо менеджер отклонил его запуск; не путайте обнаружение события с успешным завершением процедуры.

Возвращает

void — значения нет. Вызывайте отдельной строкой, без var result = и без сравнения с true/1. Завершение регистрации не подтверждает, что обработчик найден, запущен или что сервер совершил действие. Если имя процедуры не найдено при событии, вызов не происходит.

Поведение

  • Два аргумента: false отключает событие; true включает повторяющееся наблюдение без обработчика. Никакая процедура с совпадающим именем автоматически не подставляется. Эта форма не возвращает обнаруженный факт и не заменяет проверку HP/Mana/Dead.
  • Четыре аргумента явно задают имя события, включение, повторение и обработчик. Оба логических параметра пишутся без кавычек. false/0 = one-shot: подписка снимается после первого обнаруженного перехода, а не после окончания обработчика.
  • Повторная регистрация одного события заменяет его прежние настройки и запоминает текущее состояние как новую точку отсчёта. Подписки SetEvent и SetEventProc на одно нормализованное имя внутри одного скрипта используют одну запись, не накапливаются.
  • ChangeHitPoints сравнивает текущее HP self с предыдущим снимком; срабатывает и при потере, и при восстановлении здоровья. ChangeMana и ChangeStamina аналогично реагируют в обе стороны. Это не только урон, расход маны или усталость.
  • Death срабатывает при переходе живой → мёртвый. Регистрация уже мёртвого персонажа не вызывает обработчик сразу; повторная смерть после воскрешения может вызвать его снова.
  • SeeNewPlayer сравнивает набор загруженных mobiles, исключая 0 и self. Название не означает проверку реального игрока: сюда входят NPC и существа. Один или несколько новых serial за одну проверку вызывают одну попытку запуска. Возврат ушедшего из загруженного набора mobile также может считаться новым появлением. ID не передаётся обработчику — нужные объекты надо найти отдельно.
  • UO.Wait и WaitAsync делят ожидание на интервалы до 25 мс и после каждого проверяет события; UO.Wait(0) тоже выполняет проверку. Это опрос доступного состояния, а не перехват каждого сетевого пакета: быстрое изменение и возврат между проверками может быть пропущено. В вычислительном цикле без UO.Wait или после завершения основного скрипта постоянного наблюдения нет.
  • Имя обработчика заранее связывается с C#-делегатом через RegisterRoutine. Callback выполняется асинхронно в этой сессии; одна задача не накладывается сама на себя. После окончания основного скрипта её дальнейшие действия запрещены.
  • Подписка относится к экземпляру выполняющегося скрипта. Снимайте её в Main, который её создал: одноимённая команда внутри отдельно запущенного обработчика изменит его собственные подписки. Отмена регистрации не останавливает уже запущенный обработчик. Используйте try/finally для обычного выхода или обрабатываемой ошибки; принудительная остановка также прекращает дальнейший опрос этого скрипта.
  • Неизвестное непустое имя события при регистрации отклоняется с системным сообщением. Пустое имя ничего не делает. Наличие обработчика проверяется только при срабатывании; ошибку в его имени лучше обнаружить заранее через SubExists и проверку скрипта в IDE.

В C# обработчик нужно явно зарегистрировать: RegisterRoutine("Handler", Handler). Изменения HP, маны и выносливости поступают через очередь native-событий; Death и SeeNewPlayer проверяются по снимкам локального мира. Прямые подписки с аргументами доступны через SubscribeEvent("UO.JournalEntry", handler) и аналогичные native-имена. Все подписки освобождаются при завершении или отмене этого выполнения.

Загруженные C#-файлы

Помимо локальных делегатов, текущий менеджер поддерживает явно загруженные C#-входы с ключом вроде Child.csx::Main. SubExists и FunRunning принимают такой ключ; CallSub запускает файл без Args, а Exec("exec Child.csx::Main 'north gate' 42") передаёт Args. Ключ с пробелами заключается в кавычки внутри Exec. Уникальное короткое имя допустимо, неоднозначное имя не выбирает случайный файл. Локальная регистрация имеет приоритет. Файловый запуск получает собственный контекст и время жизни в текущем мире/менеджере; локальные делегаты принадлежат выполнению, которое их зарегистрировало.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

RegisterRoutine("HealthChanged", () => UO.Print("HP changed"));
UO.SetEvent("ChangeHitPoints", true, false, "HealthChanged");
await WaitAsync(5000);

1. Один раз отменить незавершённый target после смерти — прямой скрипт

Параметры и ход выполнения:

  • Наблюдение длится восемь секунд. Death уже мёртвого персонажа не вызывает обработчик немедленно. Второе событие смерти после воскрешения в этом примере уже не обрабатывается.
  • Обработчик проверяет активный курсор цели в момент выполнения и только тогда вызывает CancelTarget. Подготовленная очередь без активного курсора в этом примере не отменяется. Скрипт не воскрешает персонажа, не останавливает все скрипты и не завершает Main. Не запускайте пример, если хотите сохранить активную цель.
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;

2. Повторяющееся предупреждение о выносливости: короткая форма — функция сценария

Параметры и ход выполнения:

  • 24 × 250 мс — шесть секунд. 10 — наш порог в обработчике, не дополнительный аргумент SetEvent. Рост выносливости также вызывает обработчик, но при значении от 10 сообщение не выводится.
  • true, "OnFatigue" отличается от true, true: последний вариант задаёт повторение без процедуры. Скрипт не повышает выносливость и не меняет скорость сервера.
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;

3. Переключить обработчик маны без накопления подписок — класс сценария

Параметры и ход выполнения:

  • Первые две секунды каждое обнаруженное изменение вызывает OnManaLog. Следующие три секунды используется OnLowMana. Регистрация второго обработчика сама по себе его не запускает.
  • Замена не прерывает уже запущенную старую процедуру. Эти короткие обработчики не используют локальные переменные Main. При запрете наложения процедур менеджер может отклонить запуск — для такого режима проверяйте Mana прямо в 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.SetEvent(name, stateValue) — UO.SetEvent(Any,Any)

Параметры: name (Any), stateValue (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "SetEvent": SetStateEvent(arguments); return true;

Внутренний код реализации, не самостоятельный скрипт.

public void SetEvent(InjectionValue name, InjectionValue stateValue)

            => SetEvent(name, stateValue, InjectionValue.True, new InjectionValue(string.Empty));

UO.SetEvent(name, stateValue, repeatOrHandler) — UO.SetEvent(Any,Any,Any)

Параметры: name (Any), stateValue (Any), repeatOrHandler (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "SetEvent": SetStateEvent(arguments); return true;

Внутренний код реализации, не самостоятельный скрипт.

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

        }

UO.SetEvent(nameValue, stateValue, repeatValue, subNameValue) — UO.SetEvent(Any,Any,Any,Any)

Параметры: nameValue (Any), stateValue (Any), repeatValue (Any), subNameValue (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "SetEvent": SetStateEvent(arguments); return true;

Внутренний код реализации, не самостоятельный скрипт.

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;

        }

UO.SetEventProc

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

C#-сигнатуры

void UO.SetEventProc(object? eventname, object? procname);

Назначает процедуру, которую клиент будет запускать при изменении наблюдаемого состояния: например, при потере здоровья, изменении маны или появлении нового mobile. Позволяет отделить реакцию от основного цикла. Регистрация сама ничего не запускает: Основной скрипт должен оставаться активным; C#-контрольные точки, WaitAsync и UO.Wait обрабатывают события.

Параметры

  • eventname — Строка с одним из пяти имён: ChangeHitPoints, ChangeMana, ChangeStamina, Death, SeeNewPlayer. Регистр не важен; допустим префикс ev, например evChangeMana. Пробелы по краям удаляются. Числового каталога событий нет; произвольное имя не создаёт новое событие.

Возвращает

void — значения нет. Вызывайте отдельной строкой, без var result = и без сравнения с true/1. Завершение регистрации не подтверждает, что обработчик найден, запущен или что сервер совершил действие. Если имя процедуры не найдено при событии, вызов не происходит.

Поведение

  • Оба аргумента обязательны. Подписка повторяющаяся: новые обнаруженные изменения вызывают новые попытки запуска. В одном выполняющемся скрипте на одно нормализованное имя события назначается один обработчик; повторная регистрация заменяет его и обновляет начальный снимок. SetEventProc(event, "") снимает подписку в этом же скрипте. Для одноразовой реакции используйте SetEvent(event, true, false, "Handler").
  • ChangeHitPoints сравнивает текущее HP self с предыдущим снимком; срабатывает и при потере, и при восстановлении здоровья. ChangeMana и ChangeStamina аналогично реагируют в обе стороны. Это не только урон, расход маны или усталость.
  • Death срабатывает при переходе живой → мёртвый. Регистрация уже мёртвого персонажа не вызывает обработчик сразу; повторная смерть после воскрешения может вызвать его снова.
  • SeeNewPlayer сравнивает набор загруженных mobiles, исключая 0 и self. Название не означает проверку реального игрока: сюда входят NPC и существа. Один или несколько новых serial за одну проверку вызывают одну попытку запуска. Возврат ушедшего из загруженного набора mobile также может считаться новым появлением. ID не передаётся обработчику — нужные объекты надо найти отдельно.
  • UO.Wait и WaitAsync делят ожидание на интервалы до 25 мс и после каждого проверяет события; UO.Wait(0) тоже выполняет проверку. Это опрос доступного состояния, а не перехват каждого сетевого пакета: быстрое изменение и возврат между проверками может быть пропущено. В вычислительном цикле без UO.Wait или после завершения основного скрипта постоянного наблюдения нет.
  • Имя обработчика заранее связывается с C#-делегатом через RegisterRoutine. Callback выполняется асинхронно в этой сессии; одна задача не накладывается сама на себя. После окончания основного скрипта её дальнейшие действия запрещены.
  • Подписка относится к экземпляру выполняющегося скрипта. Снимайте её в Main, который её создал: одноимённая команда внутри отдельно запущенного обработчика изменит его собственные подписки. Отмена регистрации не останавливает уже запущенный обработчик. Используйте try/finally для обычного выхода или обрабатываемой ошибки; принудительная остановка также прекращает дальнейший опрос этого скрипта.
  • Неизвестное непустое имя события при регистрации отклоняется с системным сообщением. Пустое имя ничего не делает. Наличие обработчика проверяется только при срабатывании; ошибку в его имени лучше обнаружить заранее через SubExists и проверку скрипта в IDE.

В C# обработчик нужно явно зарегистрировать: RegisterRoutine("Handler", Handler). Изменения HP, маны и выносливости поступают через очередь native-событий; Death и SeeNewPlayer проверяются по снимкам локального мира. Прямые подписки с аргументами доступны через SubscribeEvent("UO.JournalEntry", handler) и аналогичные native-имена. Все подписки освобождаются при завершении или отмене этого выполнения.

Загруженные C#-файлы

Помимо локальных делегатов, текущий менеджер поддерживает явно загруженные C#-входы с ключом вроде Child.csx::Main. SubExists и FunRunning принимают такой ключ; CallSub запускает файл без Args, а Exec("exec Child.csx::Main 'north gate' 42") передаёт Args. Ключ с пробелами заключается в кавычки внутри Exec. Уникальное короткое имя допустимо, неоднозначное имя не выбирает случайный файл. Локальная регистрация имеет приоритет. Файловый запуск получает собственный контекст и время жизни в текущем мире/менеджере; локальные делегаты принадлежат выполнению, которое их зарегистрировало.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

RegisterRoutine("HealthChanged", () => UO.Print("HP changed"));
UO.SetEventProc("ChangeHitPoints", "HealthChanged");
await WaitAsync(5000);

1. Следить за изменениями здоровья в течение 10 секунд — прямой скрипт

Параметры и ход выполнения:

  • ChangeHitPoints наблюдает HP своего персонажа. OnHealthChanged — имя метод ниже/выше Main в том же загруженном файле. UO.Wait обслуживает события; пустой второй аргумент отключает повторения.
  • Обработчик только предупреждает и не выбирает лечение автоматически. При изменении HP 60 → 50 → 70 выполняются две реакции; это не счётчик ударов.
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;

2. Следить за маной и выносливостью во время подготовки — функция сценария

Параметры и ход выполнения:

  • evChangeMana и ChangeMana — одно событие. Здесь две независимые подписки и два разных обработчика, а не два обработчика на одно имя. Пороги 20 и 10 можно изменить.
  • Ожидание длится 5 секунд. Восстановление ресурсов тоже обнаруживается, но if не выдаёт предупреждение, когда значение уже выше порога.
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;

3. Замечать новые mobiles, затем отключить наблюдение — класс сценария

Параметры и ход выполнения:

  • SeeNewPlayer обнаруживает появление serial в загруженном наборе mobiles, включая NPC. Одновременная загрузка пяти существ не гарантирует пять вызовов.
  • GetMobiles() — отдельное чтение актуального набора, не ID из события. Чтобы выбрать врага, нужны явные условия; пример намеренно никого не атакует.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.SetEventProc(eventname, procname) — UO.SetEventProc(Any,Any)

Параметры: eventname (Any), procname (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "SetEventProc":
                SetStateEvent([arguments[0], !string.IsNullOrWhiteSpace(Convert.ToString(arguments[1])), true, arguments[1]]);
                return true;

Внутренний код реализации, не самостоятельный скрипт.

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;

                }

UO.SetPauseScriptOnDisconnectStatus

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

C#-сигнатуры

void UO.SetPauseScriptOnDisconnectStatus(object? value);

Меняет правило паузы скриптов при потере соединения.

Параметры

  • value — 0 выключает; любое ненулевое целое включает.

Возвращает

void — значения нет. Вызывайте отдельной строкой; завершение не подтверждает действие сервера.

Поведение

  • SetPauseScriptOnDisconnectStatus задаёт _manager.PauseScriptsOnDisconnect = enabled != 0. Уже запущенные скрипты не получают здесь команду PauseResumeScript.
  • Меняет настройку клиента. Значение не является результатом проверки соединения и не подтверждает обработку сервером.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Команда вызывается отдельной строкой. Сообщение перед ней показывает начало вызова, а не подтверждение действия сервера.

UO.Print("Calling UO.SetPauseScriptOnDisconnectStatus");
UO.SetPauseScriptOnDisconnectStatus(1);

1. Прямой вызов — прямой скрипт

Параметры и ход выполнения:

  • Меняет правило паузы скриптов при потере соединения.
UO.SetPauseScriptOnDisconnectStatus(1);

2. Явный вариант — функция сценария

Параметры и ход выполнения:

  • SetPauseScriptOnDisconnectStatus задаёт _manager.PauseScriptsOnDisconnect = enabled != 0. Уже запущенные скрипты не получают здесь команду PauseResumeScript.
return  RunScenario();
 object? RunScenario()
{
    UO.SetPauseScriptOnDisconnectStatus(0);
    return null;
}

3. Полная вспомогательная функция — класс сценария

Параметры и ход выполнения:

  • Меняет настройку клиента. Значение не является результатом проверки соединения и не подтверждает обработку сервером.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.SetPauseScriptOnDisconnectStatus(value) — UO.SetPauseScriptOnDisconnectStatus(Any)

Параметры: value (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "setpausescriptondisconnectstatus": bridge.SetPauseScriptOnDisconnectStatus(Arg(0)); return InjectionValue.Unit;

UO.SetScriptName

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

C#-сигнатуры

void UO.SetScriptName(object? scriptIndex, object? value);

Меняет отображаемое имя одного активного запуска. Помогает различать выполняющиеся задачи в списке и через GetScriptName.

Параметры

  • scriptIndex — Обязательный ScriptIndex — текущий индекс запуска: 0 обозначает первый, далее 1, 2… Получайте его через GetScriptsList; это не serial, имя файла и не результат StartScript. Отрицательный/отсутствующий индекс ничего не меняет.
  • value — Обязательный Value — новое имя, преобразуемое в строку. Пробелы допустимы; пустая строка также допустима. Это подпись текущего запуска, а не новое имя файла или процедуры в исходнике.

Возвращает

void — возвращаемого значения нет. Не сравнивайте вызов с true/1. Состояние проверяйте отдельным чтением GetScriptState; результат такого чтения — 0 (нет запуска), 1 (работает), 2 (пауза).

Поведение

  • Работает только с запусками текущего сеанса. Открытая вкладка без выполнения не является активным запуском. Остановка не удаляет исходный файл и не отключает соединение с сервером.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через UO.Wait.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

for (int index = 0; index < UO.GetScriptsCount(); index++)
    if (UO.GetScriptName(index) == "Worker")
    { UO.SetScriptName(index, "Worker renamed"); break; }

1. Дать задаче понятное имя — прямой скрипт

Параметры и ход выполнения:

  • MineOre ищется в активных запусках, Mining — новая подпись. Имена внутри кавычек придуманы пользователем и могут быть заменены.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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;

2. Различить задачи из разных файлов — функция сценария

Параметры и ход выполнения:

  • Замените wantedPath своим абсолютным путём. Меняется подпись первого активного запуска этого файла, а файл на диске остаётся на прежнем месте.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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) ?? "");

3. Добавить состояние в подпись — класс сценария

Параметры и ход выполнения:

  • GetScriptState = 2 добавляет текст paused к имени. Это однократная подпись: она не обновляется автоматически после продолжения и не управляет самой паузой.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.SetScriptName(scriptIndex, value) — UO.SetScriptName(Any,Any)

Параметры: scriptIndex (Any), value (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "setscriptname": bridge.SetScriptName(Arg(0), Text(1)); return InjectionValue.Unit;

UO.StartScript

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

C#-сигнатуры

int UO.StartScript(object? scriptPath);

Параметры

Возвращает

int: количество активных запусков после принятого запуска; 65535 (0xFFFF) = ошибка запуска. Это не индекс нового скрипта, не bool и не результат завершения.

Поведение

  • Учитываются выполняющиеся и приостановленные запуски; завершённые и получившие запрос отмены исключаются. Работающий скрипт обычно учитывает и себя. Просто загруженная вкладка IDE запуском не считается.
  • Индексы — текущие позиции в порядке запуска. Запуск/остановка скриптов могут сдвинуть позиции. Разные вызовы не образуют общий атомарный снимок; перед последующей командой управления перечитайте список.
  • GetScriptsList даёт индексы, GetScriptsCount — количество, GetScriptState — код из трёх состояний. Не подменяйте одно другим и не считайте любой ненулевой результат значением true.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

var result = UO.StartScript("Scripts/Worker.csx");
UO.Print("StartScript: " + Convert.ToString(result));

1. Первый вызов и результат — прямой скрипт

Параметры и ход выполнения:

  • Запускайте приведённый C#-скрипт. Число 0, где оно передано, обозначает индекс; пустые скобки означают отсутствие аргументов. Текст Print — только сообщение примера.
  • Перед примерами отдельно создайте указанные файлы Worker.csx. Ошибочный/недоступный путь, неподходящая Main, отключённый C# или запрет параллельного запуска дают отказ. Если прежний запуск ещё останавливается, возможен отложенный повтор; принятие запуска не означает завершения. Короткий скрипт может закончить работу ещё до чтения возвращённого количества.
  • int: количество активных запусков после принятого запуска; 65535 (0xFFFF) = ошибка запуска. Это не индекс нового скрипта, не bool и не результат завершения.
  • Обязательный ScriptPath — строка. Относительный путь начинается в папке AutoLoad этого клиента; абсолютный путь допустим. Путь с пробелами заключайте в кавычки. Нужен .csx с кодом на верхнем уровне либо обычный .cs/.csproj с поддерживаемой точкой входа 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;

2. Использование в цикле или условии — функция сценария

Параметры и ход выполнения:

  • Это отдельный пример совместного использования команд. Индексация массива начинается с нуля; перед обращением проверяйте длину. UO.Wait(250), где он используется, ждёт 250 миллисекунд.
  • Перед примерами отдельно создайте указанные файлы Worker.csx. Ошибочный/недоступный путь, неподходящая Main, отключённый C# или запрет параллельного запуска дают отказ. Если прежний запуск ещё останавливается, возможен отложенный повтор; принятие запуска не означает завершения. Короткий скрипт может закончить работу ещё до чтения возвращённого количества.
  • int: количество активных запусков после принятого запуска; 65535 (0xFFFF) = ошибка запуска. Это не индекс нового скрипта, не bool и не результат завершения.
  • Обязательный ScriptPath — строка. Относительный путь начинается в папке AutoLoad этого клиента; абсолютный путь допустим. Путь с пробелами заключайте в кавычки. Нужен .csx с кодом на верхнем уровне либо обычный .cs/.csproj с поддерживаемой точкой входа 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;

3. Полная вспомогательная функция — класс сценария

Параметры и ход выполнения:

  • Под Main приведена функция целиком. Её параметры и результат объясняются отдельно от API-команды, которую она вызывает.
  • TryStartCSharp сравнивает результат с 65535 и возвращает true/false. Она не ждёт завершения и не превращает количество в индекс.
  • int: количество активных запусков после принятого запуска; 65535 (0xFFFF) = ошибка запуска. Это не индекс нового скрипта, не bool и не результат завершения.
  • Обязательный ScriptPath — строка. Относительный путь начинается в папке AutoLoad этого клиента; абсолютный путь допустим. Путь с пробелами заключайте в кавычки. Нужен .csx с кодом на верхнем уровне либо обычный .cs/.csproj с поддерживаемой точкой входа 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.StartScript(scriptPath) — UO.StartScript(Any)

Параметры: scriptPath (Any)

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "startscript": return new InjectionValue(bridge.StartScript(Text(0)));

UO.StopAllScripts

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

C#-сигнатуры

void UO.StopAllScripts();

Параметры

Параметров нет.

Возвращает

void — возвращаемого значения нет. Не сравнивайте вызов с true/1. Состояние проверяйте отдельным чтением GetScriptState; результат такого чтения — 0 (нет запуска), 1 (работает), 2 (пауза).

Поведение

  • Работает только с запусками текущего сеанса. Открытая вкладка без выполнения не является активным запуском. Остановка не удаляет исходный файл и не отключает соединение с сервером.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через UO.Wait.
  • Аргументов нет. Отменяются выполняющиеся и приостановленные процедуры, ожидающие запуска запросы и очередь повторных запусков. Вызывающий скрипт тоже получает отмену: не помещайте обязательное сохранение/отчёт после этой команды. Другие профили и процессы не затрагиваются.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Команда вызывается отдельной строкой. Сообщение перед ней показывает начало вызова, а не подтверждение действия сервера.

UO.Print("Calling UO.StopAllScripts");
UO.StopAllScripts();

1. Безусловная общая остановка — прямой скрипт

Параметры и ход выполнения:

  • У команды пустые скобки. Выполните нужное сохранение до вызова. Сам контроллер тоже будет остановлен.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через UO.Wait.
UO.StopAllScripts();

2. Остановиться при смерти персонажа — функция сценария

Параметры и ход выполнения:

  • Connected проверяет вход в мир, Dead — смерть. StopAllScripts вызывается только при выполнении обоих условий; пример проверяет их один раз.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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;

3. Общая остановка через пять секунд — класс сценария

Параметры и ход выполнения:

  • 5000 — миллисекунды в UO.Wait, а не параметр StopAllScripts. Ручная или автоматическая пауза контроллера может отложить выполнение; это не независимый таймер.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.StopAllScripts() — UO.StopAllScripts()

Параметры: Параметров нет.

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "stopallscripts": bridge.StopAllScripts(); return InjectionValue.Unit;

UO.StopScript

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

C#-сигнатуры

void UO.StopScript(object? scriptIndex);

Запрашивает отмену одного активного запуска, в том числе стоящего на паузе. Используйте, когда работу этого запуска нужно закончить, а не продолжить позже.

Параметры

  • scriptIndex — Обязательный ScriptIndex — текущий индекс запуска: 0 обозначает первый, далее 1, 2… Получайте его через GetScriptsList; это не serial, имя файла и не результат StartScript. Отрицательный/отсутствующий индекс ничего не меняет.

Возвращает

void — возвращаемого значения нет. Не сравнивайте вызов с true/1. Состояние проверяйте отдельным чтением GetScriptState; результат такого чтения — 0 (нет запуска), 1 (работает), 2 (пауза).

Поведение

  • Работает только с запусками текущего сеанса. Открытая вкладка без выполнения не является активным запуском. Остановка не удаляет исходный файл и не отключает соединение с сервером.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через UO.Wait.
  • Отмена кооперативная: будит ожидание паузы и проверяется движком; уже отправленные действия серверу не отменяются. Отменённый запуск сразу исключается из активного списка, хотя освобождение ресурсов ещё может продолжаться. Не проверяйте завершение по прежнему индексу: под ним уже может быть другой запуск.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

for (int index = 0; index < UO.GetScriptsCount(); index++)
    if (UO.GetScriptName(index) == "Worker")
    { UO.StopScript(index); break; }

1. Остановить одну процедуру — прямой скрипт

Параметры и ход выполнения:

  • MineOre — имя запуска. -1 не передаётся в StopScript. После остановки сохранённый индекс больше не используется.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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;

2. Остановить зависший на паузе запуск — функция сценария

Параметры и ход выполнения:

  • Код 2 выбирает приостановленный запуск. StopScript завершает его, а не снимает паузу; остальные задачи продолжаются.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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;

3. Остановить запуск из определённого файла — класс сценария

Параметры и ход выполнения:

  • Абсолютный путь выбирает первый активный запуск файла. Это не удаление файла и не остановка всех процедур из этого файла.
  • Индексы динамические: после завершения другого запуска позиции могут сдвинуться. Несколько API-вызовов не являются атомарной операцией. Примеры рассчитаны на отсутствие параллельного изменения списка; ищите нужный запуск заново перед действием и не храните индекс через 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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.StopScript(scriptIndex) — UO.StopScript(Any)

Параметры: scriptIndex (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "stopscript": bridge.StopScript(Arg(0)); return InjectionValue.Unit;

UO.SubExists

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

C#-сигнатуры

int UO.SubExists(object? subrutineName);

Проверяет, зарегистрирован ли C#-делегат с указанным именем в текущем выполнении. Это реестр callback-функций данной сессии, без автоматического поиска произвольных методов через reflection; явно загруженные файловые входы также доступны по ключу.

Параметры

  • subrutineName — строковое имя, заданное RegisterRoutine. Пробелы по краям удаляются; поиск зарегистрированного имени не зависит от регистра.

Возвращает

int: 1 — условие выполнено, 0 — не выполнено. Сохраните результат одного вызова, если он нужен нескольким проверкам.

Поведение

  • Регистрация и задачи принадлежат одному выполнению. Другой персонаж, профиль или последующий запуск имеет собственный реестр.
  • RegisterRoutine принимает Action, Func<Task> либо делегат с массивом строковых аргументов для Exec. Удаление регистрации не запускает код и исключает дальнейший поиск имени.
  • Основной скрипт должен оставаться активным, пока нужны его помощники: после его окончания задачи отменяются, а регистрации и подписки освобождаются.
  • FunRunning не является индексом менеджера. GetScriptsList и GetScriptState описывают самостоятельные запуски файлов; локальный callback отдельного индекса не получает.
  • Ошибка callback передаётся основному скрипту на следующей проверке. WaitAsync, UO.Wait и инструментированные C#-циклы поддерживают паузу, отмену и обработку очереди событий.

Загруженные C#-файлы

Помимо локальных делегатов, текущий менеджер поддерживает явно загруженные C#-входы с ключом вроде Child.csx::Main. SubExists и FunRunning принимают такой ключ; CallSub запускает файл без Args, а Exec("exec Child.csx::Main 'north gate' 42") передаёт Args. Ключ с пробелами заключается в кавычки внутри Exec. Уникальное короткое имя допустимо, неоднозначное имя не выбирает случайный файл. Локальная регистрация имеет приоритет. Файловый запуск получает собственный контекст и время жизни в текущем мире/менеджере; локальные делегаты принадлежат выполнению, которое их зарегистрировало.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

RegisterRoutine("Hello", () => UO.Print("Hello"));
UO.Print("Registered flag: " + UO.SubExists("Hello"));

1. Запустить доступную необязательную процедуру — прямой скрипт

Параметры и ход выполнения:

  • Команда возвращает int: в C# проверяйте result != 0 или result == 1; прямое сравнение с true недопустимо. Определение WeightWatch приведено ниже и регистрируется через RegisterRoutine перед вызовом команды.
  • Сохраните пример как отдельный файл, загрузите его в IDE/менеджер и запускайте Main, чтобы остальные процедуры попали в каталог. WeightWatch/ManaWatch — полностью определённые здесь учебные процедуры: три замера веса/маны с интервалом UO.Wait(1000), то есть 1000 мс, вывод только при входе в мир. Это не постоянный мониторинг; пауза может растянуть время. Для примера с двумя задачами должен быть разрешён одновременный запуск процедур.
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;

2. Выбрать запасной обработчик — функция сценария

Параметры и ход выполнения:

  • WeightWatch может быть загружен из другого файла. Если его нет, выбирается объявленный здесь ManaWatch. Если основной найден, но не может запуститься, автоматического перехода к запасному не будет.
  • Сохраните пример как отдельный файл, загрузите его в IDE/менеджер и запускайте Main, чтобы остальные процедуры попали в каталог. WeightWatch/ManaWatch — полностью определённые здесь учебные процедуры: три замера веса/маны с интервалом UO.Wait(1000), то есть 1000 мс, вывод только при входе в мир. Это не постоянный мониторинг; пауза может растянуть время. Для примера с двумя задачами должен быть разрешён одновременный запуск процедур.
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;

3. Проверить оба определения перед запуском — класс сценария

Параметры и ход выполнения:

  • HaveMonitors проверяет два имени через &&. При отсутствии хотя бы одного Main не вызывает ни одну задачу. Проверяется комплект определений, а не гарантия успешного одновременного запуска.
  • Сохраните пример как отдельный файл, загрузите его в IDE/менеджер и запускайте Main, чтобы остальные процедуры попали в каталог. WeightWatch/ManaWatch — полностью определённые здесь учебные процедуры: три замера веса/маны с интервалом UO.Wait(1000), то есть 1000 мс, вывод только при входе в мир. Это не постоянный мониторинг; пауза может растянуть время. Для примера с двумя задачами должен быть разрешён одновременный запуск процедур.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.SubExists(subrutineName) — UO.SubExists(String)

Параметры: subrutineName (String)

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "SubExists":
                string requested = Convert.ToString(arguments[0]) ?? "";
                result = Exists(requested) || events is ICSharpScriptHost known && known.HasCSharpScript(requested) ? 1 : 0;
                return true;

Внутренний код реализации, не самостоятельный скрипт.

public int SubExists(string subrutineName) => bridge.SubExists(subrutineName) ? 1 : 0;

UO.Terminate

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

C#-сигнатуры

void UO.Terminate();
void UO.Terminate(object? subrutineName);

Параметры

  • subrutineName — Необязательная строка. Без аргумента или с пустой строкой выбирается последний запущенный файл; "all" запрашивает отмену всех запусков. Для файлов C# используйте точный ключ из каталога, например harvest.csx::Main. Произвольное имя локального callback, например Harvest, работает только после явной регистрации через RegisterRoutine. Сам файл имеет один вход Main; отдельные методы класса не становятся автоматически отдельными запускаемыми процедурами.

Возвращает

void: команда не возвращает число и не ожидает завершения рабочего потока. Она немедленно запрашивает отмену. Исполнение прекращается на ближайшей проверке отмены; паузы, точки останова и встроенные ожидания прерываются. Серверные действия, уже отправленные до отмены, не откатываются.

Поведение

Значение UO.FunRunning(name) == 0 возможно также сразу после запроса отмены. Оно не означает, что блок finally предыдущего callback уже завершился. Повторный CallSub или Exec того же локального имени пропускается, пока прежний обработчик фактически не завершит работу.

  • UO.Terminate() выбирает процедуру по порядку запуска, а не по имени, строке редактора, времени последнего действия или порядку отображения. Поставленная на паузу процедура также считается запущенной.
  • Очищается принадлежащий этому запуску локальный прицел и отменяется оставшийся автоматический маршрут. Отмена движения выполняется на игровом потоке; если маршрут уже заменён другим запуском или пользователем, прежний владелец его не останавливает. Отправленные серверу шаги не отзываются.
  • Команда остаётся доступной при включённой настройке одного одновременного запуска. Отменённая процедура освобождает место только после очистки своего рабочего потока; до этого новый запуск пропускается.
  • При остановке по имени также отменяются соответствующие отложенные запуски. Завершённые процедуры не изменяются. Об отсутствии подходящего запуска клиент сообщает в статусе. Зависший вызов стороннего нативного плагина, который не поддерживает отмену, нельзя безопасно прервать принудительным уничтожением потока.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Для файлов C# используйте точный ключ из каталога, например harvest.csx::Main. Произвольное имя локального callback, например Harvest, работает только после явной регистрации через RegisterRoutine. Сам файл имеет один вход Main; отдельные методы класса не становятся автоматически отдельными запускаемыми процедурами.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

UO.Terminate("harvest.csx::Main");
UO.Print("Cancellation requested for harvest.csx::Main");

1. Остановить харвест по имени, оставив остальные процедуры — прямой скрипт

Пример запрашивает отмену уже запущенного harvest.csx::Main. Сообщение не подтверждает завершение его finally или откат игровых действий.

var procedureName = "harvest.csx::Main";
UO.Terminate(procedureName);
UO.Print("Cancellation requested for harvest.csx::Main");

2. Выбрать Main только из определённого файла — функция сценария

Точный ключ harvest.csx::Main отличается от heal.csx::Main. Замените демонстрационный ключ на ключ своего загруженного файла; произвольные суффиксы вроде #2 не создают второй вход.

return  RunScenario();
 object? RunScenario()
{
    var procedureKey = "harvest.csx::Main";
    UO.Terminate(procedureKey);
    UO.Print("Cancellation requested: " + procedureKey);
    return null;
}

3. Совместимая консольная форма через Exec — класс сценария

Строка terminate harvest.csx::Main передаёт тот же ключ через UO.Exec. Ключ с пробелами нужно заключить в кавычки внутри командной строки.

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

4. Остановить последний запуск при низком здоровье — прямой скрипт

Параметры и ход выполнения:

  • Форма без аргументов выбирает последний запущенный скрипт, включая поставленный на паузу. Это политика примера, а не обязательная реакция на низкое здоровье; сообщение выводится до запроса отмены.
if (UO.GetHP() <= 30)
{
    UO.Print("Low health: requesting cancellation of the most recent run");
    UO.Terminate();
}

Реализация команды

C# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.Terminate() — UO.Terminate()

Параметры: Параметров нет.

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

public void Terminate() => bridge.Terminate(string.Empty);

UO.Terminate(subrutineName) — UO.Terminate(String)

Параметры: subrutineName (String)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

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;

Внутренний код реализации, не самостоятельный скрипт.

public void Terminate(string subrutineName) => bridge.Terminate(subrutineName);

UO.TerminateAll

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

C#-сигнатуры

void UO.TerminateAll();

Параметры

Параметров нет.

Возвращает

void: результата нет. Команда запрашивает отмену всех текущих запусков и возвращает управление вызывающему коду, если он сам не был отменён. Очистка рабочих потоков выполняется после срабатывания их проверок отмены.

Поведение

  • Точный синтаксис — UO.TerminateAll(), без пробела внутри имени и без аргументов. UO.Terminate("all") поддерживается как совместимая альтернативная запись.
  • Останавливаются работающие процедуры, процедуры на ручной паузе и на точке останова, встроенные ожидания и зарегистрированные отложенные запуски. Повторный вызов при отсутствии работы безопасен.
  • Снятие локальных прицелов и автоматических маршрутов учитывает их владельцев. Оставшийся маршрут остановленного скрипта отменяется на игровом потоке; новый маршрут пользователя, заменивший его, сохраняется. Уже отправленные пакеты движения не отменяются.
  • Отмена не откатывает уже отправленные серверу действия. Код сторонних нативных плагинов без поддержки отмены нельзя безопасно уничтожить вместе с потоком. Новые явные запуски после выполнения команды разрешены; при включённой галочке они ждут освобождения места в том смысле, что попытка до завершения очистки пропускается, а не ставится в очередь.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Команда вызывается отдельной строкой. Сообщение перед ней показывает начало вызова, а не подтверждение действия сервера.

UO.Print("Calling UO.TerminateAll");
UO.TerminateAll();

1. Остановить все скрипты из проверки внутри рабочего скрипта — прямой скрипт

Параметры и ход выполнения:

  • У TerminateAll нет параметров. Условие UO.Dead() проверяет смерть персонажа перед очередной итерацией.
  • При выполнении условия отменяются и текущий Main, и остальные запуски C#. Сообщение Print расположено до остановки, поскольку после неё код этой процедуры прекращается.
  • WaitAsync(250) задаёт паузу 250 миллисекунд между проверками в этом примере. Команда остановки не меняет длительность обычных пауз скрипта.
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;

2. Совместимая форма с all — функция сценария

Параметры и ход выполнения:

  • В этой альтернативной записи используется UO.Terminate с одним параметром subrutineName="all". Это эквивалент UO.TerminateAll().
  • Строка all — специальное значение, а не имя процедуры, которое требуется объявить. Отменяется и EmergencyStop.
  • Для аварийной игровой клавиши используйте однострочную команду из первого примера: отдельная процедура EmergencyStop сама подчиняется запрету параллельных запусков.
return  RunScenario();
 object? RunScenario()
{
    UO.Print("Stopping all C# scripts");
    UO.Terminate("all");
    return null;
}

3. Остановить общую работу, если обязательный объект стал недоступен — класс сценария

Параметры и ход выполнения:

  • GetSerial сохраняет serial обязательного объекта, здесь собственного рюкзака. Exists=0 означает, что этот объект сейчас неизвестен клиенту.
  • TerminateAll вызывается без аргументов только при нарушении выбранного условия. Эта политика останавливает все процедуры, а не только работу с данным рюкзаком.
  • Сначала выводится причина остановки. После запроса отмены текущая процедура тоже прекращается; рассчитывать на выполнение следующего действия нельзя.
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;
}

4. Остановить всю работу после потери соединения — прямой скрипт

Параметры и ход выполнения:

  • При отсутствии соединения выбирается остановка всех запусков. Команда не возвращает количество отменённых скриптов и отменяет также текущий запуск.
if (UO.Connected() == 0)
{
    UO.Print("Disconnected: requesting cancellation of every active script");
    UO.TerminateAll();
}

Реализация команды

C# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.TerminateAll() — UO.TerminateAll()

Параметры: Параметров нет.

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

public void TerminateAll() => bridge.Terminate("all");

UO.Time

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

C#-сигнатуры

int UO.Time();

Читает местное время суток компьютера числом HHMMSS. Удобна для выбора разрешённого времени работы, не для измерения длительности.

Параметры

Параметров нет.

Возвращает

int HHMMSS, 0..235959 с допустимыми минутами и секундами. 07:29:35 -> 72935; полночь -> 0. Ноль — допустимое время, а не ошибка и не логический флаг false.

Поведение

  • Параметров нет, форм с аргументами нет. Пустые скобки только читают время: вызов не ждёт, не запускает таймер и не отправляет пакеты в игру.
  • RealTimeSource.Now читает DateTime.Now: местную дату/время компьютера, не UTC, не время шарда и не игровые часы. Часовой пояс, летнее время и ручной перевод часов влияют на результат. Каждый вызов фиксирует отдельный момент.
  • Начальные нули не сохраняются, миллисекунд нет. Численно 13:00:00 минус 12:59:59 даёт 4041, хотя прошла одна секунда. В полночь значение снова становится нулём.
  • Не вычитайте календарные коды для расчёта задержки. Используйте Timer() и две отметки одного запуска. Календарные часы могут перевестись назад.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

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

1. Показать отдельные поля времени — прямой скрипт

Параметры и ход выполнения:

  • stamp читается один раз, чтобы не смешать разные секунды. Int(stamp/10000) — часы; Int(stamp/100) MOD 100 — минуты; MOD 100 — секунды. Простая строка не дополняет поля нулями.
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));

2. Разрешить серию только в вечернем интервале — функция сценария

Параметры и ход выполнения:

  • 180000 — 18:00:00 включительно; 220000 — 22:00:00 не включительно. Одна проверка решает, продолжать ли Main. Расписание не регистрируется, серия автоматически не запускается.
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;

3. Учесть интервал через полночь — класс сценария

Параметры и ход выполнения:

  • ClockInWindow(stamp,first,last) принимает допустимые HHMMSS. 230000..20000 означает от 23:00 включительно до 02:00 не включительно: OR учитывает полночь. Равные границы означают пустой интервал. В тихий период 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()
    {
        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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.Time() — UO.Time()

Параметры: Параметров нет.

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

public int Time()

        {

            var now = timeSource.Now;

            return now.Hour * 10000 + now.Minute * 100 + now.Second;

        }

UO.Timer

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

C#-сигнатуры

int UO.Timer();

Читает время работы текущего runtime в десятых долях секунды. Подходит для собственных задержек и отметок действий.

Параметры

Параметров нет.

Возвращает

int 0..2147483647; единица — 100 мс. При 399 мс вернётся 3. Это показание часов, а не true/false и не остаток перезарядки.

Поведение

  • Параметров нет, форм с аргументами нет. Пустые скобки только читают время: вызов не ждёт, не запускает таймер и не отправляет пакеты в игру.
  • Начало отсчёта принадлежит этому экземпляру runtime. Новая запущенная процедура может получить новые часы; нельзя сравнивать значения разных запусков или сохранять их как абсолютную дату. Во время UO.Wait и паузы скрипта реальное время продолжает идти.
  • Дробная часть десятых отбрасывается. Примерно через 2485.5 суток число достигает 2147483647 и остаётся таким, не переполняясь.
  • Это ваша задержка, а не серверная перезарядка навыка/заклинания и не подтверждение успеха. Команда не позволяет обходить ограничения сервера на движение, заклинания или скрипты.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

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

1. Измерить длительность операции — прямой скрипт

Параметры и ход выполнения:

  • WaitAsync(250) запрашивает 250 мс; фактическая задержка может быть больше. Разность двух отметок делится на 10.0 для получения секунд. Шаг показаний UO.Timer — 100 мс.
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)));

2. Ограничить ожидание уже запрошенного прицела тремя секундами — функция сценария

Параметры и ход выполнения:

  • Предел 30 задан в единицах этой функции. UO.Wait(50) освобождает выполнение между проверками. UO.Targeting читает наличие прицела; пример не кастует и не выбирает цель. Если прицел не появился, цикл заканчивается.
  • Дополнительная проверка верхней границы завершает ожидание даже при остановившемся на пределе int счётчике. Статус прицела читается повторно и может измениться между чтениями.
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;

3. Собственная функция интервала между попытками — класс сценария

Параметры и ход выполнения:

  • IntervalPassed(started,current,interval) полностью определена под Main. interval=5 — половина секунды в единицах этих часов. Отрицательный интервал, обратный ход и int на верхней границе дают false; точное достижение интервала даёт true.
  • Это ваша задержка, а не серверная перезарядка навыка/заклинания и не подтверждение успеха. Команда не позволяет обходить ограничения сервера на движение, заклинания или скрипты.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.Timer() — UO.Timer()

Параметры: Параметров нет.

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

public int Timer() => (int)Math.Max(0d, Math.Min(int.MaxValue, timeSource.SinceStart.TotalMilliseconds / 100d));

UO.Wait

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

C#-сигнатуры

void UO.Wait(object? waitTimeMS);

Приостанавливает текущий скрипт на заданное число миллисекунд. Полезно между повторными проверками состояния, чтобы скрипт не крутил непрерывный цикл.

Параметры

  • waitTimeMS — Целое время в миллисекундах: 1000 = одна секунда. Используйте 0..2147483647. Это не число шагов, не секунды и не таймаут подтверждения сервера.

Возвращает

void — результата нет. Вызывайте отдельной строкой. Окончание задержки не означает, что предмет переместился, заклинание завершилось или соединение установлено: нужное состояние проверяйте отдельно.

Поведение

  • Ноль не создаёт реальной задержки и не подходит как пауза в быстром цикле. Отрицательное время не означает бесконечность: UO.Wait сводит его к 0; UO.Wait/sleep могут отклонить его при активных callback, а в клиентском мосте без них оно не ждёт. Для переносимого скрипта используйте только неотрицательное время.

Пример .csx

UO.Wait(100);
// For an asynchronous script, use the host helper:
await WaitAsync(100);

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Команда вызывается отдельной строкой. Сообщение перед ней показывает начало вызова, а не подтверждение действия сервера.

UO.Print("Calling UO.Wait");
UO.Wait(250);

1. UO.Wait — 1 — прямой скрипт

Параметры и ход выполнения:

  • 250 — четверть секунды. Следующая строка выполняется после ожидания; сообщение Delay complete — текст примера, а не возвращённый результат.
UO.Wait(250);
UO.Print("Delay complete");

2. UO.Wait — 2 — функция сценария

Параметры и ход выполнения:

  • WaitForOnline полностью объявлена в этом скрипте. Аргумент 20 означает максимум двадцать пауз по 250 мс. Соединение проверяется перед паузами и ещё раз после последней. Помощник возвращает состояние Connected (1/0); сама команда ожидания ничего не возвращает и не подключается к серверу.
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;

3. UO.Wait — 3 — класс сценария

Параметры и ход выполнения:

  • Цикл делает ровно три чтения веса. Между чтениями есть две паузы по 500 мс, после последнего паузы нет. Сообщение печатается при первом чтении и при изменении веса. Переменная previous хранит сравниваемое значение, а не настраивает игровой вес.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.Wait(waitTimeMS) — UO.Wait(Any)

Параметры: waitTimeMS (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

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

Внутренний код реализации, не самостоятельный скрипт.

case "wait": WaitWithEvents(Arg(0)); return InjectionValue.Unit;

UO.Waiting

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

C#-сигнатуры

int UO.Waiting();

Проверяет, остались ли подготовленные ответы в очереди target.

Параметры

Параметров нет.

Возвращает

int 1 — есть неотменённая отложенная цель объекта, тайла или отмена следующего прицела; 0 — очередь пуста. Это не наличие открытого курсора; для курсора используйте TargetPresent/Targeting.

Результат команды — числовой флаг: 1 означает выполненное условие, 0 — невыполненное. После var result = команда(...) проверяйте if (result != 0) или if (result == 0). Сохраните результат одного вызова: повторный вызов может выполнить действие снова либо прочитать уже изменившееся состояние.

Поведение

  • Читается очередь всего клиента. Если одновременно работают несколько процедур, учитываются ответы каждой из них.
  • Ноль означает только отсутствие ожидающего ответа. Он не подтверждает успешное применение навыка или заклинания на сервере.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.

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

1. Проверить подготовку — прямой скрипт

Параметры и ход выполнения:

  • После WaitTargetSelf и до серверного target ожидается 1.
  • CancelWaitTarget убирает очередь; следующая проверка даст 0.
UO.WaitTargetSelf();
UO.Print(Convert.ToString(UO.Waiting()));
UO.CancelWaitTarget();

2. Ограниченно дождаться расходования ответа — функция сценария

Параметры и ход выполнения:

  • Параметров у проверки нет. UO.Wait=50 мс, максимум 20 ожиданий.
  • При оставшемся ответе он отменяется. Проверку результата серверного действия добавляйте отдельно.
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;

3. Показать разницу между очередью и прицелом — класс сценария

Параметры и ход выполнения:

  • Первая строка показывает отложенные ответы, вторая — состояние прицела.
  • Возможна очередь без курсора и курсор без очереди; эти значения не взаимозаменяемы.
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# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.

UO.Waiting() — UO.Waiting()

Параметры: Параметров нет.

Результат: int / Integer.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

public int Waiting() => bridge.GetWaitTargetQueue().Length > 0 ? 1 : 0;

Clone this wiki locally