chore: renamed to Gimbal, because I cannot spell apparently

Signed-off-by: moonleay <contact@moonleay.net>
This commit is contained in:
moonleay 2024-04-29 15:08:02 +02:00
parent 1c7138bb58
commit c14cdffeed
Signed by: moonleay
GPG key ID: 82667543CCD715FB
50 changed files with 322 additions and 279 deletions

View file

@ -0,0 +1,37 @@
/*
* 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
import net.fabricmc.api.ModInitializer
import net.moonleay.gimbal.build.BuildConstants
import net.moonleay.gimbal.networking.GimbalServer
import org.apache.logging.log4j.LogManager
internal object Main : ModInitializer {
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
override fun onInitialize() {
LOGGER.info("Initializing Gimbal on the common side...")
LOGGER.info("Registering packets...")
GimbalServer.registerPacketHandler()
LOGGER.info("Packets have been registered.")
LOGGER.info("Gimbal has been initialized on the common side.")
LOGGER.info("${BuildConstants.modName} (${BuildConstants.modId}) v.${BuildConstants.modVersion} by moonleay")
}
}

View file

@ -0,0 +1,51 @@
/*
* 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
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import net.moonleay.gimbal.build.BuildConstants
import net.moonleay.gimbal.client.keybindings.KeybindingManager
import net.moonleay.gimbal.client.keybindings.KeybindingRegistrar
import net.moonleay.gimbal.networking.GimbalClient
import org.apache.logging.log4j.LogManager
internal object ClientMain : ClientModInitializer {
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
override fun onInitializeClient() {
LOGGER.info("Initializing Gimbal on the client side...")
KeybindingRegistrar.registerKeybindings()
registerEvents()
LOGGER.info("Registering packets...")
GimbalClient.registerPacketHandlers()
LOGGER.info("Packets have been registered.")
LOGGER.info("Gimbal has been initialized on the client side.")
}
private fun registerEvents() {
LOGGER.info("Registering client events...")
ClientTickEvents.END_CLIENT_TICK.register(ClientTickEvents.EndTick { client ->
KeybindingManager.onClientTick(client)
})
LOGGER.info("Client events have been registered.")
}
}

View file

@ -0,0 +1,233 @@
/*
* 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.editor
import net.minecraft.client.MinecraftClient
import net.minecraft.text.Text
import net.moonleay.gimbal.client.util.ChatUtil
import net.moonleay.gimbal.editor.ServerEditorManager
import net.moonleay.gimbal.editor.state.EditorState
import net.moonleay.gimbal.editor.state.GimbalPolicyType
import net.moonleay.gimbal.editor.state.mode.Capability
import net.moonleay.gimbal.editor.state.mode.Mode
import net.moonleay.gimbal.editor.state.mode.ModeModifier
import net.moonleay.gimbal.editor.util.EditorUtil
import net.moonleay.gimbal.editor.util.GimbalPolicy
import net.moonleay.gimbal.networking.GimbalClient
object ClientEditor {
private var POLICY = GimbalPolicyType.NOT_PRESENT
private var CURRENT_MODE = Mode.NORMAL
private var TEMP_DISABLED_MODE = Mode.UNKNOWN
private val CURRENT_MODE_MODIFIER = mutableListOf<ModeModifier>()
private val TEMP_DISABLED_MODIFIERS = mutableListOf<ModeModifier>()
private val DISABLED_MODIFIERS_STORAGE = mutableListOf<ModeModifier>()
fun onConnectedToNewWorld() {
POLICY = GimbalPolicyType.NOT_PRESENT
GimbalClient.checkIfServerHasGimble(
EditorState(
CURRENT_MODE,
CURRENT_MODE_MODIFIER
)
)
if (TEMP_DISABLED_MODE == Mode.UNKNOWN) {
TEMP_DISABLED_MODE =
CURRENT_MODE
}
CURRENT_MODE = Mode.INSERT
DISABLED_MODIFIERS_STORAGE.addAll(
CURRENT_MODE_MODIFIER
)
CURRENT_MODE_MODIFIER.clear()
}
fun onAllowedCheck(data: GimbalPolicy) {
POLICY = data.policy
if (data.policy == GimbalPolicyType.ALLOWED) {
if (TEMP_DISABLED_MODE != Mode.UNKNOWN) {
CURRENT_MODE =
TEMP_DISABLED_MODE
TEMP_DISABLED_MODE = Mode.UNKNOWN
}
CURRENT_MODE_MODIFIER.addAll(
DISABLED_MODIFIERS_STORAGE
)
DISABLED_MODIFIERS_STORAGE.clear()
onUpdated()
}
}
fun isAllowed(): Boolean {
return POLICY == GimbalPolicyType.ALLOWED
}
fun shouldClient(capability: Capability): Boolean {
return EditorUtil.shouldPlayer(
capability, EditorState(
CURRENT_MODE,
CURRENT_MODE_MODIFIER
)
)
}
fun isInNonDefaultMode(): Boolean {
return CURRENT_MODE != Mode.NORMAL
}
/*
* Send an updated player state to the server
* */
private fun updateServerState() {
val state = EditorState(
CURRENT_MODE,
CURRENT_MODE_MODIFIER
)
GimbalClient.sendEditorState(state)
ServerEditorManager.updateEditorState(MinecraftClient.getInstance().player!!.uuid, state)
}
/*
* Set the current mode
* */
fun setMode(mode: Mode) {
if (!isAllowed()) {
ChatUtil.showToastToSelf("Gimble is disabled", "You cannot change modes", MinecraftClient.getInstance())
return
}
if (!MinecraftClient.getInstance().player!!.isCreative)
return
CURRENT_MODE = mode
onUpdated()
}
/*
* Toggle a mode modifier
* */
fun toggleModifier(mod: ModeModifier) {
if (!isAllowed()) {
ChatUtil.showToastToSelf("Gimble is disabled", "You cannot change modifiers", MinecraftClient.getInstance())
return
}
if (!MinecraftClient.getInstance().player!!.isCreative)
return
if (CURRENT_MODE.incompatibleModifiers.contains(mod)){
if (TEMP_DISABLED_MODIFIERS.contains(mod))
TEMP_DISABLED_MODIFIERS.remove(mod)
else
TEMP_DISABLED_MODIFIERS.add(mod)
}
else {
if (CURRENT_MODE_MODIFIER.contains(mod))
CURRENT_MODE_MODIFIER.remove(mod)
else
CURRENT_MODE_MODIFIER.add(mod)
}
onUpdated()
}
private fun onUpdated() {
CURRENT_MODE_MODIFIER.sortBy {
it.displayName
}
checkForIncompatibleModeModifiers()
}
/**
* This runs on Mode updated
*/
private fun checkForIncompatibleModeModifiers() {
if (TEMP_DISABLED_MODIFIERS.size > 0) {
CURRENT_MODE_MODIFIER.addAll(
TEMP_DISABLED_MODIFIERS
)
TEMP_DISABLED_MODIFIERS.clear()
}
val incompatibleModeModifiers = CURRENT_MODE.incompatibleModifiers
for (mod in CURRENT_MODE_MODIFIER) {
if (incompatibleModeModifiers.contains(mod)) {
TEMP_DISABLED_MODIFIERS.add(mod)
}
}
for (mod in TEMP_DISABLED_MODIFIERS) {
if (CURRENT_MODE_MODIFIER.contains(mod)) { // This check is probably useless.
CURRENT_MODE_MODIFIER.remove(mod)
}
}
CURRENT_MODE_MODIFIER.sortBy {
it.displayName
}
// Update State
updateServerState()
if(TEMP_DISABLED_MODIFIERS.isNotEmpty()) {
ChatUtil.showToastToSelf(
"${CURRENT_MODE.displayName} Mode disabled",
getListAsString(TEMP_DISABLED_MODIFIERS), MinecraftClient.getInstance()
)
}
}
private fun getListAsString(list: List<ModeModifier>): String{
if(list.isEmpty())
return "Empty list"
val sb = StringBuilder()
for (mod in list) {
sb.append(mod.displayName)
sb.append(", ")
}
return sb.toString().dropLast(2)
}
/*
* Get the display text to display in the HUD
* */
fun getModeDisplayText(): Text {
val displayText = StringBuilder(CURRENT_MODE.displayName)
if (CURRENT_MODE_MODIFIER.isNotEmpty() && isAllowed() && MinecraftClient.getInstance().player?.isCreative == true) {
displayText.append(" [")
for (i in CURRENT_MODE_MODIFIER.indices) {
displayText.append(CURRENT_MODE_MODIFIER[i].displayName)
if (i != CURRENT_MODE_MODIFIER.size - 1) {
displayText.append(", ")
}
}
displayText.append("]")
} else if (!isAllowed() || !MinecraftClient.getInstance().player?.isCreative!!) {
displayText.clear()
displayText.append("[GIMBLE DISABLED]")
}
return Text.of(displayText.toString())
}
fun getCurrentColor(): Int {
return CURRENT_MODE.color
}
}

View file

@ -0,0 +1,43 @@
/*
* 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.keybindings
import net.fabricmc.fabric.impl.client.keybinding.KeyBindingRegistryImpl
import net.minecraft.client.MinecraftClient
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
object KeybindingManager {
private val shortcuts = mutableListOf<GimbalShortcut>()
/**
* Register a shortcut. This will also register the keybinding.
* */
fun registerShortcut(shortcut: GimbalShortcut) {
shortcuts.add(shortcut)
KeyBindingRegistryImpl.registerKeyBinding(shortcut.keyBinding)
}
fun onClientTick(client: MinecraftClient){
// Sadly, this cannot be done without iterating over all shortcuts :madge:
for (shortcut in shortcuts)
if (shortcut.keyBinding.wasPressed())
shortcut.onPressed(client)
}
}

View file

@ -0,0 +1,127 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings
import net.minecraft.client.option.KeyBinding
import net.minecraft.client.util.InputUtil
import net.moonleay.gimbal.build.BuildConstants
import net.moonleay.gimbal.client.keybindings.impl.editormode.EnableInsertModeShortcut
import net.moonleay.gimbal.client.keybindings.impl.editormode.EnableReplaceModeShortcut
import net.moonleay.gimbal.client.keybindings.impl.editormodemodifier.ToggleBulldozerModifierShortcut
import net.moonleay.gimbal.client.keybindings.impl.editormodemodifier.ToggleForcePlaceModifierShortcut
import net.moonleay.gimbal.client.keybindings.impl.editormodemodifier.ToggleNoClipModifierShortcut
import net.moonleay.gimbal.client.keybindings.impl.editormodemodifier.ToggleNoUpdatesModifierShortcut
import net.moonleay.gimbal.client.keybindings.impl.gamemode.CreativeModeShortcut
import net.moonleay.gimbal.client.keybindings.impl.gamemode.SpectatorModeShortcut
import net.moonleay.gimbal.client.keybindings.impl.gamemode.SurvivalModeShortcut
import org.apache.logging.log4j.LogManager
import org.lwjgl.glfw.GLFW
object KeybindingRegistrar {
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
fun registerKeybindings() {
LOGGER.info("Registering keybindings...")
registerSetGameModeKeybindings()
registerSetEditorModeKeybindings()
registerSetEditorModeModifierKeybindings()
LOGGER.info("Keybindings have been registered.")
}
private fun registerSetEditorModeModifierKeybindings() {
val toggleBulldozerModifierShortcut = KeyBinding(
"gimbal.key.editor.modifier.bulldozer",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_UNKNOWN,
"gimbal.category.editormodifier"
)
val toggleForcePlaceModifierShortcut = KeyBinding(
"gimbal.key.editor.modifier.forceplace",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_UNKNOWN,
"gimbal.category.editormodifier"
)
val toggleNoUpdatesModifierShortcut = KeyBinding(
"gimbal.key.editor.modifier.noupdates",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_UNKNOWN,
"gimbal.category.editormodifier"
)
val toggleNoClipModifierShortcut = KeyBinding(
"gimbal.key.editor.modifier.noclip",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_UNKNOWN,
"gimbal.category.editormodifier"
)
KeybindingManager.registerShortcut(ToggleBulldozerModifierShortcut(toggleBulldozerModifierShortcut))
KeybindingManager.registerShortcut(ToggleForcePlaceModifierShortcut(toggleForcePlaceModifierShortcut))
KeybindingManager.registerShortcut(ToggleNoUpdatesModifierShortcut(toggleNoUpdatesModifierShortcut))
KeybindingManager.registerShortcut(ToggleNoClipModifierShortcut(toggleNoClipModifierShortcut))
}
private fun registerSetEditorModeKeybindings() {
val insertKeyBinding = KeyBinding(
"gimbal.key.editor.mode.insert",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_I,
"gimbal.category.editormode"
)
val replaceKeyBinding = KeyBinding(
"gimbal.key.editor.mode.replace",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_R,
"gimbal.category.editormode"
)
// val visualKeyBinding = KeyBinding(
// "gimbal.key.editor.mode.visual",
// InputUtil.Type.KEYSYM,
// GLFW.GLFW_KEY_V,
// "gimbal.category.editormode"
// )
KeybindingManager.registerShortcut(EnableInsertModeShortcut(insertKeyBinding))
KeybindingManager.registerShortcut(EnableReplaceModeShortcut(replaceKeyBinding))
// KeybindingManager.registerShortcut(EnableVisualModeShortcut(visualKeyBinding))
}
private fun registerSetGameModeKeybindings() {
val survivalKeyBinding = KeyBinding(
"gimbal.key.game.mode.survival",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_UNKNOWN,
"gimbal.category.gamemode"
)
val creativeKeyBinding = KeyBinding(
"gimbal.key.game.mode.creative",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_UNKNOWN,
"gimbal.category.gamemode"
)
val spectatorKeyBinding = KeyBinding(
"gimbal.key.game.mode.spectator",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_UNKNOWN,
"gimbal.category.gamemode"
)
KeybindingManager.registerShortcut(SurvivalModeShortcut(survivalKeyBinding))
KeybindingManager.registerShortcut(CreativeModeShortcut(creativeKeyBinding))
KeybindingManager.registerShortcut(SpectatorModeShortcut(spectatorKeyBinding))
}
}

View file

@ -0,0 +1,27 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
abstract class GimbalShortcut(val keyBinding: KeyBinding) {
abstract fun onPressed(client: MinecraftClient)
}

View file

@ -0,0 +1,32 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl.editormode
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.editor.state.mode.Mode
class EnableInsertModeShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ClientEditor.setMode(Mode.INSERT)
}
}

View file

@ -0,0 +1,32 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl.editormode
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.editor.state.mode.Mode
class EnableReplaceModeShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ClientEditor.setMode(Mode.REPLACE)
}
}

View file

@ -0,0 +1,32 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl.editormode
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.editor.state.mode.Mode
class EnableVisualModeShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ClientEditor.setMode(Mode.VISUAL)
}
}

View file

@ -0,0 +1,31 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl.editormodemodifier
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.editor.state.mode.ModeModifier
class ToggleBulldozerModifierShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ClientEditor.toggleModifier(ModeModifier.BULLDOZER)
}
}

View file

@ -0,0 +1,31 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl.editormodemodifier
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.editor.state.mode.ModeModifier
class ToggleForcePlaceModifierShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ClientEditor.toggleModifier(ModeModifier.FORCE_PLACE)
}
}

View file

@ -0,0 +1,31 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl.editormodemodifier
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.editor.state.mode.ModeModifier
class ToggleNoClipModifierShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ClientEditor.toggleModifier(ModeModifier.NO_CLIP)
}
}

View file

@ -0,0 +1,31 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl.editormodemodifier
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.editor.state.mode.ModeModifier
class ToggleNoUpdatesModifierShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ClientEditor.toggleModifier(ModeModifier.NO_UPDATES)
}
}

View file

@ -0,0 +1,30 @@
/*
* 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.keybindings.impl.gamemode
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.client.util.ChatUtil
class CreativeModeShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ChatUtil.sendCommand("/gamemode creative", client, false)
}
}

View file

@ -0,0 +1,30 @@
/*
* 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.keybindings.impl.gamemode
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.client.util.ChatUtil
class SpectatorModeShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ChatUtil.sendCommand("/gamemode spectator", client, false)
}
}

View file

@ -0,0 +1,32 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.client.keybindings.impl.gamemode
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.moonleay.gimbal.client.keybindings.impl.GimbalShortcut
import net.moonleay.gimbal.client.util.ChatUtil
class SurvivalModeShortcut(key: KeyBinding) : GimbalShortcut(key) {
override fun onPressed(client: MinecraftClient) {
ChatUtil.sendCommand("/gamemode survival", client, false)
}
}

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
import net.minecraft.client.MinecraftClient
import net.minecraft.client.toast.SystemToast
import net.minecraft.text.Text
object ChatUtil {
/**
* Note: command arg should contain "/" at the beginning.
* */
fun sendCommand(command: String, client: MinecraftClient, addToHistory: Boolean = true): Boolean {
if (addToHistory) {
client.inGameHud.chatHud.addToMessageHistory(command)
}
return client.player?.sendCommand(command.substring(1))?: false
}
fun addToChatHistory(message: String, client: MinecraftClient) {
client.inGameHud.chatHud.addMessage(Text.of(message))
}
fun showToastToSelf(title: String, description: String, client: MinecraftClient) {
val toast = SystemToast(SystemToast.Type.PERIODIC_NOTIFICATION, Text.of(title), Text.of(description))
client.toastManager.add(toast)
}
}

View file

@ -0,0 +1,33 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.datagen
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator
import net.moonleay.gimbal.build.BuildConstants
import org.apache.logging.log4j.LogManager
internal class DataGenerator : DataGeneratorEntrypoint {
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) {
LOGGER.info("Starting Data Generation")
fabricDataGenerator.addProvider(En_us_GimbalLanguageProvider(fabricDataGenerator))
}
}

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.datagen
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator
import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider
class En_us_GimbalLanguageProvider(dataGenerator: FabricDataGenerator?) :
FabricLanguageProvider(dataGenerator, "en_us") {
override fun generateTranslations(translationBuilder: TranslationBuilder?) {
if (translationBuilder == null) return
// Editor modes
translationBuilder.add("gimbal.category.editormode", "Editor Modes")
translationBuilder.add("gimbal.key.editor.mode.insert", "Enter Insert Mode")
translationBuilder.add("gimbal.key.editor.mode.replace", "Enter Replace Mode")
translationBuilder.add("gimbal.key.editor.mode.visual", "Enter Visual Mode")
// Editor mode modifiers
translationBuilder.add("gimbal.category.editormodifier", "Editor Mode Modifiers")
translationBuilder.add("gimbal.key.editor.modifier.bulldozer", "Toggle Bulldozer Modifier")
translationBuilder.add("gimbal.key.editor.modifier.forceplace", "Toggle Force Place Modifier")
translationBuilder.add("gimbal.key.editor.modifier.noupdates", "Toggle No Updates Modifier")
translationBuilder.add("gimbal.key.editor.modifier.noclip", "Toggle No Clip Modifier")
// Game mode
translationBuilder.add("gimbal.category.gamemode", "Game Mode")
translationBuilder.add("gimbal.key.game.mode.survival", "Enable Survival Mode")
translationBuilder.add("gimbal.key.game.mode.creative", "Enable Creative Mode")
translationBuilder.add("gimbal.key.game.mode.spectator", "Enable Spectator Mode")
}
}

View file

@ -0,0 +1,55 @@
/*
* 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.editor
import net.moonleay.gimbal.build.BuildConstants
import net.moonleay.gimbal.editor.state.EditorState
import net.moonleay.gimbal.editor.state.mode.Capability
import net.moonleay.gimbal.editor.util.EditorUtil
import org.apache.logging.log4j.LogManager
import java.util.*
object ServerEditorManager {
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
private val STATEMAP = mutableMapOf<UUID, EditorState>()
fun updateEditorState(playerUUID: UUID, editorState: EditorState) {
STATEMAP[playerUUID] = editorState
LOGGER.info("$playerUUID: ${editorState.editorMode} with ${editorState.editorModifier}")
}
/*
* Check if a player should be able to perform an action
* */
fun shouldPlayer(playerUUID: UUID, capability: Capability): Boolean {
if (!STATEMAP.containsKey(playerUUID))
return false
val state = STATEMAP[playerUUID]!!
return EditorUtil.shouldPlayer(capability, state)
}
// fun playerHasModifier(playerUUID: UUID, modifier: ModeModifier): Boolean {
// if (!STATEMAP.containsKey(playerUUID))
// return false
// return STATEMAP[playerUUID]!!.editorModifier.contains(modifier) ?: false
// }
}

View file

@ -0,0 +1,29 @@
/*
* 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.editor.state
import kotlinx.serialization.Serializable
import net.moonleay.gimbal.editor.state.mode.Mode
import net.moonleay.gimbal.editor.state.mode.ModeModifier
@Serializable
data class EditorState(
var editorMode: Mode,
var editorModifier: MutableList<ModeModifier>,
)

View file

@ -0,0 +1,25 @@
/*
* 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.editor.state
enum class GimbalPolicyType {
ALLOWED,
DENIED,
NOT_PRESENT
}

View file

@ -0,0 +1,29 @@
/*
* 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.editor.state.mode
enum class Capability {
INSERT,
REPLACE,
CAN_INTERACT,
NO_CLIP,
FORCE_PLACE,
NO_UPDATES,
BULLDOZER,
}

View file

@ -0,0 +1,27 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.editor.state.mode
enum class Mode(val displayName: String, val color: Int, val incompatibleModifiers: List<ModeModifier>){
UNKNOWN("UNKNOWN", 0x000000, listOf()), // Unknown mode. This mode cannot be entered
NORMAL("NORMAL", 0x90a959, listOf(ModeModifier.NO_UPDATES, ModeModifier.BULLDOZER, ModeModifier.FORCE_PLACE)), // Do nothing
INSERT("INSERT", 0xf4bf75, listOf()), // Place and break blocks
REPLACE("REPLACE", 0xac4242, listOf(ModeModifier.NO_UPDATES)), // Replace blocks
VISUAL("VISUAL", 0x6a9fb5, listOf(ModeModifier.BULLDOZER)), // Do fancy stuff with WE
}

View file

@ -0,0 +1,27 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.editor.state.mode
enum class ModeModifier(val displayName: String) {
// NONE("None"), // No Modifiers - default behavior
NO_UPDATES("NO UPDATES"), // Do not update blocks when placing
BULLDOZER("BULLDOZER"), // Break blocks fast
FORCE_PLACE("FORCE"), // Ignore block placement restrictions
NO_CLIP("NO CLIP"), // Do not collide with blocks
}

View file

@ -0,0 +1,63 @@
/*
* 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.editor.util
import net.moonleay.gimbal.editor.state.EditorState
import net.moonleay.gimbal.editor.state.mode.Capability
import net.moonleay.gimbal.editor.state.mode.Mode
import net.moonleay.gimbal.editor.state.mode.ModeModifier
object EditorUtil {
/*
* Check if a player should be able to perform an action
* */
fun shouldPlayer(capability: Capability, state: EditorState): Boolean {
when (capability) {
Capability.INSERT -> {
return state.editorMode == Mode.INSERT
}
Capability.REPLACE -> {
return state.editorMode == Mode.REPLACE
}
Capability.CAN_INTERACT -> {
return state.editorMode != Mode.VISUAL && state.editorMode != Mode.NORMAL
}
Capability.NO_CLIP -> {
return state.editorModifier.contains(ModeModifier.NO_CLIP)
}
Capability.FORCE_PLACE -> {
return state.editorModifier.contains(ModeModifier.FORCE_PLACE)
}
Capability.NO_UPDATES -> {
return state.editorModifier.contains(ModeModifier.NO_UPDATES)
}
Capability.BULLDOZER -> {
return state.editorModifier.contains(ModeModifier.BULLDOZER)
}
}
}
}

View file

@ -0,0 +1,27 @@
/*
* Gimbal
* Copyright (C) 2024 moonleay
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.moonleay.gimbal.editor.util
import kotlinx.serialization.Serializable
import net.moonleay.gimbal.editor.state.GimbalPolicyType
@Serializable
data class GimbalPolicy(
val policy: GimbalPolicyType,
)

View file

@ -0,0 +1,61 @@
/*
* 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.network.ClientPlayerEntity;
import net.moonleay.gimbal.client.editor.ClientEditor;
import net.moonleay.gimbal.editor.state.mode.Capability;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Objects;
@Mixin(MinecraftClient.class)
public abstract class BulldozerMixin {
@Shadow protected int attackCooldown;
@Shadow
@Nullable
public ClientPlayerEntity player;
@Inject(method = "doAttack", at = @At(value = "HEAD"))
private void func(CallbackInfoReturnable<Boolean> cir) {
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !Objects.requireNonNull(this.player).isCreative()) {
return;
}
this.attackCooldown = 0;
}
@Inject(method = "handleBlockBreaking", at = @At(value = "HEAD"))
private void func2(boolean breaking, CallbackInfo ci) {
if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !breaking || !Objects.requireNonNull(this.player).isCreative()) {
return;
}
this.attackCooldown = 0;
}
}

View file

@ -0,0 +1,52 @@
/*
* 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.network.ClientPlayerInteractionManager;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.moonleay.gimbal.client.editor.ClientEditor;
import net.moonleay.gimbal.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Objects;
@Mixin(ClientPlayerInteractionManager.class)
public class BulldozerMixin2 {
@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;
}
}

View file

@ -0,0 +1,51 @@
/*
* 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.ItemPlacementContext;
import net.moonleay.gimbal.editor.ServerEditorManager;
import net.moonleay.gimbal.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.UUID;
@Mixin(BlockItem.class)
public class ForcePlaceMixin {
@Inject(method = "canPlace", at = @At("HEAD"), cancellable = true)
private void func(ItemPlacementContext context, BlockState state, CallbackInfoReturnable<Boolean> cir) {
if (context.getPlayer() == null)
return;
if (!context.getPlayer().isCreative())
return;
UUID id = context.getPlayer().getGameProfile().getId();
if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.FORCE_PLACE)) {
return;
}
cir.setReturnValue(true);
cir.cancel();
}
}

View file

@ -0,0 +1,36 @@
/*
* 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.DownloadingTerrainScreen;
import net.moonleay.gimbal.client.editor.ClientEditor;
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.CallbackInfo;
@Mixin(DownloadingTerrainScreen.class)
public class GimbalPolicyCheckMixin {
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/DownloadingTerrainScreen;close()V", ordinal = -1))
private void func(CallbackInfo ci) {
ClientEditor.INSTANCE.onConnectedToNewWorld();
}
}

View file

@ -0,0 +1,46 @@
/*
* 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.font.TextRenderer;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.util.math.MatrixStack;
import net.moonleay.gimbal.client.editor.ClientEditor;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(InGameHud.class)
public class HudMixin {
@Shadow @Final private MinecraftClient client;
@Inject(method = "renderStatusEffectOverlay", at = @At("HEAD"))
private void render(MatrixStack matrices, CallbackInfo ci) {
TextRenderer tr = this.client.textRenderer;
tr.drawWithShadow(matrices,
ClientEditor.INSTANCE.getModeDisplayText(),
4, 4,
ClientEditor.INSTANCE.getCurrentColor());
}
}

View file

@ -0,0 +1,52 @@
/*
* 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.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.moonleay.gimbal.editor.ServerEditorManager;
import net.moonleay.gimbal.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.UUID;
@Mixin(BlockItem.class)
public class NoBlockUpdatesMixin {
@Inject(method = "place(Lnet/minecraft/item/ItemPlacementContext;Lnet/minecraft/block/BlockState;)Z", at = @At("HEAD"), cancellable = true)
private void func(ItemPlacementContext context, BlockState state, CallbackInfoReturnable<Boolean> cir) {
if (context.getPlayer() == null)
return;
if (!context.getPlayer().isCreative())
return;
UUID id = context.getPlayer().getGameProfile().getId();
if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.NO_UPDATES)) {
return;
}
cir.setReturnValue(context.getWorld().setBlockState(context.getBlockPos(), state, Block.REDRAW_ON_MAIN_THREAD, 0)); // This is scuffed, but works
cir.cancel();
}
}

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

@ -0,0 +1,82 @@
/*
* 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 com.mojang.authlib.GameProfile;
import net.minecraft.entity.EntityPose;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerAbilities;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.world.World;
import net.moonleay.gimbal.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.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.UUID;
@Mixin(PlayerEntity.class)
public abstract class NoClipMixin extends LivingEntity {
@Shadow public abstract GameProfile getGameProfile();
@Shadow public abstract PlayerAbilities getAbilities();
@Shadow
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) {
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);
}
}

View file

@ -0,0 +1,75 @@
/*
* 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.network.ClientPlayerEntity;
import net.minecraft.client.option.GameOptions;
import net.moonleay.gimbal.client.editor.ClientEditor;
import net.moonleay.gimbal.editor.state.mode.Capability;
import net.moonleay.gimbal.editor.state.mode.Mode;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class)
public class NormalModeMixin {
@Shadow
@Nullable
public GameOptions options;
@Shadow
@Nullable
public ClientPlayerEntity player;
@Inject(method = "openPauseMenu", at = @At("HEAD"), cancellable = true)
private void setNormalMode(boolean pause, CallbackInfo ci) {
if (ClientEditor.INSTANCE.isInNonDefaultMode() && ClientEditor.INSTANCE.isAllowed()) {
assert this.player != null;
if (this.player.isCreative()) {
// Set the editor mode to normal
ClientEditor.INSTANCE.setMode(Mode.NORMAL);
ci.cancel();
}
}
}
@Inject(method = "handleInputEvents", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isUsingItem()Z"), cancellable = true)
private void blockWorldManipulation(CallbackInfo ci) {
// This could be replaced by net.minecraft.world.GameMode#isBlockBreakingRestricted
if (!ClientEditor.INSTANCE.shouldClient(Capability.CAN_INTERACT)) {
assert this.player != null;
if (this.player.isCreative()) {
while (this.options.attackKey.wasPressed()) {
}
while (this.options.useKey.wasPressed()) {
}
while (this.options.pickItemKey.wasPressed()) {
}
ci.cancel();
}
}
}
}

View file

@ -0,0 +1,87 @@
/*
* 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.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.moonleay.gimbal.client.editor.ClientEditor;
import net.moonleay.gimbal.editor.state.mode.Capability;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class)
public abstract class ReplaceModeMixin {
@Shadow protected abstract void handleBlockBreaking(boolean breaking);
@Shadow @Nullable public ClientPlayerInteractionManager interactionManager;
@Shadow @Final public ParticleManager particleManager;
@Shadow @Nullable public ClientPlayerEntity player;
@Shadow @Nullable public HitResult crosshairTarget;
@Inject(method = "doItemUse", at = @At("HEAD"))
private void replaceBlock(CallbackInfo ci) {
assert this.player != null;
if (!this.player.isCreative())
return;
// Check if should run
if (!ClientEditor.INSTANCE.shouldClient(Capability.REPLACE))
return; // Mode is not REPLACE, ignore
MinecraftClient client = MinecraftClient.getInstance();
assert this.interactionManager != null;
if (!this.interactionManager.getCurrentGameMode().isCreative())
return;
if (!(this.crosshairTarget instanceof BlockHitResult blockHitResult))
return;
if (blockHitResult == null)
return;
// Gather data
BlockPos pos = blockHitResult.getBlockPos();
Direction direction = blockHitResult.getSide();
BlockState blockState = client.world.getBlockState(pos);
// Start sending shit
client.getTutorialManager().onBlockBreaking(client.world, pos, blockState, 1.0F);
this.interactionManager.sendSequencedPacket(client.world, sequence -> {
this.interactionManager.breakBlock(pos);
return new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, pos, direction, sequence);
});
this.player.swingHand(Hand.MAIN_HAND);
}
}

View file

@ -0,0 +1,62 @@
/*
* 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.networking
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
import net.minecraft.network.PacketByteBuf
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.editor.state.EditorState
import net.moonleay.gimbal.editor.util.GimbalPolicy
object GimbalClient {
fun registerPacketHandlers() {
ClientPlayNetworking.registerGlobalReceiver(PacketIDs.TRANSFER_GIMBLE_POLICY_ID) { _, _, buf, _ ->
onAllowedCheck(buf)
}
}
@OptIn(ExperimentalSerializationApi::class)
private fun onAllowedCheck(buf: PacketByteBuf) {
val policy = Cbor.decodeFromByteArray<GimbalPolicy>(buf.readByteArray())
ClientEditor.onAllowedCheck(policy) // Update the client's policy
}
/**
* Sends the given [EditorState] to the server.
*/
@OptIn(ExperimentalSerializationApi::class)
fun sendEditorState(state: EditorState) {
val buf = PacketByteBufs.create()
buf.writeByteArray(Cbor.encodeToByteArray(state))
ClientPlayNetworking.send(PacketIDs.UPDATE_EDITOR_STATE_ID, buf)
}
@OptIn(ExperimentalSerializationApi::class)
fun checkIfServerHasGimble(state: EditorState) {
val buf = PacketByteBufs.create()
buf.writeByteArray(Cbor.encodeToByteArray(state))
ClientPlayNetworking.send(PacketIDs.GIMBLE_PRERENCE_CHECK_ID, buf)
}
}

View file

@ -0,0 +1,62 @@
/*
* 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.networking
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.network.ServerPlayerEntity
import net.moonleay.gimbal.editor.ServerEditorManager
import net.moonleay.gimbal.editor.state.EditorState
import net.moonleay.gimbal.editor.state.GimbalPolicyType
import net.moonleay.gimbal.editor.util.GimbalPolicy
object GimbalServer {
fun registerPacketHandler() {
ServerPlayNetworking
.registerGlobalReceiver(PacketIDs.UPDATE_EDITOR_STATE_ID)
{ server, player, handler, buf, responseSender ->
handleStateUpdate(player, buf)
}
ServerPlayNetworking
.registerGlobalReceiver(PacketIDs.GIMBLE_PRERENCE_CHECK_ID)
{ server, player, handler, buf, responseSender ->
handlePresenceCheck(player, buf)
}
}
private fun handleStateUpdate(player: ServerPlayerEntity, buf: PacketByteBuf){
val state = Cbor.decodeFromByteArray<EditorState>(buf.readByteArray())
ServerEditorManager.updateEditorState(player.uuid, state)
// player.sendMessage(Text.of("Mode: ${state.editorMode} with ${state.editorModifier}"))
}
private fun handlePresenceCheck(player: ServerPlayerEntity, buf: PacketByteBuf) {
val state = Cbor.decodeFromByteArray<EditorState>(buf.readByteArray())
ServerEditorManager.updateEditorState(player.uuid, state)
val buffer = PacketByteBufs.create()
buffer.writeByteArray(Cbor.encodeToByteArray(GimbalPolicy(GimbalPolicyType.ALLOWED)))
ServerPlayNetworking.send(player, PacketIDs.TRANSFER_GIMBLE_POLICY_ID, buffer)
}
}

View file

@ -0,0 +1,29 @@
/*
* 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.networking
import net.minecraft.util.Identifier
import net.moonleay.gimbal.build.BuildConstants
object PacketIDs {
val UPDATE_EDITOR_STATE_ID = Identifier(BuildConstants.modId, "update_editor_state")
val GIMBLE_PRERENCE_CHECK_ID = Identifier(BuildConstants.modId, "gimble_preference_check")
val TRANSFER_GIMBLE_POLICY_ID = Identifier(BuildConstants.modId, "gimble_is_present")
}