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 для запуска клиента
137 lines
4.5 KiB
Java
137 lines
4.5 KiB
Java
package net.rustnotmc.client;
|
|
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.ScaledResolution;
|
|
import net.minecraft.client.renderer.BufferBuilder;
|
|
import net.minecraft.client.renderer.GlStateManager;
|
|
import net.minecraft.client.renderer.Tessellator;
|
|
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
|
import net.rustnotmc.api.hud.HudSurface;
|
|
|
|
/**
|
|
* The game's implementation of the mod HUD surface.
|
|
*
|
|
* A mod draws through this interface and never touches a game type, which is
|
|
* what lets the layout and the overlay code be tested with the game closed. The
|
|
* reverse cost is paid once, here: everything a mod needs from Minecraft is
|
|
* resolved in this class.
|
|
*
|
|
* Coordinates are the scaled interface ones, so an element lines up with the
|
|
* health bar and the chat without the mod knowing about GUI scale.
|
|
*
|
|
* Lives in game-fork/src-overlay; src is regenerated on every build.
|
|
*/
|
|
public final class RsxHudSurface implements HudSurface {
|
|
|
|
private final Minecraft minecraft;
|
|
|
|
/**
|
|
* Rebuilt only when the window or the scale factor actually changes. A new
|
|
* ScaledResolution per frame would be one short-lived object sixty times a
|
|
* second for a value that is almost never different.
|
|
*/
|
|
private ScaledResolution resolution;
|
|
private int cachedWidth = -1;
|
|
private int cachedHeight = -1;
|
|
private int cachedScale = -1;
|
|
|
|
public RsxHudSurface(Minecraft minecraft) {
|
|
this.minecraft = minecraft;
|
|
}
|
|
|
|
private ScaledResolution resolution() {
|
|
int width = this.minecraft.displayWidth;
|
|
int height = this.minecraft.displayHeight;
|
|
int scale = this.minecraft.gameSettings.guiScale;
|
|
if (this.resolution == null
|
|
|| width != this.cachedWidth
|
|
|| height != this.cachedHeight
|
|
|| scale != this.cachedScale) {
|
|
this.resolution = new ScaledResolution(this.minecraft);
|
|
this.cachedWidth = width;
|
|
this.cachedHeight = height;
|
|
this.cachedScale = scale;
|
|
}
|
|
return this.resolution;
|
|
}
|
|
|
|
@Override
|
|
public boolean available() {
|
|
return this.minecraft != null && this.minecraft.fontRenderer != null;
|
|
}
|
|
|
|
@Override
|
|
public int width() {
|
|
return this.available() ? resolution().getScaledWidth() : 0;
|
|
}
|
|
|
|
@Override
|
|
public int height() {
|
|
return this.available() ? resolution().getScaledHeight() : 0;
|
|
}
|
|
|
|
@Override
|
|
public float scale() {
|
|
return (float) resolution().getScaleFactor();
|
|
}
|
|
|
|
@Override
|
|
public void rect(float x, float y, float w, float h, int rgb, float alpha) {
|
|
if (w <= 0.0F || h <= 0.0F) {
|
|
return;
|
|
}
|
|
int a = clampAlpha(alpha);
|
|
int r = (rgb >> 16) & 0xFF;
|
|
int g = (rgb >> 8) & 0xFF;
|
|
int b = rgb & 0xFF;
|
|
|
|
// the vanilla debug overlay draws coloured quads the same way; this
|
|
// GlStateManager has no isTextureEnabled to ask, so the state is set
|
|
// explicitly the way the game itself does it
|
|
GlStateManager.disableTexture2D();
|
|
|
|
Tessellator tessellator = Tessellator.getInstance();
|
|
BufferBuilder buffer = tessellator.getBuffer();
|
|
buffer.begin(7, DefaultVertexFormats.POSITION_COLOR);
|
|
buffer.pos(x, y + h, 0.0D).color(r, g, b, a).endVertex();
|
|
buffer.pos(x + w, y + h, 0.0D).color(r, g, b, a).endVertex();
|
|
buffer.pos(x + w, y, 0.0D).color(r, g, b, a).endVertex();
|
|
buffer.pos(x, y, 0.0D).color(r, g, b, a).endVertex();
|
|
tessellator.draw();
|
|
|
|
GlStateManager.enableTexture2D();
|
|
}
|
|
|
|
@Override
|
|
public void text(float x, float y, String value, int rgb, float alpha) {
|
|
if (value == null || value.isEmpty() || !this.available()) {
|
|
return;
|
|
}
|
|
GlStateManager.enableTexture2D();
|
|
this.minecraft.fontRenderer.drawStringWithShadow(
|
|
value, x, y, (rgb & 0x00FFFFFF) | (clampAlpha(alpha) << 24));
|
|
}
|
|
|
|
@Override
|
|
public float textWidth(String value) {
|
|
if (value == null || value.isEmpty() || !this.available()) {
|
|
return 0.0F;
|
|
}
|
|
return (float) this.minecraft.fontRenderer.getStringWidth(value);
|
|
}
|
|
|
|
@Override
|
|
public int textHeight() {
|
|
return this.available() ? this.minecraft.fontRenderer.FONT_HEIGHT : 0;
|
|
}
|
|
|
|
private static int clampAlpha(float alpha) {
|
|
if (alpha < 0.0F) {
|
|
return 0;
|
|
}
|
|
if (alpha > 1.0F) {
|
|
return 255;
|
|
}
|
|
return (int) (alpha * 255.0F + 0.5F);
|
|
}
|
|
} |