Compare commits

...

8 commits

Author SHA1 Message Date
dc732cc39f
feat: add ESC resets mode setting 2024-06-13 18:52:04 +02:00
d59cf8f9f0
feat: add default mode setting 2024-06-13 18:39:27 +02:00
5252d9abdf
feat: changed server side state log to debug 2024-06-13 18:20:18 +02:00
42737446b2
feat: added player fly speed controls
Signed-off-by: moonleay <contact@moonleay.net>
2024-05-17 01:10:54 +02:00
6a89e5683f
refactor: combined NoClip and ReplaceMode mixins into one mixin each
Signed-off-by: moonleay <contact@moonleay.net>
2024-05-16 16:35:44 +02:00
4c8404d306
refactor: combined Bulldozer and YouInjectButton mixin into one mixin each
Signed-off-by: moonleay <contact@moonleay.net>
2024-05-16 03:52:34 +02:00
b4f46ee703
feat: added option and config for player fly speed
Signed-off-by: moonleay <contact@moonleay.net>
2024-05-16 03:31:41 +02:00
19f4b3649a
feat: created custom slider widget
Signed-off-by: moonleay <contact@moonleay.net>
2024-05-16 03:29:57 +02:00
24 changed files with 490 additions and 331 deletions

View file

@ -46,6 +46,9 @@ internal object ClientMain : ClientModInitializer {
val gimbalConfigPath = FabricLoader.getInstance().configDir.resolve(BuildConstants.modId + "/client.json") val gimbalConfigPath = FabricLoader.getInstance().configDir.resolve(BuildConstants.modId + "/client.json")
CONFIG = ClientConfigHolder(gimbalConfigPath) CONFIG = ClientConfigHolder(gimbalConfigPath)
LOGGER.info("Config has been loaded.") 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.") LOGGER.info("Gimbal has been initialized on the client side.")
} }

View file

@ -22,6 +22,7 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.json.encodeToStream
import net.moonleay.gimbal.client.editor.ClientEditor
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.* import kotlin.io.path.*
@ -36,6 +37,7 @@ class ClientConfigHolder(private val path: Path) {
fun updateConfig(new: GimbalClientConfig) { fun updateConfig(new: GimbalClientConfig) {
this.config = new this.config = new
this.save(config) this.save(config)
ClientEditor.applyConfig(config)
} }
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)

View file

@ -20,9 +20,13 @@ package net.moonleay.gimbal.client.config
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.moonleay.gimbal.client.config.enums.ToastSettings import net.moonleay.gimbal.client.config.enums.ToastSettings
import net.moonleay.gimbal.editor.state.mode.Mode
@Serializable @Serializable
data class GimbalClientConfig( data class GimbalClientConfig(
val guiSettings: GimbalGuiSettings = GimbalGuiSettings(), val guiSettings: GimbalGuiSettings = GimbalGuiSettings(),
val toastSettings: ToastSettings = ToastSettings.ALL, val toastSettings: ToastSettings = ToastSettings.ALL,
val playerFlySpeed: Int = 100,
val shouldEscResetMode: Boolean = true,
val defaultMode: Mode = Mode.NORMAL
) )

View file

@ -22,6 +22,6 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class ScaledRes( data class ScaledRes(
val scaledX: Double, val scaledX: Double, // offsetX
val scaledY: Double, val scaledY: Double, // offsetY
) )

View file

@ -40,17 +40,25 @@ import org.apache.logging.log4j.LogManager
object ClientEditor { object ClientEditor {
private var POLICY = GimbalPolicyType.NOT_PRESENT 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 var TEMP_DISABLED_MODE = Mode.UNKNOWN
private val CURRENT_MODE_MODIFIER = mutableListOf<ModeModifier>() private val CURRENT_MODE_MODIFIER = mutableListOf<ModeModifier>()
private val TEMP_DISABLED_MODIFIERS = mutableListOf<ModeModifier>() private val TEMP_DISABLED_MODIFIERS = mutableListOf<ModeModifier>()
private val DISABLED_MODIFIERS_STORAGE = mutableListOf<ModeModifier>() private val DISABLED_MODIFIERS_STORAGE = mutableListOf<ModeModifier>()
private val LOGGER = LogManager.getLogger(BuildConstants.modName) 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() { fun onConnectedToNewWorld() {
POLICY = GimbalPolicyType.NOT_PRESENT POLICY = GimbalPolicyType.NOT_PRESENT
@ -107,7 +115,7 @@ object ClientEditor {
} }
fun isInNonDefaultMode(): Boolean { fun isInNonDefaultMode(): Boolean {
return CURRENT_MODE != Mode.NORMAL return CURRENT_MODE != ClientMain.CONFIG.config.defaultMode
} }
/* /*

View file

@ -86,7 +86,10 @@ class GimbalEditHudGui(private val parent: Screen, private val cfg: ClientConfig
verticalAnchor = anchor.second, verticalAnchor = anchor.second,
hudOptions = oldConf.guiSettings.hudOptions hudOptions = oldConf.guiSettings.hudOptions
), ),
toastSettings = oldConf.toastSettings toastSettings = oldConf.toastSettings,
playerFlySpeed = oldConf.playerFlySpeed,
shouldEscResetMode = oldConf.shouldEscResetMode,
defaultMode = oldConf.defaultMode
) )
cfg.updateConfig(newConf) cfg.updateConfig(newConf)

View file

@ -29,17 +29,24 @@ import net.moonleay.gimbal.client.config.GimbalClientConfig
import net.moonleay.gimbal.client.config.GimbalGuiSettings import net.moonleay.gimbal.client.config.GimbalGuiSettings
import net.moonleay.gimbal.client.config.enums.HudOptions import net.moonleay.gimbal.client.config.enums.HudOptions
import net.moonleay.gimbal.client.config.enums.ToastSettings 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.constants.TranslationKeys
import net.moonleay.gimbal.editor.state.mode.Mode
class GimbalSettingsGui(private val parent: Screen, private val cfg: ClientConfigHolder) : class GimbalSettingsGui(private val parent: Screen, private val cfg: ClientConfigHolder) :
Screen(Text.translatable(TranslationKeys.Gui.Config.SCREEN_NAME)) { Screen(Text.translatable(TranslationKeys.Gui.Config.SCREEN_NAME)) {
private var hudOptions: HudOptions private var hudOptions: HudOptions
private var toastSettings: ToastSettings private var toastSettings: ToastSettings
private var playerFlySpeed: Int
private var defaultMode: Mode
init { init {
this.hudOptions = cfg.config.guiSettings.hudOptions this.hudOptions = cfg.config.guiSettings.hudOptions
this.toastSettings = cfg.config.toastSettings this.toastSettings = cfg.config.toastSettings
this.playerFlySpeed = cfg.config.playerFlySpeed
this.defaultMode = cfg.config.defaultMode
} }
override fun init() { override fun init() {
@ -65,7 +72,10 @@ class GimbalSettingsGui(private val parent: Screen, private val cfg: ClientConfi
) )
val newConf = GimbalClientConfig( val newConf = GimbalClientConfig(
guiSettings = newGui, guiSettings = newGui,
toastSettings = cfg.config.toastSettings toastSettings = cfg.config.toastSettings,
playerFlySpeed = cfg.config.playerFlySpeed,
shouldEscResetMode = cfg.config.shouldEscResetMode,
defaultMode = cfg.config.defaultMode
) )
cfg.updateConfig(newConf) cfg.updateConfig(newConf)
}) })
@ -123,7 +133,10 @@ class GimbalSettingsGui(private val parent: Screen, private val cfg: ClientConfi
offset = oldGuiConfig.offset, offset = oldGuiConfig.offset,
hudOptions = this.hudOptions hudOptions = this.hudOptions
), ),
toastSettings = oldConfig.toastSettings toastSettings = oldConfig.toastSettings,
playerFlySpeed = oldConfig.playerFlySpeed,
shouldEscResetMode = oldConfig.shouldEscResetMode,
defaultMode = oldConfig.defaultMode
) )
cfg.updateConfig(newConfig) cfg.updateConfig(newConfig)
} }
@ -166,15 +179,99 @@ class GimbalSettingsGui(private val parent: Screen, private val cfg: ClientConfi
val oldConfig = cfg.config val oldConfig = cfg.config
val newConfig = GimbalClientConfig( val newConfig = GimbalClientConfig(
guiSettings = oldConfig.guiSettings, guiSettings = oldConfig.guiSettings,
toastSettings = this.toastSettings toastSettings = this.toastSettings,
playerFlySpeed = oldConfig.playerFlySpeed,
shouldEscResetMode = oldConfig.shouldEscResetMode,
defaultMode = oldConfig.defaultMode
) )
cfg.updateConfig(newConfig) 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<Boolean?>?, 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<Mode>>(
CyclingButtonWidget.builder<Mode> { 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<Mode>?, 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 // Done button
this.addDrawableChild(ButtonWidget( this.addDrawableChild(ButtonWidget(
this.width / 2 - 100, this.height / 6 + 168, 200, 20, ScreenTexts.DONE this.width / 2 - 100, this.height / 6 + 168, 200, 20, ScreenTexts.DONE

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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)
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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.")
}
}

View file

@ -31,6 +31,10 @@ object TranslationKeys {
const val GENERIC_ENABLED = "${BASE}enabled" const val GENERIC_ENABLED = "${BASE}enabled"
const val GENERIC_DISABLED = "${BASE}disabled" 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 { object Hud {
const val BASE = "${TranslationKeys.Gui.Config.BASE}hud." const val BASE = "${TranslationKeys.Gui.Config.BASE}hud."

View file

@ -28,6 +28,6 @@ internal class DataGenerator : DataGeneratorEntrypoint {
override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) { override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) {
LOGGER.info("Starting Data Generation") LOGGER.info("Starting Data Generation")
fabricDataGenerator.addProvider(En_us_GimbalLanguageProvider(fabricDataGenerator)) fabricDataGenerator.addProvider(EnUsLanguageProvider(fabricDataGenerator))
} }
} }

View file

@ -22,7 +22,7 @@ import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator
import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider
import net.moonleay.gimbal.constants.TranslationKeys import net.moonleay.gimbal.constants.TranslationKeys
class En_us_GimbalLanguageProvider(dataGenerator: FabricDataGenerator?) : class EnUsLanguageProvider(dataGenerator: FabricDataGenerator?) :
FabricLanguageProvider(dataGenerator, "en_us") { FabricLanguageProvider(dataGenerator, "en_us") {
override fun generateTranslations(translationBuilder: TranslationBuilder?) { override fun generateTranslations(translationBuilder: TranslationBuilder?) {
@ -50,10 +50,15 @@ class En_us_GimbalLanguageProvider(dataGenerator: FabricDataGenerator?) :
//Gimbal Toasts //Gimbal Toasts
translationBuilder.add(TranslationKeys.Gui.Config.Toasts.SHOW_TOASTS, "Show 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_ALL, "All")
translationBuilder.add(TranslationKeys.Gui.Config.Toasts.SHOW_TOGGLE, "Only toggle") 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_SYSTEM, "Only System")
translationBuilder.add(TranslationKeys.Gui.Config.Toasts.SHOW_NONE, "None") 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 // Gimbal Generic Gui
translationBuilder.add(TranslationKeys.Gui.Config.GENERIC_ENABLED, "Enabled") translationBuilder.add(TranslationKeys.Gui.Config.GENERIC_ENABLED, "Enabled")
translationBuilder.add(TranslationKeys.Gui.Config.GENERIC_DISABLED, "Disabled") translationBuilder.add(TranslationKeys.Gui.Config.GENERIC_DISABLED, "Disabled")

View file

@ -32,7 +32,7 @@ object ServerEditorManager {
fun updateEditorState(playerUUID: UUID, editorState: EditorState) { fun updateEditorState(playerUUID: UUID, editorState: EditorState) {
STATEMAP[playerUUID] = editorState STATEMAP[playerUUID] = editorState
LOGGER.info("$playerUUID: ${editorState.editorMode} with ${editorState.editorModifier}") LOGGER.debug("{}: {} with {}", playerUUID, editorState.editorMode, editorState.editorModifier)
} }

View file

@ -18,10 +18,11 @@
package net.moonleay.gimbal.editor.state.mode package net.moonleay.gimbal.editor.state.mode
enum class Mode(val displayName: String, val color: Int, val incompatibleModifiers: List<ModeModifier>){ enum class Mode(val displayName: String, val color: Int, val hidden: Boolean, val incompatibleModifiers: List<ModeModifier>){
UNKNOWN("UNKNOWN", 0x000000, listOf()), // Unknown mode. This mode cannot be entered UNKNOWN("UNKNOWN", 0x000000, true, listOf()), // Unknown mode. This mode cannot be entered
NORMAL("NORMAL", 0x90a959, listOf(ModeModifier.NO_UPDATES, ModeModifier.BULLDOZER, ModeModifier.FORCE_PLACE)), // Do nothing NORMAL("NORMAL", 0x90a959, false, listOf(ModeModifier.NO_UPDATES, ModeModifier.BULLDOZER, ModeModifier.FORCE_PLACE)), // Do nothing
INSERT("INSERT", 0xf4bf75, listOf()), // Place and break blocks INSERT("INSERT", 0xf4bf75, false, listOf()), // Place and break blocks
REPLACE("REPLACE", 0xac4242, listOf(ModeModifier.NO_UPDATES)), // Replace blocks REPLACE("REPLACE", 0xac4242, false, listOf(ModeModifier.NO_UPDATES)), // Replace blocks
VISUAL("VISUAL", 0x6a9fb5, listOf(ModeModifier.BULLDOZER)), // Do fancy stuff with WE // Add hidden modes after this comment
VISUAL("VISUAL", 0x6a9fb5, true, listOf(ModeModifier.BULLDOZER)), // Do fancy stuff with WE
} }

View file

@ -20,9 +20,13 @@ package net.moonleay.gimbal.mixin;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity; 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.client.editor.ClientEditor;
import net.moonleay.gimbal.editor.state.mode.Capability; import net.moonleay.gimbal.editor.state.mode.Capability;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@ -32,30 +36,49 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Objects; import java.util.Objects;
@Mixin(MinecraftClient.class)
public abstract class BulldozerMixin { 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 @Inject(method = "doAttack", at = @At(value = "HEAD"))
@Nullable private void func(CallbackInfoReturnable<Boolean> cir) {
public ClientPlayerEntity player; if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.player).isCreative()) {
return;
@Inject(method = "doAttack", at = @At(value = "HEAD")) }
private void func(CallbackInfoReturnable<Boolean> cir) { this.attackCooldown = 0;
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.player).isCreative()) { }
return;
@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")) @Mixin(ClientPlayerInteractionManager.class)
private void func2(boolean breaking, CallbackInfo ci) { public static abstract class ClientPlayerInteractionManagerMixin {
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !breaking || !Objects.requireNonNull(this.player).isCreative()) {
return; @Shadow
private int blockBreakingCooldown;
@Shadow
@Final
private MinecraftClient client;
@Inject(method = "updateBlockBreakingProgress", at = @At("HEAD"))
private void func(BlockPos pos, Direction direction, CallbackInfoReturnable<Boolean> cir) {
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.client.player).isCreative()) {
return;
}
this.blockBreakingCooldown = 0;
} }
this.attackCooldown = 0;
} }
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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<Double> cir) {
if (!ClientEditor.INSTANCE.shouldClient(Capability.NO_CLIP) || (!(MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.isCreative()))) {
return;
}
cir.setReturnValue(desiredCameraDistance);
cir.cancel();
}
}

View file

@ -19,12 +19,15 @@
package net.moonleay.gimbal.mixin; package net.moonleay.gimbal.mixin;
import com.mojang.authlib.GameProfile; 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.EntityPose;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerAbilities; import net.minecraft.entity.player.PlayerAbilities;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.moonleay.gimbal.client.editor.ClientEditor;
import net.moonleay.gimbal.editor.ServerEditorManager; import net.moonleay.gimbal.editor.ServerEditorManager;
import net.moonleay.gimbal.editor.state.mode.Capability; import net.moonleay.gimbal.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.UUID; import java.util.UUID;
@Mixin(PlayerEntity.class) public abstract class NoClipMixin {
public abstract class NoClipMixin extends LivingEntity {
@Shadow public abstract GameProfile getGameProfile();
@Shadow public abstract PlayerAbilities getAbilities(); @Mixin(PlayerEntity.class) // Serverside, allows clipping
public static abstract class PlayerEntityMixin extends LivingEntity {
@Shadow protected PlayerEntityMixin(EntityType<? extends LivingEntity> entityType, World world) {
public abstract boolean isCreative(); super(entityType, world);
protected NoClipMixin(EntityType<? extends LivingEntity> 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
} }
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)) @Mixin(Camera.class) // Clientside, fixes camera
return; // NoClip is not enabled public static abstract class CameraMixin {
if (!this.getAbilities().flying)
return;
// Force standing pose in NoClip mode @Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true)
private void fixCameraInNoClip(double desiredCameraDistance, CallbackInfoReturnable<Double> cir) {
this.setPose(EntityPose.STANDING); if (!ClientEditor.INSTANCE.shouldClient(Capability.NO_CLIP) || (!(MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.isCreative()))) {
return;
}
cir.setReturnValue(desiredCameraDistance);
cir.cancel();
}
} }
} }

View file

@ -21,9 +21,9 @@ package net.moonleay.gimbal.mixin;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.option.GameOptions; import net.minecraft.client.option.GameOptions;
import net.moonleay.gimbal.client.ClientMain;
import net.moonleay.gimbal.client.editor.ClientEditor; import net.moonleay.gimbal.client.editor.ClientEditor;
import net.moonleay.gimbal.editor.state.mode.Capability; import net.moonleay.gimbal.editor.state.mode.Capability;
import net.moonleay.gimbal.editor.state.mode.Mode;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@ -43,12 +43,12 @@ public class NormalModeMixin {
public ClientPlayerEntity player; public ClientPlayerEntity player;
@Inject(method = "openPauseMenu", at = @At("HEAD"), cancellable = true) @Inject(method = "openPauseMenu", at = @At("HEAD"), cancellable = true)
private void setNormalMode(boolean pause, CallbackInfo ci) { private void setDefaultMode(boolean pause, CallbackInfo ci) {
if (ClientEditor.INSTANCE.isInNonDefaultMode() && ClientEditor.INSTANCE.isAllowed()) { if (ClientMain.CONFIG.getConfig().getShouldEscResetMode() && ClientEditor.INSTANCE.isInNonDefaultMode() && ClientEditor.INSTANCE.isAllowed()) {
assert this.player != null; assert this.player != null;
if (this.player.isCreative()) { if (this.player.isCreative()) {
// Set the editor mode to normal // Set the editor mode to normal
ClientEditor.INSTANCE.setMode(Mode.NORMAL); ClientEditor.INSTANCE.setMode(ClientMain.CONFIG.getConfig().getDefaultMode());
ci.cancel(); ci.cancel();
} }
} }

View file

@ -19,34 +19,27 @@
package net.moonleay.gimbal.mixin; package net.moonleay.gimbal.mixin;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerInteractionManager; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.util.math.BlockPos; import net.minecraft.network.packet.s2c.play.PlayerAbilitiesS2CPacket;
import net.minecraft.util.math.Direction; import net.moonleay.gimbal.client.ClientMain;
import net.moonleay.gimbal.client.editor.ClientEditor;
import net.moonleay.gimbal.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; 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(ClientPlayNetworkHandler.class)
public abstract class PlayerFlySpeedMixin {
@Mixin(ClientPlayerInteractionManager.class)
public class BulldozerMixin2 {
@Shadow private int blockBreakingCooldown;
@Shadow @Shadow
@Final @Final
private MinecraftClient client; private MinecraftClient client;
@Inject(method = "updateBlockBreakingProgress", at = @At("HEAD")) @Inject(method = "onPlayerAbilities", at = @At(value = "RETURN"))
private void func(BlockPos pos, Direction direction, CallbackInfoReturnable<Boolean> cir) { private void func(PlayerAbilitiesS2CPacket packet, CallbackInfo ci) {
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.client.player).isCreative()) { this.client.player.getAbilities().setFlySpeed((ClientMain.CONFIG.getConfig().getPlayerFlySpeed() / 100) * 0.05f);
return; //client!!.player!!.abilities.flySpeed = (this.playerFlySpeed / 100) * 0.05f
}
this.blockBreakingCooldown = 0;
} }
} }

View file

@ -18,50 +18,87 @@
package net.moonleay.gimbal.mixin; package net.moonleay.gimbal.mixin;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.*;
import net.minecraft.item.ItemStack; import net.minecraft.state.property.Property;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.moonleay.gimbal.editor.ServerEditorManager; import net.moonleay.gimbal.editor.ServerEditorManager;
import net.moonleay.gimbal.editor.state.mode.Capability; import net.moonleay.gimbal.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.UUID; import java.util.UUID;
@Mixin(ItemPlacementContext.class) public abstract class ReplaceModeMixin {
public abstract class ReplaceModeMixin extends ItemUsageContext {
@Shadow @Mixin(ItemPlacementContext.class)
protected boolean canReplaceExisting; public static abstract class ItemPlacementContextMixin extends ItemUsageContext {
@Shadow
protected boolean canReplaceExisting;
@Mutable @Mutable
@Shadow @Shadow
@Final @Final
private BlockPos placementPos; private BlockPos placementPos;
public ReplaceModeMixin(PlayerEntity player, Hand hand, BlockHitResult hit) { public ItemPlacementContextMixin(PlayerEntity player, Hand hand, BlockHitResult hit) {
super(player, hand, hit); super(player, hand, hit);
}
@Inject(method = "<init>(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 = "<init>(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")) @Mixin(BlockItem.class)
private void func(World world, PlayerEntity playerEntity, Hand hand, ItemStack itemStack, BlockHitResult blockHitResult, CallbackInfo ci) { public static abstract class BlockItemMixin extends Item {
if (playerEntity == null)
return;
UUID id = playerEntity.getGameProfile().getId();
if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.REPLACE))
return;
this.canReplaceExisting = true; public BlockItemMixin(Settings settings) {
this.placementPos = blockHitResult.getBlockPos(); 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;
}
} }
} }

View file

@ -1,70 +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 <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.mixin;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.property.Property;
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;
@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());
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;
}
}

View file

@ -20,6 +20,7 @@ package net.moonleay.gimbal.mixin;
import net.minecraft.client.gui.screen.GameMenuScreen; import net.minecraft.client.gui.screen.GameMenuScreen;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.widget.TexturedButtonWidget; import net.minecraft.client.gui.widget.TexturedButtonWidget;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@ -33,35 +34,71 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameMenuScreen.class)
// What is my purpose? // What is my purpose?
public class YouInjectButtonMixin extends Screen { public class YouInjectButtonMixin {
// Go my god. // Go my god.
@Unique @Mixin(TitleScreen.class)
private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png"); public static abstract class TitleScreenMixin extends Screen {
protected YouInjectButtonMixin(Text title) { @Unique
super(title); 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)
)
);
}
} }
@Inject(method = "initWidgets", at = @At(value = "RETURN"))
private void func(CallbackInfo ci) { @Mixin(GameMenuScreen.class)
this.addDrawableChild( public static abstract class GameMenuScreenMixin extends Screen {
new TexturedButtonWidget( @Unique
this.width / 2 - 124, private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png");
this.height / 4 + 96 + -16,
20, protected GameMenuScreenMixin(Text title) {
20, super(title);
0, }
0,
20, @Inject(method = "initWidgets", at = @At(value = "RETURN"))
GIMBAL_TEXTURE, private void func(CallbackInfo ci) {
20, this.addDrawableChild(
40, new TexturedButtonWidget(
button -> this.client.setScreen(new GimbalSettingsGui(this, ClientMain.CONFIG)), this.width / 2 - 124,
Text.translatable(TranslationKeys.Gui.Config.SCREEN_NAME) 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)
)
);
}
} }
} }

View file

@ -1,66 +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 <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.mixin;
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;
@Mixin(TitleScreen.class)
public class YouInjectButtonMixin2 extends Screen {
@Unique
private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png");
protected YouInjectButtonMixin2(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)
)
);
}
}

View file

@ -6,18 +6,19 @@
"mixins": [ "mixins": [
"ForcePlaceMixin", "ForcePlaceMixin",
"NoBlockUpdatesMixin", "NoBlockUpdatesMixin",
"NoClipMixin", "NoClipMixin$PlayerEntityMixin",
"ReplaceModeMixin", "ReplaceModeMixin$BlockItemMixin",
"ReplaceStateUpdaterMixin" "ReplaceModeMixin$ItemPlacementContextMixin"
], ],
"client": [ "client": [
"BulldozerMixin", "BulldozerMixin$ClientPlayerInteractionManagerMixin",
"BulldozerMixin2", "BulldozerMixin$MinecraftClientMixin",
"HudMixin", "HudMixin",
"NoClipCameraFixMixin", "NoClipMixin$CameraMixin",
"NormalModeMixin", "NormalModeMixin",
"YouInjectButtonMixin", "PlayerFlySpeedMixin",
"YouInjectButtonMixin2" "YouInjectButtonMixin$GameMenuScreenMixin",
"YouInjectButtonMixin$TitleScreenMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1