diff --git a/.forgejo/workflows/build-dev.yml b/.forgejo/workflows/build-dev.yml new file mode 100644 index 0000000..e0449f4 --- /dev/null +++ b/.forgejo/workflows/build-dev.yml @@ -0,0 +1,42 @@ +on: + push: + branches-ignore: + - refs/tags/ + +jobs: + dev: + runs-on: nodebullshit + steps: + - uses: actions/checkout@v3 + name: Checkout + + - name: Set short git commit reference + id: vars + run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - uses: https://github.com/actions/setup-java@v4 + name: Setup Java + with: + distribution: temurin + java-version: 17 + + - uses: https://github.com/gradle/actions/setup-gradle@v3 + name: Setup Gradle + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + + - run: ./gradlew runDatagen + name: Generate assets + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + + - run: ./gradlew build + name: Build project + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + + - run: ./gradlew publish + name: Upload + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + PACKAGE_REPO_KEY: ${{ secrets.PACKAGE_REPO_KEY }} diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml new file mode 100644 index 0000000..01b60ea --- /dev/null +++ b/.forgejo/workflows/build-release.yml @@ -0,0 +1,54 @@ +on: + release: + types: + - released + - prereleased + - created + - published + +jobs: + release: + runs-on: nodebullshit + steps: + - uses: actions/checkout@v3 + name: Checkout + + - name: Fetch the full git repository + run: git fetch --prune --unshallow + + - name: Set short git commit reference + id: vars + run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - uses: https://github.com/actions/setup-java@v4 + name: Setup Java + with: + distribution: temurin + java-version: 17 + + - uses: https://github.com/gradle/actions/setup-gradle@v3 + name: Setup Gradle + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + + - run: ./gradlew runDatagen + name: Generate assets + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + + - run: ./gradlew build + name: Build project + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + + - run: ./gradlew publish + name: Upload + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + PACKAGE_REPO_KEY: ${{ secrets.PACKAGE_REPO_KEY }} + + - run: ./gradlew modrinth + name: Publish to Modrinth + env: + GIT_SHA_SHORT: ${{ steps.vars.outputs.sha_short }} + MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} diff --git a/build.gradle.kts b/build.gradle.kts index 2ca88dd..efb5c4d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,10 +27,13 @@ plugins { `maven-publish` eclipse id("org.jetbrains.gradle.plugin.idea-ext") + id("com.modrinth.minotaur") version "2.+" } -val mavenVersion = System.getenv("CI_COMMIT_TAG") ?: System.getenv("CI_COMMIT_SHORT_SHA")?.let { "$it-dev" } -?: "0.3.0" //"0.0.0-SNAPSHOT" +val ver = if ((System.getenv("GITHUB_REF") ?: "local").startsWith("refs/tags/")) + System.getenv("GITHUB_REF_NAME") ?: "err" else System.getenv("GIT_SHA_SHORT") ?: "0.0.0" + +val mavenVersion = ver val modId: String by project val modName: String by project @@ -38,7 +41,7 @@ val modName: String by project * Gimbal version stuff * */ -val gimbalProtocolVersion = 2 +val gimbalProtocolVersion = 3 val mavenGroup: String by project @@ -184,17 +187,18 @@ publishing { } repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { + if (System.getenv("CI") != null) { maven { - name = "GitLab" - val projectId = System.getenv("CI_PROJECT_ID") - val apiV4 = System.getenv("CI_API_V4_URL") - url = uri("$apiV4/projects/$projectId/packages/maven") + name = "Codeberg" + val repoOwner = System.getenv("GITHUB_REPOSITORY_OWNER") + val serverUrl = System.getenv("GITHUB_SERVER_URL") + val accessToken = System.getenv("PACKAGE_REPO_KEY") + url = uri("$serverUrl/api/packages/$repoOwner/maven") authentication { - create("token", HttpHeaderAuthentication::class.java) { - credentials(HttpHeaderCredentials::class.java) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") + create("header", HttpHeaderAuthentication::class.java) { + credentials(HttpHeaderCredentials::class) { + name = "Authorization" + value = "token $accessToken" } } } @@ -203,6 +207,33 @@ publishing { } } +// build.gradle.kts +modrinth { + token.set(System.getenv("MODRINTH_TOKEN")) // Remember to have the MODRINTH_TOKEN environment variable set or else this will fail - just make sure it stays private! + projectId.set(modId) // This can be the project ID or the slug. Either will work! + versionNumber.set(mavenVersion) // You don't need to set this manually. Will fail if Modrinth has this version already + versionName.set("$modName $mavenVersion for $minecraftVersion") + versionType.set("alpha") // This is the default -- can also be `beta` or `alpha` + uploadFile.set(tasks.remapJar) // With Loom, this MUST be set to `remapJar` instead of `jar`! + gameVersions.addAll(listOf(project.ext["minecraft.version"] as String)) // Must be an array, even with only one version + loaders.add("fabric") // Must also be an array - no need to specify this if you're using Loom or ForgeGradle + changelog.set( + "Changelog v$mavenVersion\n\nCheckout the changelog [on codeberg](${System.getenv("GITHUB_SERVER_URL") ?: ""}/${ + System.getenv( + "GITHUB_REPOSITORY" + ) ?: "" + }/releases/tag/${System.getenv("GITHUB_REF_NAME") ?: ""})" + ) + dependencies { // A special DSL for creating dependencies + // scope.type + // The scope can be `required`, `optional`, `incompatible`, or `embedded` + // The type can either be `project` or `version` + required.project("fabric-api") // Creates a new required dependency on Fabric API + required.project("fabric-language-kotlin") + } +} + + rootProject.idea.project { this as ExtensionAware configure { diff --git a/src/main/java/net/moonleay/gimbal/client/ClientMain.kt b/src/main/java/net/moonleay/gimbal/client/ClientMain.kt index 0f6f156..d3f1823 100644 --- a/src/main/java/net/moonleay/gimbal/client/ClientMain.kt +++ b/src/main/java/net/moonleay/gimbal/client/ClientMain.kt @@ -21,7 +21,9 @@ package net.moonleay.gimbal.client import net.fabricmc.api.ClientModInitializer import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents +import net.fabricmc.loader.api.FabricLoader import net.moonleay.gimbal.build.BuildConstants +import net.moonleay.gimbal.client.config.ClientConfigHolder import net.moonleay.gimbal.client.editor.ClientEditor import net.moonleay.gimbal.client.keybindings.KeybindingManager import net.moonleay.gimbal.client.keybindings.KeybindingRegistrar @@ -30,6 +32,7 @@ import org.apache.logging.log4j.LogManager internal object ClientMain : ClientModInitializer { private val LOGGER = LogManager.getLogger(BuildConstants.modName) + lateinit var CONFIG: ClientConfigHolder override fun onInitializeClient() { @@ -39,6 +42,13 @@ internal object ClientMain : ClientModInitializer { LOGGER.info("Registering packets...") GimbalClient.registerPacketHandlers() LOGGER.info("Packets have been registered.") + LOGGER.info("Loading client config.") + val gimbalConfigPath = FabricLoader.getInstance().configDir.resolve(BuildConstants.modId + "/client.json") + CONFIG = ClientConfigHolder(gimbalConfigPath) + LOGGER.info("Config has been loaded.") + LOGGER.info("Applying Config to client.") + ClientEditor.applyConfig(CONFIG.config) + LOGGER.info("Config has been applied.") LOGGER.info("Gimbal has been initialized on the client side.") } diff --git a/src/main/java/net/moonleay/gimbal/client/config/ClientConfigHolder.kt b/src/main/java/net/moonleay/gimbal/client/config/ClientConfigHolder.kt new file mode 100644 index 0000000..4ad504d --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/ClientConfigHolder.kt @@ -0,0 +1,79 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import kotlinx.serialization.json.encodeToStream +import net.moonleay.gimbal.client.editor.ClientEditor +import java.nio.file.Path +import kotlin.io.path.* + +class ClientConfigHolder(private val path: Path) { + var config: GimbalClientConfig + private set + + init { + this.config = load() + } + + fun updateConfig(new: GimbalClientConfig) { + this.config = new + this.save(config) + ClientEditor.applyConfig(config) + } + + @OptIn(ExperimentalSerializationApi::class) + fun load(): GimbalClientConfig { + if (!path.isReadable()) + save(GimbalClientConfig()) + val iStream = path.inputStream() + val gimbalClientConfig: GimbalClientConfig = iStream.use { + Json { + ignoreUnknownKeys = true + }.decodeFromStream(it) + } + iStream.close() + return gimbalClientConfig + } + + @OptIn(ExperimentalSerializationApi::class) + fun save(conf: GimbalClientConfig) { + if (!path.parent.exists()) { + path.parent.createDirectory() + } + if (!path.parent.isWritable()) { + throw Exception("Parent of path ${path.parent.pathString} is not writable.") + } + if (!path.exists()) + path.createFile() + if (!path.isWritable()) { + throw Exception("Path (${path.pathString}) exists, but is not writable.") + } + val oStream = path.outputStream() + oStream.use { + Json { + ignoreUnknownKeys = true + }.encodeToStream(conf, it) + } + oStream.close() + } + +} diff --git a/src/main/java/net/moonleay/gimbal/client/config/GimbalClientConfig.kt b/src/main/java/net/moonleay/gimbal/client/config/GimbalClientConfig.kt new file mode 100644 index 0000000..5fd641c --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/GimbalClientConfig.kt @@ -0,0 +1,32 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config + +import kotlinx.serialization.Serializable +import net.moonleay.gimbal.client.config.enums.ToastSettings +import net.moonleay.gimbal.editor.state.mode.Mode + +@Serializable +data class GimbalClientConfig( + val guiSettings: GimbalGuiSettings = GimbalGuiSettings(), + val toastSettings: ToastSettings = ToastSettings.ALL, + val playerFlySpeed: Int = 100, + val shouldEscResetMode: Boolean = true, + val defaultMode: Mode = Mode.NORMAL +) diff --git a/src/main/java/net/moonleay/gimbal/client/config/GimbalGuiSettings.kt b/src/main/java/net/moonleay/gimbal/client/config/GimbalGuiSettings.kt new file mode 100644 index 0000000..06755a1 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/GimbalGuiSettings.kt @@ -0,0 +1,33 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config + +import kotlinx.serialization.Serializable +import net.moonleay.gimbal.client.config.enums.HorizontalAnchor +import net.moonleay.gimbal.client.config.enums.HudOptions +import net.moonleay.gimbal.client.config.enums.VerticalAnchor + +@Serializable +data class GimbalGuiSettings( + val showHud: Boolean = true, + val offset: ScaledRes = ScaledRes(4.0, 4.0), + val horizontalAnchor: HorizontalAnchor = HorizontalAnchor.LEFT, + val verticalAnchor: VerticalAnchor = VerticalAnchor.TOP, + val hudOptions: HudOptions = HudOptions.ALL, +) diff --git a/src/main/java/net/moonleay/gimbal/client/config/ScaledRes.kt b/src/main/java/net/moonleay/gimbal/client/config/ScaledRes.kt new file mode 100644 index 0000000..40d04aa --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/ScaledRes.kt @@ -0,0 +1,27 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config + +import kotlinx.serialization.Serializable + +@Serializable +data class ScaledRes( + val scaledX: Double, // offsetX + val scaledY: Double, // offsetY +) diff --git a/src/main/java/net/moonleay/gimbal/client/config/enums/Centerpoint.kt b/src/main/java/net/moonleay/gimbal/client/config/enums/Centerpoint.kt new file mode 100644 index 0000000..a9cecc4 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/enums/Centerpoint.kt @@ -0,0 +1,27 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config.enums + +enum class Centerpoint { + TOP_LEFT, + TOP_RIGHT, + BOTTOM_RIGHT, + BOTTOM_LEFT, + CENTER +} diff --git a/src/main/java/net/moonleay/gimbal/client/config/enums/HorizontalAnchor.kt b/src/main/java/net/moonleay/gimbal/client/config/enums/HorizontalAnchor.kt new file mode 100644 index 0000000..439a56f --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/enums/HorizontalAnchor.kt @@ -0,0 +1,28 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config.enums + +import kotlinx.serialization.Serializable + +@Serializable +enum class HorizontalAnchor { + LEFT, + CENTER, + RIGHT, +} diff --git a/src/main/java/net/moonleay/gimbal/client/config/enums/HudOptions.kt b/src/main/java/net/moonleay/gimbal/client/config/enums/HudOptions.kt new file mode 100644 index 0000000..e57f8c0 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/enums/HudOptions.kt @@ -0,0 +1,31 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config.enums + +import kotlinx.serialization.Serializable +import net.minecraft.text.Text +import net.moonleay.gimbal.constants.TranslationKeys + +@Serializable +enum class HudOptions(val translatableText: Text) { + ALL(Text.translatable(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_ALL)), + NUMBER(Text.translatable(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_NUMBER)), + INITIAL(Text.translatable(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_INITIALS)), + ONLY_MODE(Text.translatable(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_MODE_ONLY)), +} diff --git a/src/main/java/net/moonleay/gimbal/client/config/enums/ToastSettings.kt b/src/main/java/net/moonleay/gimbal/client/config/enums/ToastSettings.kt new file mode 100644 index 0000000..e581500 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/enums/ToastSettings.kt @@ -0,0 +1,32 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config.enums + +import kotlinx.serialization.Serializable +import net.minecraft.text.Text +import net.moonleay.gimbal.client.util.ToastType +import net.moonleay.gimbal.constants.TranslationKeys + +@Serializable +enum class ToastSettings(val translatableText: Text, val allowedTypes: List) { + ALL(Text.translatable(TranslationKeys.Gui.Config.Toasts.SHOW_ALL), listOf(ToastType.SYSTEM, ToastType.TOGGLE)), + ONLY_TOGGLE(Text.translatable(TranslationKeys.Gui.Config.Toasts.SHOW_TOGGLE), listOf(ToastType.TOGGLE)), + ONLY_SYSTEM(Text.translatable(TranslationKeys.Gui.Config.Toasts.SHOW_SYSTEM), listOf(ToastType.SYSTEM)), + NONE(Text.translatable(TranslationKeys.Gui.Config.Toasts.SHOW_NONE), listOf()) +} diff --git a/src/main/java/net/moonleay/gimbal/client/config/enums/VerticalAnchor.kt b/src/main/java/net/moonleay/gimbal/client/config/enums/VerticalAnchor.kt new file mode 100644 index 0000000..d3ae146 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/config/enums/VerticalAnchor.kt @@ -0,0 +1,28 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.config.enums + +import kotlinx.serialization.Serializable + +@Serializable +enum class VerticalAnchor { + TOP, + CENTER, + BOTTOM, +} diff --git a/src/main/java/net/moonleay/gimbal/client/editor/ClientEditor.kt b/src/main/java/net/moonleay/gimbal/client/editor/ClientEditor.kt index dde4c56..42d54c4 100644 --- a/src/main/java/net/moonleay/gimbal/client/editor/ClientEditor.kt +++ b/src/main/java/net/moonleay/gimbal/client/editor/ClientEditor.kt @@ -21,7 +21,11 @@ package net.moonleay.gimbal.client.editor import net.minecraft.client.MinecraftClient import net.minecraft.text.Text import net.moonleay.gimbal.build.BuildConstants +import net.moonleay.gimbal.client.ClientMain +import net.moonleay.gimbal.client.config.GimbalClientConfig +import net.moonleay.gimbal.client.config.enums.HudOptions import net.moonleay.gimbal.client.util.ChatUtil +import net.moonleay.gimbal.client.util.ToastType import net.moonleay.gimbal.editor.ServerEditorManager import net.moonleay.gimbal.editor.state.EditorState import net.moonleay.gimbal.editor.state.GimbalPolicyType @@ -36,17 +40,25 @@ import org.apache.logging.log4j.LogManager object ClientEditor { private var POLICY = GimbalPolicyType.NOT_PRESENT - private var CURRENT_MODE = Mode.NORMAL + private lateinit var CURRENT_MODE: Mode private var TEMP_DISABLED_MODE = Mode.UNKNOWN private val CURRENT_MODE_MODIFIER = mutableListOf() private val TEMP_DISABLED_MODIFIERS = mutableListOf() - private val DISABLED_MODIFIERS_STORAGE = mutableListOf() private val LOGGER = LogManager.getLogger(BuildConstants.modName) + fun applyConfig(config: GimbalClientConfig, isBoot: Boolean = true) { + if (isBoot) { + this.CURRENT_MODE = config.defaultMode + return + } + if (!config.shouldEscResetMode) return + this.CURRENT_MODE = config.defaultMode + onUpdated() + } fun onConnectedToNewWorld() { POLICY = GimbalPolicyType.NOT_PRESENT @@ -103,7 +115,7 @@ object ClientEditor { } fun isInNonDefaultMode(): Boolean { - return CURRENT_MODE != Mode.NORMAL + return CURRENT_MODE != ClientMain.CONFIG.config.defaultMode } /* @@ -121,7 +133,13 @@ object ClientEditor { * */ fun setMode(mode: Mode) { if (!isAllowed()) { - ChatUtil.showToastToSelf("Gimbal is disabled", "You cannot change modes", MinecraftClient.getInstance()) + ChatUtil.showToastToSelf( + "Gimbal is disabled", + "You cannot change modes", + ToastType.SYSTEM, + ClientMain.CONFIG.config, + MinecraftClient.getInstance() + ) return } if (!MinecraftClient.getInstance().player!!.isCreative) @@ -136,23 +154,44 @@ object ClientEditor { * */ fun toggleModifier(mod: ModeModifier) { if (!isAllowed()) { - ChatUtil.showToastToSelf("Gimbal is disabled", "You cannot change modifiers", MinecraftClient.getInstance()) + ChatUtil.showToastToSelf( + "Gimbal is disabled", + "You cannot change modifiers", + ToastType.SYSTEM, + ClientMain.CONFIG.config, + MinecraftClient.getInstance() + ) return } if (!MinecraftClient.getInstance().player!!.isCreative) return if (CURRENT_MODE.incompatibleModifiers.contains(mod)){ - if (TEMP_DISABLED_MODIFIERS.contains(mod)) + if (TEMP_DISABLED_MODIFIERS.contains(mod)) { TEMP_DISABLED_MODIFIERS.remove(mod) - else + } else { TEMP_DISABLED_MODIFIERS.add(mod) + } } else { - if (CURRENT_MODE_MODIFIER.contains(mod)) + if (CURRENT_MODE_MODIFIER.contains(mod)) { CURRENT_MODE_MODIFIER.remove(mod) - else + ChatUtil.showToastToSelf( + "Disabled ${mod.displayName}", + "[${this.getDisplayNameListAsString(CURRENT_MODE_MODIFIER)}]", + ToastType.TOGGLE, ClientMain.CONFIG.config, + MinecraftClient.getInstance() + ) + + } else { CURRENT_MODE_MODIFIER.add(mod) + ChatUtil.showToastToSelf( + "Enabled ${mod.displayName}", + "[${this.getDisplayNameListAsString(CURRENT_MODE_MODIFIER)}]", + ToastType.TOGGLE, ClientMain.CONFIG.config, + MinecraftClient.getInstance() + ) + } } onUpdated() @@ -199,14 +238,17 @@ object ClientEditor { if(TEMP_DISABLED_MODIFIERS.isNotEmpty()) { ChatUtil.showToastToSelf( "${CURRENT_MODE.displayName} Mode disabled", - getListAsString(TEMP_DISABLED_MODIFIERS), MinecraftClient.getInstance() + getDisplayNameListAsString(TEMP_DISABLED_MODIFIERS), + ToastType.SYSTEM, + ClientMain.CONFIG.config, + MinecraftClient.getInstance() ) } } - private fun getListAsString(list: List): String{ + private fun getDisplayNameListAsString(list: List): String { if(list.isEmpty()) - return "Empty list" + return "" val sb = StringBuilder() for (mod in list) { sb.append(mod.displayName) @@ -215,25 +257,53 @@ object ClientEditor { return sb.toString().dropLast(2) } + private fun getShortNameListAsString(list: List): String { + if (list.isEmpty()) + return "" + val sb = StringBuilder() + for (mod in list) { + sb.append(mod.shortName) + sb.append(", ") + } + return sb.toString().dropLast(2) + } + /* * Get the display text to display in the HUD * */ - fun getModeDisplayText(): Text { + fun getModeDisplayText(config: GimbalClientConfig): Text { val displayText = StringBuilder(CURRENT_MODE.displayName) - if (CURRENT_MODE_MODIFIER.isNotEmpty() && isAllowed() && MinecraftClient.getInstance().player?.isCreative == true) { - displayText.append(" [") - for (i in CURRENT_MODE_MODIFIER.indices) { - displayText.append(CURRENT_MODE_MODIFIER[i].displayName) - if (i != CURRENT_MODE_MODIFIER.size - 1) { - displayText.append(", ") + if (isAllowed() && MinecraftClient.getInstance().player?.isCreative == true) { + when (config.guiSettings.hudOptions) { + HudOptions.ALL -> { + if (CURRENT_MODE_MODIFIER.size > 0) { + displayText.append(" [") + displayText.append(this.getDisplayNameListAsString(CURRENT_MODE_MODIFIER)) + displayText.append("]") + } + return Text.of(displayText.toString()) } + + HudOptions.NUMBER -> { + displayText.append(" (") + displayText.append(CURRENT_MODE_MODIFIER.size) + displayText.append(")") + return Text.of(displayText.toString()) + } + + HudOptions.INITIAL -> { + if (CURRENT_MODE_MODIFIER.size > 0) { + displayText.append(" [") + displayText.append(this.getShortNameListAsString(CURRENT_MODE_MODIFIER)) + displayText.append("]") + } + return Text.of(displayText.toString()) + } + + HudOptions.ONLY_MODE -> return Text.of(displayText.toString()) } - displayText.append("]") - } else if (!isAllowed() || !MinecraftClient.getInstance().player?.isCreative!!) { - displayText.clear() - displayText.append("[GIMBAL DISABLED]") } - return Text.of(displayText.toString()) + return Text.of("[GIMBAL DISABLED]") } fun getCurrentColor(): Int { diff --git a/src/main/java/net/moonleay/gimbal/client/keybindings/KeybindingRegistrar.kt b/src/main/java/net/moonleay/gimbal/client/keybindings/KeybindingRegistrar.kt index 83a0e6d..fd37f7d 100644 --- a/src/main/java/net/moonleay/gimbal/client/keybindings/KeybindingRegistrar.kt +++ b/src/main/java/net/moonleay/gimbal/client/keybindings/KeybindingRegistrar.kt @@ -30,6 +30,7 @@ import net.moonleay.gimbal.client.keybindings.impl.editormodemodifier.ToggleNoUp import net.moonleay.gimbal.client.keybindings.impl.gamemode.CreativeModeShortcut import net.moonleay.gimbal.client.keybindings.impl.gamemode.SpectatorModeShortcut import net.moonleay.gimbal.client.keybindings.impl.gamemode.SurvivalModeShortcut +import net.moonleay.gimbal.constants.TranslationKeys import org.apache.logging.log4j.LogManager import org.lwjgl.glfw.GLFW @@ -47,28 +48,28 @@ object KeybindingRegistrar { private fun registerSetEditorModeModifierKeybindings() { val toggleBulldozerModifierShortcut = KeyBinding( - "gimbal.key.editor.modifier.bulldozer", + TranslationKeys.Keybindings.Binding.Editor.Modifier.BULLDOZER, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, - "gimbal.category.editormodifier" + TranslationKeys.Keybindings.Category.Editor.MODIFIER ) val toggleForcePlaceModifierShortcut = KeyBinding( - "gimbal.key.editor.modifier.forceplace", + TranslationKeys.Keybindings.Binding.Editor.Modifier.FORCE_PLACE, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, - "gimbal.category.editormodifier" + TranslationKeys.Keybindings.Category.Editor.MODIFIER ) val toggleNoUpdatesModifierShortcut = KeyBinding( - "gimbal.key.editor.modifier.noupdates", + TranslationKeys.Keybindings.Binding.Editor.Modifier.NO_UPDATES, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, - "gimbal.category.editormodifier" + TranslationKeys.Keybindings.Category.Editor.MODIFIER ) val toggleNoClipModifierShortcut = KeyBinding( - "gimbal.key.editor.modifier.noclip", + TranslationKeys.Keybindings.Binding.Editor.Modifier.NO_CLIP, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, - "gimbal.category.editormodifier" + TranslationKeys.Keybindings.Category.Editor.MODIFIER ) KeybindingManager.registerShortcut(ToggleBulldozerModifierShortcut(toggleBulldozerModifierShortcut)) @@ -79,16 +80,16 @@ object KeybindingRegistrar { private fun registerSetEditorModeKeybindings() { val insertKeyBinding = KeyBinding( - "gimbal.key.editor.mode.insert", + TranslationKeys.Keybindings.Binding.Editor.Mode.INSERT, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_I, - "gimbal.category.editormode" + TranslationKeys.Keybindings.Category.Editor.MODE ) val replaceKeyBinding = KeyBinding( - "gimbal.key.editor.mode.replace", + TranslationKeys.Keybindings.Binding.Editor.Mode.REPLACE, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_R, - "gimbal.category.editormode" + TranslationKeys.Keybindings.Category.Editor.MODE ) // val visualKeyBinding = KeyBinding( // "gimbal.key.editor.mode.visual", @@ -103,22 +104,22 @@ object KeybindingRegistrar { private fun registerSetGameModeKeybindings() { val survivalKeyBinding = KeyBinding( - "gimbal.key.game.mode.survival", + TranslationKeys.Keybindings.Binding.Game.Mode.SURVIVAL, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, - "gimbal.category.gamemode" + TranslationKeys.Keybindings.Category.Game.GAMEMODE ) val creativeKeyBinding = KeyBinding( - "gimbal.key.game.mode.creative", + TranslationKeys.Keybindings.Binding.Game.Mode.CREATIVE, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, - "gimbal.category.gamemode" + TranslationKeys.Keybindings.Category.Game.GAMEMODE ) val spectatorKeyBinding = KeyBinding( - "gimbal.key.game.mode.spectator", + TranslationKeys.Keybindings.Binding.Game.Mode.SPECTATOR, InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, - "gimbal.category.gamemode" + TranslationKeys.Keybindings.Category.Game.GAMEMODE ) KeybindingManager.registerShortcut(SurvivalModeShortcut(survivalKeyBinding)) KeybindingManager.registerShortcut(CreativeModeShortcut(creativeKeyBinding)) diff --git a/src/main/java/net/moonleay/gimbal/client/screen/GimbalEditHudGui.kt b/src/main/java/net/moonleay/gimbal/client/screen/GimbalEditHudGui.kt new file mode 100644 index 0000000..0155e35 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/screen/GimbalEditHudGui.kt @@ -0,0 +1,151 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.screen + +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.widget.ButtonWidget +import net.minecraft.client.util.math.MatrixStack +import net.minecraft.screen.ScreenTexts +import net.minecraft.text.Text +import net.moonleay.gimbal.build.BuildConstants +import net.moonleay.gimbal.client.config.ClientConfigHolder +import net.moonleay.gimbal.client.config.GimbalClientConfig +import net.moonleay.gimbal.client.config.GimbalGuiSettings +import net.moonleay.gimbal.client.config.ScaledRes +import net.moonleay.gimbal.client.util.screen.ScreenUtil +import net.moonleay.gimbal.constants.TranslationKeys +import org.apache.logging.log4j.LogManager + +class GimbalEditHudGui(private val parent: Screen, private val cfg: ClientConfigHolder) : + Screen(Text.translatable(TranslationKeys.Gui.Config.Hud.EDIT_HUD)) { + private val LOGGER = LogManager.getLogger(BuildConstants.modName) + + private var tempXOffset = 4.0 + private var tempYOffset = 4.0 + private var shouldFollow = false + + init { + this.tempXOffset = cfg.config.guiSettings.offset.scaledX + this.tempYOffset = cfg.config.guiSettings.offset.scaledY + } + + override fun init() { + super.init() + this.addDrawableChild(ButtonWidget( + this.width / 2 + 10, this.height - 27, 90, 20, Text.translatable(TranslationKeys.Gui.Config.Hud.RESET_HUD) + ) { _: ButtonWidget? -> + tempXOffset = 4.0 + tempYOffset = 4.0 + shouldFollow = false + }) + + this.addDrawableChild(ButtonWidget( + this.width / 2 - 100, this.height - 27, 90, 20, ScreenTexts.DONE + ) { _: ButtonWidget? -> + this.save() + this.client!!.setScreen( + this.parent + ) + }) + } + + private fun save() { + val oldConf = cfg.config + if (ScreenUtil.isPositionOutOfBounds(this.tempXOffset, this.tempYOffset)) { + LOGGER.info("Text is not in bounds. This will not be saved in order to keep the user from making the hud inaccessible.") + return + } + LOGGER.info("Saving Pos...") + + val anchor = ScreenUtil.getAnchor(this.tempXOffset, this.tempYOffset) + + val newConf = GimbalClientConfig( + guiSettings = GimbalGuiSettings( + showHud = oldConf.guiSettings.showHud, + offset = ScaledRes( + scaledX = this.tempXOffset, + scaledY = this.tempYOffset, + ), + horizontalAnchor = anchor.first, + verticalAnchor = anchor.second, + hudOptions = oldConf.guiSettings.hudOptions + ), + toastSettings = oldConf.toastSettings, + playerFlySpeed = oldConf.playerFlySpeed, + shouldEscResetMode = oldConf.shouldEscResetMode, + defaultMode = oldConf.defaultMode + ) + + cfg.updateConfig(newConf) + } + + override fun close() { + this.save() + this.client!!.setScreen(this.parent) + } + + override fun tick() { + super.tick() + if (this.shouldFollow) { + val mouse = this.client?.mouse!! + val wantedX = ScreenUtil.getScaled(this.client!!.window.width, mouse.x.toFloat()) + val wantedY = ScreenUtil.getScaled(this.client!!.window.height, mouse.y.toFloat()) + if (!ScreenUtil.isPositionOutOfBounds(wantedX, wantedY)) { + this.tempXOffset = wantedX + this.tempYOffset = wantedY + } + } + } + + override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + this.shouldFollow = true + return super.mouseClicked(mouseX, mouseY, button) + } + + override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { + this.shouldFollow = false + return super.mouseReleased(mouseX, mouseY, button) + } + + override fun render(matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float) { + this.renderBackground(matrices) + drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 15, 16777215) + val scaleFactor = this.client!!.window.calculateScaleFactor( + this.client!!.options.guiScale.value, + this.client!!.forcesUnicodeFont() + ) + val anchor = ScreenUtil.getAnchor(this.tempXOffset, this.tempYOffset) + this.textRenderer.drawWithShadow( + matrices, + Text.translatable(TranslationKeys.Gui.Config.Hud.EXAMPLE_HUD_TEXT), + ScreenUtil.getXForAnchor( + ScreenUtil.getReal(this.client!!.window.width, this.tempXOffset) / scaleFactor, + anchor.first, + this.textRenderer.getWidth(Text.translatable(TranslationKeys.Gui.Config.Hud.EXAMPLE_HUD_TEXT)) + ), + ScreenUtil.getYForAnchor( + ScreenUtil.getReal(this.client!!.window.height, this.tempYOffset) / scaleFactor, + anchor.second, + this.textRenderer.fontHeight + ), + 0xFFFFFF + ) + super.render(matrices, mouseX, mouseY, delta) + } +} diff --git a/src/main/java/net/moonleay/gimbal/client/screen/GimbalSettingsGui.kt b/src/main/java/net/moonleay/gimbal/client/screen/GimbalSettingsGui.kt new file mode 100644 index 0000000..4e11df4 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/screen/GimbalSettingsGui.kt @@ -0,0 +1,290 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.screen + +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.widget.ButtonWidget +import net.minecraft.client.gui.widget.CyclingButtonWidget +import net.minecraft.client.util.math.MatrixStack +import net.minecraft.screen.ScreenTexts +import net.minecraft.text.Text +import net.moonleay.gimbal.client.config.ClientConfigHolder +import net.moonleay.gimbal.client.config.GimbalClientConfig +import net.moonleay.gimbal.client.config.GimbalGuiSettings +import net.moonleay.gimbal.client.config.enums.HudOptions +import net.moonleay.gimbal.client.config.enums.ToastSettings +import net.moonleay.gimbal.client.screen.widgets.GimbalSliderWidget +import net.moonleay.gimbal.client.util.NumberUtil +import net.moonleay.gimbal.constants.TranslationKeys +import net.moonleay.gimbal.editor.state.mode.Mode + +class GimbalSettingsGui(private val parent: Screen, private val cfg: ClientConfigHolder) : + Screen(Text.translatable(TranslationKeys.Gui.Config.SCREEN_NAME)) { + + private var hudOptions: HudOptions + private var toastSettings: ToastSettings + private var playerFlySpeed: Int + private var defaultMode: Mode + + init { + this.hudOptions = cfg.config.guiSettings.hudOptions + this.toastSettings = cfg.config.toastSettings + this.playerFlySpeed = cfg.config.playerFlySpeed + this.defaultMode = cfg.config.defaultMode + } + + override fun init() { + this.addDrawableChild(CyclingButtonWidget.onOffBuilder( +// Text.translatable("gimbal.gui.enabled"), +// Text.translatable("gimbal.gui.disabled") + ) + .initially(cfg.config.guiSettings.showHud) + .build( + this.width / 2 - 155, + this.height / 6 + 24 * 0, + 150, + 20, + Text.translatable(TranslationKeys.Gui.Config.Hud.SHOW_HUD) + ) { _: CyclingButtonWidget?, isEnabled: Boolean? -> + val oldGui = cfg.config.guiSettings + val newGui = GimbalGuiSettings( + showHud = isEnabled ?: true, + offset = oldGui.offset, + horizontalAnchor = oldGui.horizontalAnchor, + verticalAnchor = oldGui.verticalAnchor, + hudOptions = oldGui.hudOptions + ) + val newConf = GimbalClientConfig( + guiSettings = newGui, + toastSettings = cfg.config.toastSettings, + playerFlySpeed = cfg.config.playerFlySpeed, + shouldEscResetMode = cfg.config.shouldEscResetMode, + defaultMode = cfg.config.defaultMode + ) + cfg.updateConfig(newConf) + }) + + this.addDrawableChild(ButtonWidget( + this.width / 2 - 155 + 160, + this.height / 6 + 24 * 0, + 150, + 20, + Text.translatable(TranslationKeys.Gui.Config.Hud.EDIT_HUD) + ) { _: ButtonWidget? -> + this.client!!.setScreen(GimbalEditHudGui(this, cfg)) + }) + + this.addDrawableChild>( + CyclingButtonWidget.builder { value: HudOptions? -> + when (value) { + HudOptions.ALL -> { + return@builder HudOptions.ALL.translatableText + } + + HudOptions.NUMBER -> { + return@builder HudOptions.NUMBER.translatableText + } + + HudOptions.INITIAL -> { + return@builder HudOptions.INITIAL.translatableText + } + + HudOptions.ONLY_MODE -> { + return@builder HudOptions.ONLY_MODE.translatableText + } + + else -> return@builder Text.translatable(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_ALL) + } + } + .values(HudOptions.entries) + .initially(this.hudOptions) + .build( + this.width / 2 - 155, + this.height / 6 + 24 * 1, + 150, + 20, + Text.translatable(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_MODIFIERS) + ) { _: CyclingButtonWidget?, hudDO: HudOptions -> + this.hudOptions = hudDO + + val oldConfig = cfg.config + val oldGuiConfig = oldConfig.guiSettings + val newConfig = GimbalClientConfig( + guiSettings = GimbalGuiSettings( + horizontalAnchor = oldGuiConfig.horizontalAnchor, + verticalAnchor = oldGuiConfig.verticalAnchor, + showHud = oldGuiConfig.showHud, + offset = oldGuiConfig.offset, + hudOptions = this.hudOptions + ), + toastSettings = oldConfig.toastSettings, + playerFlySpeed = oldConfig.playerFlySpeed, + shouldEscResetMode = oldConfig.shouldEscResetMode, + defaultMode = oldConfig.defaultMode + ) + cfg.updateConfig(newConfig) + } + ) + + + this.addDrawableChild>( + CyclingButtonWidget.builder { value: ToastSettings? -> + when (value) { + ToastSettings.ALL -> { + return@builder ToastSettings.ALL.translatableText + } + + ToastSettings.ONLY_TOGGLE -> { + return@builder ToastSettings.ONLY_TOGGLE.translatableText + } + + ToastSettings.ONLY_SYSTEM -> { + return@builder ToastSettings.ONLY_SYSTEM.translatableText + } + + ToastSettings.NONE -> { + return@builder ToastSettings.NONE.translatableText + } + + else -> return@builder Text.translatable(TranslationKeys.Gui.Config.Toasts.SHOW_ALL) + } + } + .values(ToastSettings.entries) + .initially(this.toastSettings) + .build( + this.width / 2 - 155 + 160, + this.height / 6 + 24 * 1, + 150, + 20, + Text.translatable(TranslationKeys.Gui.Config.Toasts.SHOW_TOASTS) + ) { _: CyclingButtonWidget?, toastStng: ToastSettings -> + this.toastSettings = toastStng + + val oldConfig = cfg.config + val newConfig = GimbalClientConfig( + guiSettings = oldConfig.guiSettings, + toastSettings = this.toastSettings, + playerFlySpeed = oldConfig.playerFlySpeed, + shouldEscResetMode = oldConfig.shouldEscResetMode, + defaultMode = oldConfig.defaultMode + ) + cfg.updateConfig(newConfig) + } + ) + + this.addDrawableChild(GimbalSliderWidget( + this.width / 2 - 155, + this.height / 6 + 24 * 2, + 150, + 20, + Text.translatable(TranslationKeys.Gui.Config.PLAYER_FLY_SPEED), + NumberUtil.linearInterpolate(this.playerFlySpeed.toDouble(), 100.0, 1000.0, 0.0, 1.0), + 100.0, + 1000.0, + ) { value -> + this.playerFlySpeed = NumberUtil.linearInterpolate(value, 0.0, 1.0, 100.0, 1000.0).toInt() + + if (client!!.player != null && client!!.player!!.isCreative) { + client!!.player!!.abilities.flySpeed = (this.playerFlySpeed / 100) * 0.05f + } + + val oldConfig = cfg.config + val newConfig = GimbalClientConfig( + guiSettings = oldConfig.guiSettings, + toastSettings = this.toastSettings, + playerFlySpeed = this.playerFlySpeed, + shouldEscResetMode = oldConfig.shouldEscResetMode, + defaultMode = oldConfig.defaultMode + ) + cfg.updateConfig(newConfig) + }) + + this.addDrawableChild(CyclingButtonWidget.onOffBuilder() + .initially(cfg.config.shouldEscResetMode) + .build( + this.width / 2 - 155, + this.height / 6 + 24 * 3, + 150, + 20, + Text.translatable(TranslationKeys.Gui.Config.SHOULD_ESC_RESET_MODE) + ) { _: CyclingButtonWidget?, isEnabled: Boolean? -> + val newConf = GimbalClientConfig( + guiSettings = cfg.config.guiSettings, + toastSettings = cfg.config.toastSettings, + playerFlySpeed = cfg.config.playerFlySpeed, + shouldEscResetMode = isEnabled?: true, + defaultMode = cfg.config.defaultMode + ) + cfg.updateConfig(newConf) + }) + + + + this.addDrawableChild>( + CyclingButtonWidget.builder { value: Mode? -> + if (value == null) { + return@builder Text.literal(Mode.UNKNOWN.displayName) + } + return@builder Text.literal(value.displayName) + } + .values(Mode.entries) + .initially(this.defaultMode) + .build( + this.width / 2 - 155 + 160, + this.height / 6 + 24 * 3, + 150, + 20, + Text.translatable(TranslationKeys.Gui.Config.DEFAULT_MODE) + ) { b: CyclingButtonWidget?, requestedMode: Mode -> + if (requestedMode.hidden){ + b!!.value = Mode.NORMAL + this.defaultMode = Mode.NORMAL + } + else { + this.defaultMode = requestedMode + } + + val oldConfig = cfg.config + val newConfig = GimbalClientConfig( + guiSettings = oldConfig.guiSettings, + toastSettings = oldConfig.toastSettings, + playerFlySpeed = oldConfig.playerFlySpeed, + shouldEscResetMode = oldConfig.shouldEscResetMode, + defaultMode = this.defaultMode + ) + cfg.updateConfig(newConfig) + } + ) + + // Done button + this.addDrawableChild(ButtonWidget( + this.width / 2 - 100, this.height / 6 + 168, 200, 20, ScreenTexts.DONE + ) { _: ButtonWidget? -> + this.client!!.setScreen( + this.parent + ) + }) + } + + override fun render(matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float) { + this.renderBackground(matrices) + drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 15, 16777215) + super.render(matrices, mouseX, mouseY, delta) + } +} diff --git a/src/main/java/net/moonleay/gimbal/client/screen/widgets/GimbalSliderWidget.kt b/src/main/java/net/moonleay/gimbal/client/screen/widgets/GimbalSliderWidget.kt new file mode 100644 index 0000000..c244615 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/screen/widgets/GimbalSliderWidget.kt @@ -0,0 +1,50 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.screen.widgets + +import net.minecraft.client.gui.widget.SliderWidget +import net.minecraft.text.Text +import net.moonleay.gimbal.client.util.NumberUtil + +class GimbalSliderWidget( + x: Int, + y: Int, + width: Int, + height: Int, + private val text: Text, + value: Double, + private val lowerEnd: Double, + private val upperEnd: Double, + private val setValue: (Double) -> Unit, +) : + SliderWidget(x, y, width, height, text, value) { + init { + this.message = this.text.copy() + .append(": " + (NumberUtil.linearInterpolate(this.value, 0.0, 1.0, this.lowerEnd, this.upperEnd).toInt())) + } + + override fun updateMessage() { + this.message = this.text.copy() + .append(": " + (NumberUtil.linearInterpolate(this.value, 0.0, 1.0, this.lowerEnd, this.upperEnd).toInt())) + } + + override fun applyValue() { + this.setValue(this.value) + } +} diff --git a/src/main/java/net/moonleay/gimbal/client/util/ChatUtil.kt b/src/main/java/net/moonleay/gimbal/client/util/ChatUtil.kt index 9fd2b18..b53d959 100644 --- a/src/main/java/net/moonleay/gimbal/client/util/ChatUtil.kt +++ b/src/main/java/net/moonleay/gimbal/client/util/ChatUtil.kt @@ -21,6 +21,7 @@ package net.moonleay.gimbal.client.util import net.minecraft.client.MinecraftClient import net.minecraft.client.toast.SystemToast import net.minecraft.text.Text +import net.moonleay.gimbal.client.config.GimbalClientConfig object ChatUtil { /** @@ -37,7 +38,16 @@ object ChatUtil { client.inGameHud.chatHud.addMessage(Text.of(message)) } - fun showToastToSelf(title: String, description: String, client: MinecraftClient) { + fun showToastToSelf( + title: String, + description: String, + type: ToastType, + config: GimbalClientConfig, + client: MinecraftClient, + ) { + if (!config.toastSettings.allowedTypes.contains(type)) + return + val toast = SystemToast(SystemToast.Type.PERIODIC_NOTIFICATION, Text.of(title), Text.of(description)) client.toastManager.add(toast) } diff --git a/src/main/java/net/moonleay/gimbal/client/util/NumberUtil.kt b/src/main/java/net/moonleay/gimbal/client/util/NumberUtil.kt new file mode 100644 index 0000000..e335842 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/util/NumberUtil.kt @@ -0,0 +1,44 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.util + +object NumberUtil { + + /** + * Interpolate a number between two numbers in a linear way + */ + fun linearInterpolate(number: Double, min: Double, max: Double, lowerEnd: Double, upperEnd: Double): Double { + val subtractFromNr = (0 - min) * -1 + val minMaxDiff = max - min + val onePercent = minMaxDiff / 100.0 + val percentOfNumber = (number - subtractFromNr) / onePercent + + val toAddToLowerEnd = (0 - lowerEnd) * -1 + val upperLowerDiff = upperEnd - lowerEnd + val onePercentOfEnd = upperLowerDiff / 100.0 + return (percentOfNumber * onePercentOfEnd) + toAddToLowerEnd + } + + /** + * Interpolate a number between two numbers in a logarithmic way + */ + fun logarithmicInterpolate(number: Double, min: Double, max: Double, lowerEnd: Double, upperEnd: Double): Double { + TODO("Not impl.") + } +} diff --git a/src/main/java/net/moonleay/gimbal/client/util/ToastType.kt b/src/main/java/net/moonleay/gimbal/client/util/ToastType.kt new file mode 100644 index 0000000..4dc9312 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/util/ToastType.kt @@ -0,0 +1,24 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.util + +enum class ToastType { + TOGGLE, + SYSTEM, +} diff --git a/src/main/java/net/moonleay/gimbal/client/util/screen/ScreenUtil.kt b/src/main/java/net/moonleay/gimbal/client/util/screen/ScreenUtil.kt new file mode 100644 index 0000000..5a5d4f7 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/client/util/screen/ScreenUtil.kt @@ -0,0 +1,71 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.client.util.screen + +import net.moonleay.gimbal.client.config.enums.HorizontalAnchor +import net.moonleay.gimbal.client.config.enums.VerticalAnchor + +object ScreenUtil { + + fun getAnchor(x: Double, y: Double): Pair { + return when (x) { + in 0.0..<34.0 -> HorizontalAnchor.LEFT + in 34.0..<67.0 -> HorizontalAnchor.CENTER + else -> HorizontalAnchor.RIGHT + } to when (y) { + in 0.0..<34.0 -> VerticalAnchor.TOP + in 34.0..<67.0 -> VerticalAnchor.CENTER + else -> VerticalAnchor.BOTTOM + } + } + + fun getXForAnchor(x: Float, anchor: HorizontalAnchor, textWidth: Int): Float { + return when (anchor) { + HorizontalAnchor.LEFT -> x + HorizontalAnchor.CENTER -> x - (textWidth / 2) + HorizontalAnchor.RIGHT -> x - textWidth + } + } + + + fun getYForAnchor(y: Float, anchor: VerticalAnchor, textHeight: Int): Float { + return when (anchor) { + VerticalAnchor.TOP -> y + VerticalAnchor.CENTER -> y - (textHeight / 2) + VerticalAnchor.BOTTOM -> y - textHeight + } + } + + fun getScaled(length: Int, real: Float): Double { + val scale = length / 100.0 + return real / scale + } + + fun getReal(length: Int, scaled: Double): Float { + val scale = length / 100.0 + return (scaled * scale).toFloat() + } + + fun isPositionOutOfBounds(x: Double, y: Double): Boolean { + val tooBig = 100 < x || 100 < y + val tooSmall = 0 > x || 0 > y + return tooBig || tooSmall + } + +} diff --git a/src/main/java/net/moonleay/gimbal/constants/TranslationKeys.kt b/src/main/java/net/moonleay/gimbal/constants/TranslationKeys.kt new file mode 100644 index 0000000..0db8509 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/constants/TranslationKeys.kt @@ -0,0 +1,127 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.constants + +import net.moonleay.gimbal.build.BuildConstants + +object TranslationKeys { + object Gui { + const val BASE = "gui.${BuildConstants.modId}." + + object Config { + const val BASE = "${TranslationKeys.Gui.BASE}conf." + + const val SCREEN_NAME = "${BASE}settings" + const val GENERIC_ENABLED = "${BASE}enabled" + const val GENERIC_DISABLED = "${BASE}disabled" + + const val PLAYER_FLY_SPEED = "${BASE}flySpeed" + const val SHOULD_ESC_RESET_MODE = "${BASE}shouldESCResetMode" + const val DEFAULT_MODE = "${BASE}defaultMode" + + object Hud { + const val BASE = "${TranslationKeys.Gui.Config.BASE}hud." + + const val SHOW_HUD = "${BASE}show" + const val EDIT_HUD = "${BASE}edit" + const val RESET_HUD = "${BASE}reset" + const val EXAMPLE_HUD_TEXT = "${BASE}example" + + object Modifiers { + const val BASE = "${TranslationKeys.Gui.Config.Hud.BASE}modifiers." + + const val SHOW_MODIFIERS = "${BASE}show" + const val SHOW_ALL = "${BASE}all" + const val SHOW_NUMBER = "${BASE}number" + const val SHOW_INITIALS = "${BASE}initials" + const val SHOW_MODE_ONLY = "${BASE}modeOnly" + } + } + + object Toasts { + const val BASE = "${TranslationKeys.Gui.Config.BASE}toasts." + + const val SHOW_TOASTS = "${BASE}show" + const val SHOW_ALL = "${BASE}all" + const val SHOW_TOGGLE = "${BASE}toggle" + const val SHOW_SYSTEM = "${BASE}system" + const val SHOW_NONE = "${BASE}none" + } + } + } + + object Keybindings { + // No base here, Categories and Bindings have separate BASEs + object Category { + const val BASE = "category.${BuildConstants.modId}." + + object Editor { + const val BASE = "${TranslationKeys.Keybindings.Category.BASE}editor." + + const val MODE = "${BASE}mode" + const val MODIFIER = "${BASE}modifier" + } + + object Game { + const val BASE = "${TranslationKeys.Keybindings.Category.BASE}game." + + const val GAMEMODE = "${BASE}mode" + } + } + + object Binding { + const val BASE = "key.${BuildConstants.modId}." + + object Editor { + const val BASE = "${TranslationKeys.Keybindings.Binding.BASE}editor." + + object Mode { + const val BASE = "${TranslationKeys.Keybindings.Binding.Editor.BASE}mode." + + const val INSERT = "${BASE}insert" + const val REPLACE = "${BASE}replace" + const val VISUAL = "${BASE}visual" + } + + object Modifier { + const val BASE = "${TranslationKeys.Keybindings.Binding.Editor.BASE}modifier." + + const val BULLDOZER = "${BASE}autoClicker" + const val FORCE_PLACE = "${BASE}forcePlace" + const val NO_UPDATES = "${BASE}noUpdates" + const val NO_CLIP = "${BASE}noClip" + } + } + + object Game { + const val BASE = "${TranslationKeys.Keybindings.Binding.BASE}game." + + object Mode { + const val BASE = "${TranslationKeys.Keybindings.Binding.Game.BASE}mode." + + const val SURVIVAL = "${BASE}survival" + const val CREATIVE = "${BASE}creative" + const val SPECTATOR = "${BASE}spectator" + } + } + } + } + + +} diff --git a/src/main/java/net/moonleay/gimbal/datagen/DataGenerator.kt b/src/main/java/net/moonleay/gimbal/datagen/DataGenerator.kt index b328d97..bd2452e 100644 --- a/src/main/java/net/moonleay/gimbal/datagen/DataGenerator.kt +++ b/src/main/java/net/moonleay/gimbal/datagen/DataGenerator.kt @@ -28,6 +28,6 @@ internal class DataGenerator : DataGeneratorEntrypoint { override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) { LOGGER.info("Starting Data Generation") - fabricDataGenerator.addProvider(En_us_GimbalLanguageProvider(fabricDataGenerator)) + fabricDataGenerator.addProvider(EnUsLanguageProvider(fabricDataGenerator)) } } diff --git a/src/main/java/net/moonleay/gimbal/datagen/EnUsLanguageProvider.kt b/src/main/java/net/moonleay/gimbal/datagen/EnUsLanguageProvider.kt new file mode 100644 index 0000000..00279a1 --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/datagen/EnUsLanguageProvider.kt @@ -0,0 +1,96 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.datagen + +import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator +import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider +import net.moonleay.gimbal.constants.TranslationKeys + +class EnUsLanguageProvider(dataGenerator: FabricDataGenerator?) : + FabricLanguageProvider(dataGenerator, "en_us") { + + override fun generateTranslations(translationBuilder: TranslationBuilder?) { + if (translationBuilder == null) return + /// Screens + // Gimbal settings screen + translationBuilder.add(TranslationKeys.Gui.Config.SCREEN_NAME, "Gimbal Settings") + + // Hud + translationBuilder.add(TranslationKeys.Gui.Config.Hud.SHOW_HUD, "Show HUD") + + + //Gimbal HUD pos edit + translationBuilder.add(TranslationKeys.Gui.Config.Hud.EDIT_HUD, "Edit HUD Layout...") + translationBuilder.add(TranslationKeys.Gui.Config.Hud.RESET_HUD, "Reset Layout") + translationBuilder.add(TranslationKeys.Gui.Config.Hud.EXAMPLE_HUD_TEXT, "EXAMPLE [No Clip, Force]") + + //Gimbal HUD Content + translationBuilder.add(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_MODIFIERS, "Show Modifiers") + translationBuilder.add(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_ALL, "All") + translationBuilder.add(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_NUMBER, "Number") + translationBuilder.add(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_INITIALS, "Short") + translationBuilder.add(TranslationKeys.Gui.Config.Hud.Modifiers.SHOW_MODE_ONLY, "None") + + //Gimbal Toasts + translationBuilder.add(TranslationKeys.Gui.Config.Toasts.SHOW_TOASTS, "Show Toasts") + translationBuilder.add(TranslationKeys.Gui.Config.Toasts.SHOW_ALL, "All") + translationBuilder.add(TranslationKeys.Gui.Config.Toasts.SHOW_TOGGLE, "Only Toggle") + translationBuilder.add(TranslationKeys.Gui.Config.Toasts.SHOW_SYSTEM, "Only System") + translationBuilder.add(TranslationKeys.Gui.Config.Toasts.SHOW_NONE, "None") + + // Gimbal Generic Settings + translationBuilder.add(TranslationKeys.Gui.Config.PLAYER_FLY_SPEED, "Fly Speed") + translationBuilder.add(TranslationKeys.Gui.Config.SHOULD_ESC_RESET_MODE, "ESC Resets Mode") + translationBuilder.add(TranslationKeys.Gui.Config.DEFAULT_MODE, "Default Mode") + + // Gimbal Generic Gui + translationBuilder.add(TranslationKeys.Gui.Config.GENERIC_ENABLED, "Enabled") + translationBuilder.add(TranslationKeys.Gui.Config.GENERIC_DISABLED, "Disabled") + + /// Options + // Editor modes + translationBuilder.add(TranslationKeys.Keybindings.Category.Editor.MODE, "Editor Modes") + translationBuilder.add(TranslationKeys.Keybindings.Binding.Editor.Mode.INSERT, "Enter Insert Mode") + translationBuilder.add(TranslationKeys.Keybindings.Binding.Editor.Mode.REPLACE, "Enter Replace Mode") + translationBuilder.add(TranslationKeys.Keybindings.Binding.Editor.Mode.VISUAL, "Enter Visual Mode") + + // Editor mode modifiers + translationBuilder.add(TranslationKeys.Keybindings.Category.Editor.MODIFIER, "Editor Mode Modifiers") + translationBuilder.add( + TranslationKeys.Keybindings.Binding.Editor.Modifier.BULLDOZER, + "Toggle Bulldozer Modifier" + ) + translationBuilder.add( + TranslationKeys.Keybindings.Binding.Editor.Modifier.FORCE_PLACE, + "Toggle Force Place Modifier" + ) + translationBuilder.add( + TranslationKeys.Keybindings.Binding.Editor.Modifier.NO_UPDATES, + "Toggle No Updates Modifier" + ) + translationBuilder.add(TranslationKeys.Keybindings.Binding.Editor.Modifier.NO_CLIP, "Toggle No Clip Modifier") + + // Game mode + translationBuilder.add(TranslationKeys.Keybindings.Category.Game.GAMEMODE, "Game Mode") + translationBuilder.add(TranslationKeys.Keybindings.Binding.Game.Mode.SURVIVAL, "Enable Survival Mode") + translationBuilder.add(TranslationKeys.Keybindings.Binding.Game.Mode.CREATIVE, "Enable Creative Mode") + translationBuilder.add(TranslationKeys.Keybindings.Binding.Game.Mode.SPECTATOR, "Enable Spectator Mode") + + } +} diff --git a/src/main/java/net/moonleay/gimbal/datagen/En_us_GimbalLanguageProvider.kt b/src/main/java/net/moonleay/gimbal/datagen/En_us_GimbalLanguageProvider.kt deleted file mode 100644 index 34d0fce..0000000 --- a/src/main/java/net/moonleay/gimbal/datagen/En_us_GimbalLanguageProvider.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Gimbal - * Copyright (C) 2024 moonleay - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.moonleay.gimbal.datagen - -import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator -import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider - -class En_us_GimbalLanguageProvider(dataGenerator: FabricDataGenerator?) : - FabricLanguageProvider(dataGenerator, "en_us") { - - override fun generateTranslations(translationBuilder: TranslationBuilder?) { - if (translationBuilder == null) return - // Editor modes - translationBuilder.add("gimbal.category.editormode", "Editor Modes") - translationBuilder.add("gimbal.key.editor.mode.insert", "Enter Insert Mode") - translationBuilder.add("gimbal.key.editor.mode.replace", "Enter Replace Mode") - translationBuilder.add("gimbal.key.editor.mode.visual", "Enter Visual Mode") - - - // Editor mode modifiers - translationBuilder.add("gimbal.category.editormodifier", "Editor Mode Modifiers") - translationBuilder.add("gimbal.key.editor.modifier.bulldozer", "Toggle Bulldozer Modifier") - translationBuilder.add("gimbal.key.editor.modifier.forceplace", "Toggle Force Place Modifier") - translationBuilder.add("gimbal.key.editor.modifier.noupdates", "Toggle No Updates Modifier") - translationBuilder.add("gimbal.key.editor.modifier.noclip", "Toggle No Clip Modifier") - - // Game mode - translationBuilder.add("gimbal.category.gamemode", "Game Mode") - translationBuilder.add("gimbal.key.game.mode.survival", "Enable Survival Mode") - translationBuilder.add("gimbal.key.game.mode.creative", "Enable Creative Mode") - translationBuilder.add("gimbal.key.game.mode.spectator", "Enable Spectator Mode") - - } -} diff --git a/src/main/java/net/moonleay/gimbal/editor/ServerEditorManager.kt b/src/main/java/net/moonleay/gimbal/editor/ServerEditorManager.kt index 5bb3f96..c0a1801 100644 --- a/src/main/java/net/moonleay/gimbal/editor/ServerEditorManager.kt +++ b/src/main/java/net/moonleay/gimbal/editor/ServerEditorManager.kt @@ -32,7 +32,7 @@ object ServerEditorManager { fun updateEditorState(playerUUID: UUID, editorState: EditorState) { STATEMAP[playerUUID] = editorState - LOGGER.info("$playerUUID: ${editorState.editorMode} with ${editorState.editorModifier}") + LOGGER.debug("{}: {} with {}", playerUUID, editorState.editorMode, editorState.editorModifier) } diff --git a/src/main/java/net/moonleay/gimbal/editor/state/mode/Mode.kt b/src/main/java/net/moonleay/gimbal/editor/state/mode/Mode.kt index 157cfa3..5481b22 100644 --- a/src/main/java/net/moonleay/gimbal/editor/state/mode/Mode.kt +++ b/src/main/java/net/moonleay/gimbal/editor/state/mode/Mode.kt @@ -18,10 +18,11 @@ package net.moonleay.gimbal.editor.state.mode -enum class Mode(val displayName: String, val color: Int, val incompatibleModifiers: List){ - UNKNOWN("UNKNOWN", 0x000000, listOf()), // Unknown mode. This mode cannot be entered - NORMAL("NORMAL", 0x90a959, listOf(ModeModifier.NO_UPDATES, ModeModifier.BULLDOZER, ModeModifier.FORCE_PLACE)), // Do nothing - INSERT("INSERT", 0xf4bf75, listOf()), // Place and break blocks - REPLACE("REPLACE", 0xac4242, listOf(ModeModifier.NO_UPDATES)), // Replace blocks - VISUAL("VISUAL", 0x6a9fb5, listOf(ModeModifier.BULLDOZER)), // Do fancy stuff with WE +enum class Mode(val displayName: String, val color: Int, val hidden: Boolean, val incompatibleModifiers: List){ + UNKNOWN("UNKNOWN", 0x000000, true, listOf()), // Unknown mode. This mode cannot be entered + NORMAL("NORMAL", 0x90a959, false, listOf(ModeModifier.NO_UPDATES, ModeModifier.BULLDOZER, ModeModifier.FORCE_PLACE)), // Do nothing + INSERT("INSERT", 0xf4bf75, false, listOf()), // Place and break blocks + REPLACE("REPLACE", 0xac4242, false, listOf(ModeModifier.NO_UPDATES)), // Replace blocks + // Add hidden modes after this comment + VISUAL("VISUAL", 0x6a9fb5, true, listOf(ModeModifier.BULLDOZER)), // Do fancy stuff with WE } diff --git a/src/main/java/net/moonleay/gimbal/editor/state/mode/ModeModifier.kt b/src/main/java/net/moonleay/gimbal/editor/state/mode/ModeModifier.kt index 221941f..da99041 100644 --- a/src/main/java/net/moonleay/gimbal/editor/state/mode/ModeModifier.kt +++ b/src/main/java/net/moonleay/gimbal/editor/state/mode/ModeModifier.kt @@ -18,10 +18,10 @@ package net.moonleay.gimbal.editor.state.mode -enum class ModeModifier(val displayName: String) { +enum class ModeModifier(val displayName: String, val shortName: String) { // NONE("None"), // No Modifiers - default behavior - NO_UPDATES("NO UPDATES"), // Do not update blocks when placing - BULLDOZER("BULLDOZER"), // Break blocks fast - FORCE_PLACE("FORCE"), // Ignore block placement restrictions - NO_CLIP("NO CLIP"), // Do not collide with blocks +NO_UPDATES("NO UPDATES", "NU"), // Do not update blocks when placing + BULLDOZER("BULLDOZER", "BLR"), // Break blocks fast + FORCE_PLACE("FORCE", "FP"), // Ignore block placement restrictions + NO_CLIP("NO CLIP", "NC"), // Do not collide with blocks } diff --git a/src/main/java/net/moonleay/gimbal/mixin/BulldozerMixin.java b/src/main/java/net/moonleay/gimbal/mixin/BulldozerMixin.java index a89604a..df682c2 100644 --- a/src/main/java/net/moonleay/gimbal/mixin/BulldozerMixin.java +++ b/src/main/java/net/moonleay/gimbal/mixin/BulldozerMixin.java @@ -20,9 +20,13 @@ package net.moonleay.gimbal.mixin; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.moonleay.gimbal.client.editor.ClientEditor; import net.moonleay.gimbal.editor.state.mode.Capability; import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -32,30 +36,49 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Objects; -@Mixin(MinecraftClient.class) public abstract class BulldozerMixin { - @Shadow protected int attackCooldown; + @Mixin(MinecraftClient.class) + public static abstract class MinecraftClientMixin { + @Shadow + @Nullable + public ClientPlayerEntity player; + @Shadow + protected int attackCooldown; - @Shadow - @Nullable - public ClientPlayerEntity player; - - @Inject(method = "doAttack", at = @At(value = "HEAD")) - private void func(CallbackInfoReturnable cir) { - if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.player).isCreative()) { - return; + @Inject(method = "doAttack", at = @At(value = "HEAD")) + private void func(CallbackInfoReturnable cir) { + if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.player).isCreative()) { + return; + } + this.attackCooldown = 0; + } + + @Inject(method = "handleBlockBreaking", at = @At(value = "HEAD")) + private void func2(boolean breaking, CallbackInfo ci) { + if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !breaking || !Objects.requireNonNull(this.player).isCreative()) { + return; + } + this.attackCooldown = 0; } - this.attackCooldown = 0; } - @Inject(method = "handleBlockBreaking", at = @At(value = "HEAD")) - private void func2(boolean breaking, CallbackInfo ci) { - if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !breaking || !Objects.requireNonNull(this.player).isCreative()) { - return; + @Mixin(ClientPlayerInteractionManager.class) + public static abstract class ClientPlayerInteractionManagerMixin { + + @Shadow + private int blockBreakingCooldown; + + @Shadow + @Final + private MinecraftClient client; + + @Inject(method = "updateBlockBreakingProgress", at = @At("HEAD")) + private void func(BlockPos pos, Direction direction, CallbackInfoReturnable cir) { + if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.client.player).isCreative()) { + return; + } + this.blockBreakingCooldown = 0; } - this.attackCooldown = 0; } - - } diff --git a/src/main/java/net/moonleay/gimbal/mixin/HudMixin.java b/src/main/java/net/moonleay/gimbal/mixin/HudMixin.java index fab79b6..5c0f58f 100644 --- a/src/main/java/net/moonleay/gimbal/mixin/HudMixin.java +++ b/src/main/java/net/moonleay/gimbal/mixin/HudMixin.java @@ -22,7 +22,11 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.hud.InGameHud; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import net.moonleay.gimbal.client.ClientMain; +import net.moonleay.gimbal.client.config.GimbalGuiSettings; import net.moonleay.gimbal.client.editor.ClientEditor; +import net.moonleay.gimbal.client.util.screen.ScreenUtil; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -31,16 +35,40 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(InGameHud.class) -public class HudMixin { +public abstract class HudMixin { @Shadow @Final private MinecraftClient client; @Inject(method = "renderStatusEffectOverlay", at = @At("HEAD")) private void render(MatrixStack matrices, CallbackInfo ci) { TextRenderer tr = this.client.textRenderer; - tr.drawWithShadow(matrices, - ClientEditor.INSTANCE.getModeDisplayText(), - 4, 4, - ClientEditor.INSTANCE.getCurrentColor()); + GimbalGuiSettings conf = ClientMain.CONFIG.getConfig().getGuiSettings(); + if (!conf.getShowHud()) + return; + int scaleFactor = this.client.getWindow() + .calculateScaleFactor( + this.client.options.getGuiScale().getValue(), + this.client.forcesUnicodeFont() + ); + Text displayText = ClientEditor.INSTANCE.getModeDisplayText(ClientMain.CONFIG.getConfig()); + tr.drawWithShadow( + matrices, + displayText, + ScreenUtil.INSTANCE.getXForAnchor( + ScreenUtil.INSTANCE.getReal( + this.client.getWindow().getWidth(), + conf.getOffset().getScaledX() / scaleFactor), + conf.getHorizontalAnchor(), + this.client.textRenderer.getWidth(displayText) + ), + ScreenUtil.INSTANCE.getYForAnchor( + ScreenUtil.INSTANCE.getReal( + this.client.getWindow().getHeight(), + conf.getOffset().getScaledY() / scaleFactor), + conf.getVerticalAnchor(), + this.client.textRenderer.fontHeight + ), + ClientEditor.INSTANCE.getCurrentColor() + ); } } diff --git a/src/main/java/net/moonleay/gimbal/mixin/NoClipCameraFixMixin.java b/src/main/java/net/moonleay/gimbal/mixin/NoClipCameraFixMixin.java deleted file mode 100644 index 55a8882..0000000 --- a/src/main/java/net/moonleay/gimbal/mixin/NoClipCameraFixMixin.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Gimbal - * Copyright (C) 2024 moonleay - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.moonleay.gimbal.mixin; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.Camera; -import net.moonleay.gimbal.client.editor.ClientEditor; -import net.moonleay.gimbal.editor.state.mode.Capability; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(Camera.class) -public class NoClipCameraFixMixin { - - @Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true) - private void fixCameraInNoClip(double desiredCameraDistance, CallbackInfoReturnable cir) { - if (!ClientEditor.INSTANCE.shouldClient(Capability.NO_CLIP) || (!(MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.isCreative()))) { - return; - } - cir.setReturnValue(desiredCameraDistance); - cir.cancel(); - } -} diff --git a/src/main/java/net/moonleay/gimbal/mixin/NoClipMixin.java b/src/main/java/net/moonleay/gimbal/mixin/NoClipMixin.java index fafab39..8e9a80a 100644 --- a/src/main/java/net/moonleay/gimbal/mixin/NoClipMixin.java +++ b/src/main/java/net/moonleay/gimbal/mixin/NoClipMixin.java @@ -19,12 +19,15 @@ package net.moonleay.gimbal.mixin; import com.mojang.authlib.GameProfile; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.Camera; import net.minecraft.entity.EntityPose; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerAbilities; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.World; +import net.moonleay.gimbal.client.editor.ClientEditor; import net.moonleay.gimbal.editor.ServerEditorManager; import net.moonleay.gimbal.editor.state.mode.Capability; import org.spongepowered.asm.mixin.Mixin; @@ -32,51 +35,72 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.UUID; -@Mixin(PlayerEntity.class) -public abstract class NoClipMixin extends LivingEntity { - @Shadow public abstract GameProfile getGameProfile(); +public abstract class NoClipMixin { - @Shadow public abstract PlayerAbilities getAbilities(); - - @Shadow - public abstract boolean isCreative(); - - protected NoClipMixin(EntityType entityType, World world) { - super(entityType, world); - } // Server side - - - @Inject(method = "tick", at = @At(value = "FIELD", - target = "Lnet/minecraft/entity/player/PlayerEntity;noClip:Z", shift = At.Shift.AFTER) - ) private void enoClip(CallbackInfo ci) { - if (!this.isCreative()) - return; - UUID uuid = this.getGameProfile().getId(); - - if (!ServerEditorManager.INSTANCE.shouldPlayer(uuid, Capability.NO_CLIP)) { - return; // NoClip is not enabled + @Mixin(PlayerEntity.class) // Serverside, allows clipping + public static abstract class PlayerEntityMixin extends LivingEntity { + protected PlayerEntityMixin(EntityType entityType, World world) { + super(entityType, world); } - if (!this.getAbilities().flying) { - return; + + @Shadow + public abstract GameProfile getGameProfile(); + + @Shadow + public abstract PlayerAbilities getAbilities(); + + @Shadow + public abstract boolean isCreative(); + + @Inject(method = "tick", at = @At(value = "FIELD", + target = "Lnet/minecraft/entity/player/PlayerEntity;noClip:Z", shift = At.Shift.AFTER) + ) + private void enoClip(CallbackInfo ci) { + if (!this.isCreative()) + return; + UUID uuid = this.getGameProfile().getId(); + + if (!ServerEditorManager.INSTANCE.shouldPlayer(uuid, Capability.NO_CLIP)) { + return; // NoClip is not enabled + } + if (!this.getAbilities().flying) { + return; + } + // Enable NoClip + this.noClip = true; + } + + @Inject(method = "updatePose", at = @At("HEAD")) + private void onUpdatePose(CallbackInfo ci) { + UUID uuid = this.getGameProfile().getId(); + + if (!ServerEditorManager.INSTANCE.shouldPlayer(uuid, Capability.NO_CLIP)) + return; // NoClip is not enabled + if (!this.getAbilities().flying) + return; + + // Force standing pose in NoClip mode + + this.setPose(EntityPose.STANDING); } - // Enable NoClip - this.noClip = true; } - @Inject(method = "updatePose", at = @At("HEAD")) - private void onUpdatePose(CallbackInfo ci) { - UUID uuid = this.getGameProfile().getId(); - if (!ServerEditorManager.INSTANCE.shouldPlayer(uuid, Capability.NO_CLIP)) - return; // NoClip is not enabled - if (!this.getAbilities().flying) - return; + @Mixin(Camera.class) // Clientside, fixes camera + public static abstract class CameraMixin { - // Force standing pose in NoClip mode - - this.setPose(EntityPose.STANDING); + @Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true) + private void fixCameraInNoClip(double desiredCameraDistance, CallbackInfoReturnable cir) { + if (!ClientEditor.INSTANCE.shouldClient(Capability.NO_CLIP) || (!(MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.isCreative()))) { + return; + } + cir.setReturnValue(desiredCameraDistance); + cir.cancel(); + } } + } diff --git a/src/main/java/net/moonleay/gimbal/mixin/NormalModeMixin.java b/src/main/java/net/moonleay/gimbal/mixin/NormalModeMixin.java index 7dc6232..d8f769a 100644 --- a/src/main/java/net/moonleay/gimbal/mixin/NormalModeMixin.java +++ b/src/main/java/net/moonleay/gimbal/mixin/NormalModeMixin.java @@ -21,9 +21,9 @@ package net.moonleay.gimbal.mixin; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.option.GameOptions; +import net.moonleay.gimbal.client.ClientMain; import net.moonleay.gimbal.client.editor.ClientEditor; import net.moonleay.gimbal.editor.state.mode.Capability; -import net.moonleay.gimbal.editor.state.mode.Mode; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -43,12 +43,12 @@ public class NormalModeMixin { public ClientPlayerEntity player; @Inject(method = "openPauseMenu", at = @At("HEAD"), cancellable = true) - private void setNormalMode(boolean pause, CallbackInfo ci) { - if (ClientEditor.INSTANCE.isInNonDefaultMode() && ClientEditor.INSTANCE.isAllowed()) { + private void setDefaultMode(boolean pause, CallbackInfo ci) { + if (ClientMain.CONFIG.getConfig().getShouldEscResetMode() && ClientEditor.INSTANCE.isInNonDefaultMode() && ClientEditor.INSTANCE.isAllowed()) { assert this.player != null; if (this.player.isCreative()) { // Set the editor mode to normal - ClientEditor.INSTANCE.setMode(Mode.NORMAL); + ClientEditor.INSTANCE.setMode(ClientMain.CONFIG.getConfig().getDefaultMode()); ci.cancel(); } } diff --git a/src/main/java/net/moonleay/gimbal/mixin/BulldozerMixin2.java b/src/main/java/net/moonleay/gimbal/mixin/PlayerFlySpeedMixin.java similarity index 58% rename from src/main/java/net/moonleay/gimbal/mixin/BulldozerMixin2.java rename to src/main/java/net/moonleay/gimbal/mixin/PlayerFlySpeedMixin.java index 19f1378..e846c91 100644 --- a/src/main/java/net/moonleay/gimbal/mixin/BulldozerMixin2.java +++ b/src/main/java/net/moonleay/gimbal/mixin/PlayerFlySpeedMixin.java @@ -19,34 +19,27 @@ package net.moonleay.gimbal.mixin; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerInteractionManager; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.moonleay.gimbal.client.editor.ClientEditor; -import net.moonleay.gimbal.editor.state.mode.Capability; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.network.packet.s2c.play.PlayerAbilitiesS2CPacket; +import net.moonleay.gimbal.client.ClientMain; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.Objects; - -@Mixin(ClientPlayerInteractionManager.class) -public class BulldozerMixin2 { - - @Shadow private int blockBreakingCooldown; +@Mixin(ClientPlayNetworkHandler.class) +public abstract class PlayerFlySpeedMixin { @Shadow @Final private MinecraftClient client; - @Inject(method = "updateBlockBreakingProgress", at = @At("HEAD")) - private void func(BlockPos pos, Direction direction, CallbackInfoReturnable cir) { - if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.client.player).isCreative()) { - return; - } - this.blockBreakingCooldown = 0; + @Inject(method = "onPlayerAbilities", at = @At(value = "RETURN")) + private void func(PlayerAbilitiesS2CPacket packet, CallbackInfo ci) { + this.client.player.getAbilities().setFlySpeed((ClientMain.CONFIG.getConfig().getPlayerFlySpeed() / 100) * 0.05f); + //client!!.player!!.abilities.flySpeed = (this.playerFlySpeed / 100) * 0.05f } + } diff --git a/src/main/java/net/moonleay/gimbal/mixin/ReplaceModeMixin.java b/src/main/java/net/moonleay/gimbal/mixin/ReplaceModeMixin.java index 8e2a4fa..8a28216 100644 --- a/src/main/java/net/moonleay/gimbal/mixin/ReplaceModeMixin.java +++ b/src/main/java/net/moonleay/gimbal/mixin/ReplaceModeMixin.java @@ -18,50 +18,87 @@ package net.moonleay.gimbal.mixin; +import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemPlacementContext; -import net.minecraft.item.ItemStack; -import net.minecraft.item.ItemUsageContext; +import net.minecraft.item.*; +import net.minecraft.state.property.Property; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.moonleay.gimbal.editor.ServerEditorManager; import net.moonleay.gimbal.editor.state.mode.Capability; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.UUID; -@Mixin(ItemPlacementContext.class) -public abstract class ReplaceModeMixin extends ItemUsageContext { +public abstract class ReplaceModeMixin { - @Shadow - protected boolean canReplaceExisting; + @Mixin(ItemPlacementContext.class) + public static abstract class ItemPlacementContextMixin extends ItemUsageContext { + @Shadow + protected boolean canReplaceExisting; - @Mutable - @Shadow - @Final - private BlockPos placementPos; + @Mutable + @Shadow + @Final + private BlockPos placementPos; - public ReplaceModeMixin(PlayerEntity player, Hand hand, BlockHitResult hit) { - super(player, hand, hit); + public ItemPlacementContextMixin(PlayerEntity player, Hand hand, BlockHitResult hit) { + super(player, hand, hit); + } + + @Inject(method = "(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/item/ItemStack;Lnet/minecraft/util/hit/BlockHitResult;)V", at = @At(value = "RETURN")) + private void func(World world, PlayerEntity playerEntity, Hand hand, ItemStack itemStack, BlockHitResult blockHitResult, CallbackInfo ci) { + if (playerEntity == null) + return; + UUID id = playerEntity.getGameProfile().getId(); + if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.REPLACE)) + return; + + this.canReplaceExisting = true; + this.placementPos = blockHitResult.getBlockPos(); + } } - @Inject(method = "(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/item/ItemStack;Lnet/minecraft/util/hit/BlockHitResult;)V", at = @At(value = "RETURN")) - private void func(World world, PlayerEntity playerEntity, Hand hand, ItemStack itemStack, BlockHitResult blockHitResult, CallbackInfo ci) { - if (playerEntity == null) - return; - UUID id = playerEntity.getGameProfile().getId(); - if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.REPLACE)) - return; + @Mixin(BlockItem.class) + public static abstract class BlockItemMixin extends Item { - this.canReplaceExisting = true; - this.placementPos = blockHitResult.getBlockPos(); + public BlockItemMixin(Settings settings) { + super(settings); + } + + @Shadow + protected abstract boolean place(ItemPlacementContext context, BlockState state); + + @Redirect(method = "place(Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/util/ActionResult;", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;place(Lnet/minecraft/item/ItemPlacementContext;Lnet/minecraft/block/BlockState;)Z")) + private boolean func(BlockItem instance, ItemPlacementContext context, BlockState state) { + if (context.getPlayer() == null) + return this.place(context, state); + UUID id = context.getPlayer().getGameProfile().getId(); + if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.REPLACE)) + return this.place(context, state); + BlockState oldState = context.getWorld().getBlockState(context.getBlockPos()); + return this.place(context, getTargetBlockState(oldState, instance.getBlock().getDefaultState())); + } + + @Unique + public BlockState getTargetBlockState(BlockState oldState, BlockState newBlock) { + var oldManager = oldState.getBlock().getStateManager(); + var newManager = newBlock.getBlock().getStateManager(); + for (var prop : oldManager.getProperties()) { + var matchingProp = newManager.getProperty(prop.getName()); + if (matchingProp != null) { + //noinspection rawtypes,unchecked + newBlock = newBlock.with((Property) matchingProp, oldState.get(matchingProp)); + } + } + return newBlock; + } } + } diff --git a/src/main/java/net/moonleay/gimbal/mixin/ReplaceStateUpdaterMixin.java b/src/main/java/net/moonleay/gimbal/mixin/ReplaceStateUpdaterMixin.java deleted file mode 100644 index 7d0772d..0000000 --- a/src/main/java/net/moonleay/gimbal/mixin/ReplaceStateUpdaterMixin.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Gimbal - * Copyright (C) 2024 moonleay - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.moonleay.gimbal.mixin; - -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.StairsBlock; -import net.minecraft.item.BlockItem; -import net.minecraft.item.Item; -import net.minecraft.item.ItemPlacementContext; -import net.moonleay.gimbal.editor.ServerEditorManager; -import net.moonleay.gimbal.editor.state.mode.Capability; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.UUID; - -import static net.minecraft.block.StairsBlock.*; - -@Mixin(BlockItem.class) -public abstract class ReplaceStateUpdaterMixin extends Item { - - public ReplaceStateUpdaterMixin(Settings settings) { - super(settings); - } - - @Shadow - protected abstract boolean place(ItemPlacementContext context, BlockState state); - - @Redirect(method = "place(Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/util/ActionResult;", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;place(Lnet/minecraft/item/ItemPlacementContext;Lnet/minecraft/block/BlockState;)Z")) - private boolean func(BlockItem instance, ItemPlacementContext context, BlockState state) { - if (context.getPlayer() == null) - return this.place(context, state); - UUID id = context.getPlayer().getGameProfile().getId(); - if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.REPLACE)) - return this.place(context, state); - BlockState oldState = context.getWorld().getBlockState(context.getBlockPos()); - Block targetBl = oldState.getBlock(); - if (state.getBlock() instanceof StairsBlock && targetBl instanceof StairsBlock targetStair) { - // Block and item is stairs, parse Stairs data - return this.place(context, copyStairState(oldState, instance)); - } - return this.place(context, state); - } - - - @Unique - public BlockState copyStairState(BlockState targetState, BlockItem item) { - BlockState blockState = item.getBlock().getDefaultState() - .with(FACING, targetState.get(FACING)) - .with(HALF, targetState.get(HALF)) - .with(WATERLOGGED, targetState.get(WATERLOGGED)); - return blockState.with(SHAPE, targetState.get(SHAPE)); - } -} diff --git a/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin.java b/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin.java new file mode 100644 index 0000000..efa2a9c --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin.java @@ -0,0 +1,104 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.mixin; + +import net.minecraft.client.gui.screen.GameMenuScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.TitleScreen; +import net.minecraft.client.gui.widget.TexturedButtonWidget; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.moonleay.gimbal.build.BuildConstants; +import net.moonleay.gimbal.client.ClientMain; +import net.moonleay.gimbal.client.screen.GimbalSettingsGui; +import net.moonleay.gimbal.constants.TranslationKeys; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +// What is my purpose? +public class YouInjectButtonMixin { + // Go my god. + + @Mixin(TitleScreen.class) + public static abstract class TitleScreenMixin extends Screen { + + @Unique + private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png"); + + protected TitleScreenMixin(Text title) { + super(title); + } + + @Inject(method = "init", at = @At(value = "RETURN")) + private void func(CallbackInfo ci) { + int l = this.height / 4 + 48; + this.addDrawableChild( + new TexturedButtonWidget( + this.width / 2 - 124 - 24, + l + 72 + 12, + 20, + 20, + 0, + 0, + 20, + GIMBAL_TEXTURE, + 20, + 40, + button -> this.client.setScreen(new GimbalSettingsGui(this, ClientMain.CONFIG)), + Text.translatable(TranslationKeys.Gui.Config.SCREEN_NAME) + ) + ); + } + } + + + @Mixin(GameMenuScreen.class) + public static abstract class GameMenuScreenMixin extends Screen { + @Unique + private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png"); + + protected GameMenuScreenMixin(Text title) { + super(title); + } + + @Inject(method = "initWidgets", at = @At(value = "RETURN")) + private void func(CallbackInfo ci) { + this.addDrawableChild( + new TexturedButtonWidget( + this.width / 2 - 124, + this.height / 4 + 96 + -16, + 20, + 20, + 0, + 0, + 20, + GIMBAL_TEXTURE, + 20, + 40, + button -> this.client.setScreen(new GimbalSettingsGui(this, ClientMain.CONFIG)), + Text.translatable(TranslationKeys.Gui.Config.SCREEN_NAME) + ) + ); + } + } + +} diff --git a/src/main/resources/assets/gimbal/textures/gimbal_options_texture_button.png b/src/main/resources/assets/gimbal/textures/gimbal_options_texture_button.png new file mode 100644 index 0000000..e11bf08 Binary files /dev/null and b/src/main/resources/assets/gimbal/textures/gimbal_options_texture_button.png differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 2a47121..0f9c789 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -7,6 +7,9 @@ "authors": [ "moonleay" ], + "contributors": [ + "Cookieso" + ], "icon": "assets/${modId}/logo.png", "contact": { "email": "contact@moonleay.net", diff --git a/src/main/resources/gimbal.mixins.json b/src/main/resources/gimbal.mixins.json index adf557d..9de8288 100644 --- a/src/main/resources/gimbal.mixins.json +++ b/src/main/resources/gimbal.mixins.json @@ -6,16 +6,19 @@ "mixins": [ "ForcePlaceMixin", "NoBlockUpdatesMixin", - "NoClipMixin", - "ReplaceModeMixin", - "ReplaceStateUpdaterMixin" + "NoClipMixin$PlayerEntityMixin", + "ReplaceModeMixin$BlockItemMixin", + "ReplaceModeMixin$ItemPlacementContextMixin" ], "client": [ - "BulldozerMixin", - "BulldozerMixin2", + "BulldozerMixin$ClientPlayerInteractionManagerMixin", + "BulldozerMixin$MinecraftClientMixin", "HudMixin", - "NoClipCameraFixMixin", - "NormalModeMixin" + "NoClipMixin$CameraMixin", + "NormalModeMixin", + "PlayerFlySpeedMixin", + "YouInjectButtonMixin$GameMenuScreenMixin", + "YouInjectButtonMixin$TitleScreenMixin" ], "injectors": { "defaultRequire": 1