Formula Loader
Загрузка Json в Formula для моддеров и пользователей датапаков.
- Загрузчики
- FabricNeoForgeForge
- Скачиваний на Modrinth
- 4,5 тыс.
- Подписчиков на Modrinth
- 0
- версий
- 37
- Обновлён
- 18 июня 202618 июн.
- Опубликован
- в 2026
Коротко о моде
Нужен на сервере; в игре — по желанию
Мод для разработчиков и создателей датапаков. Читает пользовательские формулы из JSON-файлов и вычисляет их с параметрами, переданными через игровую команду:
/formula <namespace:name> <parameters...>или Java-метод.
Поддерживаемые операции
Арифметические операторы: +, -, *, /, % (остаток с округлением вниз: a - floor(a / b) * b)
Формулы с двумя аргументами: min(x, y), max(x, y), pow(x, y), log(x, y) (логарифм по основанию x от y), div(x, y) (деление с округлением вниз к минус бесконечности)
Формулы с одним аргументом: round(x), floor(x), ceil(x), abs(x), exp(x), trunc(x) (отбрасывание дробной части к нулю)
Тригонометрические формулы: sin(x), cos(x), tan(x), asin(x), acos(x), atan(x)
Особая — rem(x, y): возвращает остаток x - floor(x / y) * y и одновременно записывает частное floor(x / y) обратно в x. Учтите, что если написать #a = rem(#a, #b), присваивание #a = ... перезапишет частное, которое rem только что сохранил в #a, возвращаемым значением (остатком), так что в итоге #a будет содержать остаток, а не частное.
Унарный минус поддерживается без обязательных скобок: -3, -x, -min(a, b), 1 + -x — всё работает.
Производительность
Формулы предкомпилируются при загрузке в оптимизированную стековую байткод-виртуальную машину. Короткие формулы (4 операции или меньше) выигрывают от развёртки циклов и объединения супер-инструкций (например, бинарные операции переменная-переменная становятся едиными опкодами). Свёртка констант применяется на этапе компиляции. Бенчмарки на типичной настольной JVM:
- Простая арифметика: ~9 нс на вычисление
- Цепочка условий (3 ветки): ~15 нс на вычисление
- Последовательные шаги (3 шага): ~14 нс на вычисление
- Тригонометрия с округлением: ~40 нс на вычисление
На практике даже 3 миллиона вызовов завершаются менее чем за 120 мс. Производительность не вызывает беспокойства.
Установка на клиенте
Этот мод необязателен на клиенте. Если вам нужны только серверные вычисления, установка на клиенте не требуется. Если нужно отображать значения в элементах интерфейса, установите на клиент, чтобы формулы были доступны локально. Когда другой мод, требующий обе стороны (например, Tinkers' Armor Extension), глубоко интегрирует этот мод, он становится транзитивной зависимостью и тоже должен быть установлен на клиенте.
Для разработчиков
- Получайте загруженные формулы через
FormulaManager.getOrNull(ResourceLocation)и вычисляйте с помощьюformula.accept(double...). - Используйте
FormulaBuilderдля программного построения JSON формул. Формулы загружаются из `data//formula/` при запуске.
Для создателей датапаков
Используйте команду /formula для вычисления формул с параметрами прямо в игре. Это заменяет вычисления на основе scoreboard, которые потребовали бы десятков команд, одним вызовом, значительно улучшая производительность и читаемость датапаков.
Справочник по формату JSON
Простая формула
{
"inputs": ["result", "left", "right"],
"formula": "#result = #left + #right * 2"
}Последовательные шаги
{
"inputs": ["a", "b", "c"],
"formula": [
"#a = #b + #c",
"#b = #a * #c",
"#a = #b + 1"
]
}Ветвление if/else
{
"inputs": ["score", "kills", "deaths"],
"formula": {
"if": "#kills > #deaths",
"formula": "#score = #kills * 3 - #deaths * 2",
"else": "#score = max(0, #kills - #deaths)"
}
}Цепочка условий (if / else-if / else)
{
"inputs": ["result", "value"],
"formula": {
"chain": [
{"condition": "#value >= 100", "formula": "#result = #value * 2"},
{"condition": "#value >= 50", "formula": "#result = #value * 1.5"},
{"condition": "#value >= 10", "formula": "#result = #value"}
],
"default": "#result = 0"
}
}
### Смешанная вложенность (последовательная + условная)
{ "inputs": ["result", "base", "modifier"], "caches": ["scaled"], "formula": [ "#scaled = log(2, #base + 1)", { "chain": [ {"condition": "#modifier > 10", "formula": "#result = #scaled * #modifier * 2"}, {"condition": "#modifier > 0", "formula": "#result = #scaled * #modifier"} ], "default": "#result = #scaled" } ] }
### Явный слот вывода
По умолчанию возвращается результат последнего шага. Используйте `"output"`, чтобы указать другую переменную:
{ "inputs": ["dummy", "x", "y"], "caches": ["tmp", "extra"], "output": "#extra", "formula": [ "#tmp = #x * #y", "#dummy = #tmp + #x", "#extra = #tmp * 2 + #y" ] }
### Числовой режим (сокращённый)
{ "input": 3, "cache": 2, "output": "#4", "formula": [ "#0 = #1 + #2", "#3 = #0 * #1", "#4 = #3 + #2" ] }
## FormulaBuilder — руководство для разработчика
`FormulaBuilder` — это fluent API для генерации JSON формул на Java. Он автоматически добавляет префикс `#` к именованным переменным.
### I. Быстрый старт
```java
// Именованные переменные — автоматический префикс #. "result = x + y" превращается в "#result = #x + #y"
String json = FormulaBuilder
.inputs("result", "x", "y")
.flat("result = x + y + 2")
.build();
// Числовой режим — пишите # вручную
String json = FormulaBuilder
.input(3)
.flat("#0 = #1 + #2 + 2")
.build();II. Определение входов
// Именованные входы (рекомендуется) — автоматический префикс #
FormulaBuilder.inputs("result", "amount", "tick")
// Числовой вход — генерирует "input": 3; пишите #0, #1, #2 вручную
FormulaBuilder.input(3)III. Определение кэшей (промежуточных переменных)
// Именованные кэши — автоматический префикс #
.caches("tmp1", "tmp2") // генерирует "caches": ["tmp1","tmp2"]
// Числовые кэши — пишите #3, #4 вручную
.cache(2) // генерирует "cache": 2IV. Поле вывода
.output("#result") // указать, какую переменную возвращать
// Если опущено, возвращается результат последнего шага.V. Формы формул
flat — одна строка формулы. Возвращает FormulaBuilder.
.flat("result = x + y + 2")array — последовательные шаги. Каждый шаг может читать результаты предыдущих шагов. Возвращает FormulaBuilder.
.array(
"a = b + c",
"b = a * 2"
)ifelse — сокращение для одного if/else. Возвращает FormulaBuilder.
.ifelse(
"kills > deaths", // условие
"score = kills * 3", // ветка если истина
"score = deaths" // ветка иначе
). Записи выполняются последовательно и могут включать плоские формулы, встроенные условия и вложенные блоки. Вызовите .exit(), чтобы вернуться в родительский контекст (параметр типа P`).
b.inputs("a", "b").mixed()
.flat("a = b + 1") // обычная формула
.cond("b > 10", "a = b * 2") // сокращение для встроенного условия
.exit() // возврат к FormulaBuilder
.build();Внутри MixedBlock доступны следующие методы:
.flat(String)— одна формула.array(String...)— последовательные формулы.cond(String condition, String formula)— сокращение для встроенного условия.cond(String condition)— входит в `CondBlock<MixedBlock>` для сложного тела
.ifelse(String cond, String ifTrue, String ifFalse)— сокращение if/else.chain()— входит в `ChainBlock<MixedBlock>`
.mixed()— входит во вложенный `MixedBlock<MixedBlock>`
, представляющий одну запись {"condition":..., "formula":...}. Поддерживает .flat(), .array(), .mixed(). .exit()` возвращает к родителю.
b.inputs("a", "b").mixed()
.cond("b > 10").mixed() // сложное тело через вложенный mixed
.flat("a = b * 2")
.flat("a = a + 1")
.exit() // выход из вложенного mixed
.exit() // выход из CondBlock, обратно в MixedBlock
.exit()
.build();. Условия проверяются сверху вниз; выполняется первое совпавшее. Если ни одно не совпало, выполняется ветка def. .exit()` возвращает к родителю.
b.inputs("result", "value").chain()
.cond("value >= 100", "result = value * 2") // инлайн: cond(c, f)
.cond("value >= 50").mixed() // CondBlock со сложным телом
.flat("result = value * 1.5")
.exit()
.def("result = 0") // ветка по умолчанию
.exit()
.build();Внутри ChainBlock доступны следующие методы:
.cond(String condition, String formula)— сокращение для инлайн-условия.cond(String condition)— входит в `CondBlock<ChainBlock>`
.def(String formula)— одна формула как значение по умолчанию.defArray(String... formulas)— последовательные формулы как значение по умолчанию.defMixed()— входит в `MixedBlock<ChainBlock>` как значение по умолчанию
VI. Сводка по вложенным блокам
| Точка входа | Возвращает | Доступные методы | Возврат при exit |
|---|---|---|---|
.mixed() | `MixedBlock ` | .flat() .array() .cond() .ifelse() .chain() .mixed() | P |
.cond(String) | `CondBlock ` | .flat() .array() .mixed() | P |
.chain() | `ChainBlock ` | .cond() .def() .defArray() .defMixed() | P |
Параметризованный тип P отслеживает родительский контекст на этапе компиляции — .exit() всегда возвращает правильный родительский тип без приведения.
VII. Построение вывода
.build() // Возвращает String — отформатированный JSON, для записи в файлы
.buildFormula() // Возвращает JsonFormula — для прямого вычисления в кодеVIII. Автоматические правила префикса
После объявления inputs("result","x","y") эти строки обрабатываются автоматически:
| Вход | Выход |
|---|---|
"result = x + y" | "#result = #x + #y" |
"max(0, min(1, x))" | "max(0, min(1, #x))" |
"x >= 10" (условие) | "#x >= 10" |
Префикс # НЕ добавляется повторно, если он уже есть: "#result = #x + #y" остаётся как есть, а числовые ссылки вроде "#0 = #1 + #2" не затрагиваются.
Важно: Не называйте переменные именами формул (избегайте min, max, pow, log и т.п. в качестве идентификаторов переменных).
IX. Соответствие формату JSON
| Метод билдера | Генерируемый JSON |
|---|---|
.flat("x") | "formula": "..." |
.array("a","b") | "formula": ["...", "..."] |
.chain() ... .exit() | "formula": {"chain": [...], "default": ...} |
.ifelse(c,a,b) | "formula": {"if": "...", "formula": ..., "else": ...} |
.output("x") | "output": "#x" |
.inputs("a","b") | "inputs": ["a", "b"] |
.input(3) | "input": 3 |
.caches("a","b") | "caches": ["a", "b"] |
.cache(2) | "cache": 2 |
X. Типичные случаи использования
// Базовая арифметика
FormulaBuilder.inputs("r", "a", "b")
.flat("r = a + b * 2")
.build();
// Последовательное вычисление с кэшем
FormulaBuilder.inputs("r", "base", "mod")
.caches("logval")
.array(
"logval = log(2, base + 1)",
"r = logval * mod"
)
.build();
// Цепочка с несколькими условиями
FormulaBuilder.inputs("result", "val").chain()
.cond("val >= 100", "result = val * 2")
.cond("val >= 50", "result = val")
.def("result = 0")
.exit().build();
// Сокращение if/else
FormulaBuilder.inputs("score", "kills", "deaths")
.ifelse(
"kills > deaths",
"score = kills * 3 - deaths * 2",
"score = max(0, kills - deaths)"
)
.build();
// Числовой режим (без именованных переменных)
FormulaBuilder.input(3).cache(1).output("#3")
.array(
"#0 = #1 + #2",
"#3 = #0 * 2 + #1"
)
.build();
// Смешанный блок с условием и цепочкой
FormulaBuilder.inputs("a", "b", "c").mixed()
.flat("a = b + c")
.cond("b > 10", "a = a * 2")
.chain()
.cond("a > 100", "a = 100")
.def("a = a")
.exit()
.exit().build();Частые вопросы
Как установить Formula Loader?
Установите Fabric под свою версию Minecraft, скачайте Formula Loader под ту же версию и загрузчик, положите файл в папку mods и запустите игру с профилем загрузчика.
Для каких версий Minecraft есть Formula Loader?
У Formula Loader есть сборки для Minecraft 1.18.2, 1.19.2–1.19.4, 1.20.1, 1.21.1–26.1.2.
Нужен ли Formula Loader на сервере?
Нужен на сервере; в игре — по желанию