feat!: added Gimble Server side check, reworked mode & modifier checks

Signed-off-by: moonleay <contact@moonleay.net>
moonleay 2024-04-28 02:38:52 +02:00
parent 724618a052
commit acd318c5f1
Signed by: moonleay
GPG key ID: 82667543CCD715FB
26 changed files with 305 additions and 116 deletions

@ -1,10 +1,11 @@
package net.moonleay.gimble.client package net.moonleay.gimble.client
import net.moonleay.gimble.build.BuildConstants
import net.fabricmc.api.ClientModInitializer import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import net.moonleay.gimble.build.BuildConstants
import net.moonleay.gimble.client.keybindings.KeybindingManager import net.moonleay.gimble.client.keybindings.KeybindingManager
import net.moonleay.gimble.client.keybindings.KeybindingRegistrar import net.moonleay.gimble.client.keybindings.KeybindingRegistrar
import net.moonleay.gimble.networking.GimbleClient
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
internal object ClientMain : ClientModInitializer { internal object ClientMain : ClientModInitializer {
@ -15,6 +16,9 @@ internal object ClientMain : ClientModInitializer {
LOGGER.info("Initializing Gimble on the client side...") LOGGER.info("Initializing Gimble on the client side...")
KeybindingRegistrar.registerKeybindings() KeybindingRegistrar.registerKeybindings()
registerEvents() registerEvents()
LOGGER.info("Registering packets...")
GimbleClient.registerPacketHandlers()
LOGGER.info("Packets have been registered.")
LOGGER.info("Gimble has been initialized on the client side.") LOGGER.info("Gimble has been initialized on the client side.")
} }

@ -1,29 +1,97 @@
package net.moonleay.gimble.client.editor package net.moonleay.gimble.client.editor
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.text.Text
import net.moonleay.gimble.client.util.ChatUtil import net.moonleay.gimble.client.util.ChatUtil
import net.moonleay.gimble.editor.ServerEditorManager import net.moonleay.gimble.editor.ServerEditorManager
import net.moonleay.gimble.editor.state.EditorState import net.moonleay.gimble.editor.state.EditorState
import net.moonleay.gimble.editor.state.GimblePolicyType
import net.moonleay.gimble.editor.state.mode.Capability
import net.moonleay.gimble.editor.state.mode.Mode import net.moonleay.gimble.editor.state.mode.Mode
import net.moonleay.gimble.editor.state.mode.ModeModifier import net.moonleay.gimble.editor.state.mode.ModeModifier
import net.moonleay.gimble.editor.util.EditorUtil
import net.moonleay.gimble.editor.util.GimblePolicy
import net.moonleay.gimble.networking.GimbleClient import net.moonleay.gimble.networking.GimbleClient
object ClientEditor { object ClientEditor {
var CURRENT_MODE = Mode.NORMAL private var POLICY = GimblePolicyType.NOT_PRESENT
val CURRENT_MODE_MODIFIER = mutableListOf<ModeModifier>()
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 TEMP_DISABLED_MODIFIERS = mutableListOf<ModeModifier>()
private val DISABLED_MODIFIERS_STORAGE = mutableListOf<ModeModifier>()
fun onConnectedToNewWorld() {
POLICY = GimblePolicyType.NOT_PRESENT
GimbleClient.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: GimblePolicy) {
POLICY = data.policy
if (data.policy == GimblePolicyType.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()
this.onUpdated()
}
}
fun isAllowed(): Boolean {
return POLICY == GimblePolicyType.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 * Send an updated player state to the server
* */ * */
fun updateServerState() { private fun updateServerState() {
val state = EditorState(CURRENT_MODE, CURRENT_MODE_MODIFIER) val state = EditorState(CURRENT_MODE, CURRENT_MODE_MODIFIER)
GimbleClient.sendEditorState(state) GimbleClient.sendEditorState(state)
ServerEditorManager.updateEditorState(MinecraftClient.getInstance().player!!.uuid, 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
}
CURRENT_MODE = mode
this.onUpdated()
}
/*
* Toggle a mode modifier
* */
fun toggleModifier(mod: ModeModifier) { fun toggleModifier(mod: ModeModifier) {
if (!isAllowed()) {
ChatUtil.showToastToSelf("Gimble is disabled", "You cannot change modifiers", MinecraftClient.getInstance())
return
}
if (CURRENT_MODE.incompatibleModifiers.contains(mod)){ if (CURRENT_MODE.incompatibleModifiers.contains(mod)){
if (TEMP_DISABLED_MODIFIERS.contains(mod)) if (TEMP_DISABLED_MODIFIERS.contains(mod))
TEMP_DISABLED_MODIFIERS.remove(mod) TEMP_DISABLED_MODIFIERS.remove(mod)
@ -37,18 +105,20 @@ object ClientEditor {
CURRENT_MODE_MODIFIER.add(mod) CURRENT_MODE_MODIFIER.add(mod)
} }
onModifiersUpdated() this.onUpdated()
} }
fun onModifiersUpdated() { private fun onUpdated() {
CURRENT_MODE_MODIFIER.sort() CURRENT_MODE_MODIFIER.sortBy {
checkForIncompatibleModeModifiers() it.displayName
}
this.checkForIncompatibleModeModifiers()
} }
/** /**
* This runs on Mode updated * This runs on Mode updated
*/ */
fun checkForIncompatibleModeModifiers() { private fun checkForIncompatibleModeModifiers() {
if (TEMP_DISABLED_MODIFIERS.size > 0) { if (TEMP_DISABLED_MODIFIERS.size > 0) {
CURRENT_MODE_MODIFIER.addAll( CURRENT_MODE_MODIFIER.addAll(
TEMP_DISABLED_MODIFIERS TEMP_DISABLED_MODIFIERS
@ -68,6 +138,10 @@ object ClientEditor {
} }
} }
CURRENT_MODE_MODIFIER.sortBy {
it.displayName
}
// Update State // Update State
this.updateServerState() this.updateServerState()
@ -87,15 +161,27 @@ object ClientEditor {
return sb.toString().dropLast(2) return sb.toString().dropLast(2)
} }
fun getCurrentMode(): Mode { /*
return CURRENT_MODE * Get the display text to display in the HUD
* */
fun getModeDisplayText(): Text {
val displayText = StringBuilder(CURRENT_MODE.displayName)
if (CURRENT_MODE_MODIFIER.isNotEmpty() && this.isAllowed()) {
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 (!this.isAllowed()) {
displayText.append(" [DISABLED]")
}
return Text.of(displayText.toString())
} }
fun getCurrentModifier(): List<ModeModifier> { fun getCurrentColor(): Int {
return CURRENT_MODE_MODIFIER return CURRENT_MODE.color
}
fun containsModifier(mod: ModeModifier): Boolean {
return CURRENT_MODE_MODIFIER.contains(mod)
} }
} }

@ -5,7 +5,6 @@ import net.minecraft.client.util.InputUtil
import net.moonleay.gimble.build.BuildConstants import net.moonleay.gimble.build.BuildConstants
import net.moonleay.gimble.client.keybindings.impl.editormode.EnableInsertModeShortcut import net.moonleay.gimble.client.keybindings.impl.editormode.EnableInsertModeShortcut
import net.moonleay.gimble.client.keybindings.impl.editormode.EnableReplaceModeShortcut import net.moonleay.gimble.client.keybindings.impl.editormode.EnableReplaceModeShortcut
import net.moonleay.gimble.client.keybindings.impl.editormode.EnableVisualModeShortcut
import net.moonleay.gimble.client.keybindings.impl.editormodemodifier.ToggleBulldozerModifierShortcut import net.moonleay.gimble.client.keybindings.impl.editormodemodifier.ToggleBulldozerModifierShortcut
import net.moonleay.gimble.client.keybindings.impl.editormodemodifier.ToggleForcePlaceModifierShortcut import net.moonleay.gimble.client.keybindings.impl.editormodemodifier.ToggleForcePlaceModifierShortcut
import net.moonleay.gimble.client.keybindings.impl.editormodemodifier.ToggleNoClipModifierShortcut import net.moonleay.gimble.client.keybindings.impl.editormodemodifier.ToggleNoClipModifierShortcut
@ -32,25 +31,25 @@ object KeybindingRegistrar {
val toggleBulldozerModifierShortcut = KeyBinding( val toggleBulldozerModifierShortcut = KeyBinding(
"gimble.key.editor.modifier.bulldozer", "gimble.key.editor.modifier.bulldozer",
InputUtil.Type.KEYSYM, InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_B, GLFW.GLFW_KEY_UNKNOWN,
"gimble.category.editormodifier" "gimble.category.editormodifier"
) )
val toggleForcePlaceModifierShortcut = KeyBinding( val toggleForcePlaceModifierShortcut = KeyBinding(
"gimble.key.editor.modifier.forceplace", "gimble.key.editor.modifier.forceplace",
InputUtil.Type.KEYSYM, InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_F, GLFW.GLFW_KEY_UNKNOWN,
"gimble.category.editormodifier" "gimble.category.editormodifier"
) )
val toggleNoUpdatesModifierShortcut = KeyBinding( val toggleNoUpdatesModifierShortcut = KeyBinding(
"gimble.key.editor.modifier.noupdates", "gimble.key.editor.modifier.noupdates",
InputUtil.Type.KEYSYM, InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_N, GLFW.GLFW_KEY_UNKNOWN,
"gimble.category.editormodifier" "gimble.category.editormodifier"
) )
val toggleNoClipModifierShortcut = KeyBinding( val toggleNoClipModifierShortcut = KeyBinding(
"gimble.key.editor.modifier.noclip", "gimble.key.editor.modifier.noclip",
InputUtil.Type.KEYSYM, InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_C, GLFW.GLFW_KEY_UNKNOWN,
"gimble.category.editormodifier" "gimble.category.editormodifier"
) )
@ -73,15 +72,15 @@ object KeybindingRegistrar {
GLFW.GLFW_KEY_R, GLFW.GLFW_KEY_R,
"gimble.category.editormode" "gimble.category.editormode"
) )
val visualKeyBinding = KeyBinding( // val visualKeyBinding = KeyBinding(
"gimble.key.editor.mode.visual", // "gimble.key.editor.mode.visual",
InputUtil.Type.KEYSYM, // InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_V, // GLFW.GLFW_KEY_V,
"gimble.category.editormode" // "gimble.category.editormode"
) // )
KeybindingManager.registerShortcut(EnableInsertModeShortcut(insertKeyBinding)) KeybindingManager.registerShortcut(EnableInsertModeShortcut(insertKeyBinding))
KeybindingManager.registerShortcut(EnableReplaceModeShortcut(replaceKeyBinding)) KeybindingManager.registerShortcut(EnableReplaceModeShortcut(replaceKeyBinding))
KeybindingManager.registerShortcut(EnableVisualModeShortcut(visualKeyBinding)) // KeybindingManager.registerShortcut(EnableVisualModeShortcut(visualKeyBinding))
} }
private fun registerSetGameModeKeybindings() { private fun registerSetGameModeKeybindings() {

@ -9,8 +9,6 @@ import net.moonleay.gimble.editor.state.mode.Mode
class EnableInsertModeShortcut(key: KeyBinding): GimbleShortcut(key) { class EnableInsertModeShortcut(key: KeyBinding): GimbleShortcut(key) {
override fun onPressed(client: MinecraftClient) { override fun onPressed(client: MinecraftClient) {
ClientEditor.CURRENT_MODE = Mode.INSERT ClientEditor.setMode(Mode.INSERT)
ClientEditor.checkForIncompatibleModeModifiers()
} }
} }

@ -9,8 +9,6 @@ import net.moonleay.gimble.editor.state.mode.Mode
class EnableReplaceModeShortcut(key: KeyBinding): GimbleShortcut(key) { class EnableReplaceModeShortcut(key: KeyBinding): GimbleShortcut(key) {
override fun onPressed(client: MinecraftClient) { override fun onPressed(client: MinecraftClient) {
ClientEditor.CURRENT_MODE = Mode.REPLACE ClientEditor.setMode(Mode.REPLACE)
ClientEditor.checkForIncompatibleModeModifiers()
} }
} }

@ -9,8 +9,6 @@ import net.moonleay.gimble.editor.state.mode.Mode
class EnableVisualModeShortcut(key: KeyBinding): GimbleShortcut(key) { class EnableVisualModeShortcut(key: KeyBinding): GimbleShortcut(key) {
override fun onPressed(client: MinecraftClient) { override fun onPressed(client: MinecraftClient) {
ClientEditor.CURRENT_MODE = Mode.VISUAL ClientEditor.setMode(Mode.VISUAL)
ClientEditor.checkForIncompatibleModeModifiers()
} }
} }

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

@ -0,0 +1,7 @@
package net.moonleay.gimble.editor.state
enum class GimblePolicyType {
ALLOWED,
DENIED,
NOT_PRESENT
}

@ -0,0 +1,11 @@
package net.moonleay.gimble.editor.state.mode
enum class Capability {
INSERT,
REPLACE,
CAN_INTERACT,
NO_CLIP,
FORCE_PLACE,
NO_UPDATES,
BULLDOZER,
}

@ -1,7 +1,7 @@
package net.moonleay.gimble.editor.state.mode package net.moonleay.gimble.editor.state.mode
enum class Mode(val displayName: String, val color: Int, val incompatibleModifiers: List<ModeModifier>){ enum class Mode(val displayName: String, val color: Int, val incompatibleModifiers: List<ModeModifier>){
// UNKNOWN("UNKNOWN", 0x000000, listOf()), // Unknown mode. This mode cannot be entered 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 NORMAL("NORMAL", 0x90a959, listOf(ModeModifier.NO_UPDATES, ModeModifier.BULLDOZER, ModeModifier.FORCE_PLACE)), // Do nothing
INSERT("INSERT", 0xf4bf75, listOf()), // Place and break blocks INSERT("INSERT", 0xf4bf75, listOf()), // Place and break blocks
REPLACE("REPLACE", 0xac4242, listOf()), // Replace blocks REPLACE("REPLACE", 0xac4242, listOf()), // Replace blocks

@ -0,0 +1,44 @@
package net.moonleay.gimble.editor.util
import net.moonleay.gimble.editor.state.EditorState
import net.moonleay.gimble.editor.state.mode.Capability
import net.moonleay.gimble.editor.state.mode.Mode
import net.moonleay.gimble.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)
}
}
}
}

@ -0,0 +1,9 @@
package net.moonleay.gimble.editor.util
import kotlinx.serialization.Serializable
import net.moonleay.gimble.editor.state.GimblePolicyType
@Serializable
data class GimblePolicy(
val policy: GimblePolicyType,
)

@ -2,7 +2,7 @@ package net.moonleay.gimble.mixin;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.moonleay.gimble.client.editor.ClientEditor; import net.moonleay.gimble.client.editor.ClientEditor;
import net.moonleay.gimble.editor.state.mode.ModeModifier; import net.moonleay.gimble.editor.state.mode.Capability;
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;
@ -17,7 +17,7 @@ public abstract class BulldozerMixin {
@Inject(method = "doAttack", at = @At(value = "HEAD")) @Inject(method = "doAttack", at = @At(value = "HEAD"))
private void func(CallbackInfoReturnable<Boolean> cir) { private void func(CallbackInfoReturnable<Boolean> cir) {
if (!ClientEditor.INSTANCE.containsModifier(ModeModifier.BULLDOZER)){ if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER)) {
return; return;
} }
this.attackCooldown = 0; this.attackCooldown = 0;
@ -25,7 +25,7 @@ public abstract class BulldozerMixin {
@Inject(method = "handleBlockBreaking", at = @At(value = "HEAD")) @Inject(method = "handleBlockBreaking", at = @At(value = "HEAD"))
private void func2(boolean breaking, CallbackInfo ci) { private void func2(boolean breaking, CallbackInfo ci) {
if (!ClientEditor.INSTANCE.containsModifier(ModeModifier.BULLDOZER) || !breaking){ if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER) || !breaking) {
return; return;
} }
this.attackCooldown = 0; this.attackCooldown = 0;

@ -4,7 +4,7 @@ import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.moonleay.gimble.client.editor.ClientEditor; import net.moonleay.gimble.client.editor.ClientEditor;
import net.moonleay.gimble.editor.state.mode.ModeModifier; import net.moonleay.gimble.editor.state.mode.Capability;
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;
@ -18,7 +18,7 @@ public class BulldozerMixin2 {
@Inject(method = "updateBlockBreakingProgress", at = @At("HEAD")) @Inject(method = "updateBlockBreakingProgress", at = @At("HEAD"))
private void func(BlockPos pos, Direction direction, CallbackInfoReturnable<Boolean> cir) { private void func(BlockPos pos, Direction direction, CallbackInfoReturnable<Boolean> cir) {
if (!ClientEditor.INSTANCE.containsModifier(ModeModifier.BULLDOZER)) { if (!ClientEditor.INSTANCE.shouldClient(Capability.BULLDOZER)) {
return; return;
} }
this.blockBreakingCooldown = 0; this.blockBreakingCooldown = 0;

@ -1,16 +1,17 @@
package net.moonleay.gimble.mixin; package net.moonleay.gimble.mixin;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.moonleay.gimble.editor.ServerEditorManager; import net.moonleay.gimble.editor.ServerEditorManager;
import net.moonleay.gimble.editor.state.mode.ModeModifier; import net.moonleay.gimble.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.UUID;
@Mixin(BlockItem.class) @Mixin(BlockItem.class)
public class ForcePlaceMixin { public class ForcePlaceMixin {
@ -19,9 +20,9 @@ public class ForcePlaceMixin {
if (context.getPlayer() == null) { if (context.getPlayer() == null) {
return; return;
} }
PlayerEntity player = context.getPlayer(); UUID id = context.getPlayer().getGameProfile().getId();
if (!ServerEditorManager.INSTANCE.playerHasModifier(player.getGameProfile().getId(), ModeModifier.FORCE_PLACE)) { if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.FORCE_PLACE)) {
return; return;
} }
cir.setReturnValue(true); cir.setReturnValue(true);

@ -0,0 +1,18 @@
package net.moonleay.gimble.mixin;
import net.minecraft.client.gui.screen.DownloadingTerrainScreen;
import net.moonleay.gimble.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 GimblePolicyCheckMixin {
@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();
}
}

@ -4,21 +4,14 @@ import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.hud.InGameHud; import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import net.moonleay.gimble.client.editor.ClientEditor; import net.moonleay.gimble.client.editor.ClientEditor;
import net.moonleay.gimble.editor.state.mode.Mode;
import net.moonleay.gimble.editor.state.mode.ModeModifier;
import org.jetbrains.annotations.NotNull;
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.Unique;
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 java.util.List;
@Mixin(InGameHud.class) @Mixin(InGameHud.class)
public class HudMixin { public class HudMixin {
@ -26,41 +19,10 @@ public class HudMixin {
@Inject(method = "renderStatusEffectOverlay", at = @At("HEAD")) @Inject(method = "renderStatusEffectOverlay", at = @At("HEAD"))
private void render(MatrixStack matrices, CallbackInfo ci) { private void render(MatrixStack matrices, CallbackInfo ci) {
Mode currentMode = ClientEditor.INSTANCE.getCurrentMode(); TextRenderer tr = this.client.textRenderer;
StringBuilder displayText = getStringBuilder(currentMode);
MinecraftClient mc = MinecraftClient.getInstance();
TextRenderer tr = mc.textRenderer;
// int screenWidth = mc.getWindow().getWidth();
// int screenHeight = mc.getWindow().getHeight();
// int scale = (mc.options.getGuiScale().getValue() == 0 ? 1 : mc.options.getGuiScale().getValue());
// int bottomY = screenHeight / scale - 4 - tr.fontHeight - 12;
tr.drawWithShadow(matrices, tr.drawWithShadow(matrices,
Text.of(displayText.toString()), ClientEditor.INSTANCE.getModeDisplayText(),
4, 4, 4, 4,
currentMode.getColor()); ClientEditor.INSTANCE.getCurrentColor());
}
@Unique
private static @NotNull StringBuilder getStringBuilder(Mode currentMode) {
List<ModeModifier> currentModifier = ClientEditor.INSTANCE.getCurrentModifier();
StringBuilder displayText = new StringBuilder(currentMode.getDisplayName());
if (!currentModifier.isEmpty()) {
displayText.append(" [");
for (int i = 0; i < currentModifier.size(); i++) {
displayText.append(currentModifier.get(i).getDisplayName());
if (i != currentModifier.size() - 1) {
displayText.append(", ");
}
}
displayText.append("]");
}
return displayText;
}
@Unique
private int getXRight(String txt, int screenWidth) {
return screenWidth / (MinecraftClient.getInstance().options.getGuiScale().getValue() == 0 ? 1 : MinecraftClient.getInstance().options.getGuiScale().getValue()) - 4 - MinecraftClient.getInstance().textRenderer.getWidth(Text.of(txt));
} }
} }

@ -2,16 +2,17 @@ package net.moonleay.gimble.mixin;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.moonleay.gimble.editor.ServerEditorManager; import net.moonleay.gimble.editor.ServerEditorManager;
import net.moonleay.gimble.editor.state.mode.ModeModifier; import net.moonleay.gimble.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.UUID;
@Mixin(BlockItem.class) @Mixin(BlockItem.class)
public class NoBlockUpdatesMixin { public class NoBlockUpdatesMixin {
@ -20,9 +21,9 @@ public class NoBlockUpdatesMixin {
if (context.getPlayer() == null) { if (context.getPlayer() == null) {
return; return;
} }
PlayerEntity player = context.getPlayer(); UUID id = context.getPlayer().getGameProfile().getId();
if (!ServerEditorManager.INSTANCE.playerHasModifier(player.getGameProfile().getId(), ModeModifier.NO_UPDATES)) { if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.NO_UPDATES)) {
return; return;
} }

@ -2,7 +2,7 @@ package net.moonleay.gimble.mixin;
import net.minecraft.client.render.Camera; import net.minecraft.client.render.Camera;
import net.moonleay.gimble.client.editor.ClientEditor; import net.moonleay.gimble.client.editor.ClientEditor;
import net.moonleay.gimble.editor.state.mode.ModeModifier; import net.moonleay.gimble.editor.state.mode.Capability;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
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;
@ -13,9 +13,10 @@ public class NoClipCameraFixMixin {
@Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true) @Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true)
private void fixCameraInNoClip(double desiredCameraDistance, CallbackInfoReturnable<Double> cir) { private void fixCameraInNoClip(double desiredCameraDistance, CallbackInfoReturnable<Double> cir) {
if (ClientEditor.INSTANCE.containsModifier(ModeModifier.NO_CLIP)){ if (!ClientEditor.INSTANCE.shouldClient(Capability.NO_CLIP)) {
return;
}
cir.setReturnValue(desiredCameraDistance); cir.setReturnValue(desiredCameraDistance);
cir.cancel(); cir.cancel();
} }
}
} }

@ -8,7 +8,7 @@ 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.gimble.editor.ServerEditorManager; import net.moonleay.gimble.editor.ServerEditorManager;
import net.moonleay.gimble.editor.state.mode.ModeModifier; import net.moonleay.gimble.editor.state.mode.Capability;
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;
@ -33,7 +33,7 @@ public abstract class NoClipMixin extends LivingEntity {
) private void enoClip(CallbackInfo ci) { ) private void enoClip(CallbackInfo ci) {
UUID uuid = this.getGameProfile().getId(); UUID uuid = this.getGameProfile().getId();
if (!ServerEditorManager.INSTANCE.playerHasModifier(uuid, ModeModifier.NO_CLIP)) { if (!ServerEditorManager.INSTANCE.shouldPlayer(uuid, Capability.NO_CLIP)) {
return; // NoClip is not enabled return; // NoClip is not enabled
} }
if (!this.getAbilities().flying) { if (!this.getAbilities().flying) {
@ -47,7 +47,7 @@ public abstract class NoClipMixin extends LivingEntity {
private void onUpdatePose(CallbackInfo ci) { private void onUpdatePose(CallbackInfo ci) {
UUID uuid = this.getGameProfile().getId(); UUID uuid = this.getGameProfile().getId();
if (!ServerEditorManager.INSTANCE.playerHasModifier(uuid, ModeModifier.NO_CLIP)) if (!ServerEditorManager.INSTANCE.shouldPlayer(uuid, Capability.NO_CLIP))
return; // NoClip is not enabled return; // NoClip is not enabled
if (!this.getAbilities().flying) if (!this.getAbilities().flying)
return; return;

@ -2,8 +2,9 @@ package net.moonleay.gimble.mixin;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.GameOptions; import net.minecraft.client.option.GameOptions;
import net.moonleay.gimble.editor.state.mode.Mode;
import net.moonleay.gimble.client.editor.ClientEditor; import net.moonleay.gimble.client.editor.ClientEditor;
import net.moonleay.gimble.editor.state.mode.Capability;
import net.moonleay.gimble.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;
@ -20,17 +21,17 @@ public class NormalModeMixin {
@Inject(method = "openPauseMenu", at = @At("HEAD"), cancellable = true) @Inject(method = "openPauseMenu", at = @At("HEAD"), cancellable = true)
private void setNormalMode(boolean pause, CallbackInfo ci) { private void setNormalMode(boolean pause, CallbackInfo ci) {
if (ClientEditor.INSTANCE.getCURRENT_MODE() != Mode.NORMAL){ if (ClientEditor.INSTANCE.isInNonDefaultMode() && ClientEditor.INSTANCE.isAllowed()) {
// Set the editor mode to normal // Set the editor mode to normal
ClientEditor.INSTANCE.setCURRENT_MODE(Mode.NORMAL); ClientEditor.INSTANCE.setMode(Mode.NORMAL);
ClientEditor.INSTANCE.checkForIncompatibleModeModifiers();
ci.cancel(); ci.cancel();
} }
} }
@Inject(method = "handleInputEvents", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isUsingItem()Z"), cancellable = true) @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 private void blockWorldManipulation(CallbackInfo ci) {
if (ClientEditor.INSTANCE.getCURRENT_MODE() == Mode.NORMAL || ClientEditor.INSTANCE.getCURRENT_MODE() == Mode.VISUAL) { // This could be replaced by net.minecraft.world.GameMode#isBlockBreakingRestricted
if (!ClientEditor.INSTANCE.shouldClient(Capability.CAN_INTERACT)) {
while(this.options.attackKey.wasPressed()) { while(this.options.attackKey.wasPressed()) {
} }

@ -12,7 +12,7 @@ import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.moonleay.gimble.client.editor.ClientEditor; import net.moonleay.gimble.client.editor.ClientEditor;
import net.moonleay.gimble.editor.state.mode.Mode; import net.moonleay.gimble.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.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@ -38,7 +38,7 @@ public abstract class ReplaceModeMixin {
@Inject(method = "doItemUse", at = @At("HEAD")) @Inject(method = "doItemUse", at = @At("HEAD"))
private void replaceBlock(CallbackInfo ci) { private void replaceBlock(CallbackInfo ci) {
// Check if should run // Check if should run
if (!ClientEditor.INSTANCE.getCURRENT_MODE().equals(Mode.REPLACE)) if (!ClientEditor.INSTANCE.shouldClient(Capability.REPLACE))
return; // Mode is not REPLACE, ignore return; // Mode is not REPLACE, ignore
MinecraftClient client = MinecraftClient.getInstance(); MinecraftClient client = MinecraftClient.getInstance();
assert this.interactionManager != null; assert this.interactionManager != null;

@ -2,13 +2,28 @@ package net.moonleay.gimble.networking
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.cbor.Cbor import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.encodeToByteArray
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
import net.minecraft.network.PacketByteBuf
import net.moonleay.gimble.client.editor.ClientEditor
import net.moonleay.gimble.editor.state.EditorState import net.moonleay.gimble.editor.state.EditorState
import net.moonleay.gimble.editor.util.GimblePolicy
object GimbleClient { object GimbleClient {
fun registerPacketHandlers() {
ClientPlayNetworking.registerGlobalReceiver(PacketIDs.TRANSFER_GIMBLE_POLICY_ID) { _, _, buf, _ ->
onAllowedCheck(buf)
}
}
private fun onAllowedCheck(buf: PacketByteBuf) {
val policy = Cbor.decodeFromByteArray<GimblePolicy>(buf.readByteArray())
ClientEditor.onAllowedCheck(policy) // Update the client's policy
}
/** /**
* Sends the given [EditorState] to the server. * Sends the given [EditorState] to the server.
*/ */
@ -18,4 +33,10 @@ object GimbleClient {
buf.writeByteArray(Cbor.encodeToByteArray(state)) buf.writeByteArray(Cbor.encodeToByteArray(state))
ClientPlayNetworking.send(PacketIDs.UPDATE_EDITOR_STATE_ID, buf) ClientPlayNetworking.send(PacketIDs.UPDATE_EDITOR_STATE_ID, buf)
} }
fun checkIfServerHasGimble(state: EditorState) {
val buf = PacketByteBufs.create()
buf.writeByteArray(Cbor.encodeToByteArray(state))
ClientPlayNetworking.send(PacketIDs.GIMBLE_PRERENCE_CHECK_ID, buf)
}
} }

@ -2,12 +2,15 @@ package net.moonleay.gimble.networking
import kotlinx.serialization.cbor.Cbor import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromByteArray 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.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
import net.minecraft.network.PacketByteBuf import net.minecraft.network.PacketByteBuf
import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.moonleay.gimble.editor.ServerEditorManager import net.moonleay.gimble.editor.ServerEditorManager
import net.moonleay.gimble.editor.state.EditorState import net.moonleay.gimble.editor.state.EditorState
import net.moonleay.gimble.editor.state.GimblePolicyType
import net.moonleay.gimble.editor.util.GimblePolicy
object GimbleServer { object GimbleServer {
@ -17,6 +20,11 @@ object GimbleServer {
{ server, player, handler, buf, responseSender -> { server, player, handler, buf, responseSender ->
handleStateUpdate(player, buf) 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){ private fun handleStateUpdate(player: ServerPlayerEntity, buf: PacketByteBuf){
@ -24,4 +32,13 @@ object GimbleServer {
ServerEditorManager.updateEditorState(player.uuid, state) ServerEditorManager.updateEditorState(player.uuid, state)
// player.sendMessage(Text.of("Mode: ${state.editorMode} with ${state.editorModifier}")) // 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(GimblePolicy(GimblePolicyType.ALLOWED)))
ServerPlayNetworking.send(player, PacketIDs.TRANSFER_GIMBLE_POLICY_ID, buffer)
}
} }

@ -5,4 +5,7 @@ import net.moonleay.gimble.build.BuildConstants
object PacketIDs { object PacketIDs {
val UPDATE_EDITOR_STATE_ID = Identifier(BuildConstants.modId, "update_editor_state") 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")
} }

@ -11,6 +11,7 @@
"client": [ "client": [
"BulldozerMixin", "BulldozerMixin",
"BulldozerMixin2", "BulldozerMixin2",
"GimblePolicyCheckMixin",
"HudMixin", "HudMixin",
"NoClipCameraFixMixin", "NoClipCameraFixMixin",
"NormalModeMixin", "NormalModeMixin",