- keyboard now enables mouse controls - fix some issues with the keyboard camera - helper methods on the screen block entity for dealing with hit results - update non-english language files to json
373 lines
12 KiB
Java
373 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2019 BARBOTIN Nicolas
|
|
*/
|
|
|
|
package net.montoyo.wd.client.gui;
|
|
|
|
import com.cinemamod.mcef.MCEFBrowser;
|
|
import com.mojang.blaze3d.vertex.PoseStack;
|
|
import com.mojang.math.Axis;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.phys.BlockHitResult;
|
|
import net.minecraft.world.phys.HitResult;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraftforge.api.distmarker.Dist;
|
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
|
import net.minecraftforge.client.event.ViewportEvent;
|
|
import net.minecraftforge.fml.ModList;
|
|
import net.minecraftforge.fml.loading.FMLPaths;
|
|
import net.montoyo.wd.WebDisplays;
|
|
import net.montoyo.wd.client.gui.camera.KeyboardCamera;
|
|
import net.montoyo.wd.client.gui.controls.Button;
|
|
import net.montoyo.wd.client.gui.controls.Control;
|
|
import net.montoyo.wd.client.gui.controls.Label;
|
|
import net.montoyo.wd.client.gui.loading.FillControl;
|
|
import net.montoyo.wd.controls.builtin.ClickControl;
|
|
import net.montoyo.wd.entity.ScreenBlockEntity;
|
|
import net.montoyo.wd.entity.ScreenData;
|
|
import net.montoyo.wd.net.WDNetworkRegistry;
|
|
import net.montoyo.wd.net.server_bound.C2SMessageScreenCtrl;
|
|
import net.montoyo.wd.utilities.Log;
|
|
import net.montoyo.wd.utilities.data.BlockSide;
|
|
import net.montoyo.wd.utilities.math.Vector2i;
|
|
import net.montoyo.wd.utilities.serialization.TypeData;
|
|
import net.montoyo.wd.utilities.serialization.Util;
|
|
import org.cef.browser.CefBrowser;
|
|
import org.cef.misc.CefCursorType;
|
|
import org.joml.Matrix4f;
|
|
import org.joml.Vector4f;
|
|
import org.lwjgl.glfw.GLFW;
|
|
import org.vivecraft.client_vr.gameplay.VRPlayer;
|
|
import org.vivecraft.client_vr.gameplay.screenhandlers.KeyboardHandler;
|
|
|
|
import java.io.*;
|
|
import java.lang.reflect.Method;
|
|
import java.util.ArrayList;
|
|
import java.util.Map;
|
|
import java.util.function.Consumer;
|
|
|
|
@OnlyIn(Dist.CLIENT)
|
|
public class GuiKeyboard extends WDScreen {
|
|
|
|
private static final String WARNING_FNAME = "wd_keyboard_warning.txt";
|
|
|
|
private ScreenBlockEntity tes;
|
|
private BlockSide side;
|
|
private ScreenData data;
|
|
private final ArrayList<TypeData> evStack = new ArrayList<>();
|
|
private BlockPos kbPos;
|
|
private boolean showWarning = true;
|
|
|
|
@FillControl
|
|
private Label lblInfo;
|
|
|
|
@FillControl
|
|
private Button btnOk;
|
|
|
|
public GuiKeyboard() {
|
|
super(Component.nullToEmpty(null));
|
|
}
|
|
|
|
public GuiKeyboard(ScreenBlockEntity tes, BlockSide side, BlockPos kbPos) {
|
|
this();
|
|
this.tes = tes;
|
|
this.side = side;
|
|
this.kbPos = kbPos;
|
|
}
|
|
|
|
@Override
|
|
protected void addLoadCustomVariables(Map<String, Double> vars) {
|
|
vars.put("showWarning", showWarning ? 1.0 : 0.0);
|
|
}
|
|
|
|
private static final boolean vivecraftPresent;
|
|
|
|
static {
|
|
boolean vivePres = false;
|
|
if (ModList.get().isLoaded("vivecraft")) vivePres = true;
|
|
// I believe the non-mixin version of vivecraft is not a proper mod, so
|
|
// detect the mod reflectively if the mod is not found
|
|
else {
|
|
try {
|
|
Class<?> clazz = Class.forName("org.vivecraft.gameplay.screenhandlers.KeyboardHandler");
|
|
//noinspection ConstantConditions
|
|
if (clazz == null) vivePres = false;
|
|
else {
|
|
Method m = clazz.getMethod("setOverlayShowing", boolean.class);
|
|
//noinspection ConstantConditions
|
|
vivePres = m != null;
|
|
}
|
|
} catch (Throwable ignored) {
|
|
vivePres = false;
|
|
}
|
|
}
|
|
vivecraftPresent = vivePres;
|
|
}
|
|
|
|
@Override
|
|
public void init() {
|
|
super.init();
|
|
|
|
if (minecraft.getSingleplayerServer() != null && !minecraft.getSingleplayerServer().isPublished())
|
|
showWarning = false; //NO NEED
|
|
else
|
|
showWarning = !hasUserReadWarning();
|
|
|
|
loadFrom(new ResourceLocation("webdisplays", "gui/kb_right.json"));
|
|
|
|
if (showWarning) {
|
|
int maxLabelW = 0;
|
|
int totalH = 0;
|
|
|
|
for (Control ctrl : controls) {
|
|
if (ctrl != lblInfo && ctrl instanceof Label) {
|
|
if (ctrl.getWidth() > maxLabelW)
|
|
maxLabelW = ctrl.getWidth();
|
|
|
|
totalH += ctrl.getHeight();
|
|
ctrl.setPos((width - ctrl.getWidth()) / 2, 0);
|
|
}
|
|
}
|
|
|
|
btnOk.setWidth(maxLabelW);
|
|
btnOk.setPos((width - maxLabelW) / 2, 0);
|
|
totalH += btnOk.getHeight();
|
|
|
|
int y = (height - totalH) / 2;
|
|
for (Control ctrl : controls) {
|
|
if (ctrl != lblInfo) {
|
|
ctrl.setPos(ctrl.getX(), y);
|
|
y += ctrl.getHeight();
|
|
}
|
|
}
|
|
} else {
|
|
if (!minecraft.isWindowActive()) {
|
|
minecraft.setWindowActive(true);
|
|
minecraft.mouseHandler.grabMouse();
|
|
}
|
|
}
|
|
|
|
defaultBackground = showWarning;
|
|
syncTicks = 5;
|
|
|
|
if (vivecraftPresent)
|
|
if (VRPlayer.get() != null)
|
|
KeyboardHandler.setOverlayShowing(true);
|
|
|
|
KeyboardCamera.focus(tes, side);
|
|
|
|
data = tes.getScreen(side);
|
|
CefBrowser browser = data.browser;
|
|
((MCEFBrowser) browser).setCursor(CefCursorType.fromId(data.mouseType));
|
|
((MCEFBrowser) browser).setCursorChangeListener((id) -> {
|
|
data.mouseType = id;
|
|
((MCEFBrowser) browser).setCursor(CefCursorType.fromId(id));
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void removed() {
|
|
super.removed();
|
|
if (vivecraftPresent)
|
|
if (VRPlayer.get() != null)
|
|
KeyboardHandler.setOverlayShowing(false);
|
|
KeyboardCamera.focus(null, null);
|
|
CefBrowser browser = data.browser;
|
|
if (browser instanceof MCEFBrowser mcef) {
|
|
mcef.setCursor(CefCursorType.POINTER);
|
|
mcef.setCursorChangeListener((cursor) -> data.mouseType = cursor);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onClose() {
|
|
removed();
|
|
super.onClose();
|
|
this.minecraft.popGuiLayer();
|
|
}
|
|
|
|
@Override
|
|
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
|
if (quitOnEscape && keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
|
onClose();
|
|
return true;
|
|
}
|
|
addKey(new TypeData(TypeData.Action.PRESS, keyCode, modifiers, scanCode));
|
|
return super.keyPressed(keyCode, scanCode, modifiers);
|
|
}
|
|
|
|
@Override
|
|
public boolean charTyped(char codePoint, int modifiers) {
|
|
addKey(new TypeData(TypeData.Action.TYPE, codePoint, modifiers, 0));
|
|
return super.charTyped(codePoint, modifiers);
|
|
}
|
|
|
|
@Override
|
|
public boolean keyReleased(int keyCode, int scanCode, int modifiers) {
|
|
addKey(new TypeData(TypeData.Action.RELEASE, keyCode, modifiers, scanCode));
|
|
return super.keyPressed(keyCode, scanCode, modifiers);
|
|
}
|
|
|
|
void addKey(TypeData data) {
|
|
tes.type(side, "[" + WebDisplays.GSON.toJson(data) + "]", kbPos);
|
|
|
|
evStack.add(data);
|
|
if (!evStack.isEmpty() && !syncRequested())
|
|
requestSync();
|
|
}
|
|
|
|
@Override
|
|
protected void sync() {
|
|
if(!evStack.isEmpty()) {
|
|
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.type(tes, side, WebDisplays.GSON.toJson(evStack), kbPos));
|
|
evStack.clear();
|
|
}
|
|
}
|
|
|
|
@GuiSubscribe
|
|
public void onClick(Button.ClickEvent ev) {
|
|
if(showWarning && ev.getSource() == btnOk) {
|
|
writeUserAcknowledge();
|
|
|
|
for(Control ctrl: controls) {
|
|
if(ctrl instanceof Label) {
|
|
Label lbl = (Label) ctrl;
|
|
lbl.setVisible(!lbl.isVisible());
|
|
}
|
|
}
|
|
|
|
btnOk.setDisabled(true);
|
|
btnOk.setVisible(false);
|
|
showWarning = false;
|
|
defaultBackground = false;
|
|
minecraft.setWindowActive(true);
|
|
minecraft.mouseHandler.grabMouse();
|
|
}
|
|
}
|
|
|
|
private boolean hasUserReadWarning() {
|
|
try {
|
|
File f = new File(FMLPaths.GAMEDIR.get().toString(), WARNING_FNAME);
|
|
|
|
if(f.exists()) {
|
|
BufferedReader br = new BufferedReader(new FileReader(f));
|
|
String str = br.readLine();
|
|
Util.silentClose(br);
|
|
|
|
return str != null && str.trim().equalsIgnoreCase("read");
|
|
}
|
|
} catch(Throwable t) {
|
|
Log.warningEx("Can't know if user has already read the warning", t);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void writeUserAcknowledge() {
|
|
try {
|
|
File f = new File(FMLPaths.GAMEDIR.get().toString(), WARNING_FNAME);
|
|
|
|
BufferedWriter bw = new BufferedWriter(new FileWriter(f));
|
|
bw.write("read\n");
|
|
Util.silentClose(bw);
|
|
} catch(Throwable t) {
|
|
Log.warningEx("Can't write that the user read the warning", t);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isForBlock(BlockPos bp, BlockSide side) {
|
|
return bp.equals(kbPos) || (bp.equals(tes.getBlockPos()) && side == this.side);
|
|
}
|
|
|
|
protected void mouse(double mouseX, double mouseY, Consumer<Vector2i> func) {
|
|
float pct = Minecraft.getInstance().getPartialTick();
|
|
|
|
ViewportEvent.ComputeFov fov = new ViewportEvent.ComputeFov(
|
|
Minecraft.getInstance().gameRenderer,
|
|
Minecraft.getInstance().getEntityRenderDispatcher().camera,
|
|
pct, Minecraft.getInstance().options.fov().get(),
|
|
true
|
|
);
|
|
|
|
mouseX /= width;
|
|
mouseY /= height;
|
|
|
|
mouseX -= 0.5;
|
|
mouseY -= 0.5;
|
|
mouseY = -mouseY;
|
|
|
|
Matrix4f proj = Minecraft.getInstance().gameRenderer.getProjectionMatrix(fov.getFOV());
|
|
|
|
Entity e = Minecraft.getInstance().getEntityRenderDispatcher().camera.getEntity();
|
|
|
|
PoseStack camera = new PoseStack();
|
|
float[] angle = KeyboardCamera.getAngle(e, pct);
|
|
camera.mulPose(Axis.XP.rotationDegrees(angle[0]));
|
|
camera.mulPose(Axis.YP.rotationDegrees(angle[1] + 180.0F));
|
|
|
|
Vector4f coord = new Vector4f(0, 0, 0, 0);
|
|
coord.add(proj.invert().transform(new Vector4f(2f * (float) mouseX, 2 * (float) mouseY, 0, 1f)));
|
|
coord = camera.last().pose().invert().transform(coord);
|
|
coord.w = 0;
|
|
coord.normalize();
|
|
|
|
Vec3 vec3 = e.getEyePosition(pct);
|
|
Vec3 vec31 = new Vec3(coord.x, coord.y, coord.z);
|
|
|
|
BlockHitResult result = tes.trace(side, vec3, vec31);
|
|
if (result.getType() != HitResult.Type.MISS) {
|
|
tes.interact(result, func);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void mouseMoved(double mouseX, double mouseY) {
|
|
mouse(mouseX, mouseY, (hit) -> {
|
|
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
|
|
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserMove(tes, side, hit));
|
|
});
|
|
|
|
super.mouseMoved(mouseX, mouseY);
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
|
mouse(mouseX, mouseY, (hit) -> {
|
|
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
|
|
tes.handleMouseEvent(side, ClickControl.ControlType.DOWN, hit, button);
|
|
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserDown(tes, side, hit, button));
|
|
});
|
|
|
|
return super.mouseClicked(mouseX, mouseY, button);
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
|
mouse(mouseX, mouseY, (hit) -> {
|
|
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
|
|
tes.handleMouseEvent(side, ClickControl.ControlType.UP, hit, button);
|
|
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserUp(tes, side, button));
|
|
});
|
|
|
|
return super.mouseReleased(mouseX, mouseY, button);
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
double mouseX = Minecraft.getInstance().mouseHandler.xpos() / Minecraft.getInstance().getWindow().getWidth();
|
|
double mouseY = Minecraft.getInstance().mouseHandler.ypos() / Minecraft.getInstance().getWindow().getHeight();
|
|
|
|
mouse(mouseX * width, mouseY * height, (hit) -> {
|
|
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
|
|
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserMove(tes, side, hit));
|
|
});
|
|
|
|
super.tick();
|
|
}
|
|
}
|