Разработка Java-серверов на Paper · 6 мин чтения

Свяжите Minecraft со статистикой сайта через устойчивый API

Начните с маленького публичного JSON, который сайт может прочитать без серверного секрета. Учебный API ниже запускается локально, отдаёт один фиктивный профиль и позволяет проверить 200/404 до подключения настоящей статистики.

Редакция LonelyCraft · Обновлено

Схема: Сервер → API → Сайт
Закрытая серверная доставка, проверка API и публичное представление результата. Авторская схема LonelyCraft.

Создайте локальный API с явным контрактом

Установите Python 3, создайте пустую папку stats-demo и сохраните следующий код как api.py. Он использует только стандартную библиотеку и слушает 127.0.0.1:8088, поэтому предназначен для локального обучения; http.server не следует выставлять как готовый production API.

Единственный профиль содержит учебный UUID и две метрики с единицами в имени: секунды и количество побед. Ответ не читается из Minecraft и не является статистикой реального игрока. Следующий шаг интеграции — заполнить такие поля из документированного публичного источника своего плагина, не отдавая приватные таблицы целиком.

import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

PLAYER_ID = "00000000-0000-4000-8000-000000000001"
PROFILE = {"uuid": PLAYER_ID, "name": "Demo",
           "parkour_best_seconds": 42.5, "pixel_wins": 3}

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        found = self.path == f"/api/v1/players/{PLAYER_ID}"
        data = PROFILE if found else {"error": "player_not_found"}
        body = json.dumps(data, ensure_ascii=False).encode("utf-8")
        self.send_response(200 if found else 404)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.send_header("Cache-Control", "no-store")
        self.end_headers()
        self.wfile.write(body)

ThreadingHTTPServer(("127.0.0.1", 8088), Handler).serve_forever()

Запустите и проверьте ответы

В терминале этой папки выполните в Linux/macOS python3 api.py, в Windows py api.py. Оставьте окно открытым. В другом терминале выполните curl -i http://127.0.0.1:8088/api/v1/players/00000000-0000-4000-8000-000000000001; в Windows используйте curl.exe. Должен прийти HTTP 200, JSON и pixel_wins: 3.

Повторите запрос с окончанием /missing: ожидается HTTP 404 и player_not_found. Откройте точный URL профиля в браузере: JSON виден без Java-плагина и токена. Если порт занят, измените 8088 в коде и во всех запросах. Для остановки локального примера нажмите Ctrl+C.

Подключите чтение к сайту

В приложении сайта запрашивайте свой /api/v1/players/UUID через серверную загрузку или same-origin маршрут. В браузере обработайте response.ok, затем response.json() по Fetch API. Страница должна показать ошибку или отсутствие профиля отдельно от честного нулевого показателя.

Для локального browser fetch от другого порта возникает другой origin: подготовьте reverse proxy либо серверную загрузку, вместо добавления произвольного Access-Control-Allow-Origin: * ко всем приватным ответам. На публичном сайте используйте HTTPS и поддерживаемый веб-фреймворк, а учебный Python обработчик оставьте только для проверки контракта.

Передавайте игровые результаты отдельно от чтения

Запись из Minecraft должна обращаться к защищённому ingest API с секретом, который хранится в серверном окружении. В браузерные переменные, HTML и публичный JSON этот ключ не попадает. Для повторной доставки сохраняйте eventId и подтверждённый результат в устойчивой очереди.

Полный снимок статистики обычно заменяет прежнее представление, а событие добавляет один новый факт. Определите это до первой записи: повтор снимка не должен удваивать победы. Проверяйте путь с настоящим тестовым результатом: игра → хранилище → ingest → публичный JSON → страница, включая отключение API и повтор того же идентификатора.

Опишите публичную метрику до передачи данных

Для каждого показателя задайте ключ, единицу, направление сортировки и смысл отсутствия. Время Dropper меньше — лучше, очки Mining Rush больше — лучше; нулевой результат отличается от отсутствующей записи. Передавайте стабильный идентификатор метрики, а русскую подпись держите в согласованном каталоге. Отдельно определите период, если рейтинг дневной или сезонный.

Игровой UUID и отображаемый ник выполняют разные задачи. UUID связывает записи выбранной системы аккаунтов, ник удобно показывать пользователю. Не раскрывайте через публичный DTO приватные заметки, IP и данные аутентификации. Даже если они доступны в исходной базе, API должен выбирать разрешённые поля, а не возвращать всю строку таблицы.

Пример контракта результата

Публичный ответ может содержать playerUuid, displayName, metric, value и recordedAt. Для точной валюты используйте формат, который не теряет дробные значения при преобразовании клиента. Для времени договоритесь о миллисекундах либо секундах. Запишите пример запроса и ответа рядом с валидацией, чтобы игра, API и сайт не трактовали число по-разному.

Передавать нужно подтверждённый факт, а не промежуточное состояние раунда. Устойчивые игровые записи.

Выберите события или снимки осознанно

Событие сообщает один результат с eventId; API принимает повтор идемпотентно. Снимок сообщает актуальный набор записей с версией или временем, а API заменяет соответствующее представление по контракту. Эти модели нельзя смешивать без правил: повтор снимка не должен суммировать уже суммированную статистику.

Если источник — SQLite или YAML другого плагина, используйте поддерживаемое чтение и проверяйте отсутствие частичной записи. Для SQLite предусмотрена согласованная копия, для файлов — атомарная публикация или документированный формат. Не держите игровой поток в ожидании сетевого ответа. Отправляющий процесс может читать подтверждённые данные по расписанию, если это соответствует допустимой задержке.

У события сначала сохраняется локальная очередь, затем выполняется HTTP. Подтверждение API позволяет отметить доставку. При тайм-ауте неизвестно, принял ли сервер запрос, поэтому нужен повтор с тем же eventId. Уникальность на стороне API предотвращает вторую запись. Ошибка 4xx из-за неверного контракта требует разбора, а временный 5xx — ограниченного повторения.

Таймаут и предел очереди защищают игру от недоступного веб-сервиса. Сеть вне игрового тика.

Авторизация на каждой стороне

Машинный токен хранится только у доверенного отправителя и API. Его права ограничены нужной операцией доставки; публичный браузер не получает этот ключ. Административный интерфейс использует отдельную сессию и проверяет доступ на каждом endpoint. Для cookie-сессии предусмотрите защиту изменяющих запросов от нежелательного внешнего источника.

Отклоняйте слишком большой payload, неизвестную метрику и некорректный UUID до записи. Задайте предел числа элементов и времени обработки. TLS защищает канал, но не заменяет аутентификацию и валидацию. В журнале нужен идентификатор доставки и итог, а не значение Authorization.

Проверка всей цепи

В интерфейсе покажите загрузку, отсутствие результатов и временную ошибку отдельно. «0» при отказе API превращает недоступность в ложную статистику. Если используется кэш, укажите его допустимый срок и проверяйте, что приватные ответы не получают публичное кэширование. Основной текст сайта должен оставаться читаемым даже при ошибке живых счётчиков.

  1. Создайте один подтверждённый результат тестового игрока.
  2. Найдите его в устойчивом игровом источнике или очереди.
  3. Отправьте в отдельную API-базу с тестовым ключом.
  4. Повторите тот же идентификатор и проверьте отсутствие дубликата.
  5. Получите публичный ответ и сравните число и единицу.
  6. Откройте страницу, затем смоделируйте отсутствие API.

Обновление контракта

Новая метрика требует согласованного каталога игры, API и клиента. Добавьте её валидацию, единицу и сортировку до публикации кнопки. При изменении смысла существующего поля используйте явную миграцию или версию контракта, иначе старые записи будут сравниваться с новыми некорректно. Идентификатор названия сам по себе не объясняет такую смену.

Проверьте восстановление очереди после рестарта и наблюдаемость задержки: сколько событий ожидают, как давно последний успешный запрос и какие записи требуют разбора. Большая очередь не должна незаметно заполнять диск. Возможность повторной доставки и строгий публичный DTO делают интеграцию устойчивой к сбоям каждой отдельной части.

Частые вопросы

Можно ли положить серверный токен в браузерный JavaScript?

Нет. Браузерный код доступен пользователю. Публичное чтение и доверенная доставка используют разные контракты и права.

Зачем eventId при повторном запросе?

После тайм-аута API мог уже записать событие. Тот же идентификатор позволяет принять повтор без второй записи и награды.

Что показывать при недоступном API?

Состояние временной ошибки либо допустимый кэш. Не подменяйте отсутствующие данные нулём: это другое значение. Сохраните основной текст страницы и предусмотрите понятный способ повторного запроса.

Снимок можно суммировать как событие?

Только при явно выбранном контракте. Обычно повтор полного снимка заменяет представление, а не добавляет уже агрегированный итог ещё раз.

Просмотры: … · Один просмотр с браузера за день

Было полезно?Общий счёт: …

Одна реакция с браузера. Нажмите повторно, чтобы снять её, или выберите другую.

Источники и документация

Minecraftразработка плагиновPaper

Читайте дальше