Примеры#
Обкатать награду до подключения первого мониторинга#
Прописали награду в 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, пока перевод не дописан, иначе слияние новых ключей отключится.
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.
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):