mirror of
https://codeberg.org/moonleay/Gimbal.git
synced 2024-11-25 22:22:10 +00:00
feat!: reworked replace mode to work server side, added propper stair replacement support
Signed-off-by: moonleay <contact@moonleay.net>
This commit is contained in:
parent
c321746b08
commit
dca01b275f
4 changed files with 108 additions and 52 deletions
|
@ -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 = "<init>(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/item/ItemStack;Lnet/minecraft/util/hit/BlockHitResult;)V", at = @At(value = "RETURN"))
|
||||
private void func(World world, PlayerEntity playerEntity, Hand hand, ItemStack itemStack, BlockHitResult blockHitResult, CallbackInfo ci) {
|
||||
if (playerEntity == null)
|
||||
return;
|
||||
UUID id = playerEntity.getGameProfile().getId();
|
||||
if (!ServerEditorManager.INSTANCE.shouldPlayer(id, Capability.REPLACE))
|
||||
return;
|
||||
|
||||
this.canReplaceExisting = true;
|
||||
this.placementPos = blockHitResult.getBlockPos();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -6,15 +6,16 @@
|
|||
"mixins": [
|
||||
"ForcePlaceMixin",
|
||||
"NoBlockUpdatesMixin",
|
||||
"NoClipMixin"
|
||||
"NoClipMixin",
|
||||
"ReplaceModeMixin",
|
||||
"ReplaceStateUpdaterMixin"
|
||||
],
|
||||
"client": [
|
||||
"BulldozerMixin",
|
||||
"BulldozerMixin2",
|
||||
"HudMixin",
|
||||
"NoClipCameraFixMixin",
|
||||
"NormalModeMixin",
|
||||
"ReplaceModeMixin"
|
||||
"NormalModeMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
Loading…
Reference in a new issue