Compare commits

..

No commits in common. "master" and "0.4.0" have entirely different histories.

24 changed files with 330 additions and 489 deletions

View file

@ -46,9 +46,6 @@ 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,7 +22,6 @@ 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.*
@ -37,7 +36,6 @@ 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,13 +20,9 @@ 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, // offsetX val scaledX: Double,
val scaledY: Double, // offsetY val scaledY: Double,
) )

View file

@ -40,25 +40,17 @@ import org.apache.logging.log4j.LogManager
object ClientEditor { object ClientEditor {
private var POLICY = GimbalPolicyType.NOT_PRESENT private var POLICY = GimbalPolicyType.NOT_PRESENT
private lateinit var CURRENT_MODE: Mode private var CURRENT_MODE = Mode.NORMAL
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
@ -115,7 +107,7 @@ object ClientEditor {
} }
fun isInNonDefaultMode(): Boolean { fun isInNonDefaultMode(): Boolean {
return CURRENT_MODE != ClientMain.CONFIG.config.defaultMode return CURRENT_MODE != Mode.NORMAL
} }
/* /*

View file

@ -86,10 +86,7 @@ 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,24 +29,17 @@ 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() {
@ -72,10 +65,7 @@ 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)
}) })
@ -133,10 +123,7 @@ 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)
} }
@ -179,99 +166,15 @@ 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

@ -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 <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

@ -1,44 +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.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,10 +31,6 @@ 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(EnUsLanguageProvider(fabricDataGenerator)) fabricDataGenerator.addProvider(En_us_GimbalLanguageProvider(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 EnUsLanguageProvider(dataGenerator: FabricDataGenerator?) : class En_us_GimbalLanguageProvider(dataGenerator: FabricDataGenerator?) :
FabricLanguageProvider(dataGenerator, "en_us") { FabricLanguageProvider(dataGenerator, "en_us") {
override fun generateTranslations(translationBuilder: TranslationBuilder?) { override fun generateTranslations(translationBuilder: TranslationBuilder?) {
@ -50,15 +50,10 @@ class EnUsLanguageProvider(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.debug("{}: {} with {}", playerUUID, editorState.editorMode, editorState.editorModifier) LOGGER.info("$playerUUID: ${editorState.editorMode} with ${editorState.editorModifier}")
} }

View file

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

View file

@ -20,13 +20,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.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;
@ -36,49 +32,30 @@ 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 {
@Mixin(MinecraftClient.class) @Shadow protected int attackCooldown;
public static abstract class MinecraftClientMixin {
@Shadow
@Nullable
public ClientPlayerEntity player;
@Shadow
protected int attackCooldown;
@Inject(method = "doAttack", at = @At(value = "HEAD")) @Shadow
private void func(CallbackInfoReturnable<Boolean> cir) { @Nullable
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.player).isCreative()) { public ClientPlayerEntity player;
return;
}
this.attackCooldown = 0;
}
@Inject(method = "handleBlockBreaking", at = @At(value = "HEAD")) @Inject(method = "doAttack", at = @At(value = "HEAD"))
private void func2(boolean breaking, CallbackInfo ci) { private void func(CallbackInfoReturnable<Boolean> cir) {
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !breaking || !Objects.requireNonNull(this.player).isCreative()) { if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.player).isCreative()) {
return; return;
}
this.attackCooldown = 0;
} }
this.attackCooldown = 0;
} }
@Mixin(ClientPlayerInteractionManager.class) @Inject(method = "handleBlockBreaking", at = @At(value = "HEAD"))
public static abstract class ClientPlayerInteractionManagerMixin { private void func2(boolean breaking, CallbackInfo ci) {
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !breaking || !Objects.requireNonNull(this.player).isCreative()) {
@Shadow return;
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

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

View file

@ -0,0 +1,41 @@
/*
* 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,15 +19,12 @@
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;
@ -35,72 +32,51 @@ 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;
public abstract class NoClipMixin { @Mixin(PlayerEntity.class)
public abstract class NoClipMixin extends LivingEntity {
@Shadow public abstract GameProfile getGameProfile();
@Mixin(PlayerEntity.class) // Serverside, allows clipping @Shadow public abstract PlayerAbilities getAbilities();
public static abstract class PlayerEntityMixin extends LivingEntity {
protected PlayerEntityMixin(EntityType<? extends LivingEntity> entityType, World world) { @Shadow
super(entityType, world); public abstract boolean isCreative();
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) {
@Shadow return;
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();
@Mixin(Camera.class) // Clientside, fixes camera if (!ServerEditorManager.INSTANCE.shouldPlayer(uuid, Capability.NO_CLIP))
public static abstract class CameraMixin { return; // NoClip is not enabled
if (!this.getAbilities().flying)
return;
@Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true) // Force standing pose in NoClip mode
private void fixCameraInNoClip(double desiredCameraDistance, CallbackInfoReturnable<Double> cir) {
if (!ClientEditor.INSTANCE.shouldClient(Capability.NO_CLIP) || (!(MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.isCreative()))) { this.setPose(EntityPose.STANDING);
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 setDefaultMode(boolean pause, CallbackInfo ci) { private void setNormalMode(boolean pause, CallbackInfo ci) {
if (ClientMain.CONFIG.getConfig().getShouldEscResetMode() && ClientEditor.INSTANCE.isInNonDefaultMode() && ClientEditor.INSTANCE.isAllowed()) { if (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(ClientMain.CONFIG.getConfig().getDefaultMode()); ClientEditor.INSTANCE.setMode(Mode.NORMAL);
ci.cancel(); ci.cancel();
} }
} }

View file

@ -18,87 +18,50 @@
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.*; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.property.Property; import net.minecraft.item.ItemStack;
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.*; 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.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;
public abstract class ReplaceModeMixin { @Mixin(ItemPlacementContext.class)
public abstract class ReplaceModeMixin extends ItemUsageContext {
@Mixin(ItemPlacementContext.class) @Shadow
public static abstract class ItemPlacementContextMixin extends ItemUsageContext { protected boolean canReplaceExisting;
@Shadow
protected boolean canReplaceExisting;
@Mutable @Mutable
@Shadow @Shadow
@Final @Final
private BlockPos placementPos; private BlockPos placementPos;
public ItemPlacementContextMixin(PlayerEntity player, Hand hand, BlockHitResult hit) { public ReplaceModeMixin(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();
}
} }
@Mixin(BlockItem.class) @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"))
public static abstract class BlockItemMixin extends Item { 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;
public BlockItemMixin(Settings settings) { this.canReplaceExisting = true;
super(settings); this.placementPos = blockHitResult.getBlockPos();
}
@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

@ -0,0 +1,70 @@
/*
* 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,7 +20,6 @@ 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;
@ -34,71 +33,35 @@ 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 { public class YouInjectButtonMixin extends Screen {
// Go my god. // Go my god.
@Mixin(TitleScreen.class) @Unique
public static abstract class TitleScreenMixin extends Screen { private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png");
@Unique protected YouInjectButtonMixin(Text title) {
private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png"); super(title);
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"))
@Mixin(GameMenuScreen.class) private void func(CallbackInfo ci) {
public static abstract class GameMenuScreenMixin extends Screen { this.addDrawableChild(
@Unique new TexturedButtonWidget(
private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png"); this.width / 2 - 124,
this.height / 4 + 96 + -16,
protected GameMenuScreenMixin(Text title) { 20,
super(title); 20,
} 0,
0,
@Inject(method = "initWidgets", at = @At(value = "RETURN")) 20,
private void func(CallbackInfo ci) { GIMBAL_TEXTURE,
this.addDrawableChild( 20,
new TexturedButtonWidget( 40,
this.width / 2 - 124, button -> this.client.setScreen(new GimbalSettingsGui(this, ClientMain.CONFIG)),
this.height / 4 + 96 + -16, Text.translatable(TranslationKeys.Gui.Config.SCREEN_NAME)
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

@ -0,0 +1,66 @@
/*
* 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,19 +6,18 @@
"mixins": [ "mixins": [
"ForcePlaceMixin", "ForcePlaceMixin",
"NoBlockUpdatesMixin", "NoBlockUpdatesMixin",
"NoClipMixin$PlayerEntityMixin", "NoClipMixin",
"ReplaceModeMixin$BlockItemMixin", "ReplaceModeMixin",
"ReplaceModeMixin$ItemPlacementContextMixin" "ReplaceStateUpdaterMixin"
], ],
"client": [ "client": [
"BulldozerMixin$ClientPlayerInteractionManagerMixin", "BulldozerMixin",
"BulldozerMixin$MinecraftClientMixin", "BulldozerMixin2",
"HudMixin", "HudMixin",
"NoClipMixin$CameraMixin", "NoClipCameraFixMixin",
"NormalModeMixin", "NormalModeMixin",
"PlayerFlySpeedMixin", "YouInjectButtonMixin",
"YouInjectButtonMixin$GameMenuScreenMixin", "YouInjectButtonMixin2"
"YouInjectButtonMixin$TitleScreenMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1