package net.rustnotmc.client; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiChat; import net.minecraft.client.gui.GuiIngameMenu; import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiOptions; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiWorldSelection; import net.minecraft.client.gui.achievement.GuiStats; import net.minecraft.client.gui.advancements.GuiScreenAdvancements; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.settings.KeyBinding; import net.rustnotmc.api.client.ClientServices; import net.rustnotmc.api.client.Commands; import net.rustnotmc.api.client.Input; import net.rustnotmc.api.client.Screen; import net.rustnotmc.api.client.ScreenKind; import org.lwjgl.input.Mouse; /** * The client's implementation of the mod API. * * Everything here reads presentation state the client already has: what screen * is open, what the window measures, which key is held. Nothing reaches into * the world, a player or a packet, because the server owns those and a client * mod is not allowed to decide them. * * The values are recomputed per call rather than cached. A mod may ask once a * frame, and a stale answer would be worse than a cheap one. * * Lives in game-fork/src-overlay; src is regenerated on every build. */ public final class RsxClientServices implements ClientServices { private final Minecraft minecraft; private final RsxScreen screen = new RsxScreen(); private final RsxInput input = new RsxInput(); private final RsxCommands commands = new RsxCommands(); private long tick; private long frame; public RsxClientServices(Minecraft minecraft) { this.minecraft = minecraft; } /** Called once per frame by the bootstrap, before anything is asked. */ public void advanceFrame() { this.frame++; } /** Called once per tick by the bootstrap. */ public void advanceTick() { this.tick++; } boolean dispatchCommand(String line, String sender) { return this.commands.dispatch(line, sender); } @Override public Screen screen() { return this.screen; } @Override public Input input() { return this.input; } @Override public Commands commands() { return this.commands; } @Override public long tick() { return this.tick; } @Override public long frame() { return this.frame; } @Override public net.rustnotmc.api.client.FrameStats frameStats() { return RsxFrameStats.latest(); } private final class RsxScreen implements Screen { @Override public ScreenKind kind() { GuiScreen current = minecraft.currentScreen; if (current == null) { return minecraft.world == null ? ScreenKind.UNKNOWN : ScreenKind.IN_GAME; } if (current instanceof GuiChat) { return ScreenKind.CHAT; } if (current instanceof GuiIngameMenu) { return ScreenKind.PAUSED; } if (current instanceof GuiMainMenu) { return ScreenKind.MAIN_MENU; } if (current instanceof GuiWorldSelection) { return ScreenKind.WORLD_SELECTION; } if (current instanceof GuiOptions) { return ScreenKind.OPTIONS; } if (current instanceof GuiContainer) { return ScreenKind.INVENTORY; } if (current instanceof GuiScreenAdvancements || current instanceof GuiStats) { return ScreenKind.ADVANCEMENTS; } // a screen this build has no name for, over a loaded world or not return minecraft.world == null ? ScreenKind.UNKNOWN : ScreenKind.PAUSED; } @Override public int width() { return minecraft.displayWidth / Math.max(1, scaleFactor()); } @Override public int height() { return minecraft.displayHeight / Math.max(1, scaleFactor()); } @Override public float scale() { return (float) scaleFactor(); } @Override public int windowWidth() { return minecraft.displayWidth; } @Override public int windowHeight() { return minecraft.displayHeight; } private int scaleFactor() { int gui = minecraft.gameSettings == null ? 2 : minecraft.gameSettings.guiScale; if (gui <= 0) { // auto: the client picks the largest scale that keeps the // interface at least this wide int width = minecraft.displayWidth; gui = 1; while (width / (gui + 1) >= 320 && gui < 4) { gui++; } } return gui; } } private final class RsxInput implements Input { @Override public boolean keyDown(String keyId) { if (keyId == null || minecraft.gameSettings == null) { return false; } KeyBinding[] bindings = minecraft.gameSettings.keyBindings; if (bindings == null) { return false; } for (int i = 0; i < bindings.length; i++) { if (keyId.equals(bindings[i].getKeyDescription()) && bindings[i].isKeyDown()) { return true; } } return false; } @Override public boolean anyKeyDown() { KeyBinding[] bindings = minecraft.gameSettings == null ? null : minecraft.gameSettings.keyBindings; if (bindings == null) { return false; } for (int i = 0; i < bindings.length; i++) { if (bindings[i].isKeyDown()) { return true; } } return false; } @Override public boolean focused() { return minecraft.inGameHasFocus; } @Override public int mouseX() { return scaled(Mouse.getX()); } @Override public int mouseY() { return scaled(Mouse.getY()); } @Override public int mouseButtons() { int buttons = 0; if (Mouse.isButtonDown(0)) { buttons |= 1; } if (Mouse.isButtonDown(1)) { buttons |= 2; } if (Mouse.isButtonDown(2)) { buttons |= 4; } return buttons; } @Override public boolean mouseDown(int button) { return button >= 0 && button <= 2 && Mouse.isButtonDown(button); } private int scaled(int windowCoord) { return windowCoord / Math.max(1, screen.scaleFactor()); } } }