- Загрузчики
- Fabric
- Скачиваний на Modrinth
- 132
- Подписчиков на Modrinth
- 5
- версии
- 3
- Обновлён
- 19 сентября 202619 сент.
- Опубликован
- в 2026
Коротко о моде
Ставится только на сервер; игрокам ставить не нужно
Saddle
Saddle: A Datapack Debugger for Live Editing Мод для Fabric, который встраивает в Minecraft сервер Debug Adapter Protocol, чтобы файлы датапаков
.mcfunctionможно было отлаживать из VS Code (или любого DAP-клиента) — точки останова, пошаговое выполнение, путешествие во времени и живой просмотр и редактирование состояния игры, с которым работают ваши функции. В Minecraft седло — это то, что позволяет взять в руки поводья чего-то, что иначе бежит само по себе. Этот мод надевает седло на выполнение датапаков: остановите его там, где нужно (точки останова), откатите назад (путешествие во времени) и управляйте им (живые правки) — вместо того чтобы смотреть, как оно проносится мимо.Скачать
Мод Modrinth · CurseForge · GitHub Releases Расширение для VS Code Visual Studio Marketplace (или найдите "Saddle" в разделе расширений) Исходники и проблемы github.com/d1n-0/saddle · Issues Требования
- Minecraft 26.2 – 26.3, Fabric Loader ≥ 0.19.0, Fabric API, Java 25
- Мод на той стороне, которая запускает мир: клиент для одиночной игры, сервер для многопользовательской
- DAP-эндпоинт:
127.0.0.1:16352— переопределяется через-Dsaddle.host/-Dsaddle.port. Привязка к не-loopback адресам отклоняется, если не задано-Dsaddle.allowRemote=true: порт отладки допускает выполнение команд без аутентификации, поэтому открывайте его только в доверенных сетях или через туннельБыстрый старт
- Установите мод (вместе с Fabric API) и запустите мир или сервер.
- Установите расширение для VS Code.
- Откройте папку датапака в VS Code, поставьте точки останова в файлах
.mcfunctionи запустите Attach to Minecraft (Saddle) (или нажмите F5). Консоль отладки и игровой чат сообщат, к какому миру вы подключились.- Вызовите функцию в игре — из tick-функции или консоли отладки — либо нажмите Ctrl+Alt+Enter (Cmd+Alt+Enter на macOS) / ▶ в редакторе
.mcfunction, чтобы выполнить функцию этого файла.- По желанию: включите
saddle.reloadOnSave(или нажмите Reload on Save в строке состояния), чтобы выполнять/reloadпри сохранении файла датапака. Остальная часть этого README описывает мод и его протокол; обзор возможностей со стороны редактора см. вvscode-extension/README.md.Как это работает
- Миксин в
CommandFunction.fromLinesоборачивает каждую разобранную команду декоратором, несущим её происхождение(function id, source line), и записывает исходный текст функции. Строки макросов ($...) помечаются во время разбора и оборачиваются, когдаMacroFunctionих создаёт, так что они тоже отлаживаются. Когда клиент отладки не подключён, накладные расходы на команду — одно volatile-чтение.- При срабатывании точки останова/шага/паузы поток сервера останавливается внутри выполнения команды — игра замирает посреди функции с нетронутым состоянием ванильного выполнения. Пока поток приостановлен, он обслуживает очередь задач, так что DAP-запросы, которым нужно состояние игры (переменные, evaluate, интроспекция), всё равно безопасно выполняются в потоке сервера.
- Пути точек останова сопоставляются с id функций по сегменту `data//function/.mcfunction` в пути к файлу, так что любая структура рабочего пространства работает без настройки. Точки останова, запрошенные на комментариях или пустых строках, сдвигаются вниз к следующей исполняемой строке.
Поддержка DAP
Область Запросы Жизненный цикл initialize,launch/attach,configurationDone,disconnectТочки останова setBreakpoints,breakpointLocations— обычные команды и строки макросов; строки комментариев/пустые сдвигаются к следующей исполняемой строкеВыполнение continue,next,stepIn,stepOut,pauseСостояние threads,stackTrace,scopes,variables,setVariable,sourceКонсоль evaluate— выполняет любую команду, отвечая асинхронно, так что команда, попавшая на точку останова, никогда не задерживает вид стека; пока выполнение приостановлено, она исполняется в изолированномExecutionContext.completionsотдаёт подсказки Brigadier, так что консоль отладки автодополняется как игровой чатНаведение evaluate(context: hover)разрешает аргументы макросов ($(name)), селекторы сущностей (@e[...]) и тройки координат (~ ~2 ~) без выполнения командВывод игровой чат зеркалится клиенту как события output: системные объявления (/say, смерти, входы в игру), чат игроков и сообщения отдельным игрокам (/tellraw,/msg)Путешествие во времени stepBack/reverseContinueперемещаются по записи выполненных команд (кольцевой буфер,-Dsaddle.ttd.steps, по умолчанию 20k). Находясь в прошлом, стек, исполнитель, аргументы макросов и восстановленное состояние scoreboard/storage показываются для того момента — области сохраняют свои живые имена, так что раскрытые строки переживают перемещение между настоящим и историей, а область Executor несёт метку(time travel). Шаг вперёд воспроизводит запись обратно к настоящему;continueза пределами записи возобновляет живое выполнение.saddle/traceвозвращает недавнюю трассировку выполненияПеременные ("регистры и память")
Каждый кадр стека предоставляет живые области, все читающие состояние игры во время остановки:
- Executor — сводка источника команды (исполнитель, позиция, поворот, измерение) плюс лениво раскрываемое, редактируемое NBT-дерево исполняющей сущности.
- Macro Arguments — значения
$(...)текущего кадра макроса.- Command — команда, которая вот-вот выполнится, со всеми селекторами сущностей, разрешёнными в сущности, которым они сейчас соответствуют (каждая раскрывается в живой NBT), и всеми тройками координат, разрешёнными в блок, на который они указывают.
- Watched — закреплённые пользователем выражения (см. ниже), заново разрешаемые вживую при каждом запросе.
- Scoreboard — каждая цель с её очками; значения очков редактируются через
setVariable.
- Хранилище — каждое хранилище команд в виде редактируемого NBT-дерева; листовые значения принимают SNBT через
setVariable. Предпросмотр контейнеров основан на размере ({400 entries}), поэтому просмотр больших хранилищ остаётся быстрым; правки отправляют событиеinvalidated, и клиент заново запрашивает устаревшие строки. Наведение на$(name),@e[...]или1 2 3/~ ~2 ~в файле.mcfunctionво время остановки показывает те же данные прямо в строке (через расширение VS Code).
Отслеживание и закрепление выражений
Панель WATCH в VS Code, закреплённая область "Watched" (saddle/pin, saddle/unpin, saddle/pins) и вид Saddle Watch принимают:
@e[type=pig]— совпавшие сущности, раскрываются в живой NBT- `storage [path]
/entity [path]/block [path]— живой (редактируемый) NBT по цели;— всё, что принимает/data get entity: имя игрока, UUID или селектор одиночной сущности (@s` — исполнитель выбранного кадра) - `score [holder]` — один счёт или вся цель (редактируемая), если holder опущен
scoreboard/storage— каждая цель / каждое хранилище$(name)— аргумент макроса выбранного кадра; голые тройки координат разрешаются в блок, на который они указывают
Saddle Watch (реальное время + редактирование, точка останова не нужна)
Расширение добавляет вид Saddle Watch в боковую панель Run and Debug — одна панель отслеживания, которая делает то, что WATCH и Variables делают вместе, без необходимости в точке останова:
- Реальное время: закреплённые выражения обновляются по таймеру (
saddle.liveWatchRefreshInterval, по умолчанию 1 с) через не сохраняющий состояние запросsaddle/live {expression, path}, который читает состояние игры в потоке сервера независимо от того, запущена игра или приостановлена — счётчики тикают вживую, позиции сущностей двигаются, хранилище обновляется по мере записи вашими функциями. - Редактирование: строки, опирающиеся на счёты или NBT, показывают встроенный карандаш; правки идут через
saddle/liveSet {expression, path, name, value}и применяются к живой игре немедленно. Закрепления, называющие одно значение — `scoreили NBT-путь вродеstorage/entity(например, добавленные через **Add to Saddle Watch**) — тоже редактируемы:saddle/liveSetбезname` задаёт собственное значение выражения. - Отслеживание вложенного узла: щёлкните правой кнопкой по раскрытой дочерней строке (ключ NBT или элемент списка, цель, держатель счёта, id хранилища, совпавшая сущность) и выберите Add to Saddle Watch, чтобы закрепить её как отдельную запись — например,
storage mypack:store players.list[0],entity Steve Inventory[0]илиscore kills Steve. Игроки закрепляются по имени; другие сущности — по UUID, единственной ссылке, которая продолжает указывать на ту же сущность. Каждая переменная, имеющая живой автономный эквивалент, сообщает его как свой DAPevaluateName, что также включает встроенную в VS Code функцию Add to Watch в виде Variables. У записанных (time-travel) значений и NBT блоков его нет: выражение отслеживания не может назвать измерение блока. - Отображаемые имена: щёлкните правой кнопкой по закреплению и выберите Rename (или выберите его и нажмите F2), чтобы показать его под своим именем, например
pig inventoryвместоentity 1a2b…-… Inventory. Меняется только отображаемое имя — закреплённое выражение остаётся тем же и показывается во всплывающей подсказке. Имена хранятся для каждого рабочего пространства, поэтому закрепление получает своё имя обратно, когда его закрепляют снова в следующей сессии; очистка имени восстанавливает выражение.
Интеграция с редактором: запуск из файла и перезагрузка при сохранении
- Run Function From File (
saddle/runFunction {path, arguments?, executor?}): запускает функцию, определённую файлом.mcfunction. Путь разрешается точно так же, как путь точки останова (`data//function/.mcfunction), поэтому подходит любая структура рабочего пространства.arguments— SNBT-компаунд для макрофункций — расширение запрашивает его, когда файл содержит строки с$, и запоминает последнее значение для каждого файла.executor(настройкаsaddle.runFunctionExecutor, например@p) оборачивает вызов вexecute as at @s run`; пустой запускает его от имени сервера, как Debug Console. Ответ асинхронный, поэтому функция, останавливающаяся на точке останова, ведёт себя точно так же, как запущенная из консоли. Несохранённые изменения сначала сохраняются, а при перезагрузке при сохранении запуск ждёт завершения этой перезагрузки. - Reload on save (
saddle/reload): эквивалент/reload(текущие паки плюс вновь обнаруженные, не отключённые), отвечает, когда новые ресурсы вступают в силу. Расширение объединяет сохранения файлов датапака —pack.mcmetaи всего, что находится подdata/в папке, которая также содержитpack.mcmeta— поэтому "Save All" вызывает одну перезагрузку и сообщает результат в строке состояния; Debug Console пишетSaddle: datapacks reloaded. Перезагрузка отклоняется, пока выполнение остановлено на точке останова, потому что приостановленная функция иначе выполнялась бы против подменённой библиотеки функций — сначала продолжите.Saddle: Reload Datapacksперезагружает по требованию.
/deploy — выпуск карты
/deploy (уровень разрешений 4) сохраняет мир как распространяемую карту: постройки и командные блоки (файлы регионов), сущности, точки интереса, сохранённые данные (счётчики, хранилище команд, карты, …), датапаки и настройки мира (level.dat) сохраняются; данные игроков — папка players/ (инвентари, позиции, достижения, статистика; также устаревшие папки playerdata/, advancements/, stats/) и владелец одиночного мира и время последней игры (singleplayer_uuid, устаревшие Player, LastPlayed), записанные в level.dat/level.dat_old — удаляются. Пока развёрнутый мир не открыт впервые, список одиночных миров не показывает осмысленную дату последней игры для него. Запущенный игроком, /deploy открывает диалог с полем назначения и двумя вариантами:
- Create Copy (
/deploy copy [path]): сбрасывает мир на диск и записывает очищенную копию вpath. Относительные пути создаются рядом с папкой мира (в одиночной игре это папка saves, так что копия появляется в списке миров); по умолчанию — `-deploy`. Папка назначения должна находиться вне мира и либо отсутствовать, либо быть пустой; при неудачном копировании всё записанное удаляется. Работающий мир не изменяется. Копирование выполняется в потоке сервера, поэтому во время копирования в мир ничего не может записаться — игра приостанавливается на всё время копирования. - Apply to This World (
/deploy apply): очищает сам этот мир. Поскольку при завершении работы игра заново записывает данные всех игроков онлайн, мир сначала закрывается (в одиночной игре происходит возврат в главное меню; выделенный сервер останавливается), а очистка выполняется после финального сохранения. Копировать по произвольному пути могут только консоль сервера (включая rcon и Debug Console) и владелец одиночного мира; остальные операторы — например, гости по LAN с включёнными читами — ограничены папками внутри папки, содержащей мир, а в одиночном/LAN-мире применять на месте может только владелец. Оба действия отклоняются, пока выполнение остановлено на точке останова. Без игрока (консоль сервера, rcon, Debug Console)/deployвыводит две подкоманды вместо диалога. Кнопки диалога срабатывают сразу, без обычного для Minecraft запроса «выполнить эту команду?»: они отправляют пользовательские действия клика (saddle:deploy/copy,saddle:deploy/apply), которые сервер сам проверяет на уровень разрешений 4.
Пользовательские запросы
minecraft/getScoreboard,minecraft/setScore {objective, holder, value}minecraft/getStorage {id?, path?},minecraft/listEntities {selector},minecraft/getEntity {uuid}(включая NBT)minecraft/getData/minecraft/setData{type: storage|entity|block, target, path, value}— доступ в стиле ванильного/data; цели-блоки задаются как"x y z [dimension]"minecraft/getBlock {pos, dimension?}— состояние блока плюс NBT блок-сущностиsaddle/runFunction {path, arguments?, executor?},saddle/reload— см. Интеграция с редактором
Сборка
./gradlew build # jar мода в build/libs/
cd vscode-extension # расширение .vsix
npx --yes @vscode/vsce package
code --install-extension saddle-debug-*.vsixMinecraft 26.x не обфусцирован, поэтому в сборочном скрипте используется плагин net.fabricmc.fabric-loom без ремапа. Мод собирается под 26.2 (gradle.properties); передайте `-Pminecraft_version=26.3 -Pfabric_api_version=, чтобы собрать или запустить runServer` под другой поддерживаемой версией.
Тестирование
# только в первый раз: примите EULA и отключите tick watchdog
mkdir -p run
printf 'eula=true\n' > run/eula.txt
printf 'max-tick-time=-1\nonline-mode=false\npause-when-empty-seconds=0\n' > run/server.properties
./gradlew runServer # терминал 1
python3 scripts/dap_smoke_test.py # терминал 2Скрипт устанавливает scripts/test-datapack в мир, выполняет /reload и прогоняет весь цикл отладки от начала до конца (141 проверка): точки останова, пошаговое выполнение, путешествие во времени (шаг назад / обратное продолжение / восстановление исторического состояния), макро-точки останова и значения макро-аргументов, сдвиг строк комментариев, чтение/запись переменных в реальном времени (scoreboard, NBT хранилища, NBT сущности), разрешение селекторов/координат, вычисление при наведении, автодополнение консоли, зеркалирование вывода чата, запросы данных сущностей/блоков, evaluate и pause, reload и запуск из файла, а также /deploy copy. CI прогоняет тот же набор на настоящем выделенном сервере для каждой поддерживаемой версии Minecraft (26.2 и 26.3), а при пуше тега v* jar и vsix публикуются как GitHub Release.
Примечания
- Держите мод и расширение VS Code на совпадающих версиях: при подключении мод отправляет событие
saddle/version, и расширение предупреждает, когда версии расходятся (major.minor). - На выделенных серверах установите
max-tick-time=-1: приостановка на точке останова останавливает поток сервера, что иначе вызвало бы срабатывание watchdog. - В одиночной игре внутренний клиент может отключиться, если игра остаётся приостановленной слишком долго.
- Шаг за конец всех поставленных в очередь команд оставляет сессию в состоянии выполнения до следующего попадания в точку останова/паузу (например, до следующей команды tick-функции).
Решение проблем
- Правки будто не применяются /
data getпоказывает старые значения. Сначала проверьте объявление о подключении: при подключении Saddle выводитSaddle: debugger attached to world '…'в Debug Console и[Saddle] Debugger attachedв чат игры. Если сообщение в чате не появляется в вашем мире, значит порт занят другим экземпляром Minecraft с Saddle (в его логе будетFailed to bind DAP server), и ваши правки уходят в тот экземпляр — закройте его или используйте-Dsaddle.port, чтобы их разделить. Также учтите, что tick-функция, перезаписывающая scoreboard/хранилище каждый тик, перезапишет ручные правки, как только вы возобновите выполнение. - Области
Watched/Storageредактируют только живые данные. Во время путешествия во времени одноимённые области показывают записанные значения и доступны только для чтения — строка(time travel)в области Executor подсказывает, на какой режим вы смотрите.
Лицензия
Частые вопросы
Как установить saddle?
Установите Fabric под свою версию Minecraft, скачайте saddle под ту же версию и загрузчик, положите файл в папку mods и запустите игру с профилем загрузчика.
Для каких версий Minecraft есть saddle?
У saddle есть сборки для Minecraft 26.2–26.3.
Нужен ли saddle на сервере?
Ставится только на сервер; игрокам ставить не нужно