diff --git a/src/main/java/de/bixilon/minosoft/config/Configuration.java b/src/main/java/de/bixilon/minosoft/config/Configuration.java index e028d64d0..d28b7abaf 100644 --- a/src/main/java/de/bixilon/minosoft/config/Configuration.java +++ b/src/main/java/de/bixilon/minosoft/config/Configuration.java @@ -15,10 +15,12 @@ package de.bixilon.minosoft.config; import com.google.common.collect.HashBiMap; import com.google.gson.*; +import de.bixilon.minosoft.config.key.KeyBinding; import de.bixilon.minosoft.data.accounts.Account; import de.bixilon.minosoft.data.accounts.MicrosoftAccount; import de.bixilon.minosoft.data.accounts.MojangAccount; import de.bixilon.minosoft.data.accounts.OfflineAccount; +import de.bixilon.minosoft.data.mappings.ModIdentifier; import de.bixilon.minosoft.gui.main.Server; import de.bixilon.minosoft.util.Util; import de.bixilon.minosoft.util.logging.Log; @@ -35,6 +37,7 @@ public class Configuration { private final HashMap config = new HashMap<>(); private final HashBiMap accountList = HashBiMap.create(); private final HashBiMap serverList = HashBiMap.create(); + private final HashBiMap keyBindings = HashBiMap.create(); private final Object lock = new Object(); public Configuration() throws IOException, ConfigMigrationException { @@ -84,6 +87,12 @@ public class Configuration { this.accountList.put(account.getId(), account); } + // key bindings + for (Map.Entry entry : json.getAsJsonObject("game").getAsJsonObject("key_bindings").entrySet()) { + final var identifier = ModIdentifier.getIdentifier(entry.getKey()); + this.keyBindings.put(identifier, KeyBinding.Companion.deserialize(identifier, entry.getValue().getAsJsonObject())); + } + final File finalFile = file; new Thread(() -> { while (true) { @@ -119,6 +128,12 @@ public class Configuration { accountsEntriesJson.add(entry.getKey(), entry.getValue().serialize()); } + // key combinations + JsonObject keyBindingsJson = jsonObject.getAsJsonObject("game").getAsJsonObject("key_bindings"); + for (Map.Entry entry : this.keyBindings.entrySet()) { + keyBindingsJson.add(entry.getKey().getFullIdentifier(), entry.getValue().serialize()); + } + // rest of data for (ConfigurationPaths.ConfigurationPath path : ConfigurationPaths.ALL_PATHS) { saveData(jsonObject, path, this.config.get(path)); @@ -209,6 +224,10 @@ public class Configuration { return this.accountList; } + public HashBiMap getKeyBindings() { + return this.keyBindings; + } + private void migrateConfiguration() throws ConfigMigrationException { int configVersion = getInt(ConfigurationPaths.IntegerPaths.GENERAL_CONFIG_VERSION); Log.info(String.format("Migrating config from version %d to %d", configVersion, LATEST_CONFIG_VERSION)); @@ -231,9 +250,9 @@ public class Configuration { private Object getData(JsonObject json, ConfigurationPaths.ConfigurationPath path) { if (path instanceof ConfigurationPaths.BooleanPaths booleanPath) { return switch (booleanPath) { - case NETWORK_FAKE_CLIENT_BRAND -> json.getAsJsonObject("network").get("fake-network-brand").getAsBoolean(); - case NETWORK_SHOW_LAN_SERVERS -> json.getAsJsonObject("network").get("show-lan-servers").getAsBoolean(); - case DEBUG_VERIFY_ASSETS -> json.getAsJsonObject("debug").get("verify-assets").getAsBoolean(); + case NETWORK_FAKE_CLIENT_BRAND -> json.getAsJsonObject("network").get("fake_network_brand").getAsBoolean(); + case NETWORK_SHOW_LAN_SERVERS -> json.getAsJsonObject("network").get("show_lan_servers").getAsBoolean(); + case DEBUG_VERIFY_ASSETS -> json.getAsJsonObject("debug").get("verify_assets").getAsBoolean(); case CHAT_COLORED -> json.getAsJsonObject("chat").get("colored").getAsBoolean(); case CHAT_OBFUSCATED -> json.getAsJsonObject("chat").get("obfuscated").getAsBoolean(); }; @@ -241,16 +260,16 @@ public class Configuration { if (path instanceof ConfigurationPaths.IntegerPaths integerPath) { return switch (integerPath) { case GENERAL_CONFIG_VERSION -> json.getAsJsonObject("general").get("version").getAsInt(); - case GAME_RENDER_DISTANCE -> json.getAsJsonObject("game").get("render-distance").getAsInt(); + case GAME_RENDER_DISTANCE -> json.getAsJsonObject("game").get("render_distance").getAsInt(); }; } if (path instanceof ConfigurationPaths.StringPaths stringPath) { return switch (stringPath) { case ACCOUNT_SELECTED -> json.getAsJsonObject("accounts").get("selected").getAsString(); - case GENERAL_LOG_LEVEL -> json.getAsJsonObject("general").get("log-level").getAsString(); + case GENERAL_LOG_LEVEL -> json.getAsJsonObject("general").get("log_level").getAsString(); case GENERAL_LANGUAGE -> json.getAsJsonObject("general").get("language").getAsString(); case RESOURCES_URL -> json.getAsJsonObject("download").getAsJsonObject("urls").get("resources").getAsString(); - case CLIENT_TOKEN -> json.getAsJsonObject("accounts").get("client-token").getAsString(); + case CLIENT_TOKEN -> json.getAsJsonObject("accounts").get("client_token").getAsString(); }; } throw new IllegalArgumentException(); @@ -259,24 +278,24 @@ public class Configuration { private void saveData(JsonObject input, ConfigurationPaths.ConfigurationPath path, Object data) { if (data instanceof Boolean bool) { switch ((ConfigurationPaths.BooleanPaths) path) { - case NETWORK_FAKE_CLIENT_BRAND -> input.getAsJsonObject("network").addProperty("fake-network-brand", bool); - case NETWORK_SHOW_LAN_SERVERS -> input.getAsJsonObject("network").addProperty("show-lan-servers", bool); - case DEBUG_VERIFY_ASSETS -> input.getAsJsonObject("debug").addProperty("verify-assets", bool); + case NETWORK_FAKE_CLIENT_BRAND -> input.getAsJsonObject("network").addProperty("fake_network_brand", bool); + case NETWORK_SHOW_LAN_SERVERS -> input.getAsJsonObject("network").addProperty("show_lan_servers", bool); + case DEBUG_VERIFY_ASSETS -> input.getAsJsonObject("debug").addProperty("verify_assets", bool); case CHAT_COLORED -> input.getAsJsonObject("chat").addProperty("colored", bool); case CHAT_OBFUSCATED -> input.getAsJsonObject("chat").addProperty("obfuscated", bool); } } else if (data instanceof Integer integer) { switch ((ConfigurationPaths.IntegerPaths) path) { case GENERAL_CONFIG_VERSION -> input.getAsJsonObject("general").addProperty("version", integer); - case GAME_RENDER_DISTANCE -> input.getAsJsonObject("game").addProperty("render-distance", integer); + case GAME_RENDER_DISTANCE -> input.getAsJsonObject("game").addProperty("render_distance", integer); } } else if (data instanceof String string) { switch ((ConfigurationPaths.StringPaths) path) { case ACCOUNT_SELECTED -> input.getAsJsonObject("accounts").addProperty("selected", string); - case GENERAL_LOG_LEVEL -> input.getAsJsonObject("general").addProperty("log-level", string); + case GENERAL_LOG_LEVEL -> input.getAsJsonObject("general").addProperty("log_level", string); case GENERAL_LANGUAGE -> input.getAsJsonObject("general").addProperty("language", string); case RESOURCES_URL -> input.getAsJsonObject("download").getAsJsonObject("urls").addProperty("resources", string); - case CLIENT_TOKEN -> input.getAsJsonObject("accounts").addProperty("client-token", string); + case CLIENT_TOKEN -> input.getAsJsonObject("accounts").addProperty("client_token", string); } } else { throw new IllegalArgumentException(); diff --git a/src/main/java/de/bixilon/minosoft/config/key/KeyBinding.kt b/src/main/java/de/bixilon/minosoft/config/key/KeyBinding.kt new file mode 100644 index 000000000..c3419dc11 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/config/key/KeyBinding.kt @@ -0,0 +1,75 @@ +package de.bixilon.minosoft.config.key + +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import de.bixilon.minosoft.data.mappings.ModIdentifier + +class KeyBinding( + val identifier: ModIdentifier, + val action: MutableMap>, + val `when`: MutableSet>, +) { + + fun serialize(): JsonObject { + val ret = JsonObject() + for ((keyEvent, keyCodes) in action) { + val keyEventArray = JsonArray() + for (keyCode in keyCodes) { + keyEventArray.add(keyCode.keyName.toUpperCase().replace(' ', '_')) + } + ret.add(keyEvent.name.toLowerCase(), keyEventArray) + } + val `when` = JsonArray() + for (whenIdentifierSet in this.`when`) { + val whenIdentifierArray = JsonArray() + for (whenIdentifier in whenIdentifierSet) { + whenIdentifierArray.add(whenIdentifier.fullIdentifier) + } + `when`.add(whenIdentifierArray) + } + + ret.add("when", `when`) + return ret + } + + companion object { + fun deserialize(identifier: ModIdentifier, json: JsonObject): KeyBinding { + val action: MutableMap> = mutableMapOf() + for (keyAction in KeyAction.VALUES) { + json[keyAction.name.toLowerCase()]?.asJsonArray?.let { + val keyCodes: MutableSet = mutableSetOf() + for (keyCode in it) { + keyCodes.add(KeyCodes.KEY_CODE_MAP[keyCode.asString.toUpperCase()]!!) + } + action[keyAction] = keyCodes + } + } + val `when`: MutableSet> = mutableSetOf() + json["when"]?.asJsonArray?.let { + val currentWhenIdentifierSet: MutableSet = mutableSetOf() + for (whenIdentifierJsonElementArray in it) { + val whenIdentifierArray = whenIdentifierJsonElementArray.asJsonArray + for (whenIdentifier in whenIdentifierArray) { + currentWhenIdentifierSet.add(ModIdentifier.getIdentifier(whenIdentifier.asString)) + } + } + `when`.add(currentWhenIdentifierSet) + } + return KeyBinding(identifier, action, `when`) + } + } + + enum class KeyAction { + MODIFIER, + CHANGE, + PRESS, + RELEASE, + DOUBLE_CLICK, + TOGGLE, + ; + + companion object { + val VALUES = values() + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/config/key/KeyCodes.kt b/src/main/java/de/bixilon/minosoft/config/key/KeyCodes.kt new file mode 100644 index 000000000..c49e26895 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/config/key/KeyCodes.kt @@ -0,0 +1,168 @@ +package de.bixilon.minosoft.config.key + +import org.lwjgl.glfw.GLFW.* + +enum class KeyCodes(val glfwKeyId: Int) { + KEY_UNKNOWN(GLFW_KEY_UNKNOWN), + KEY_SPACE(GLFW_KEY_SPACE), + KEY_APOSTROPHE(GLFW_KEY_APOSTROPHE), + KEY_COMMA(GLFW_KEY_COMMA), + KEY_MINUS(GLFW_KEY_MINUS), + KEY_PERIOD(GLFW_KEY_PERIOD), + KEY_SLASH(GLFW_KEY_SLASH), + KEY_0(GLFW_KEY_0), + KEY_1(GLFW_KEY_1), + KEY_2(GLFW_KEY_2), + KEY_3(GLFW_KEY_3), + KEY_4(GLFW_KEY_4), + KEY_5(GLFW_KEY_5), + KEY_6(GLFW_KEY_6), + KEY_7(GLFW_KEY_7), + KEY_8(GLFW_KEY_8), + KEY_9(GLFW_KEY_9), + KEY_SEMICOLON(GLFW_KEY_SEMICOLON), + KEY_EQUAL(GLFW_KEY_EQUAL), + KEY_A(GLFW_KEY_A), + KEY_B(GLFW_KEY_B), + KEY_C(GLFW_KEY_C), + KEY_D(GLFW_KEY_D), + KEY_E(GLFW_KEY_E), + KEY_F(GLFW_KEY_F), + KEY_G(GLFW_KEY_G), + KEY_H(GLFW_KEY_H), + KEY_I(GLFW_KEY_I), + KEY_J(GLFW_KEY_J), + KEY_K(GLFW_KEY_K), + KEY_L(GLFW_KEY_L), + KEY_M(GLFW_KEY_M), + KEY_N(GLFW_KEY_N), + KEY_O(GLFW_KEY_O), + KEY_P(GLFW_KEY_P), + KEY_Q(GLFW_KEY_Q), + KEY_R(GLFW_KEY_R), + KEY_S(GLFW_KEY_S), + KEY_T(GLFW_KEY_T), + KEY_U(GLFW_KEY_U), + KEY_V(GLFW_KEY_V), + KEY_W(GLFW_KEY_W), + KEY_X(GLFW_KEY_X), + KEY_Y(GLFW_KEY_Y), + KEY_Z(GLFW_KEY_Z), + KEY_LEFT_BRACKET(GLFW_KEY_LEFT_BRACKET), + KEY_BACKSLASH(GLFW_KEY_BACKSLASH), + KEY_RIGHT_BRACKET(GLFW_KEY_RIGHT_BRACKET), + KEY_GRAVE_ACCENT(GLFW_KEY_GRAVE_ACCENT), + KEY_WORLD_1(GLFW_KEY_WORLD_1), + KEY_WORLD_2(GLFW_KEY_WORLD_2), + + KEY_ESCAPE(GLFW_KEY_ESCAPE), + KEY_ENTER(GLFW_KEY_ENTER), + KEY_TAB(GLFW_KEY_TAB), + KEY_BACKSPACE(GLFW_KEY_BACKSPACE), + KEY_INSERT(GLFW_KEY_INSERT), + KEY_DELETE(GLFW_KEY_DELETE), + KEY_RIGHT(GLFW_KEY_RIGHT), + KEY_LEFT(GLFW_KEY_LEFT), + KEY_DOWN(GLFW_KEY_DOWN), + KEY_UP(GLFW_KEY_UP), + KEY_PAGE_UP(GLFW_KEY_PAGE_UP), + KEY_PAGE_DOWN(GLFW_KEY_PAGE_DOWN), + KEY_HOME(GLFW_KEY_HOME), + KEY_END(GLFW_KEY_END), + KEY_CAPS_LOCK(GLFW_KEY_CAPS_LOCK), + KEY_SCROLL_LOCK(GLFW_KEY_SCROLL_LOCK), + KEY_NUM_LOCK(GLFW_KEY_NUM_LOCK), + KEY_PRINT_SCREEN(GLFW_KEY_PRINT_SCREEN), + KEY_PAUSE(GLFW_KEY_PAUSE), + KEY_F1(GLFW_KEY_F1), + KEY_F2(GLFW_KEY_F2), + KEY_F3(GLFW_KEY_F3), + KEY_F4(GLFW_KEY_F4), + KEY_F5(GLFW_KEY_F5), + KEY_F6(GLFW_KEY_F6), + KEY_F7(GLFW_KEY_F7), + KEY_F8(GLFW_KEY_F8), + KEY_F9(GLFW_KEY_F9), + KEY_F10(GLFW_KEY_F10), + KEY_F11(GLFW_KEY_F11), + KEY_F12(GLFW_KEY_F12), + KEY_F13(GLFW_KEY_F13), + KEY_F14(GLFW_KEY_F14), + KEY_F15(GLFW_KEY_F15), + KEY_F16(GLFW_KEY_F16), + KEY_F17(GLFW_KEY_F17), + KEY_F18(GLFW_KEY_F18), + KEY_F19(GLFW_KEY_F19), + KEY_F20(GLFW_KEY_F20), + KEY_F21(GLFW_KEY_F21), + KEY_F22(GLFW_KEY_F22), + KEY_F23(GLFW_KEY_F23), + KEY_F24(GLFW_KEY_F24), + KEY_F25(GLFW_KEY_F25), + KEY_KP_0(GLFW_KEY_KP_0), + KEY_KP_1(GLFW_KEY_KP_1), + KEY_KP_2(GLFW_KEY_KP_2), + KEY_KP_3(GLFW_KEY_KP_3), + KEY_KP_4(GLFW_KEY_KP_4), + KEY_KP_5(GLFW_KEY_KP_5), + KEY_KP_6(GLFW_KEY_KP_6), + KEY_KP_7(GLFW_KEY_KP_7), + KEY_KP_8(GLFW_KEY_KP_8), + KEY_KP_9(GLFW_KEY_KP_9), + KEY_KP_DECIMAL(GLFW_KEY_KP_DECIMAL), + KEY_KP_DIVIDE(GLFW_KEY_KP_DIVIDE), + KEY_KP_MULTIPLY(GLFW_KEY_KP_MULTIPLY), + KEY_KP_SUBTRACT(GLFW_KEY_KP_SUBTRACT), + KEY_KP_ADD(GLFW_KEY_KP_ADD), + KEY_KP_ENTER(GLFW_KEY_KP_ENTER), + KEY_KP_EQUAL(GLFW_KEY_KP_EQUAL), + KEY_LEFT_SHIFT(GLFW_KEY_LEFT_SHIFT), + KEY_LEFT_CONTROL(GLFW_KEY_LEFT_CONTROL), + KEY_LEFT_ALT(GLFW_KEY_LEFT_ALT), + KEY_LEFT_SUPER(GLFW_KEY_LEFT_SUPER), + KEY_RIGHT_SHIFT(GLFW_KEY_RIGHT_SHIFT), + KEY_RIGHT_CONTROL(GLFW_KEY_RIGHT_CONTROL), + KEY_RIGHT_ALT(GLFW_KEY_RIGHT_ALT), + KEY_RIGHT_SUPER(GLFW_KEY_RIGHT_SUPER), + KEY_MENU(GLFW_KEY_MENU), + KEY_LAST(GLFW_KEY_LAST), + + + MOUSE_BUTTON_1(GLFW_MOUSE_BUTTON_1), + MOUSE_BUTTON_2(GLFW_MOUSE_BUTTON_2), + MOUSE_BUTTON_3(GLFW_MOUSE_BUTTON_3), + MOUSE_BUTTON_4(GLFW_MOUSE_BUTTON_4), + MOUSE_BUTTON_5(GLFW_MOUSE_BUTTON_5), + MOUSE_BUTTON_6(GLFW_MOUSE_BUTTON_6), + MOUSE_BUTTON_7(GLFW_MOUSE_BUTTON_7), + MOUSE_BUTTON_8(GLFW_MOUSE_BUTTON_8), + MOUSE_BUTTON_LAST(GLFW_MOUSE_BUTTON_LAST), + MOUSE_BUTTON_LEFT(GLFW_MOUSE_BUTTON_LEFT), + MOUSE_BUTTON_RIGHT(GLFW_MOUSE_BUTTON_RIGHT), + MOUSE_BUTTON_MIDDLE(GLFW_MOUSE_BUTTON_MIDDLE), + + ; + + val keyName = name.toUpperCase().removePrefix("KEY_") + + + companion object { + val VALUES = values() + val KEY_CODE_MAP: Map + val KEY_CODE_GLFW_ID_MAP: Map + + init { + val keyCodeMap: MutableMap = mutableMapOf() + val keyCodeGLFWIdMap: MutableMap = mutableMapOf() + + for (keyCode in VALUES) { + keyCodeMap[keyCode.keyName.toUpperCase().replace(' ', '_')] = keyCode + keyCodeGLFWIdMap[keyCode.glfwKeyId] = keyCode + } + + KEY_CODE_MAP = keyCodeMap.toMap() + KEY_CODE_GLFW_ID_MAP = keyCodeGLFWIdMap.toMap() + } + + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java b/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java index 1223e9cef..d82dfb659 100644 --- a/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java +++ b/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java @@ -23,6 +23,7 @@ import de.bixilon.minosoft.config.StaticConfiguration; import de.bixilon.minosoft.data.mappings.ModIdentifier; import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; import de.bixilon.minosoft.util.CountUpAndDownLatch; +import de.bixilon.minosoft.util.GitInfo; import de.bixilon.minosoft.util.Util; import de.bixilon.minosoft.util.logging.Log; @@ -30,6 +31,7 @@ import java.io.*; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; +import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.ZipEntry; @@ -247,7 +249,13 @@ public class AssetsManager { private void downloadAsset(AssetsSource source, String hash) throws IOException { switch (source) { case MINECRAFT -> downloadAsset(String.format(ProtocolDefinition.MINECRAFT_URL_RESOURCES, hash.substring(0, 2), hash), hash); - case MINOSOFT_GIT -> downloadAsset(String.format(Minosoft.getConfig().getString(ConfigurationPaths.StringPaths.RESOURCES_URL), hash.substring(0, 2), hash), hash, false); + case MINOSOFT_GIT -> downloadAsset(Util.formatString( + Minosoft.getConfig().getString(ConfigurationPaths.StringPaths.RESOURCES_URL), + Map.of( + "branch", GitInfo.INSTANCE.getGIT_BRANCH(), + "hashPrefix", hash.substring(0, 2), + "fullHash", hash) + ), hash, false); } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt index 8a137ce42..51fbe07eb 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt @@ -1,7 +1,10 @@ package de.bixilon.minosoft.gui.rendering +import de.bixilon.minosoft.config.key.KeyBinding +import de.bixilon.minosoft.config.key.KeyCodes import de.bixilon.minosoft.data.entities.EntityRotation import de.bixilon.minosoft.data.entities.Location +import de.bixilon.minosoft.data.mappings.ModIdentifier import de.bixilon.minosoft.gui.rendering.shader.Shader import de.bixilon.minosoft.protocol.network.Connection import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketPlayerPositionAndRotationSending @@ -34,6 +37,14 @@ class Camera(private val connection: Connection, private var fov: Float, private private var screenWidth = 0 private val shaders: MutableList = mutableListOf() + private var keyForwardDown = false + private var keyLeftDown = false + private var keyRightDown = false + private var keyBackDown = false + private var keyFlyUp = false + private var keyFlyDown = false + private var keySprintDown = false + fun mouseCallback(xPos: Double, yPos: Double) { var xOffset = xPos - this.lastMouseX var yOffset = this.lastMouseY - yPos // reversed since y-coordinates go from bottom to top @@ -59,31 +70,55 @@ class Camera(private val connection: Connection, private var fov: Float, private setRotation(yaw, pitch) } + fun init(renderWindow: RenderWindow) { + renderWindow.registerKeyCallback(ModIdentifier("minosoft:move_forward")) { keyCodes: KeyCodes, keyAction: KeyBinding.KeyAction -> + keyForwardDown = keyAction == KeyBinding.KeyAction.PRESS + } + renderWindow.registerKeyCallback(ModIdentifier("minosoft:move_left")) { keyCodes: KeyCodes, keyAction: KeyBinding.KeyAction -> + keyLeftDown = keyAction == KeyBinding.KeyAction.PRESS + } + renderWindow.registerKeyCallback(ModIdentifier("minosoft:move_back")) { keyCodes: KeyCodes, keyAction: KeyBinding.KeyAction -> + keyBackDown = keyAction == KeyBinding.KeyAction.PRESS + } + renderWindow.registerKeyCallback(ModIdentifier("minosoft:move_right")) { keyCodes: KeyCodes, keyAction: KeyBinding.KeyAction -> + keyRightDown = keyAction == KeyBinding.KeyAction.PRESS + } + renderWindow.registerKeyCallback(ModIdentifier("minosoft:fly_up")) { keyCodes: KeyCodes, keyAction: KeyBinding.KeyAction -> + keyFlyUp = keyAction == KeyBinding.KeyAction.PRESS + } + renderWindow.registerKeyCallback(ModIdentifier("minosoft:fly_down")) { keyCodes: KeyCodes, keyAction: KeyBinding.KeyAction -> + keyFlyDown = keyAction == KeyBinding.KeyAction.PRESS + } + renderWindow.registerKeyCallback(ModIdentifier("minosoft:sprint")) { keyCodes: KeyCodes, keyAction: KeyBinding.KeyAction -> + keySprintDown = keyAction == KeyBinding.KeyAction.PRESS + } + } + fun handleInput(deltaTime: Double) { var cameraSpeed = movementSpeed * deltaTime - if (glfwGetKey(windowId, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) { + if (keySprintDown) { cameraSpeed *= 5 } val lastPosition = cameraPosition val currentY = cameraPosition.y - if (glfwGetKey(windowId, GLFW_KEY_W) == GLFW_PRESS) { + if (keyForwardDown) { cameraPosition = cameraPosition + cameraFront * cameraSpeed } - if (glfwGetKey(windowId, GLFW_KEY_S) == GLFW_PRESS) { + if (keyBackDown) { cameraPosition = cameraPosition - cameraFront * cameraSpeed } - if (glfwGetKey(windowId, GLFW_KEY_A) == GLFW_PRESS) { + if (keyLeftDown) { cameraPosition = cameraPosition - cameraRight * cameraSpeed } - if (glfwGetKey(windowId, GLFW_KEY_D) == GLFW_PRESS) { + if (keyRightDown) { cameraPosition = cameraPosition + cameraRight * cameraSpeed } this.cameraPosition.y = currentY // stay on xz line when moving (aka. no clip): ToDo: make movement not slower when you look up - if (glfwGetKey(windowId, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) { + if (keyFlyDown) { cameraPosition = cameraPosition - CAMERA_UP_VEC3 * cameraSpeed } - if (glfwGetKey(windowId, GLFW_KEY_SPACE) == GLFW_PRESS) { + if (keyFlyUp) { cameraPosition = cameraPosition + CAMERA_UP_VEC3 * cameraSpeed } if (lastPosition != cameraPosition) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt index 8b8a90982..e5baf1ccb 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt @@ -1,5 +1,10 @@ package de.bixilon.minosoft.gui.rendering +import de.bixilon.minosoft.Minosoft +import de.bixilon.minosoft.config.StaticConfiguration +import de.bixilon.minosoft.config.key.KeyBinding +import de.bixilon.minosoft.config.key.KeyCodes +import de.bixilon.minosoft.data.mappings.ModIdentifier import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer import de.bixilon.minosoft.gui.rendering.hud.elements.RenderStats @@ -20,10 +25,12 @@ import org.lwjgl.system.MemoryUtil import java.util.concurrent.ConcurrentLinkedQueue class RenderWindow(private val connection: Connection, val rendering: Rendering) { + private val keyBindingCallbacks: MutableMap Unit)>>> = mutableMapOf() + private val keysDown: MutableSet = mutableSetOf() + private val keyBindingDown: MutableSet = mutableSetOf() val renderStats = RenderStats() var screenWidth = 900 var screenHeight = 500 - private var polygonEnabled = false private var windowId: Long = 0 private var deltaTime = 0.0 // time between current frame and last frame @@ -33,6 +40,9 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) private var renderingStatus = RenderingStates.RUNNING + private var polygonEnabled = false + private var mouseCatch = !StaticConfiguration.DEBUG_MODE + // all renderers val chunkRenderer: ChunkRenderer = ChunkRenderer(connection, connection.player.world, this) val hudRenderer: HUDRenderer = HUDRenderer(connection, this) @@ -86,25 +96,84 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) throw RuntimeException("Failed to create the GLFW window") } camera = Camera(connection, 60f, windowId) + camera.init(this) + glfwSetKeyCallback(this.windowId) { _: Long, key: Int, _: Int, action: Int, _: Int -> - run { - if (action != GLFW_RELEASE) { - return@run - } - when (key) { - GLFW_KEY_ESCAPE -> { - glfwSetWindowShouldClose(this.windowId, true) + val keyCode = KeyCodes.KEY_CODE_GLFW_ID_MAP[key] ?: KeyCodes.KEY_UNKNOWN + val keyAction = when (action) { + GLFW_PRESS -> KeyBinding.KeyAction.PRESS + GLFW_RELEASE -> KeyBinding.KeyAction.RELEASE + // ToDo: Double, Hold + else -> return@glfwSetKeyCallback + } + if (keyAction == KeyBinding.KeyAction.PRESS) { + keysDown.add(keyCode) + } else if (keyAction == KeyBinding.KeyAction.RELEASE) { + keysDown.remove(keyCode) + } + Log.verbose("Keycode $keyCode: $keyAction") + + for ((identifier, keyCallbackPair) in keyBindingCallbacks) { + run { + val keyBinding = keyCallbackPair.first + val keyCallbacks = keyCallbackPair.second + + var anyCheckRun = false + keyBinding.action[KeyBinding.KeyAction.MODIFIER]?.let { + val previousKeysDown = if (keyAction == KeyBinding.KeyAction.RELEASE) { + val previousKeysDown = keysDown.toMutableList() + previousKeysDown.add(keyCode) + previousKeysDown + } else { + keysDown + } + if (!previousKeysDown.containsAll(it)) { + return@run + } + anyCheckRun = true } - GLFW_KEY_P -> { - switchPolygonMode() + keyBinding.action[KeyBinding.KeyAction.CHANGE]?.let { + if (!it.contains(keyCode)) { + return@run + } + anyCheckRun = true + } + + // release or press + if (keyBinding.action[KeyBinding.KeyAction.CHANGE] == null) { + keyBinding.action[keyAction].let { + if (it == null) { + return@run + } + if (!it.contains(keyCode)) { + return@run + } + anyCheckRun = true + } + } + + if (!anyCheckRun) { + return@run + } + + Log.debug("Keycode ($identifier) -> $keyCode: $keyAction") + if (keyAction == KeyBinding.KeyAction.PRESS) { + keyBindingDown.add(keyBinding) + } else if (keyAction == KeyBinding.KeyAction.RELEASE) { + keyBindingDown.remove(keyBinding) + } + for (keyCallback in keyCallbacks) { + keyCallback.invoke(keyCode, keyAction) } } } - } - glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_DISABLED) + + if (mouseCatch) { + glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_DISABLED) + } glfwSetCursorPosCallback(windowId) { _: Long, xPos: Double, yPos: Double -> camera.mouseCallback(xPos, yPos) } MemoryStack.stackPush().let { stack -> val pWidth = stack.mallocInt(1) @@ -169,6 +238,25 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) } }) + registerKeyCallback(ModIdentifier("minosoft:debug_polygen")) { _: KeyCodes, _: KeyBinding.KeyAction -> + polygonEnabled = !polygonEnabled + glPolygonMode(GL_FRONT_AND_BACK, if (polygonEnabled) { + GL_LINE + } else { + GL_FILL + }) + } + registerKeyCallback(ModIdentifier("minosoft:debug_mouse_catch")) { _: KeyCodes, _: KeyBinding.KeyAction -> + mouseCatch = !mouseCatch + if (mouseCatch) { + glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_DISABLED) + } else { + glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_NORMAL) + } + } + registerKeyCallback(ModIdentifier("minosoft:quit")) { _: KeyCodes, _: KeyBinding.KeyAction -> + glfwSetWindowShouldClose(windowId, true) + } hudRenderer.screenChangeResizeCallback(screenWidth, screenHeight) @@ -242,15 +330,6 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) connection.disconnect() } - private fun switchPolygonMode() { - glPolygonMode(GL_FRONT_AND_BACK, if (polygonEnabled) { - GL_LINE - } else { - GL_FILL - }) - polygonEnabled = !polygonEnabled - } - private fun setRenderStatus(renderingStatus: RenderingStates) { if (renderingStatus == this.renderingStatus) { return @@ -261,4 +340,14 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) } this.renderingStatus = renderingStatus } + + fun registerKeyCallback(identifier: ModIdentifier, callback: ((keyCode: KeyCodes, keyEvent: KeyBinding.KeyAction) -> Unit)) { + var identifierCallbacks = keyBindingCallbacks[identifier]?.second + if (identifierCallbacks == null) { + identifierCallbacks = mutableSetOf() + val keyBinding = Minosoft.getConfig().keyBindings[identifier] ?: return + keyBindingCallbacks[identifier] = Pair(keyBinding, identifierCallbacks) + } + identifierCallbacks.add(callback) + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDDebugScreenElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDDebugScreenElement.kt index 04b55ac77..a46f6e451 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDDebugScreenElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDDebugScreenElement.kt @@ -1,6 +1,10 @@ package de.bixilon.minosoft.gui.rendering.hud.elements.text +import de.bixilon.minosoft.config.StaticConfiguration +import de.bixilon.minosoft.config.key.KeyBinding +import de.bixilon.minosoft.config.key.KeyCodes import de.bixilon.minosoft.data.entities.Location +import de.bixilon.minosoft.data.mappings.ModIdentifier import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.ChunkLocation import de.bixilon.minosoft.data.world.InChunkSectionLocation @@ -31,7 +35,12 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex private var sectionHeight: Int = 0 private lateinit var inChunkSectionLocation: InChunkSectionLocation + private var debugScreenEnabled = StaticConfiguration.DEBUG_MODE + override fun prepare(chatComponents: Map>) { + if (!debugScreenEnabled) { + return + } calculateDynamicValues() chatComponents[FontBindings.LEFT_UP]!!.addAll(listOf( @@ -67,6 +76,10 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex override fun init() { gpuText = glGetString(GL_RENDERER) ?: "unknown" gpuVersionText = glGetString(GL_VERSION) ?: "unknown" + + hudTextElement.renderWindow.registerKeyCallback(ModIdentifier("minosoft:debug_screen")) { _: KeyCodes, _: KeyBinding.KeyAction -> + debugScreenEnabled = !debugScreenEnabled + } } private fun nanoToMillis1d(nanos: Long): String { diff --git a/src/main/java/de/bixilon/minosoft/util/GitInfo.kt b/src/main/java/de/bixilon/minosoft/util/GitInfo.kt index c10eada3d..dcddfb14c 100644 --- a/src/main/java/de/bixilon/minosoft/util/GitInfo.kt +++ b/src/main/java/de/bixilon/minosoft/util/GitInfo.kt @@ -7,7 +7,7 @@ import java.text.SimpleDateFormat object GitInfo { - var GIT_BRANCH: String = "" + var GIT_BRANCH: String = "master" private set var GIT_BUILD_HOST_BRANCH: String = "" private set diff --git a/src/main/java/de/bixilon/minosoft/util/Util.java b/src/main/java/de/bixilon/minosoft/util/Util.java index 3ffab1c30..4466a4b3d 100644 --- a/src/main/java/de/bixilon/minosoft/util/Util.java +++ b/src/main/java/de/bixilon/minosoft/util/Util.java @@ -43,13 +43,11 @@ public final class Util { public static final Pattern UUID_FIX_PATTERN = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})"); // thanks https://www.spigotmc.org/threads/free-code-easily-convert-between-trimmed-and-full-uuids.165615 public static final char[] RANDOM_STRING_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); public static final String LINE_SEPARATOR = System.getProperty("line.separator"); + public static final Gson GSON = new Gson(); private static final Random THREAD_LOCAL_RANDOM = ThreadLocalRandom.current(); - private static final Field JSON_READER_POS_FIELD; private static final Field JSON_READER_LINE_START_FIELD; - public static final Gson GSON = new Gson(); - static { new JsonReader(new StringReader("")); Class jsonReadClass = JsonReader.class; @@ -372,4 +370,12 @@ public final class Util { } return headerList.toArray(new String[]{}); } + + public static String formatString(String string, Map format) { + String output = string; + for (var entry : format.entrySet()) { + output = output.replace("${" + entry.getKey() + "}", entry.getValue().toString()); + } + return output; + } } diff --git a/src/main/resources/config/config.json b/src/main/resources/config/config.json index b53f171e1..386c335f6 100644 --- a/src/main/resources/config/config.json +++ b/src/main/resources/config/config.json @@ -1,23 +1,146 @@ { "general": { "version": 1, - "log-level": "WARNING", + "log_level": "WARNING", "language": "en_US" }, "game": { - "render-distance": 12 + "render_distance": 10, + "key_bindings": { + "minosoft:move_forward": { + "change": [ + "W" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:move_left": { + "change": [ + "A" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:move_back": { + "change": [ + "S" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:move_right": { + "change": [ + "D" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:sprint": { + "change": [ + "LEFT_CONTROL" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:fly_up": { + "change": [ + "SPACE" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:fly_down": { + "change": [ + "LEFT_SHIFT" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:debug_screen": { + "release": [ + "F3" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:debug_polygen": { + "modifier": [ + "F4" + ], + "release": [ + "P" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:debug_mouse_catch": { + "modifier": [ + "F4" + ], + "release": [ + "M" + ] + }, + "minosoft:screenshot": { + "press": [ + "F2" + ] + }, + "minosoft:quit": { + "release": [ + "ESCAPE" + ], + "when": [ + [ + "minosoft:in_game" + ] + ] + }, + "minosoft:dummy_send_chat_message": { + "double_click": [ + "K" + ] + } + } }, "chat": { "colored": true, "obfuscated": true }, "network": { - "fake-network-brand": false, - "show-lan-servers": true + "fake_network_brand": false, + "show_lan_servers": true }, "accounts": { "selected": "", - "client-token": "", + "client_token": "", "entries": {} }, "servers": { @@ -25,10 +148,10 @@ }, "download": { "urls": { - "resources": "https://gitlab.com/Bixilon/minosoft/-/raw/development/data/resources/%s/%s.tar.gz?inline=false" + "resources": "https://gitlab.com/Bixilon/minosoft/-/raw/${branch}/data/resources/${hashPrefix}/${fullHash}.tar.gz?inline=false" } }, "debug": { - "verify-assets": true + "verify_assets": true } }