Farmstead Mods
EN

API и плейсхолдеры

PlaceholderAPI, Vault, LeaderboardAPI и REST-эндпоинты для сторонних модов.

На этой странице

Команды 2#

КомандаСинтаксисАлиасыПравоОписание
/placeholder/placeholder [list | stats | info <identifier> | test <text>]—neoessentials.admin.placeholdersInspect and test the NeoEssentials placeholder systemКорень закрыт neoessentials.admin.placeholders через validateAdminPermission (OP 2 и консоль проходят). Голый /placeholder — справка. <identifier> — word с автодополнением из реестра; <text> — greedy, в том числе со скобками {neoessentials_balance}. test берёт контекст только у самого исполнителя: аргумента игрока в дереве нет (вопреки javadoc класса). С консоли player == null, персональные токены остаются нераскрытыми. Команда не регистрируется при commands.placeholder: false.
/vault/vault [info | convert <from> <to>]—neoessentials.vault.adminNeoEssentials Vault API info and managementРегистрация только если modules.vaultEnabled и commands.vault. .requires: ванильный OP уровня 3 **или** neoessentials.vault.admin (для консоли UUID нет, остаётся hasPermission(3)). Голый /vault равен info. convert сравнивает имена провайдеров без пробелов (case-insensitive) и требует минимум двух зарегистрированных VaultEconomy. Узел neoessentials.vault.admin в PermissionRegistry не объявлен — без OP 3 его нужно выдать явно, registry-default его не даст.

Примеры#

Проверить канонические {neoessentials_*} в игре#

Убедиться, что реестр жив и что именно видит PlaceholderManager — не короткий алиас таблиста.

/placeholder list
/placeholder info neoessentials_balance
/placeholder test Привет {neoessentials_name}, баланс {neoessentials_balance}, группа {neoessentials_group}

list печатает идентификаторы вида neoessentials_name, neoessentials_balance, leaderboard_…, votifier_…, crate_keys — expansion id + _ + имя. info neoessentials_balance — registered, потому что DefaultPlaceholderExpansion.getIdentifier() = neoessentials и в наборе есть balance. test прогоняет PlaceholderManager.setPlaceholders от вашего игрока: имя/баланс/группа подставляются. Тот же текст с {balance} или {player} test не раскроет — этих ключей в реестре нет.

Совместимые {luckperms_*} и {ftbranks_*}#

Вставить текст, скопированный со сборки с LuckPerms PAPI, и понять, что это алиасы PermissionAPI, а не мост к чужому expansion.

/placeholder test {luckperms_prefix}{luckperms_group}{luckperms_suffix}
/placeholder test {ftbranks_prefix}{ftbranks_rank}
/placeholder test {neoessentials_prefix}{neoessentials_group}

Оба семейства резолвятся через PermissionAPI.getPrefix/getSuffix/группу: при активном LuckPerms — его мета, при FTB Ranks — его, без внешнего мода — внутренние группы. {luckperms_prefix} работает даже если LuckPerms никогда не стоял. info luckperms_prefix скорее скажет not_registered: это не запись реестра, а отдельная ветка resolveExternalPlaceholder.

REST рядом с командой: ping и resolve#

Внешний бэкенд проверяет порт без ключа, а подстановку текста — уже с Bearer.

/dashboard start
/apikey create placeholders ADMIN
/placeholder stats

После start: GET http://:8080/api/ping без Authorization → {"success":true,"mod":"neoessentials","mode":"external"} (порт в поставке 8080). GET /api/placeholders/list, /api/placeholders/stats и /api/placeholders/resolve?player=&text=… требуют AUTH (сессия или API key). resolve на сервере — тот же PlaceholderManager, что /placeholder test. stats в игре печатает registered_placeholders и прочие счётчики реестра. Сопряжение панели целиком — /dashboard pair "<url>" <code> (токен панели не вводится руками).

Кто сейчас экономика/права/чат для других модов#

Сторонний NeoForge-мод ходит в VaultServiceRegistry, не в классы NeoEssentials. Нужно увидеть активного провайдера.

/vault info

Три строки: Economy, Permission, Chat — активное имя и список всех регистраций с меткой [registeredBy]. Встроенные провайдеры садятся с ServicePriority.NORMAL и registeredBy neoessentials. При economy.useExternalEconomy и моде sg_economy в список попадает адаптер HIGH. Другой мод перекрывает встроенную экономику, зарегистрировав VaultEconomy с HIGH/HIGHEST — магазины NeoEssentials после этого тоже идут через VaultShopEconomyAdapter.

Свести балансы двух экономик у онлайна#

На сервере два VaultEconomy, нужно выровнять балансы текущего онлайна, не офлайна.

/vault info
/vault convert NeoEssentials OtherEco

Имена в convert — getName() без пробелов. Меньше двух провайдеров — convert_need_two. Цикл только по server.getPlayerList().getPlayers(): офлайн-счета не трогаются. Для каждого: createPlayerAccount на приёмнике при необходимости, затем deposit/withdraw на разницу балансов. Счётчик в convert_complete = число обработанных онлайн-игроков, не число ненулевых переводов.

Тонкие места#

Канон — {neoessentials_*}; короткие {player}/{tps} живут в других конвейерах#

DefaultPlaceholderExpansion id neoessentials без подчёркиваний (резолвер режет токен по первому _: {neoessentials_balance} → expansion neoessentials + balance; id вида my_mod никогда не матчится). Набор: name/username/displayname, prefix/suffix/group, channel, world/x/y/z/biome, health/food/level/exp/gamemode/ping, balance/balance_formatted/balance_raw/currency_symbol/baltop_rank/pay_toggle, server_name/server_motd/online_players/max_players, time/time_24/date, afk/afk_time/afk_reason, deaths/player_kills/mob_kills/play_time. {tps}, {online}, {max}, {player} PlaceholderManager не знает: их подставляют TablistManager.applyPlaceholders и ChatFormatter.resolveShortPlaceholders. /placeholder test {tps} вернёт токен как есть. {balance} без префикса в test тоже не раскроется — в реестре ключ neoessentials_balance.

Что делать: В чат-форматах, MOTD, таблисте можно мешать оба стиля. Для /placeholder test, REST /api/placeholders/resolve и чужого мода через PlaceholderAPI пишите {neoessentials_…}. Свой expansion регистрируйте id без _.

Консоль и игрок в test; REST resolve умеет чужой ник#

/placeholder test ловит getPlayerOrException и при неудаче (консоль) резолвит с player=null. Токены с requiresPlayer (почти все, кроме server_name/motd/online/max/time/date) остаются {…}. В Brigadier нет аргумента цели — javadoc «test [player]» врёт. HTTP GET /api/placeholders/resolve?player=&text= как раз принимает чужой онлайн-ник.

Что делать: Проверять персональные токены — из игры. С консоли смотрите server_* или бейте в REST с ?player=.

REST — это HTTP панели, Vault — отдельная Java-шина#

Dashboard API (порт webDashboard.port, в поставке 8080) — JSON для внешней панели: Bearer-сессия или /apikey, /api/ping без ключа, /api/placeholders/* с ключом, pairing через /dashboard pair. Vault API — четыре интерфейса в отдельном артефакте (VaultEconomy / VaultPermission / VaultChat / VaultServiceRegistry), без Minecraft-классов, без HTTP. Чужой мод делает compileOnly на vault-api и registerEconomy(…, HIGH, "mymod"). Это не взаимозаменяемые поверхности: REST не депозитит через VaultServiceRegistry, Vault не авторизует панель.

Что делать: Панель и скрипты — REST + API key. Межмодовая экономика/права/префиксы — VaultServiceRegistry. Не кладите Bearer в фронтенд.

/vault convert не видит офлайн и не переносит историю#

В коде прямой комментарий «no offline player scanning needed in NeoForge» и цикл только по онлайн-списку. Разница балансов догоняется deposit/withdraw на приёмнике; исходный провайдер не обнуляется. Имя для автодополнения — provider.getName().replace(" ", "").

Что делать: Перед convert зайдите нужными игроками. Офлайн-остатки переносите средствами самих экономик. Сверьте имена в /vault info, не «человеческое» название мода.

OP 2 недостаточно для /vault#

Большинство админ-команд мода пропускает уровень 2. /vault требует src.hasPermission(3) либо узел neoessentials.vault.admin. OP 2 без узла команду не увидит. Консоль обычно проходит как уровень 4. Узла нет в PermissionRegistry — neoessentials.* внутренним wildcard его не создаст как «зарегистрированный default», только если вы сами выдадите литерал или покрывающий wildcard.

Что делать: Выдайте neoessentials.vault.admin или поднимите оператора до 3+.

Известные проблемы#

{neoessentials_tps} из апстрим-вики в реестре нет#

APISystem.md и таблица «Server-wide» обещают {neoessentials_tps} / {tps}. В DefaultPlaceholderExpansion.initializePlaceholders ключа tps нет, ветки switch тоже нет. TPS считает TablistManager.getTps и подставляет только короткий {tps} в шапке таблиста и в chat short-form. /placeholder test {neoessentials_tps} и REST resolve оставят токен нетронутым. То же с {player}: в expansion это name/username/displayname, а {player} режет только таблист.

Что делать: Для PlaceholderAPI/REST используйте {neoessentials_name} и смиритесь, что TPS через этот реестр не отдаётся. В tablist.json пишите {tps}.

Javaddoc /placeholder test [player] не совпадает с деревом#

Заголовок класса PlaceholderCommand обещает необязательную цель. Регистрация: test → greedy text → executes. Всё после первого слова уходит в текст, отдельного аргумента player нет. Попытка /placeholder test {neoessentials_name} Steve просто резолвит строку «{neoessentials_name} Steve» от исполнителя.

Что делать: Чужой контекст — только GET /api/placeholders/resolve?player=Steve&text=….

/vault convert молча пропускает офлайн-счета#

Это не недописанный TODO в вики: метод сознательно итерирует только онлайн. Админ, сверивший baltop из SQLite с двумя провайдерами, увидит convert_complete по числу онлайна (часто 0–несколько) и решит, что конвертация «сломалась», хотя офлайн просто вне скоупа.

Что делать: Считать команду сессионной сверкой онлайна, не миграцией базы. Полный перенос — экспорт/импорт самой экономики.

Права 1#

УзелВ реестреЧто даёт
neoessentials.admin.placeholdersреестр: нетManage and test the placeholder system (/placeholder)

Подробное руководство#

Раздел ниже — исходная документация проекта (docs/Wiki/APISystem.md). Ссылки на другие страницы вики переписаны на этот сайт; текст самих руководств не редактируется, чтобы синк с апстримом не давал конфликтов.

Version: 1.0.5+build.54 · API Version: 1.2.0


Overview#

NeoEssentials exposes five integration surfaces for server admins and mod developers:

Surface What it provides
Built-in Placeholders {neoessentials_*} tokens usable in chat, MOTD, tablist, join/quit messages, etc.
Placeholder Java API Register custom tokens (PlaceholderProvider) or grouped expansions (PlaceholderExpansion)
REST API HTTP endpoints for external tools, dashboards, and automation
NeoEssentialsAPI Single stable entry-point exposing Economy, Permissions, and Placeholder services
Vault API Standalone, dependency-free economy/permission/chat interop for other NeoForge mods — see docs/VaultAPI.md
LeaderboardAPI Register your own ranked-stat boards — same one-line pattern as the Placeholder API, see Leaderboard System → LeaderboardAPI

Built-in Placeholders#

All NeoEssentials placeholders use the neoessentials expansion prefix. Syntax: {neoessentials_<identifier>}. Short-form aliases (legacy, no prefix) are also supported in most contexts.

Player Placeholders#

Placeholder Short-form Description
{neoessentials_name} {player} Player's real username
{neoessentials_displayname} — Display name (nick or real name)
{neoessentials_prefix} {prefix} Permission group prefix
{neoessentials_suffix} {suffix} Permission group suffix
{neoessentials_group} {group} Primary permission group name
{neoessentials_balance} {balance} Economy balance (raw number)
{neoessentials_balance_formatted} — Economy balance (formatted string)
{neoessentials_balance_raw} — Economy balance as a plain number with no trailing zeros
{neoessentials_currency_symbol} — Configured currency symbol (e.g. $)
{neoessentials_baltop_rank} — Player's rank on the /baltop leaderboard ("N/A" if unavailable)
{neoessentials_pay_toggle} — "enabled"/"disabled" — whether the player currently accepts /pay payments
{neoessentials_world} {world} Current dimension name
{neoessentials_x} {x} Player X coordinate
{neoessentials_y} {y} Player Y coordinate
{neoessentials_z} {z} Player Z coordinate
{neoessentials_biome} — Current biome name
{neoessentials_health} — Current health
{neoessentials_max_health} — Maximum health
{neoessentials_food} — Food/saturation level
{neoessentials_level} — Experience level
{neoessentials_exp} — Experience progress (0–100 %)
{neoessentials_gamemode} — Gamemode (survival / creative / …)
{neoessentials_ping} {ping} Connection latency (ms)

Stat Placeholders#

Placeholder Description
{neoessentials_deaths} Total death count
{neoessentials_player_kills} Total player kills
{neoessentials_mob_kills} Total mob kills
{neoessentials_play_time} Total play time (e.g. 3d 2h 15m, 45m 30s)

AFK Placeholders#

Placeholder Description
{neoessentials_afk} "AFK" when the player is AFK, empty string otherwise
{neoessentials_afk_time} Time spent AFK (e.g. "5m 30s")
{neoessentials_afk_reason} AFK reason text (if set)

Server-wide Placeholders#

Placeholder Short-form Description
{neoessentials_online_players} {online} Number of online players
{neoessentials_max_players} {max} Maximum player slots
{neoessentials_server_name} {server_name} Plain server name (general.serverName in config.json)
{neoessentials_server_motd} {server_motd} The configured MOTD (/motd if set, otherwise server.properties) — usually multi-line/heavily formatted, not meant for a single scoreboard/tablist line
{neoessentials_time} {time} Server time (12-hour format)
{neoessentials_time_24} — Server time (24-hour format)
{neoessentials_date} — Current date (yyyy-MM-dd)
{neoessentials_tps} {tps} Server TPS

Utility Placeholders#

Placeholder Description
{newline} Line break (tablist header / footer only)
{bar} Horizontal separator bar

LuckPerms / FTB Ranks Compatibility Placeholders#

A small set of compatibility-named placeholders, for text copied from a server that used to run LuckPerms's or FTB Ranks's own PlaceholderAPI expansion:

Placeholder Description
{luckperms_prefix} Permission group prefix
{luckperms_suffix} Permission group suffix
{luckperms_group} / {luckperms_primary_group} Primary permission group name
{luckperms_displayname} prefix + name + suffix
{ftbranks_prefix} Permission group prefix
{ftbranks_suffix} Permission group suffix
{ftbranks_rank} / {ftbranks_group} Primary permission group name

These are aliases into NeoEssentials' own permission system, not a live bridge to the LuckPerms/FTB Ranks plugin's own placeholder expansion. They resolve through PermissionAPI, which checks whichever external adapter is actually active (LuckPerms, FTB Ranks, or neither) and falls back to the internal permission system — so {luckperms_prefix} works correctly even on a server that has never installed LuckPerms at all. The equivalent {neoessentials_prefix}/{neoessentials_group} tokens above resolve identically; these exist purely so pasted-in text using the other naming convention still works.


Custom Placeholders (Java API)#

1. Single placeholder — PlaceholderProvider#

PlaceholderProvider is a public @FunctionalInterface you can implement with a lambda or class.

import com.zerog.neoessentials.api.PlaceholderAPI;
import com.zerog.neoessentials.api.NeoEssentialsAPI;

// Via static facade (simplest):
PlaceholderAPI.registerPlaceholder("mymod_kills", (player, params) ->
    player != null ? String.valueOf(MyStats.getKills(player.getUUID())) : "0"
);

// Via PlaceholderManager directly:
NeoEssentialsAPI.getPlaceholderManager()
    .registerPlaceholder("mymod_kills", (player, params) -> "42");

PlaceholderProvider.onRequest(ServerPlayer player, String params): - player — the ServerPlayer context, may be null for server-wide resolution - params — the part after : in {mymod_stat:some_param} — may be null

Return null to leave the original token unchanged in the output string.

2. Expansion — multiple placeholders under one prefix#

Extend PlaceholderExpansion to register many related placeholders at once. Expansion id "mymod" → placeholders {mymod_kills}, {mymod_deaths}, {mymod_playtime}.

Important: the expansion identifier must be lowercase, alphanumeric, and contain no underscores. Resolution splits a placeholder token on its first _ to separate the expansion id from the placeholder name ({mymod_kills} → id mymod, placeholder kills), so an id like "my_mod" would never match — {my_mod_kills} would look for an expansion named "my".

import com.zerog.neoessentials.api.PlaceholderExpansion;
import com.zerog.neoessentials.api.PlaceholderAPI;
import net.minecraft.server.level.ServerPlayer;
import java.util.Set;

public class MyModExpansion extends PlaceholderExpansion {

    @Override public String getIdentifier() { return "mymod"; }
    @Override public String getVersion()    { return "1.0.0"; }
    @Override public String getAuthor()     { return "YourName"; }

    @Override
    public Set<String> getPlaceholders() {
        return Set.of("kills", "deaths", "playtime");
    }

    @Override
    public String onPlaceholderRequest(ServerPlayer player, String identifier, String params) {
        if (player == null) return null;
        return switch (identifier) {
            case "kills"    -> String.valueOf(MyStats.getKills(player.getUUID()));
            case "deaths"   -> String.valueOf(MyStats.getDeaths(player.getUUID()));
            case "playtime" -> MyStats.getFormattedPlaytime(player.getUUID());
            default         -> null;
        };
    }
}

// Register (call during ServerStartingEvent or your mod's init):
PlaceholderAPI.registerExpansion(new MyModExpansion());

3. Resolving placeholders in text#

import com.zerog.neoessentials.api.PlaceholderManager;

PlaceholderManager pm = NeoEssentialsAPI.getPlaceholderManager();

// Resolve all placeholders in a string
String formatted = pm.setPlaceholders(player, "Hello {neoessentials_name}, kills: {mymod_kills}!");

// Resolve a single placeholder value
String value = pm.getPlaceholderValue(player, "mymod_kills", null);

// Check if a placeholder is registered
boolean exists = pm.isPlaceholderRegistered("mymod_kills");

// List all registered identifiers
Set<String> all = pm.getRegisteredPlaceholders();

// Unregister
pm.unregisterPlaceholder("mymod_kills");
PlaceholderAPI.unregisterExpansion(myExpansionInstance);

PlaceholderManager is thread-safe (ConcurrentHashMap). All operations are safe to call from async threads.


NeoEssentialsAPI — Full Reference#

The NeoEssentialsAPI class is the single stable entry-point for inter-mod integration.

import com.zerog.neoessentials.api.NeoEssentialsAPI;

// Check availability before calling (useful if NeoEssentials is optional)
if (NeoEssentialsAPI.isAvailable()) { ... }

// API version (SemVer string, e.g. "1.2.0")
String version = NeoEssentialsAPI.API_VERSION;

Economy API#

import com.zerog.neoessentials.api.economy.EconomyService;

EconomyService eco = NeoEssentialsAPI.getEconomyService();
eco.deposit(uuid, 100.0);
eco.withdraw(uuid, 50.0);
double balance   = eco.getBalance(uuid);
boolean hasEnough = eco.getBalance(uuid) >= 30.0;   // no has(uuid, amount) helper — compare getBalance()
boolean hasAcct   = eco.hasAccount(uuid);

Economy Events (NeoForge event bus)

Event Fires when Cancellable
EconomyDepositEvent Balance increased ✅
EconomyWithdrawEvent Balance decreased ✅

Both events extend EconomyEvent and expose getPlayerId(), getAmount() (double), and getBigDecimalAmount() (precise BigDecimal). They implement ICancellableEvent — cancel the event to veto the deposit/withdrawal.

Permissions API#

import com.zerog.neoessentials.api.permissions.PermissionsService;

PermissionsService perms = NeoEssentialsAPI.getPermissionsService();

boolean canFly  = perms.hasPermission(playerUUID, "neoessentials.fly");
String  group   = perms.getGroup(playerUUID);
String  prefix  = perms.getPrefix(playerUUID);
String  suffix  = perms.getSuffix(playerUUID);

// Register your mod's permission nodes (visible in /permissions search + dashboard)
perms.registerPermission("mymod.feature", "Enables my mod's cool feature");

// Register legacy alias
perms.registerAlias("oldmod.fly", "neoessentials.fly");

Placeholder API (quick reference)#

import com.zerog.neoessentials.api.PlaceholderManager;

PlaceholderManager pm = NeoEssentialsAPI.getPlaceholderManager();
pm.registerPlaceholder("mymod_online", (player, params) ->
    String.valueOf(server.getPlayerCount()));
String resolved = pm.setPlaceholders(player, "Players: {mymod_online}");

See the Custom Placeholders section above for the full API.

Versioning contract#

NeoEssentials follows SemVer for API changes:

Change type Description
PATCH Bug fixes only, fully backward compatible
MINOR New methods / classes added, backward compatible
MAJOR Breaking changes (rare, announced in advance)

Guard version-specific feature use:

String[] parts = NeoEssentialsAPI.API_VERSION.split("\\.");
int minor = Integer.parseInt(parts[1]);
if (minor >= 2) {
    // getPlaceholderManager() is available since 1.2.0
    NeoEssentialsAPI.getPlaceholderManager().registerPlaceholder("...", ...);
}

REST API (External Dashboard)#

The mod no longer ships its own dashboard UI. webDashboard.mode is "external" — the embedded HTTP server on config.json → webDashboard.port (default 8080) serves REST only; a separately-hosted dashboard app is the actual UI. Everything below is the integration surface for that app.

This section is a summary. The full, exhaustive reference — every route across all ~29 endpoint groups, real example request/response JSON pulled from a live server, quirks like binary downloads and non-UUID path segments, and the WebSocket protocol — lives in docs/API.md at the repo root. That file is what an external dashboard developer should actually build against; treat this wiki section as an index into it.

Authentication#

Two credential types, both presented the same way — Authorization: Bearer <token> — tried in this order by the server:

  1. API key (recommended for a dashboard backend) — a long-lived, independently-revocable credential, never expiring on its own. The recommended way to get one connected is pairing, not manually running /apikey create and copy-pasting the token:

#### Pairing

  1. On the external dashboard's own admin Configuration page, click "Generate Pairing Code" — it shows a one-time code (valid 10 minutes) and the exact command to run.
  2. On the Minecraft server console (or in-game, if OP), run the command it showed: /dashboard pair "<dashboardUrl>" <code> The URL must be quoted — unquoted command arguments can't contain : or /. Requires permission neoessentials.dashboard.pair.
  3. In that single round trip: the mod mints an API key and sends it to the dashboard (for the dashboard's own outbound calls to this API), and the dashboard mints a token back for the mod's outbound account-sync webhook (see below) — both directions connected, nothing hand-copied between two config files.

/dashboard unpair clears the connection and revokes the key that was minted for it.

The manual path still exists for scripting/one-off keys: /apikey create <label> [role] # role: ADMIN (default) | OPERATOR | MODERATOR | VIEWER /apikey list /apikey revoke <id> The full token (neo_<keyId>_<secret>) is printed exactly once at creation — only its salted hash is stored, so it can't be recovered later, only revoked and re-created. Never put it in frontend/browser code; it belongs on the external dashboard's own server, which then calls this API on the browser's behalf. Also manageable over REST once a first key exists — GET/POST /api/apikeys, DELETE /api/apikeys/{id} (ADMIN-gated). 2. Session token — from POST /api/auth/login (username/password) or the Discord-linked flow — see Web Dashboard for account registration. This is the mod's own human-login concept; an external dashboard backend calling this API as a service should generally prefer an API key instead.

Every route is gated as PUBLIC (no token needed — a small deliberate set: public moderation lookup, /api/docs/*, /api/ping), AUTH (any valid key/session, any role), or ADMIN (the credential's role must be ADMIN) — see docs/API.md for the exact tier of every route.

# Login (session-based)
curl -X POST http://localhost:8080/api/auth/login \
     -H "Content-Type: application/json" \
     -d '{"username":"admin","password":"secret"}'
# Response includes: {"success":true,"sessionId":"...", "user":{...}}

# Authenticated request — works identically with a session id or an API key
curl http://localhost:8080/api/placeholders/list \
     -H "Authorization: Bearer <sessionId-or-api-key>"

Endpoint groups (see docs/API.md for full detail on each)#

Prefix Covers
/api/apikeys Manage API keys (see above)
/api/auth Login/logout/session validation, dashboard-account CRUD
/api/player Online/offline player info, kick/teleport/heal/gamemode
/api/server, /api/game Server/world profile, performance, game events
/api/logging Dashboard's own request/error/performance logs
/api/admin Restart/stop/save/reload/broadcast
/api/files Sandboxed config/log file browser, editor, backups
/api/permissions Groups, users, aliases, node catalog — the internal permission system
/api/motd, /api/rules MOTD profiles + rotation; server rules (both ADMIN-gated for writes)
/api/teleport Teleport/home/warp/spawn/back settings
/api/placeholders List/resolve/stats for the placeholder registry
/api/shops Sign shops + NPC shops, CSV import/export (ADMIN-gated for writes)
/api/backup Snapshot create/list/restore/delete/download
/api/discord Bridge adapter status, event log, channel-ID test message, auth-config
/api/cloud Dropbox/Google Drive backup upload config
/api/users Dashboard account management + external-dashboard user sync (see below)
/api/moderation, /api/public/moderation Bans/mutes/kicks/warns/notes/reports (public variant is unauthenticated, privacy-limited)
/api/kits, /api/holograms, /api/warps Read/manage kits (read-only), holograms, warps (holograms/warps ADMIN-gated for writes)
/api/commands Run arbitrary server commands (blocklist applies)
/api/economy Balance lookups + admin give/take/set
/api/stats Economy/activity/performance dashboards, 60-min history
/api/docs The mod's own public in-game documentation content (unrelated to this API doc)

A live event feed also runs on websocketPort (default 8081) alongside REST — channels events/chat/stats, authenticated the same way (session id or API key). Full protocol in docs/API.md.

Syncing dashboard accounts with an external user table#

If the external dashboard has its own user accounts and wants a matching mod-side account to exist (or vice versa), see the "Keeping dashboard accounts in sync" section under /api/users in docs/API.md — covers POST /api/users/sync (idempotent create-or-update, for the external dashboard pushing its accounts into the mod) and the outbound webhook fired whenever a dashboard_user is created/updated/deleted here (in-game /dashboardregister included), so the external dashboard can mirror it back out automatically. The webhook's target URL and auth token are set automatically by pairing (see above), not hand-configured.


In-Game Admin Commands#

/placeholder#

Manage and test the placeholder system in-game.

Permission: neoessentials.admin.placeholders (default: OP-only)

Sub-command Description
/placeholder list List all registered placeholder identifiers
/placeholder info <id> Check if an identifier is registered (tab-completes known IDs)
/placeholder test <text> Resolve placeholders in <text> using your player context
/placeholder stats Show registry statistics

Custom Language System#

NeoEssentials supports full internationalisation with per-server language overrides.

Bundled Languages#

en_us, fr_fr, de_de, es_es, pt_br, zh_cn, nl_nl, pl_pl, ru_ru

All files are available in the JAR and auto-deployed to neoessentials/languages/custom/ when first selected.

Switching Language#

In config.json under the localization section:

"localization": {
  "language": "fr_fr"
}

Then reload in-game:

/neoessentials reload

Fallback: Any key not translated in the chosen language file falls back to English automatically.

Custom / Overriding Translations#

Edit any file in neoessentials/languages/custom/<lang>.json. Changes are preserved across mod updates — new keys from the JAR are merged in without overwriting your edits.

Adding a New Language#

  1. Create neoessentials/languages/custom/xx_xx.json
  2. Copy all keys from en_us.json and translate values
  3. Set "language": "xx_xx" in config.json → localization
  4. Run /neoessentials reload to apply

Lang Key Format#

{
  "_langVersion": 17,
  "commands.neoessentials.home.teleported": "§aTeleported to home §e{0}§a.",
  "commands.neoessentials.home.not_found": "§cHome §e{0}§c not found."
}

{0}, {1}, … are positional MessageFormat arguments substituted at runtime.

See Localization System for full documentation.


Back to Wiki Home