Перейти к содержимому

Вход в игре и скины

В игру игроки заходят через authlib-injector. Он направляет игру на Yggdrasil-endpoint Laminara, а тот проверяет логин через тот же источник аккаунтов, что и лаунчер, выдаёт игровые токены и подписывает скины.

Включается разделом yggdrasil:

{
"yggdrasil": {
"enabled": true,
"serverName": "Laminara",
"rsaKeyPath": "/var/lib/laminara/yggdrasil-rsa.pem",
"skinProvider": "template",
"skinConfig": { "skin": "https://skins.example.com/%nickname%.png", "slim": true },
"skinDomains": ["skins.example.com"]
}
}

serverName — то имя, которым сервер представляется authlib-injector. Игра показывает его при входе и пишет в свои логи; ни на сборки, ни на лаунчер, ни на папки игрока оно не влияет. Установщик подставляет туда название проекта, потому что в девяти случаях из десяти это одно и то же имя — поменять можно в любой момент: laminara-server settings yggdrasil.serverName "Своё имя".

Yggdrasil поднимается на том же публичном порту, что и лаунчер, по пути /yggdrasil/. authlib-injector запускается с этим адресом:

-javaagent:authlib-injector.jar=https://ваш-домен/yggdrasil/

Сам authlib-injector.jar скачивать не нужно: install кладёт его в корень сборки сам, сверив контрольную сумму с сайтом проекта, и лаунчер подставляет агент при запуске. Файл общий для всех платформ. Если положить туда свою версию, сервер её не тронет — так можно закрепить конкретную сборку агента.

На игровом сервере

Всё, что выше, — про сторону игрока. Сам Minecraft-сервер по умолчанию проверяет входящих у Mojang и ваших игроков не пустит, поэтому его тоже нужно направить на Laminara. Делается это тем же агентом и один раз:

  1. Положите authlib-injector.jar рядом с server.jar. Свежий — на странице релизов проекта; сгодится и тот, что Laminara положила в корень сборки.

  2. Допишите агент в команду запуска сервера перед -jar, с тем же адресом, что и у лаунчера:

    Окно терминала
    java -javaagent:authlib-injector.jar=https://ваш-домен/yggdrasil/ -Xmx4G -jar server.jar nogui
  3. В server.properties оставьте online-mode=true. Это не опечатка: агент подменяет не саму проверку, а того, у кого сервер спрашивает. С online-mode=false сервер перестанет проверять вообще кого-либо, и под любым ником сможет зайти кто угодно.

  4. Впишите адрес сервера в настройки сборки — serverAddress — и опубликуйте сборку заново. Тогда лаунчер покажет игрокам, сколько человек сейчас в игре.

Скины — тоже адаптеры

Откуда брать скины, задаёт skinProvider. Способа три: собрать ссылку по шаблону, спросить ваш сайт по HTTP или прочитать колонки базы.

Из чего складывается ссылка

В адресах template и json работают подстановки. Для игрока Steve с uuid 8667ba71-b85a-4004-af54-457a9734eed7 они дают:

ПодстановкаВо что превращается
%nickname%Steve
%username%Steve (то же самое, для привычки)
%uuid%8667ba71-b85a-4004-af54-457a9734eed7
%hash%8667ba71b85a4004af54457a9734eed7 (uuid без дефисов)

Ник берётся ровно в том написании, в котором он лежит в вашем источнике аккаунтов, а uuid — тот, что Laminara выдала игроку (из колонки uuid, если вы её указали, иначе выведенный из ника).

Ссылка на файл собирается прямо из шаблона — ни база, ни ваш сайт при этом не опрашиваются.

Самый частый случай — файлы по нику:

"skinProvider": "template",
"skinConfig": {
"skin": "https://skins.example.com/%nickname%.png"
}

https://skins.example.com/Steve.png

С плащами и тонкой моделью рук:

"skinConfig": {
"skin": "https://skins.example.com/skins/%nickname%.png",
"cape": "https://skins.example.com/capes/%nickname%.png",
"slim": true
}

Если файлы разложены по uuid — так делают, когда ники разрешено менять:

"skinConfig": {
"skin": "https://cdn.example.com/textures/%hash%.png"
}

https://cdn.example.com/textures/8667ba71b85a4004af54457a9734eed7.png

Годится и адрес с параметрами, если скины отдаёт скрипт:

"skinConfig": {
"skin": "https://site.example/api/skin.php?nick=%nickname%",
"cape": "https://site.example/api/cape.php?nick=%nickname%"
}

cape и slim необязательны. slim: true включает тонкую модель рук сразу для всех — выбрать её игроку по отдельности этот способ не позволяет, для этого нужен json или sql.

skinDomains перечисляет домены, с которых игра примет текстуры. Домены ваших скинов должны быть в этом списке.

Сервер раздаёт картинки сам

Игра узнаёт скачанную текстуру по адресу и больше за ней не ходит: файл ложится в её кэш навсегда. Поэтому скин, который лежит по неизменному адресу вроде https://skins.example.com/Steve.png, после замены картинки так и останется старым — сколько игру ни перезапускай. Модель рук при этом меняется сразу, потому что она приходит в самом профиле, а не картинкой.

Чтобы этого не случалось, Laminara не отдаёт игре ваш адрес напрямую. Сервер скачивает картинку, считает её хеш и публикует у себя:

https://ваш-домен/yggdrasil/textures/<хеш картинки>

Сменилась картинка — сменился хеш, а с ним и адрес: игра видит новый файл и скачивает его. Так же устроены адреса скинов у самого Mojang. Отсюда две приятные мелочи: в skinDomains достаточно домена самого сервера (Laminara добавляет его туда сама), а ваш сайт со скинами перестаёт держать нагрузку от игроков — к нему ходит одна Laminara, не чаще раза в минуту на игрока.

Публичный адрес зеркало берёт из launcher.endpoints — первого в списке; без него зеркало не включается. Картинка должна открываться с самого сервера Laminara: если она не открылась или по адресу лежит не PNG, зеркало отступает и отдаёт игре исходную ссылку — тогда домен вашего сайта обязан быть в skinDomains. Выключается зеркало целиком:

"yggdrasil": {
"mirrorTextures": false
}

Скин в одиночной игре

Скин виден не только на сервере. Клиент сам спрашивает профиль игрока по его uuid у того же /yggdrasil/ и рисует по нему себя в одиночном мире — запроса к игровому серверу тут нет вовсе. Laminara отвечает на такой запрос теми же текстурами, что уходят игровому серверу.

Профили игроков лежат рядом с игровыми сессиями: в redis, если он настроен в auth.sessions, — и тогда перезапуск сервера их не теряет. Игрока, о котором сервер ещё не слышал, профиль отдаётся пустым — это видно как «в одиночке скина нет, и к источнику скинов не приходит ни одного запроса». Лаунчер продлевает игровую сессию при каждом запуске, так что после следующего старта лаунчера скин появляется сам.

Голова в лаунчере

Лаунчер показывает лицо игрока в шапке рядом с ником. Берёт он его из того же профиля, что и игра в одиночном мире: скачивает картинку скина и вырезает лицо вместе со вторым слоем (шлем, волосы). У старых скинов 64×32 сплошь непрозрачный слой шлема не рисуется, как и в самой игре.

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

Подпись текстур

В корне /yggdrasil/ сервер публикует RSA-ключ — он создаётся и сохраняется по rsaKeyPath при первом запуске. Свойство текстур каждого профиля подписывается этим ключом по схеме RSA-SHA1, ровно так, как проверяет authlib-injector. Поэтому игра доверяет скинам.