Farmstead Mods
EN

Votifier

Приём голосов с мониторингов по протоколам V1 (RSA) и V2 (NuVotifier).

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

Команды 5#

КомандаСинтаксисАлиасыПравоОписание
/togglevotebroadcast/togglevotebroadcast—neoessentials.votifier.togglebroadcastOpt out of vote broadcastsТолько игрок плюс neoessentials.votifier.togglebroadcast (из консоли не работает). Отключает лишь получение чужих оповещений: собственный голос игрока по-прежнему разошлёт броадкаст всем остальным.
/vote/vote—neoessentials.votifier.voteShow vote site linksКорень требует neoessentials.votifier.vote (консоль проходит). Печатает пары ключ/значение из votifier.json → voteLinks обычным текстом (§e<название>§7: §f<ссылка>) — ссылки не кликабельные, цветовые коды и градиенты внутри voteLinks не обрабатываются; при пустом или отсутствующем voteLinks отвечает «No vote links are configured».
/voteparty/voteparty—neoessentials.votifier.voteShow vote party progressТот же узел, что /vote: neoessentials.votifier.vote. Счётчик общесерверный и кумулятивный, а не личный вклад игрока; при voteParty.enabled = false команда всё равно отвечает, просто показывает замороженное значение.
/votes/votes [player]—neoessentials.votifier.voteShow vote totalsБез аргумента показывает статистику отправителя команды (из консоли это игрок с именем «Server»). Отдельного права на чужую статистику нет — любой с neoessentials.votifier.vote видит счётчик любого ника; автодополнение предлагает только онлайн-игроков, но принимается любое слово, и неизвестный ник молча покажет 0 вместо ошибки.
/votifier/votifier reload | /votifier genkeys | /votifier testvote <site> [player]—neoessentials.votifier.adminManage the Votifier vote listenerКорень требует neoessentials.votifier.admin (консоль проходит). Без подкоманды команда не выполняется (у корневого узла нет .executes) и отдаёт подсказку по синтаксису. reload сбрасывает кеш всех конфигов мода, но не перезапускает сокет слушателя; genkeys ничего не генерирует, а только печатает инструкцию; testvote выдаёт настоящую награду и засчитывает голос в статистику и в vote party.

Примеры#

Обкатать награду до подключения первого мониторинга#

Прописали награду в votifier.sites.default и хотите убедиться, что она выдаётся, не дожидаясь реального голоса с сайта.

/votifier reload
/votes Steve
/votifier testvote default Steve
/votes Steve

После reload — «Votifier config reloaded.». Первый /votes покажет текущий счётчик Steve, testvote ответит «Simulated a vote from default for Steve.», Steve (если он онлайн) получит награду и увидит броадкаст из broadcastMessage, второй /votes покажет счётчик на единицу больше. Счётчик vote party при этом тоже вырастет на 1 — testvote публикует настоящее событие голоса, а не имитацию.

Проверить vote party, не собирая 50 голосов#

Убедиться, что порог срабатывает и voteParty.commands действительно выполняются, прежде чем выставлять боевое значение votesRequired.

/votifier reload
/voteparty
/votifier testvote default Steve
/votifier testvote default Steve
/voteparty

Временно поставьте voteParty.votesRequired = 2 в votifier.json перед reload. Первый /voteparty покажет «Vote Party: 0/2», после второго testvote в консоли появится «Vote party triggered!», выполнятся команды из voteParty.commands, а счётчик сразу сбросится в 0 — итоговый /voteparty снова покажет «Vote Party: 0/2». Награда за сайт при этом выдалась дважды, так что тестируйте на служебном нике.

Голос пришёл, пока игрок был офлайн#

Проверить, что награда за голос не теряется, если игрок проголосовал с сайта и вышел из игры (или вообще не заходил).

/votifier testvote default Steve
/votes Steve

Steve офлайн — в консоли появится «Steve voted while offline — reward queued for next login», награда ляжет в очередь votifier_pending, броадкаста НЕ будет (он рассылается только для онлайн-голосующего), но статистика и счётчик vote party увеличатся сразу — /votes Steve уже покажет +1. При следующем входе Steve увидит «Claimed 1 pending vote reward(s)!» и получит все накопленные награды разом.

Перевыпустить RSA-ключ протокола V1#

Приватный ключ утёк или вы переносите сервер и хотите чистую пару ключей для мониторингов, работающих по старому протоколу V1.

/votifier genkeys

Команда ничего не генерирует и не удаляет — она только печатает инструкцию: «Delete config/neoessentials/votifier/public.pem and private.pem, then restart the server to generate a new V1 keypair.» Выполнять её нужно руками: остановить сервер, удалить оба .pem, запустить снова. При старте новый публичный ключ будет напечатан в консоль строкой «Votifier V1 public key (paste into your vote site's panel):» — его придётся заново вставить в панель каждого мониторинга, иначе их голоса перестанут расшифровываться.

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

host, port и v2Token читаются только при старте сервера#

VotifierServer.start() вызывается один раз, по событию ServerStartedEvent: он читает host/port, открывает сокет и кладёт v2Token в собственное поле. /votifier reload лишь чистит кеш конфигов — уже открытый сокет остаётся на старом адресе, а проверка подписи V2 продолжает использовать старый токен. Об этом же прямо предупреждает комментарий в votifier.json: enabled: false тоже не останавливает уже работающий слушатель.

Живыми через reload подхватываются только те секции, которые читаются заново при каждом использовании: sites, voteLinks, broadcastMessage и voteParty.

Что делать: После правки host, port, v2Token или enabled перезапускайте сервер целиком. /votifier reload держите для наград, ссылок, броадкаста и настроек vote party.

/votifier reload сбрасывает кеш всех конфигов мода, а не только votifier.json#

Подкоманда вызывает ConfigManager.getInstance().clearCache(), который полностью очищает общий configCache. Это значит, что вместе с votifier.json с диска будут перечитаны economy.json, kits.json, crates.json и все остальные — включая незаконченные правки, которые вы оставили в редакторе.

Что делать: Не запускайте /votifier reload, пока на диске лежат наполовину отредактированные конфиги других систем. Если нужен только Votifier — сначала сохраните всё остальное в консистентном виде.

Два независимых выключателя, и они выключают разное#

modules.votifierEnabled в config.json проверяется в момент регистрации команд: при false команды /vote, /votes, /voteparty, /togglevotebroadcast и /votifier вообще не появляются в дереве — их не будет ни в автодополнении, ни в справке.

votifier.enabled в votifier.json проверяется отдельно, уже при старте слушателя: команды остаются на месте, но TCP-порт не открывается. Побочный эффект: voteParty.resetOnRestart обрабатывается строкой ниже запуска слушателя, поэтому при выключенном Votifier счётчик вечеринки при рестарте не обнуляется, даже если флаг стоит в true.

Что делать: Чтобы совсем убрать систему из игры — modules.votifierEnabled: false. Чтобы временно перестать принимать голоса, но сохранить команды и статистику — votifier.enabled: false плюс перезапуск сервера.

Совпадение сайта регистронезависимое, запасной вариант — только «default»#

resolveSiteConfig() перебирает ключи sites и сравнивает их с присланным serviceName через equalsIgnoreCase, то есть регистр в названии сайта значения не имеет. Если точного совпадения нет, берётся запись default. Если и её нет (или секции sites нет вовсе), награда не выдаётся молча — ни команд, ни ключей кейсов, ни сообщения об ошибке. При этом статистика игрока и счётчик vote party всё равно увеличиваются.

Что делать: Всегда держите запись default в sites. Точное значение serviceName берите из панели мониторинга, а проверяйте командой /votifier testvote <serviceName> <ник>.

Ключи кейсов требуют включённого модуля Crates#

Блок sites.<site>.keys обрабатывается только под условием ConfigManager.isCratesModuleEnabled(). При modules.cratesEnabled: false ключи тихо не выдаются, а вот команды из sites.<site>.commands выполняются как обычно — награда за голос становится частичной без единого предупреждения.

Что делать: Если ключи кейсов — основная награда за голос, проверьте modules.cratesEnabled в config.json и убедитесь, что баланс растёт: /crate keys <ник>.

broadcastMessage понимает больше, чем написано в комментарии конфига#

Текст прогоняется через RichTextFormatter.processTablistText(), поэтому кроме заявленного {player} работают: подстановка {site} (название мониторинга), коды &, градиенты и радуга, именованные цветовые теги и даже токены {animation:имя} из animations.json. А вот hover/click-разметка вырезается — кликабельную ссылку в оповещение о голосе вставить нельзя.

Пустая строка в broadcastMessage полностью отключает оповещение.

Что делать: Оформляйте оповещение так же, как строки таблиста; ссылку на мониторинг оставляйте для /vote.

Статистика и очередь наград привязаны к нику, а не к UUID#

Коллекции votifier_stats и votifier_pending (в neoessentials/store/) используют ключом username.toLowerCase() — ник, который прислал мониторинг. Регистр не важен, но смена ника — важна: после переименования /votes покажет 0, а уже поставленная в очередь награда останется висеть под старым ником и никогда не будет выдана. UUID здесь используется только для выдачи ключей кейсов и для запоминания отказа от броадкастов (votifier_broadcast_toggle).

Что делать: При переименовании игрока перенесите его записи в neoessentials/store/votifier_stats.json и votifier_pending.json вручную на остановленном сервере.

Из консоли /votes и /votifier testvote подставляют имя «Server»#

Обе команды при отсутствии аргумента player берут source.getTextName(). Для консоли это строка «Server», поэтому /votes из консоли всегда покажет «Server has 0 total vote(s).», а /votifier testvote default без ника засчитает голос несуществующему игроку «Server»: увеличит счётчик vote party, создаст запись статистики под ключом server и поставит награду в очередь (игрока-то онлайн нет).

Что делать: Из консоли всегда указывайте ник явно: /votes Steve, /votifier testvote default Steve.

Проверка прав пропускает не-игроков#

/vote, /votes, /voteparty и /votifier написаны как p == null || PermissionAPI.hasPermission(...): консоль, командный блок и функция проходят всегда, независимо от узлов neoessentials.votifier.vote и neoessentials.votifier.admin. /togglevotebroadcast требует и игрока, и neoessentials.votifier.togglebroadcast.

Что делать: Помните, что командный блок с /votifier testvote выдаст награду без каких-либо прав — ограничивайте доступ к командным блокам обычными средствами Minecraft.

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

Мониторинг рапортует «голос отправлен», а на сервере ничего не происходит и в логе пусто#

Все отказы приёма голоса логируются через NeoLog.debug(..., LogCategory.VOTIFIER, ...): неверная HMAC-подпись V2, несовпавший challenge, битый 256-байтовый блок V1, любое исключение при разборе соединения. В config.json logging.categories.votifier.debug по умолчанию false, а debug-уровень пишется только в logs/debug.log, не в консоль. В результате самая частая ошибка настройки — токен V2 или публичный ключ V1, скопированные не полностью, — выглядит как полная тишина.

Что делать: Поставьте logging.categories.votifier.debug: true в config.json, выполните /neoessentials reload и смотрите logs/debug.log; там будет «Invalid V2 vote signature» либо «Invalid V2 vote challenge». Успешно принятый голос, наоборот, логируется на уровне info строкой «Received V1/V2 vote from ... for player ...».

Опечатка в sites.*.keys тихо выдаёт ключ от несуществующего кейса#

Комментарий в votifier.json обещает, что без подходящего кейса в crates.json ключ будет «silently skipped». На деле VoteRewardManager вызывает CrateKeyManager.addKeys() напрямую и существование кейса не проверяет: баланс ключа записывается в коллекцию crate_keys под указанным id. Игрок увидит ключи в /crate keys, но /crate open <id> ответит «crate not found», и вернуть эти ключи на настоящий кейс автоматически нечем.

Что делать: Сверяйте id в sites.<site>.keys с id кейсов из crates.json символ в символ. После правки конфига проверяйте связку целиком: /votifier testvote <site> <ник> → /crate keys <ник> → /crate open <id>.

/votifier genkeys ничего не генерирует#

И название подкоманды, и её описание в реестре команд обещают генерацию ключей. Реализация состоит из одного sendSuccess с текстом-подсказкой: ключи создаются только внутри RSAKeyManager.load(), а он срабатывает при старте сервера и только если на диске нет config/neoessentials/votifier/public.pem и private.pem.

Что делать: Воспринимайте genkeys как справку. Реальная перегенерация: остановить сервер, удалить оба .pem, запустить сервер, забрать новый публичный ключ из консоли и вставить его во все панели мониторингов.

Голоса по V2 перестают приходить после каждого перезапуска сервера#

Если v2Token в votifier.json пуст, сервер генерирует случайный UUID и пытается сохранить его обратно в конфиг. Когда запись не удаётся (права на файл, конфиг только для чтения, том смонтирован read-only), в лог уходит ERROR «Failed to save auto-generated Votifier V2 token — it will regenerate every restart until this is fixed», и при следующем старте токен будет уже другой — тот, что вы вставили в панель мониторинга, станет неверным.

Что делать: Найдите этот ERROR в логе, исправьте права на config/neoessentials/votifier.json либо впишите постоянный v2Token руками и перезапустите сервер.

/votifier testvote — не безопасная проверка, а полноценный голос#

Подкоманда собирает объект Vote и публикует PlayerVoteEvent напрямую, минуя весь слой протокола. Все подписчики отрабатывают по-настоящему: выполняются команды награды (включая выдачу денег и предметов), начисляются ключи кейсов, статистика игрока растёт, счётчик vote party растёт и может прямо здесь перевалить порог, а всем онлайн-игрокам уходит броадкаст «X проголосовал». Подтверждения и отмены нет.

Что делать: Тестируйте на служебном нике, а не на реальном игроке. Откатывать последствия придётся руками: neoessentials/store/votifier_stats.json и votifier_voteparty.json на остановленном сервере.

Протокол V1 не защищён от повтора#

В V2 сервер выдаёт в приветствии одноразовый challenge и отклоняет голос, если присланный challenge не совпал, — это защита от переотправки перехваченного пакета. В V1 такого поля в протоколе нет, и handleV1() ничего подобного не проверяет: один и тот же 256-байтовый RSA-блок можно отправить на порт повторно и получить награду столько раз, сколько захочется.

Что делать: Там, где мониторинг умеет NuVotifier (V2), настраивайте именно его. Порт слушателя (по умолчанию 8192) не выставляйте в интернет шире необходимого — ограничьте доступ фаерволом до адресов мониторингов.

Сообщения Votifier не переведены на русский#

В поставляемом файле data/lang/ru_ru.json нет ни одного ключа commands.neoessentials.votifier.* — все 10 строк системы есть только в en_us.json. При localization.language: ru_ru загрузчик докладывает недостающие ключи английским фолбэком, поэтому и игроки, и админы увидят «Vote Party: 3/50», «Claimed 1 pending vote reward(s)!» и остальные сообщения по-английски.

Что делать: Эти строки автоматически попадают в neoessentials/languages/custom/ru_ru.json при слиянии на старте — переведите их прямо там и перезапустите сервер. Не включайте при этом localization.preserveCustomTranslations, пока перевод не дописан, иначе слияние новых ключей отключится.

Конфигурация#

votifier.json#

КлючПо умолчаниюОписание
votifier
votifier.enabledtrueMaster switch — also see modules.votifierEnabled in config.json (that one requires a restart to take effect; this one is reloadable but only actually stops/starts the listener on the next server restart too, since the socket lifecycle is tied to server start/stop, not /neoe reload).
votifier.host"0.0.0.0"Bind address for the vote-listener socket — separate from the Minecraft server port.
votifier.port8192—
votifier.v2Token""Auto-generated on first boot if empty — this is the shared secret used to verify Votifier PROTOCOL V2 (NuVotifier-compatible) votes via HMAC-SHA256. Paste this into your vote site's panel wherever it asks for a "Votifier v2 token"/"NuVotifier token". Leave untouched once generated — changing it invalidates any vote site already configured with the old value.
votifier.sites — Per-vote-site reward configuration, keyed by the exact "serviceName" string the vote site sends (case-insensitive) — e.g. "PlanetMinecraft", "minecraftservers.org". A vote from a site with no matching entry here uses "default" instead.
votifier.sites.default
votifier.sites.default.commands — Console commands run on vote, {player} replaced with the voter's username.
votifier.sites.default.keys{}Crate keys granted per vote, by crate id -> amount. Requires modules.cratesEnabled and a matching crate defined in crates.json — silently skipped otherwise.
votifier.voteLinks — Shown by /vote — plain links/instructions for where to vote, not used for anything else.
votifier.voteLinks.example-site"https://example-vote-site.com/vote/YourServerName"—
votifier.broadcastMessage"&a{player} &7voted for the server! Thanks for your support &c&l❤"Broadcast to the whole server when someone votes. Empty = no broadcast.
votifier.voteParty — Players can opt out of seeing (or causing) the broadcast via /togglevotebroadcast — this only controls whether IT'S SENT to a given player, not whether their own vote triggers one for everyone else.
votifier.voteParty.enabledtrue—
votifier.voteParty.votesRequired50Total votes (server-wide, cumulative) needed to trigger the party
votifier.voteParty.commands
votifier.voteParty.resetOnRestartfalsefalse = the counter keeps counting across restarts toward the next party; true = it resets to 0 every time the server restarts

Права 3#

УзелВ реестреЧто даёт
neoessentials.votifier.adminреестр: нетManage the Votifier listener (/votifier)
neoessentials.votifier.togglebroadcastреестр: нетOpt out of vote broadcasts (/togglevotebroadcast)
neoessentials.votifier.voteреестр: нетUse /vote, /votes, and /voteparty

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

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

Config file: config/neoessentials/votifier.json Module toggle: modules.votifierEnabled (config.json) Introduced: v1.0.6


Overview#

A vote-listener compatible with both Votifier protocol versions (V1/RSA and V2/NuVotifier), so vote-site relay services (PlanetMinecraft, minecraftservers.org, etc.) can notify this server when a player votes. Runs on its own TCP port, separate from the Minecraft server port.

Votifier can be configured to grant Crate keys as a vote reward — the common "vote crate" server setup — but the two systems work fully independently too.


Setup#

  1. Start the server once with modules.votifierEnabled: true (default). The console logs: - A V1 public key (base64) — paste this into your vote site's panel wherever it asks for a Votifier public key. - A V2 token — paste this wherever the vote site asks for a "NuVotifier token"/"Votifier v2 token". This is auto-generated once and saved to votifier.json — don't regenerate it unless you also update every vote site using it.
  2. Point your vote site(s) at this server's IP and the configured port (default 8192) — not the Minecraft server port.
  3. Configure a reward under votifier.sites for each vote site's exact serviceName (the vote site's panel usually shows you this string), or rely on the "default" entry for any site without a specific match.

Protocol#

Both wire protocols are auto-detected on the same port per connection (byte-verified against the reference NeoForge implementation, github.com/uberswe/votifier, rather than reconstructed from memory):

  • V1 — a 256-byte RSA/PKCS1-encrypted block, no framing. Decrypted plaintext is 5 newline-separated fields: VOTE, serviceName, username, address, timestamp.
  • V2 (NuVotifier-compatible) — magic bytes 0x73 0x3A, a 2-byte big-endian length prefix, then that many bytes of {"payload": "<json>", "signature": "<base64 HMAC-SHA256>"}. The signature is HMAC-SHA256 over the raw payload string, keyed by the shared V2 token. The inner payload carries serviceName/username/address/timestamp/challenge (the challenge must match this connection's greeting — replay protection).

Config (votifier.json)#

Key Default Description
enabled true Master switch
host / port 0.0.0.0 / 8192 Bind address/port for the vote-listener socket
v2Token (auto-generated) Shared secret for V2 signature verification
sites.<serviceName>.commands (list) Console commands run on vote, {player} substituted
sites.<serviceName>.keys (map) Crate id → key amount granted on vote (requires modules.cratesEnabled)
voteLinks (map) Shown by /vote — plain links/instructions, not functional
broadcastMessage (text) Server-wide "X voted!" message, {player}/{site}. Empty = no broadcast
voteParty.enabled true Cumulative server-wide vote counter with a bonus at a threshold
voteParty.votesRequired 50 Total votes (server-wide) needed to trigger
voteParty.resetOnRestart false Whether the counter resets every restart

A vote from an offline player is queued, not dropped — delivered automatically the next time that player logs in.

Commands#

Command Permission Description
/vote neoessentials.votifier.vote Show configured vote links
/votes [player] neoessentials.votifier.vote Show total vote count
/togglevotebroadcast none Opt out of seeing the vote broadcast
/voteparty neoessentials.votifier.vote Show vote party progress
/votifier reload neoessentials.votifier.admin Reload votifier.json
/votifier testvote <site> [player] neoessentials.votifier.admin Simulate a vote — verify reward config without a real vote site round-trip
/votifier genkeys neoessentials.votifier.admin Instructions to regenerate the V1 RSA keypair

Placeholders#

{votifier_total}, {votifier_voteparty_progress}, {votifier_voteparty_required}.


Back to Wiki Home · See also Crates