Commit Graph

4 Commits

Author SHA1 Message Date
sasheg dev 450bdecc34 Клиент говорит с сервером по нашему протоколу
Раньше клиент умел только показывать меню. Теперь он заходит на сервер,
получает наш мир и рисует его.

Как это устроено:

- Наш сетевой слой (net/rustnotmc/client/net) читает наш протокол и
  переводит его пакеты в пакеты игры. Игра остаётся игрой: у неё есть
  мир, игрок, сущности и рендер, и все они работают. Не переписывать их
  ради того, чтобы нарисовать тот же мир иначе, незачем.
- Пакеты игры, которые не имеют соответствия у нас, не отправляются, а
  наоборот: перемещение игрока и ломание блока переводятся в наши.
- Экран входа игры (GuiConnecting) заменён наш: он спрашивает у лаунчера,
  кем пришёл игрок, и подставляет это в рукопожатие.
- Соединение качается из тика игры, а не из экрана. Рукопожатие, которое
  зависит от того, что экран ещё на экране, ломается alt-tab'ом.

Вход теперь через лаунчер, а не внутри игры:

- Лаунчер уже умеет логинить игрока и отдаёт игре имя, uuid и токен
  (--username, --uuid, --accessToken). Это та же роль, что у Yggdrasil в
  ванилле, только лаунчер наш.
- Сервер спрашивает лаунчер, один из ли наш этот игрок, и заодно получает
  бан-лист. Токен используется один раз и не хранится.
- Свои аккаунты сервера, когда настроен лаунчер, закрыты: сервер с двумя
  дверями не имеет дверей. Открыть их можно флагом, но намеренно.
- Если игру запустили без лаунчера, экран всё равно спрашивает имя и
  пароль: это путь для своего сервера и для разработки.

Проверено живьём: клиент вошёл по токену лаунчера, сервер спросил
лаунчер, лаунчер сказал "свой", игрок получил мир, 210 чанков пришли и
лежат на диске, рельеф нарисован.

Что чинилось по дороге:

- Minecraft.setIngameFocus() возвращал на главное меню, когда окно
  получало фокус. Игрок как раз кликает в окно, чтобы напечатать пароль, и
  экран входа исчезал в этот самый момент.
- Ответ лаунчера разбирается, а не ищется подстрокой: {"valid": true} и
  {"valid":true} это один и тот же ответ, и сервер, который понимает
  только второй, ломает работающий лаунчер.
2026-10-19 20:10:20 +03:00
sasheg dev 966a144a64 Тик мода должен идти по игровому тику, HUD должен виден при загрузке
Тик. Моды тикали из покадрового хука, то есть получали столько тиков,
сколько кадров успевает нарисовать машина. На 200 fps это в семь раз
быстрее игры: таймер мода уезжал, а счётчик тиков модов расходился с
миром, поверх которого он нарисован. Теперь тик вызывается рядом с
runTick(), внутри того же цикла, так что у мода ровно двадцать шагов в
секунду независимо от частоты кадров.

HUD. Экран загрузки мира рисует сам, мимо хвоста кадра. Пока генерируется
ландшафт, LoadingScreenRenderer рисует в собственный framebuffer, блитит
его в окно и сам вызывает updateDisplay() — до десяти раз в секунду, из
потока, который занят генерацией. Всё, что нарисовано из покадрового хука,
стирается следующим таким выводом, поэтому оверлей пропадал на всю
загрузку и возвращался уже в мире. Добавлен отдельный вход в тот же код
рисования оверлеев, вызываемый после блита и до вывода в окно.

Заодно разделены две ошибки в RsxBootstrap: сбой тика и сбой отрисовки
больше не выключают друг друга, у каждого своя защита.

Проверено вживую: оверлей виден на экране генерации ландшафта.
2026-10-19 01:02:30 +03:00
sasheg dev b6e847e981 Инструменты для сверки с оригинальным байткодом
struct_diff сравнивает классы по составу и показывает, каких не хватает.
Но он отвечает на вопрос «того же ли набора методов», а не «тот ли код».
Оба вопроса приходится задавать: прыжки и звуки шагов были сломаны именно
кодом, который компилировался без ошибок и состоял из правильных методов.

javap_diff сравнивает инструкции построчно, вызывая javap на обоих деревьях
классификации. Наивное сравнение текста здесь бесполезно: перекомпиляция
перенумеровывает constant pool, поэтому почти каждая строка отличается
номером #123, хотя код совпадает. Инструмент убирает номера пула, сдвиги
смещений от ldc/ldc_w и форму инструкций, после чего остаётся реальная
разница.

На звуке инструмент показал, что все аудио-классы совпадают с оригиналом.
Остатки объясняются версией компилятора, а не правками: javac 8 собирает
конкатенацию строк через StringBuilder, javac 17 через invokedynamic, а
$values() у enum появился в более новых версиях.

ogg_duration читает частоту дискретизации из заголовка Vorbis и число
отсчётов из granule position последней страницы OggS. Отсюда видно, что файл
сам по себе короче или что его просто обрывают при воспроизведении, —
вопрос, который иначе приходится решать на слух.

Проверено, что все 1085 ogg совпадают с индексом по SHA-1.
2026-10-19 00:25:35 +03:00
sasheg dev bdf0ae866a Форк клиента собирается из исходников, модзагрузчик встроен в игру
Проект начинается с двух вещей: клиент, который мы действительно собрали, и
загрузчик, который является его частью, а не чем-то рядом.

Форк
- prepare-game.ps1 пересобирает клиент целиком: ванильный jar, SRG и MCP,
  SpecialSource, FernFlower, патчи, автофиксеры, javac. Идемпотентен.
- Свой код игры лежит в src-overlay, потому что src пересоздаётся при каждой
  сборке и всё написанное вручную там погибает.
- 116 патчей в patches/decompiler-fixes.txt. Каждый сверялся с оригинальным
  байткодом через javap: компиляция не ловит ошибки декомпиляции.
- struct_diff.py сравнивает исходный байткод с нашей сборкой по классам,
  интерфейсам, суперклассам, методам и полям, и отдельно проверяет, что
  пропавшие классы действительно никому не нужны.

Загрузчик
- Собирается и тестируется без игры: build.ps1 не использует classpath
  вообще, поэтому SelfTest из 128 проверок проходит без запущенного форка.
- Клиентский API не содержит ни одного игрового типа, реализацию даёт игра.
- Политика: мод меняет то, что клиент показывает, но никогда то, что истинно
  в мире. Мир, предметы, игроки и сеть принадлежат серверу.
- Capability приведены в соответствие с этой политикой: осталось шесть, и
  за каждым стоит реальная проверка. Прежние WORLD, PLAYER, NETWORK, TEXT,
  INPUT и COMMAND выдавались, но ничего не проверяли.
- Команды модов имеют область CLIENT, SERVER или BOTH. Серверные не
  выполняются на клиенте никогда.
- Свой jar загрузчика лежит в classpath игры и сборки форка.

Инструменты
- add_patch.py, чтобы каждое исправление переживало передекомпиляцию
- find_self_comparisons.py, find_remove_index_bugs.py, find_callers.py
- stage_run.py и fetch_missing.py для запуска клиента
2026-10-18 21:31:27 +03:00