From e409ae41087d185d3b9f5432a97d199a8297bfe9 Mon Sep 17 00:00:00 2001 From: DeadlyMC Date: Wed, 5 Jun 2019 10:02:22 +0530 Subject: [PATCH] added movableTE's with help from @2No2Name --- src/main/java/carpet/CarpetSettings.java | 2 +- .../BlockEntityRenderDispatcherInterface.java | 8 + .../fakes/PistonBlockEntityInterface.java | 12 ++ .../carpet/fakes/WorldChunkInterface.java | 10 ++ .../java/carpet/fakes/WorldInterface.java | 5 + ...EntityRenderDispatcher_movableTEMixin.java | 53 ++++++ ...tonBlockEntityRenderer_movableTEMixin.java | 42 +++++ .../PistonBlockEntity_movableTEMixin.java | 139 +++++++++++++++ .../mixins/PistonBlock_movableTEMixin.java | 123 ++++++++++++++ .../mixins/WorldChunk_movableTEMixin.java | 158 ++++++++++++++++++ .../carpet/mixins/World_movableTEMixin.java | 137 +++++++++++++++ src/main/resources/carpet.mixins.json | 12 +- 12 files changed, 698 insertions(+), 3 deletions(-) create mode 100644 src/main/java/carpet/fakes/BlockEntityRenderDispatcherInterface.java create mode 100644 src/main/java/carpet/fakes/PistonBlockEntityInterface.java create mode 100644 src/main/java/carpet/fakes/WorldChunkInterface.java create mode 100644 src/main/java/carpet/mixins/BlockEntityRenderDispatcher_movableTEMixin.java create mode 100644 src/main/java/carpet/mixins/PistonBlockEntityRenderer_movableTEMixin.java create mode 100644 src/main/java/carpet/mixins/PistonBlockEntity_movableTEMixin.java create mode 100644 src/main/java/carpet/mixins/PistonBlock_movableTEMixin.java create mode 100644 src/main/java/carpet/mixins/WorldChunk_movableTEMixin.java create mode 100644 src/main/java/carpet/mixins/World_movableTEMixin.java diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java index 5e3357dd..064e7020 100644 --- a/src/main/java/carpet/CarpetSettings.java +++ b/src/main/java/carpet/CarpetSettings.java @@ -114,7 +114,7 @@ public class CarpetSettings "Items counted are destroyed, count up to one stack per tick per hopper") .isACommand().boolAccelerate().defaultFalse(), rule("renewableSponges", "experimental feature", "Guardians turn into Elder Guardian when struck by lightning").wip(), - rule("movableTileEntities", "experimental", "Pistons can push tile entities, like hoppers, chests etc.").boolAccelerate().wip(), + rule("movableTileEntities", "experimental", "Pistons can push tile entities, like hoppers, chests etc.").boolAccelerate(), rule("desertShrubs", "feature", "Saplings turn into dead shrubs in hot climates and no water access when it attempts to grow into a tree").wip(), rule("silverFishDropGravel", "experimental", "Silverfish drop a gravel item when breaking out of a block"), rule("summonNaturalLightning","creative", "summoning a lightning bolt has all the side effects of natural lightning"), diff --git a/src/main/java/carpet/fakes/BlockEntityRenderDispatcherInterface.java b/src/main/java/carpet/fakes/BlockEntityRenderDispatcherInterface.java new file mode 100644 index 00000000..98605d63 --- /dev/null +++ b/src/main/java/carpet/fakes/BlockEntityRenderDispatcherInterface.java @@ -0,0 +1,8 @@ +package carpet.fakes; + +import net.minecraft.block.entity.BlockEntity; + +public interface BlockEntityRenderDispatcherInterface +{ + void renderBlockEntityOffset(BlockEntity blockEntity, float partialTicks, int destroyStage, double xOffset, double yOffset, double zOffset); +} diff --git a/src/main/java/carpet/fakes/PistonBlockEntityInterface.java b/src/main/java/carpet/fakes/PistonBlockEntityInterface.java new file mode 100644 index 00000000..dfc692d1 --- /dev/null +++ b/src/main/java/carpet/fakes/PistonBlockEntityInterface.java @@ -0,0 +1,12 @@ +package carpet.fakes; + +import net.minecraft.block.entity.BlockEntity; + +public interface PistonBlockEntityInterface +{ + void setCarriedBlockEntity(BlockEntity blockEntity); + BlockEntity getCarriedBlockEntity(); + void setRenderCarriedBlockEntity(boolean b); + boolean getRenderCarriedBlockEntity(); + boolean isRenderModeSet(); +} diff --git a/src/main/java/carpet/fakes/WorldChunkInterface.java b/src/main/java/carpet/fakes/WorldChunkInterface.java new file mode 100644 index 00000000..7590c02a --- /dev/null +++ b/src/main/java/carpet/fakes/WorldChunkInterface.java @@ -0,0 +1,10 @@ +package carpet.fakes; + +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; + +public interface WorldChunkInterface +{ + BlockState setBlockStateWithBlockEntity(BlockPos blockPos, BlockState newBlockState, BlockEntity newBlockEntity, boolean boolean1); +} diff --git a/src/main/java/carpet/fakes/WorldInterface.java b/src/main/java/carpet/fakes/WorldInterface.java index 0ba86651..495ecde9 100644 --- a/src/main/java/carpet/fakes/WorldInterface.java +++ b/src/main/java/carpet/fakes/WorldInterface.java @@ -1,11 +1,16 @@ package carpet.fakes; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.util.math.BlockPos; import java.util.Map; public interface WorldInterface { Map getPrecookedMobs(); + + boolean setBlockStateWithBlockEntity(BlockPos blockPos, BlockState blockState, BlockEntity newBlockEntity, int int1); } diff --git a/src/main/java/carpet/mixins/BlockEntityRenderDispatcher_movableTEMixin.java b/src/main/java/carpet/mixins/BlockEntityRenderDispatcher_movableTEMixin.java new file mode 100644 index 00000000..b5106d01 --- /dev/null +++ b/src/main/java/carpet/mixins/BlockEntityRenderDispatcher_movableTEMixin.java @@ -0,0 +1,53 @@ +package carpet.mixins; + +import carpet.fakes.BlockEntityRenderDispatcherInterface; +import com.mojang.blaze3d.platform.GLX; +import com.mojang.blaze3d.platform.GlStateManager; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.GuiLighting; +import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(BlockEntityRenderDispatcher.class) +public abstract class BlockEntityRenderDispatcher_movableTEMixin implements BlockEntityRenderDispatcherInterface +{ + @Shadow + public static double renderOffsetX; + @Shadow + public static double renderOffsetY; + @Shadow + public static double renderOffsetZ; + @Shadow + public Camera cameraEntity; + @Shadow + public World world; + + @Shadow + public abstract void renderEntity(BlockEntity blockEntity_1, double double_1, double double_2, double double_3, + float float_1, int int_1, boolean boolean_1); + + /** + * @author 2No2Name + */ + //Renders the BlockEntity offset by the amount specified in the arguments xOffset yOffset zOffset (the moving block moved in the animation by this) + public void renderBlockEntityOffset(BlockEntity blockEntity_1, float partialTicks, int destroyStage, double xOffset, + double yOffset, double zOffset) + { + if (blockEntity_1.getSquaredDistance(this.cameraEntity.getPos().x - xOffset, this.cameraEntity.getPos().y - yOffset, + this.cameraEntity.getPos().z - zOffset) < blockEntity_1.getSquaredRenderDistance()) + { + GuiLighting.enable(); + int i = this.world.getLightmapIndex(blockEntity_1.getPos(), 0); + int j = i % 65536; + int k = i / 65536; + GLX.glMultiTexCoord2f(GLX.GL_TEXTURE1, (float) j, (float) k); + GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F); + BlockPos blockpos = blockEntity_1.getPos(); + this.renderEntity(blockEntity_1, (double) blockpos.getX() - renderOffsetX + xOffset, (double) blockpos.getY() - renderOffsetY + yOffset, (double) blockpos.getZ() - renderOffsetZ + zOffset, partialTicks, destroyStage, false); + } + } +} diff --git a/src/main/java/carpet/mixins/PistonBlockEntityRenderer_movableTEMixin.java b/src/main/java/carpet/mixins/PistonBlockEntityRenderer_movableTEMixin.java new file mode 100644 index 00000000..584b879f --- /dev/null +++ b/src/main/java/carpet/mixins/PistonBlockEntityRenderer_movableTEMixin.java @@ -0,0 +1,42 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.fakes.BlockEntityRenderDispatcherInterface; +import carpet.fakes.PistonBlockEntityInterface; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.PistonBlockEntity; +import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; +import net.minecraft.client.render.block.entity.BlockEntityRenderer; +import net.minecraft.client.render.block.entity.PistonBlockEntityRenderer; +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(PistonBlockEntityRenderer.class) +public abstract class PistonBlockEntityRenderer_movableTEMixin extends BlockEntityRenderer +{ + @Inject(method = "method_3576", at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/render/block/entity/PistonBlockEntityRenderer;method_3575(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/client/render/BufferBuilder;Lnet/minecraft/world/World;Z)Z", ordinal = 3)) + private void updateRenderBool(PistonBlockEntity pistonBlockEntity_1, double double_1, double double_2, + double double_3, float float_1, int int_1, CallbackInfo ci) + { + if (!((PistonBlockEntityInterface) pistonBlockEntity_1).isRenderModeSet()) + ((PistonBlockEntityInterface) pistonBlockEntity_1).setRenderCarriedBlockEntity(CarpetSettings.b_movableTileEntities && ((PistonBlockEntityInterface) pistonBlockEntity_1).getCarriedBlockEntity() != null); + } + + @Inject(method = "method_3576", at = @At("RETURN")) + private void endMethod3576(PistonBlockEntity pistonBlockEntity_1, double double_1, double double_2, double double_3, + float float_1, int int_1, CallbackInfo ci) + { + if (((PistonBlockEntityInterface) pistonBlockEntity_1).getRenderCarriedBlockEntity()) + { + BlockEntity carriedBlockEntity = ((PistonBlockEntityInterface) pistonBlockEntity_1).getCarriedBlockEntity(); + if (carriedBlockEntity != null) + { + carriedBlockEntity.setPos(pistonBlockEntity_1.getPos()); + ((BlockEntityRenderDispatcherInterface) BlockEntityRenderDispatcher.INSTANCE).renderBlockEntityOffset(carriedBlockEntity, float_1, int_1, pistonBlockEntity_1.getRenderOffsetX(float_1), pistonBlockEntity_1.getRenderOffsetY(float_1), pistonBlockEntity_1.getRenderOffsetZ(float_1)); + } + } + } +} diff --git a/src/main/java/carpet/mixins/PistonBlockEntity_movableTEMixin.java b/src/main/java/carpet/mixins/PistonBlockEntity_movableTEMixin.java new file mode 100644 index 00000000..0e34fa5d --- /dev/null +++ b/src/main/java/carpet/mixins/PistonBlockEntity_movableTEMixin.java @@ -0,0 +1,139 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.fakes.PistonBlockEntityInterface; +import carpet.fakes.WorldInterface; +import net.minecraft.block.Block; +import net.minecraft.block.BlockEntityProvider; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.PistonBlockEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(PistonBlockEntity.class) +public abstract class PistonBlockEntity_movableTEMixin extends BlockEntity implements PistonBlockEntityInterface +{ + @Shadow + private boolean source; + @Shadow + private BlockState pushedBlock; + + private BlockEntity carriedBlockEntity; + private boolean renderCarriedBlockEntity = false; + private boolean renderSet = false; + + public PistonBlockEntity_movableTEMixin(BlockEntityType blockEntityType_1) + { + super(blockEntityType_1); + } + + + /** + * @author 2No2Name + */ + public BlockEntity getCarriedBlockEntity() + { + return carriedBlockEntity; + } + + public void setCarriedBlockEntity(BlockEntity blockEntity) + { + this.carriedBlockEntity = blockEntity; + if (this.carriedBlockEntity != null) + this.carriedBlockEntity.setPos(this.pos); + } + + public boolean isRenderModeSet() + { + return renderSet; + } + + public boolean getRenderCarriedBlockEntity() + { + return renderCarriedBlockEntity; + } + + public void setRenderCarriedBlockEntity(boolean b) + { + renderCarriedBlockEntity = b; + renderSet = true; + } + + /** + * @author 2No2Name + */ + @Redirect(method = "tick", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + private boolean movableTEsetBlockState0(World world, BlockPos blockPos_1, BlockState blockAState_2, int int_1) + { + if (!CarpetSettings.b_movableTileEntities) + return world.setBlockState(blockPos_1, blockAState_2, int_1); + else + return ((WorldInterface) (world)).setBlockStateWithBlockEntity(blockPos_1, blockAState_2, this.carriedBlockEntity, int_1); + } + + @Redirect(method = "finish", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + private boolean movableTEsetBlockState1(World world, BlockPos blockPos_1, BlockState blockState_2, int int_1) + { + if (!CarpetSettings.b_movableTileEntities) + return world.setBlockState(blockPos_1, blockState_2, int_1); + else + { + boolean ret = ((WorldInterface) (world)).setBlockStateWithBlockEntity(blockPos_1, blockState_2, this.carriedBlockEntity, int_1); + this.carriedBlockEntity = null; //this will cancel the finishHandleBroken + return ret; + } + } + + @Inject(method = "finish", at = @At(value = "RETURN")) + private void finishHandleBroken(CallbackInfo cir) + { + //Handle TNT Explosions or other ways the moving Block is broken + //Also /setblock will cause this to be called, and drop e.g. a moving chest's contents. + // This is MC-40380 (BlockEntities that aren't Inventories drop stuff when setblock is called ) + if (CarpetSettings.b_movableTileEntities && this.carriedBlockEntity != null && !this.world.isClient && this.world.getBlockState(this.pos).getBlock() == Blocks.AIR) + { + BlockState blockState_2; + if (this.source) + blockState_2 = Blocks.AIR.getDefaultState(); + else + blockState_2 = Block.getRenderingState(this.pushedBlock, this.world, this.pos); + ((WorldInterface) (this.world)).setBlockStateWithBlockEntity(this.pos, blockState_2, this.carriedBlockEntity, 3); + this.world.breakBlock(this.pos, true); + } + } + + @Inject(method = "fromTag", at = @At(value = "TAIL")) + private void onFromTag(CompoundTag compoundTag_1, CallbackInfo ci) + { + if (CarpetSettings.b_movableTileEntities && compoundTag_1.containsKey("carriedTileEntity", 10)) + { + if (this.pushedBlock.getBlock() instanceof BlockEntityProvider) + this.carriedBlockEntity = ((BlockEntityProvider) (this.pushedBlock.getBlock())).createBlockEntity(this.world); + if (carriedBlockEntity != null) //Can actually be null, as BlockPistonMoving.createNewTileEntity(...) returns null + this.carriedBlockEntity.fromTag(compoundTag_1.getCompound("carriedTileEntity")); + } + } + + @Inject(method = "toTag", at = @At(value = "RETURN", shift = At.Shift.BEFORE)) + private void onToTag(CompoundTag compoundTag_1, CallbackInfoReturnable cir) + { + if (CarpetSettings.b_movableTileEntities && this.carriedBlockEntity != null) + { + //Leave name "carriedTileEntity" instead of "carriedBlockEntity" for upgrade compatibility with 1.12 movable TE + compoundTag_1.put("carriedTileEntity", this.carriedBlockEntity.toTag(new CompoundTag())); + } + } +} diff --git a/src/main/java/carpet/mixins/PistonBlock_movableTEMixin.java b/src/main/java/carpet/mixins/PistonBlock_movableTEMixin.java new file mode 100644 index 00000000..cb8bf1f6 --- /dev/null +++ b/src/main/java/carpet/mixins/PistonBlock_movableTEMixin.java @@ -0,0 +1,123 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.fakes.PistonBlockEntityInterface; +import com.google.common.collect.Lists; +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.piston.PistonHandler; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.List; +import java.util.Set; + +@Mixin(PistonBlock.class) +public abstract class PistonBlock_movableTEMixin extends FacingBlock +{ + protected PistonBlock_movableTEMixin(Settings block$Settings_1) + { + super(block$Settings_1); + } + + private ThreadLocal> list1_BlockEntities = new ThreadLocal<>(); //Unneccessary ThreadLocal if client and server use different PistonBlock instances + + @Inject(method = "isMovable", at = @At(value = "RETURN", ordinal = 3, shift = At.Shift.BEFORE)) + private static void movableCMD(BlockState blockState_1, World world_1, BlockPos blockPos_1, + Direction direction_1, boolean boolean_1, Direction direction_2, CallbackInfoReturnable cir) + { + Block block_1 = blockState_1.getBlock(); + //Make CommandBlocks movable, either use instanceof CommandBlock or the 3 cmd block objects, + if (CarpetSettings.b_movableTileEntities && block_1 instanceof CommandBlock) + { + cir.setReturnValue(true); + } + } + + private static boolean isPushableTileEntityBlock(Block block) + { + //Making PISTON_EXTENSION (BlockPistonMoving) pushable would not work as its createNewTileEntity()-method returns null + return block != Blocks.ENDER_CHEST && block != Blocks.ENCHANTING_TABLE && + block != Blocks.END_GATEWAY && block != Blocks.END_PORTAL && block != Blocks.MOVING_PISTON && + block != Blocks.SPAWNER; + } + + @Redirect(method = "isMovable", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;hasBlockEntity()Z")) + private static boolean ifHasBlockEntity(Block block) + { + if (!block.hasBlockEntity()) + { + return !true; + } + else + { + return !(CarpetSettings.b_movableTileEntities && isPushableTileEntityBlock(block)); + } + } + @Inject(method = "move", at = @At(value = "INVOKE", shift = At.Shift.BEFORE, + target = "Ljava/util/List;size()I", ordinal = 4),locals = LocalCapture.CAPTURE_FAILHARD) + private void onMove(World world_1, BlockPos blockPos_1, Direction direction_1, boolean boolean_1, + CallbackInfoReturnable cir, BlockPos blockPos_2, PistonHandler pistonHandler_1, + List list_1, List list_2, List list_3, int int_2, BlockState blockStates_1[], + Direction direction_2, Set set_1) + { + //Get the blockEntities and remove them from the world before any magic starts to happen + if (CarpetSettings.b_movableTileEntities) + { + list1_BlockEntities.set(Lists.newArrayList()); + for (int i = 0; i < list_1.size(); ++i) + { + BlockPos blockpos = list_1.get(i); + BlockEntity blockEntity = (list_2.get(i).getBlock().hasBlockEntity()) ? world_1.getBlockEntity(blockpos) : null; + list1_BlockEntities.get().add(blockEntity); + if (blockEntity != null) + { + //hopefully this call won't have any side effects in the future, such as dropping all the BlockEntity's items + //we want to place this same(!) BlockEntity object into the world later when the movement stops again + world_1.removeBlockEntity(blockpos); + blockEntity.markDirty(); + } + } + } + } + + @Inject(method = "move", at = @At(value = "INVOKE", shift = At.Shift.BEFORE, + target = "Lnet/minecraft/world/World;setBlockEntity(Lnet/minecraft/util/math/BlockPos;" + + "Lnet/minecraft/block/entity/BlockEntity;)V", ordinal = 0), + locals = LocalCapture.CAPTURE_FAILHARD) + private void setBlockEntityWithCarried(World world_1, BlockPos blockPos_1, Direction direction_1, boolean boolean_1, + CallbackInfoReturnable cir, BlockPos blockPos_2, PistonHandler pistonHandler_1, List list_1, + List list_2, List list_3, int int_2, BlockState blockStates_1[], Direction direction_2, Set set_1, + int int_3, BlockPos blockPos_4, BlockState blockState_1) + { + BlockEntity blockEntityPiston = PistonExtensionBlock.createBlockEntityPiston((BlockState) list_2.get(int_3), + direction_1, boolean_1, false); + if (CarpetSettings.b_movableTileEntities) + ((PistonBlockEntityInterface) blockEntityPiston).setCarriedBlockEntity(list1_BlockEntities.get().get(int_3)); + world_1.setBlockEntity(blockPos_4, blockEntityPiston); + } + + @Redirect(method = "move", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/World;setBlockEntity(Lnet/minecraft/util/math/BlockPos;" + + "Lnet/minecraft/block/entity/BlockEntity;)V", + ordinal = 0)) + private void dontDoAnything(World world, BlockPos blockPos_6, BlockEntity blockEntityPiston) + { + } + + @Redirect(method = "move", at = @At(value = "INVOKE", + target = "Lnet/minecraft/block/PistonExtensionBlock;createBlockEntityPiston(Lnet/minecraft/block/BlockState;" + + "Lnet/minecraft/util/math/Direction;ZZ)Lnet/minecraft/block/entity/BlockEntity;", + ordinal = 0)) + private BlockEntity returnNull(BlockState blockState_1, Direction direction_1, boolean boolean_1, boolean boolean_2) + { + return null; + } +} diff --git a/src/main/java/carpet/mixins/WorldChunk_movableTEMixin.java b/src/main/java/carpet/mixins/WorldChunk_movableTEMixin.java new file mode 100644 index 00000000..88b2ca1b --- /dev/null +++ b/src/main/java/carpet/mixins/WorldChunk_movableTEMixin.java @@ -0,0 +1,158 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.fakes.WorldChunkInterface; +import com.sun.istack.internal.Nullable; +import net.minecraft.block.Block; +import net.minecraft.block.BlockEntityProvider; +import net.minecraft.block.BlockState; +import net.minecraft.block.PistonExtensionBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.Heightmap; +import net.minecraft.world.World; +import net.minecraft.world.chunk.ChunkSection; +import net.minecraft.world.chunk.WorldChunk; +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.Redirect; + +import java.util.Map; + +import static net.minecraft.world.chunk.WorldChunk.EMPTY_SECTION; + +@Mixin(WorldChunk.class) +public abstract class WorldChunk_movableTEMixin implements WorldChunkInterface +{ + @Shadow + @Final + private ChunkSection[] sections; + @Shadow + @Final + private Map heightmaps; + @Shadow + private boolean shouldSave; + @Shadow + @Final + private World world; + + @Shadow + @Nullable + public abstract BlockEntity getBlockEntity(BlockPos blockPos_1, WorldChunk.CreationType worldChunk$CreationType_1); + + // Fix Failure: If a moving BlockEntity is placed while BlockEntities are ticking, this will not find it and then replace it with a new TileEntity! + // blockEntity_2 = this.getBlockEntity(blockPos_1, WorldChunk.CreationType.CHECK); + @Redirect(method = "setBlockState", at = @At(value = "INVOKE", ordinal = 1, + target = "Lnet/minecraft/world/chunk/WorldChunk;getBlockEntity(Lnet/minecraft/util/math/BlockPos;" + "Lnet/minecraft/world/chunk/WorldChunk$CreationType;)" + "Lnet/minecraft/block/entity/BlockEntity;")) + private BlockEntity ifGetBlockEntity(WorldChunk worldChunk, BlockPos blockPos_1, + WorldChunk.CreationType worldChunk$CreationType_1) + { + if (!CarpetSettings.b_movableTileEntities) + { + return this.getBlockEntity(blockPos_1, WorldChunk.CreationType.CHECK); + } + else + { + return this.world.getBlockEntity(blockPos_1); + } + } + + + /** + * Sets the Blockstate and the BlockEntity. + * Only sets BlockEntity if Block is BlockEntityProvider, but doesn't check if it actually matches (e.g. can assign beacon to chest entity). + * + * @author 2No2Name + */ + @Nullable + public BlockState setBlockStateWithBlockEntity(BlockPos blockPos_1, BlockState newBlockState, BlockEntity newBlockEntity, + boolean boolean_1) + { + int x = blockPos_1.getX() & 15; + int y = blockPos_1.getY(); + int z = blockPos_1.getZ() & 15; + ChunkSection chunkSection = this.sections[y >> 4]; + if (chunkSection == EMPTY_SECTION) + { + if (newBlockState.isAir()) + { + return null; + } + + chunkSection = new ChunkSection(y >> 4 << 4); + this.sections[y >> 4] = chunkSection; + } + + boolean boolean_2 = chunkSection.isEmpty(); + BlockState oldBlockState = chunkSection.setBlockState(x, y & 15, z, newBlockState); + if (oldBlockState == newBlockState) + { + return null; + } + else + { + Block newBlock = newBlockState.getBlock(); + Block oldBlock = oldBlockState.getBlock(); + ((Heightmap) this.heightmaps.get(Heightmap.Type.MOTION_BLOCKING)).trackUpdate(x, y, z, newBlockState); + ((Heightmap) this.heightmaps.get(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES)).trackUpdate(x, y, z, newBlockState); + ((Heightmap) this.heightmaps.get(Heightmap.Type.OCEAN_FLOOR)).trackUpdate(x, y, z, newBlockState); + ((Heightmap) this.heightmaps.get(Heightmap.Type.WORLD_SURFACE)).trackUpdate(x, y, z, newBlockState); + boolean boolean_3 = chunkSection.isEmpty(); + if (boolean_2 != boolean_3) + { + this.world.getChunkManager().getLightingProvider().updateSectionStatus(blockPos_1, boolean_3); + } + + if (!this.world.isClient) + { + if (!(oldBlock instanceof PistonExtensionBlock))//this is a movableTE special case, if condition wasn't there it would remove the blockentity that was carried for some reason + oldBlockState.onBlockRemoved(this.world, blockPos_1, newBlockState, boolean_1);//this kills it + } + else if (oldBlock != newBlock && oldBlock instanceof BlockEntityProvider) + { + this.world.removeBlockEntity(blockPos_1); + } + + if (chunkSection.getBlockState(x, y & 15, z).getBlock() != newBlock) + { + return null; + } + else + { + BlockEntity oldBlockEntity = null; + if (oldBlock instanceof BlockEntityProvider) + { + oldBlockEntity = this.getBlockEntity(blockPos_1, WorldChunk.CreationType.CHECK); + if (oldBlockEntity != null) + { + oldBlockEntity.resetBlock(); + } + } + + if (newBlock instanceof BlockEntityProvider) + { + if (newBlockEntity == null) + { + newBlockEntity = ((BlockEntityProvider) newBlock).createBlockEntity(this.world); + } + if (newBlockEntity != oldBlockEntity && newBlockEntity != null) + { + newBlockEntity.validate(); + this.world.setBlockEntity(blockPos_1, newBlockEntity); + newBlockEntity.resetBlock(); + } + } + + if (!this.world.isClient) + { + newBlockState.onBlockAdded(this.world, blockPos_1, oldBlockState, boolean_1); //This can call setblockstate! (e.g. hopper does) + } + + this.shouldSave = true; + return oldBlockState; + } + } + } +} diff --git a/src/main/java/carpet/mixins/World_movableTEMixin.java b/src/main/java/carpet/mixins/World_movableTEMixin.java new file mode 100644 index 00000000..2952d4e5 --- /dev/null +++ b/src/main/java/carpet/mixins/World_movableTEMixin.java @@ -0,0 +1,137 @@ +package carpet.mixins; + +import carpet.fakes.WorldChunkInterface; +import carpet.fakes.WorldInterface; +import net.minecraft.block.Block; +import net.minecraft.block.BlockEntityProvider; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.server.world.ChunkHolder; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.profiler.Profiler; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.chunk.ChunkManager; +import net.minecraft.world.chunk.EmptyChunk; +import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.level.LevelGeneratorType; +import net.minecraft.world.level.LevelProperties; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(World.class) +public abstract class World_movableTEMixin implements WorldInterface +{ + @Shadow + @Final + public boolean isClient; + @Shadow + @Final + protected LevelProperties properties; + @Shadow + @Final + private Profiler profiler; + + @Shadow + protected abstract WorldChunk getWorldChunk(BlockPos blockPos_1); + + @Shadow + public abstract BlockState getBlockState(BlockPos blockPos_1); + + @Shadow + public abstract ChunkManager getChunkManager(); + + @Shadow + public abstract void scheduleBlockRender(BlockPos blockPos_1); + + @Shadow + public abstract void updateListeners(BlockPos var1, BlockState var2, BlockState var3, int var4); + + @Shadow + public abstract void updateNeighbors(BlockPos blockPos_1, Block block_1); + + @Shadow + public abstract void updateHorizontalAdjacent(BlockPos blockPos_1, Block block_1); + + @Shadow + public abstract void onBlockChanged(BlockPos blockPos_1, BlockState blockState_1, BlockState blockState_2); + + /** + * @author 2No2Name + */ + public boolean setBlockStateWithBlockEntity(BlockPos blockPos_1, BlockState blockState_1, BlockEntity newBlockEntity, int int_1) + { + if ((Object) this instanceof EmptyChunk) + return false; + + if (World.isHeightInvalid(blockPos_1)) + { + return false; + } + else if (!this.isClient && this.properties.getGeneratorType() == LevelGeneratorType.DEBUG_ALL_BLOCK_STATES) + { + return false; + } + else + { + WorldChunk worldChunk_1 = this.getWorldChunk(blockPos_1); + Block block_1 = blockState_1.getBlock(); + + BlockState blockState_2; + if (newBlockEntity != null && block_1 instanceof BlockEntityProvider) + blockState_2 = ((WorldChunkInterface) worldChunk_1).setBlockStateWithBlockEntity(blockPos_1, blockState_1, newBlockEntity, (int_1 & 64) != 0); + else + blockState_2 = worldChunk_1.setBlockState(blockPos_1, blockState_1, (int_1 & 64) != 0); + + if (blockState_2 == null) + { + return false; + } + else + { + BlockState blockState_3 = this.getBlockState(blockPos_1); + + if (blockState_3 != blockState_2 && (blockState_3.getLightSubtracted((BlockView) this, blockPos_1) != blockState_2.getLightSubtracted((BlockView) this, blockPos_1) || blockState_3.getLuminance() != blockState_2.getLuminance() || blockState_3.hasSidedTransparency() || blockState_2.hasSidedTransparency())) + { + this.profiler.push("queueCheckLight"); + this.getChunkManager().getLightingProvider().enqueueLightUpdate(blockPos_1); + this.profiler.pop(); + } + + if (blockState_3 == blockState_1) + { + if (blockState_2 != blockState_3) + { + this.scheduleBlockRender(blockPos_1); + } + + if ((int_1 & 2) != 0 && (!this.isClient || (int_1 & 4) == 0) && (this.isClient || worldChunk_1.getLevelType() != null && worldChunk_1.getLevelType().isAfter(ChunkHolder.LevelType.TICKING))) + { + this.updateListeners(blockPos_1, blockState_2, blockState_1, int_1); + } + + if (!this.isClient && (int_1 & 1) != 0) + { + this.updateNeighbors(blockPos_1, blockState_2.getBlock()); + if (blockState_1.hasComparatorOutput()) + { + this.updateHorizontalAdjacent(blockPos_1, block_1); + } + } + + if ((int_1 & 16) == 0) + { + int int_2 = int_1 & -2; + blockState_2.method_11637((net.minecraft.world.IWorld) this, blockPos_1, int_2); + blockState_1.updateNeighborStates((net.minecraft.world.IWorld) this, blockPos_1, int_2); + blockState_1.method_11637((net.minecraft.world.IWorld) this, blockPos_1, int_2); + } + + this.onBlockChanged(blockPos_1, blockState_2, blockState_3); + } + return true; + } + } + } +} diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 19c5fb26..79c9ad10 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -72,13 +72,21 @@ "EndCityFeatureMixin", "InfestedBlockMixin", - "PlayerEntityMixin" + "PlayerEntityMixin", + + "PistonBlock_movableTEMixin", + "PistonBlockEntity_movableTEMixin", + "World_movableTEMixin", + "WorldChunk_movableTEMixin" ], "client": [ "MinecraftClientMixin", "IntegratedServerMixin", "InGameHudMixin", - "PlayerListHudMixin" + "PlayerListHudMixin", + + "BlockEntityRenderDispatcher_movableTEMixin", + "PistonBlockEntityRenderer_movableTEMixin" ], "server": [ "MinecraftDedicatedServerMixin"