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;
|
package net.moonleay.gimbal.mixin;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
import net.minecraft.client.network.ClientPlayerEntity;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.client.network.ClientPlayerInteractionManager;
|
import net.minecraft.item.ItemUsageContext;
|
||||||
import net.minecraft.client.particle.ParticleManager;
|
|
||||||
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
|
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
import net.minecraft.util.hit.HitResult;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.world.World;
|
||||||
import net.moonleay.gimbal.client.editor.ClientEditor;
|
import net.moonleay.gimbal.editor.ServerEditorManager;
|
||||||
import net.moonleay.gimbal.editor.state.mode.Capability;
|
import net.moonleay.gimbal.editor.state.mode.Capability;
|
||||||
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;
|
||||||
|
import org.spongepowered.asm.mixin.Mutable;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(MinecraftClient.class)
|
import java.util.UUID;
|
||||||
public abstract class ReplaceModeMixin {
|
|
||||||
|
|
||||||
|
@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;
|
public ReplaceModeMixin(PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||||
|
super(player, hand, hit);
|
||||||
@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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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/MinecraftClient handleBlockBreaking (Z)V
|
||||||
accessible method net/minecraft/client/network/ClientPlayerInteractionManager sendSequencedPacket (Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/client/network/SequencedPacketCreator;)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": [
|
"mixins": [
|
||||||
"ForcePlaceMixin",
|
"ForcePlaceMixin",
|
||||||
"NoBlockUpdatesMixin",
|
"NoBlockUpdatesMixin",
|
||||||
"NoClipMixin"
|
"NoClipMixin",
|
||||||
|
"ReplaceModeMixin",
|
||||||
|
"ReplaceStateUpdaterMixin"
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
"BulldozerMixin",
|
"BulldozerMixin",
|
||||||
"BulldozerMixin2",
|
"BulldozerMixin2",
|
||||||
"HudMixin",
|
"HudMixin",
|
||||||
"NoClipCameraFixMixin",
|
"NoClipCameraFixMixin",
|
||||||
"NormalModeMixin",
|
"NormalModeMixin"
|
||||||
"ReplaceModeMixin"
|
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
|
|
Loading…
Reference in a new issue