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 для запуска клиента
50 lines
1.4 KiB
Java
50 lines
1.4 KiB
Java
package net.rustnotmc.loader;
|
|
|
|
/**
|
|
* Which side of the game a mod is meant to run on.
|
|
*
|
|
* The loader runs on the client today, but the value is part of the descriptor
|
|
* from the first release: a mod jar that says BOTH today must not need a format
|
|
* change tomorrow when the loader moves to the server.
|
|
*/
|
|
public enum ModEnv {
|
|
|
|
CLIENT,
|
|
SERVER,
|
|
BOTH;
|
|
|
|
/** True when a mod declaring this env should load on the given side. */
|
|
public boolean runsOn(ModEnv side) {
|
|
if (side == null) {
|
|
return false;
|
|
}
|
|
return this == BOTH || this == side;
|
|
}
|
|
|
|
/** The side we are running on right now. */
|
|
public static ModEnv current() {
|
|
return CLIENT;
|
|
}
|
|
|
|
/**
|
|
* Parses the descriptor value. Unreadable input is treated as BOTH rather
|
|
* than rejected: refusing to start because of one bad character is worse
|
|
* than loading a mod that may then find itself unusable.
|
|
*/
|
|
public static ModEnv parse(String raw) {
|
|
if (raw == null) {
|
|
return BOTH;
|
|
}
|
|
String v = raw.trim().toUpperCase();
|
|
if (v.length() == 0) {
|
|
return BOTH;
|
|
}
|
|
if (v.equals("CLIENT") || v.equals("CLIENT_ONLY") || v.equals("CLIENTONLY")) {
|
|
return CLIENT;
|
|
}
|
|
if (v.equals("SERVER") || v.equals("SERVER_ONLY") || v.equals("SERVERONLY")) {
|
|
return SERVER;
|
|
}
|
|
return BOTH;
|
|
}
|
|
} |