From dca01b275f96753c4e5e9906c5df756ce815f8a5 Mon Sep 17 00:00:00 2001 From: moonleay Date: Sat, 4 May 2024 02:35:49 +0200 Subject: [PATCH] feat!: reworked replace mode to work server side, added propper stair replacement support Signed-off-by: moonleay --- .../gimbal/mixin/ReplaceModeMixin.java | 78 +++++++------------ .../mixin/ReplaceStateUpdaterMixin.java | 74 ++++++++++++++++++ src/main/resources/gimbal.accesswidener | 1 + src/main/resources/gimbal.mixins.json | 7 +- 4 files changed, 108 insertions(+), 52 deletions(-) create mode 100644 src/main/java/net/moonleay/gimbal/mixin/ReplaceStateUpdaterMixin.java diff --git a/src/main/java/net/moonleay/gimbal/mixin/ReplaceModeMixin.java b/src/main/java/net/moonleay/gimbal/mixin/ReplaceModeMixin.java index 477b86d..8e2a4fa 100644 --- a/src/main/java/net/moonleay/gimbal/mixin/ReplaceModeMixin.java +++ b/src/main/java/net/moonleay/gimbal/mixin/ReplaceModeMixin.java @@ -18,70 +18,50 @@ 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.entity.player.PlayerEntity; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUsageContext; 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.minecraft.world.World; +import net.moonleay.gimbal.editor.ServerEditorManager; 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.Mutable; 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 { +import java.util.UUID; +@Mixin(ItemPlacementContext.class) +public abstract class ReplaceModeMixin extends ItemUsageContext { - @Shadow protected abstract void handleBlockBreaking(boolean breaking); + @Shadow + protected boolean canReplaceExisting; - @Shadow @Nullable public ClientPlayerInteractionManager interactionManager; + @Mutable + @Shadow + @Final + private BlockPos placementPos; - @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); + public ReplaceModeMixin(PlayerEntity player, Hand hand, BlockHitResult hit) { + super(player, hand, hit); } + @Inject(method = "(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/item/ItemStack;Lnet/minecraft/util/hit/BlockHitResult;)V", at = @At(value = "RETURN")) + private void func(World world, PlayerEntity playerEntity, Hand hand, ItemStack itemStack, BlockHitResult blockHitResult, CallbackInfo ci) { + if (playerEntity == null) + return; + UUID id = playerEntity.getGameProfile().getId(); + if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.REPLACE)) + return; + + this.canReplaceExisting = true; + this.placementPos = blockHitResult.getBlockPos(); + } } diff --git a/src/main/java/net/moonleay/gimbal/mixin/ReplaceStateUpdaterMixin.java b/src/main/java/net/moonleay/gimbal/mixin/ReplaceStateUpdaterMixin.java new file mode 100644 index 0000000..7d0772d --- /dev/null +++ b/src/main/java/net/moonleay/gimbal/mixin/ReplaceStateUpdaterMixin.java @@ -0,0 +1,74 @@ +/* + * Gimbal + * Copyright (C) 2024 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.gimbal.mixin; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.StairsBlock; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemPlacementContext; +import net.moonleay.gimbal.editor.ServerEditorManager; +import net.moonleay.gimbal.editor.state.mode.Capability; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.UUID; + +import static net.minecraft.block.StairsBlock.*; + +@Mixin(BlockItem.class) +public abstract class ReplaceStateUpdaterMixin extends Item { + + public ReplaceStateUpdaterMixin(Settings settings) { + super(settings); + } + + @Shadow + protected abstract boolean place(ItemPlacementContext context, BlockState state); + + @Redirect(method = "place(Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/util/ActionResult;", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;place(Lnet/minecraft/item/ItemPlacementContext;Lnet/minecraft/block/BlockState;)Z")) + private boolean func(BlockItem instance, ItemPlacementContext context, BlockState state) { + if (context.getPlayer() == null) + return this.place(context, state); + UUID id = context.getPlayer().getGameProfile().getId(); + if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.REPLACE)) + return this.place(context, state); + BlockState oldState = context.getWorld().getBlockState(context.getBlockPos()); + Block targetBl = oldState.getBlock(); + if (state.getBlock() instanceof StairsBlock && targetBl instanceof StairsBlock targetStair) { + // Block and item is stairs, parse Stairs data + return this.place(context, copyStairState(oldState, instance)); + } + return this.place(context, state); + } + + + @Unique + public BlockState copyStairState(BlockState targetState, BlockItem item) { + BlockState blockState = item.getBlock().getDefaultState() + .with(FACING, targetState.get(FACING)) + .with(HALF, targetState.get(HALF)) + .with(WATERLOGGED, targetState.get(WATERLOGGED)); + return blockState.with(SHAPE, targetState.get(SHAPE)); + } +} diff --git a/src/main/resources/gimbal.accesswidener b/src/main/resources/gimbal.accesswidener index e659ad2..ee44473 100644 --- a/src/main/resources/gimbal.accesswidener +++ b/src/main/resources/gimbal.accesswidener @@ -2,3 +2,4 @@ accessWidener v2 named accessible method net/minecraft/client/MinecraftClient handleBlockBreaking (Z)V accessible method net/minecraft/client/network/ClientPlayerInteractionManager sendSequencedPacket (Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/client/network/SequencedPacketCreator;)V +accessible field net/minecraft/state/State owner Ljava/lang/Object; diff --git a/src/main/resources/gimbal.mixins.json b/src/main/resources/gimbal.mixins.json index 1b47528..adf557d 100644 --- a/src/main/resources/gimbal.mixins.json +++ b/src/main/resources/gimbal.mixins.json @@ -6,15 +6,16 @@ "mixins": [ "ForcePlaceMixin", "NoBlockUpdatesMixin", - "NoClipMixin" + "NoClipMixin", + "ReplaceModeMixin", + "ReplaceStateUpdaterMixin" ], "client": [ "BulldozerMixin", "BulldozerMixin2", "HudMixin", "NoClipCameraFixMixin", - "NormalModeMixin", - "ReplaceModeMixin" + "NormalModeMixin" ], "injectors": { "defaultRequire": 1