fix detached rifts being placed after door gets destroyed
change ExplosionMixin Overwrite to Inject
This commit is contained in:
parent
f8f8d00f98
commit
e60e20bbd9
7 changed files with 205 additions and 136 deletions
|
@ -0,0 +1,43 @@
|
|||
package org.dimdev.dimdoors.api.block;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.util.Pair;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Only works in cases where {@link net.minecraft.block.AbstractBlock#getStateForNeighborUpdate AbstractBlock#getStateForNeighborUpdate} returns an air {@link BlockState}
|
||||
*/
|
||||
public interface CustomBreakBlock {
|
||||
TypedActionResult<Pair<BlockState, Consumer<BlockEntity>>> customBreakBlock(World world, BlockPos pos, BlockState blockState, Entity breakingEntity);
|
||||
|
||||
/*
|
||||
If this causes any issue mixin into FluidState instead.
|
||||
Also remember to remove the access wideners.
|
||||
*/
|
||||
class HackyFluidState extends FluidState {
|
||||
private final BlockState blockState;
|
||||
private final Consumer<BlockEntity> blockEntityConsumer;
|
||||
|
||||
public HackyFluidState(BlockState blockState, Consumer<BlockEntity> blockEntityConsumer) {
|
||||
super(blockState.getFluidState().getFluid(), blockState.getFluidState().getEntries(), blockState.getFluidState().codec);
|
||||
this.blockState = blockState;
|
||||
this.blockEntityConsumer = blockEntityConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState() {
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public Consumer<BlockEntity> getBlockEntityConsumer() {
|
||||
return blockEntityConsumer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.dimdev.dimdoors.api.block;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface ExplosionConvertibleBlock {
|
||||
ActionResult explode(World world, BlockPos pos, BlockState state, BlockEntity blockEntity);
|
||||
}
|
|
@ -2,16 +2,23 @@ package org.dimdev.dimdoors.block.door;
|
|||
|
||||
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.piston.PistonBehavior;
|
||||
import net.minecraft.util.Pair;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.world.explosion.Explosion;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.dimdev.dimdoors.DimensionalDoorsInitializer;
|
||||
import org.dimdev.dimdoors.api.block.CustomBreakBlock;
|
||||
import org.dimdev.dimdoors.api.block.ExplosionConvertibleBlock;
|
||||
import org.dimdev.dimdoors.api.util.math.MathUtil;
|
||||
import org.dimdev.dimdoors.api.util.math.TransformationMatrix3d;
|
||||
import org.dimdev.dimdoors.block.CoordinateTransformerBlock;
|
||||
import org.dimdev.dimdoors.block.DetachedRiftBlock;
|
||||
import org.dimdev.dimdoors.block.ModBlocks;
|
||||
import org.dimdev.dimdoors.block.RiftProvider;
|
||||
import org.dimdev.dimdoors.block.entity.DetachedRiftBlockEntity;
|
||||
import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity;
|
||||
import org.dimdev.dimdoors.block.entity.RiftData;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
|
@ -35,7 +42,9 @@ import net.minecraft.world.event.GameEvent;
|
|||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements RiftProvider<EntranceRiftBlockEntity>, CoordinateTransformerBlock {
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements RiftProvider<EntranceRiftBlockEntity>, CoordinateTransformerBlock, ExplosionConvertibleBlock, CustomBreakBlock {
|
||||
public DimensionalDoorBlock(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
@ -99,6 +108,11 @@ public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements Rift
|
|||
createDetachedRift(world, pos, world.getBlockState(pos));
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: rewrite so it can only be used from the lower door block.
|
||||
I fear this method may be called twice otherwise.
|
||||
~CreepyCre
|
||||
*/
|
||||
public void createDetachedRift(World world, BlockPos pos, BlockState state) {
|
||||
DoubleBlockHalf doubleBlockHalf = state.get(HALF);
|
||||
BlockPos blockPos = pos;
|
||||
|
@ -224,4 +238,29 @@ public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements Rift
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult explode(World world, BlockPos pos, BlockState state, BlockEntity blockEntity) {
|
||||
if (blockEntity == null) {
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
createDetachedRift(world, pos, state);
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PistonBehavior getPistonBehavior(BlockState state) {
|
||||
return state.get(HALF) == DoubleBlockHalf.LOWER ? PistonBehavior.BLOCK : super.getPistonBehavior(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedActionResult<Pair<BlockState, Consumer<BlockEntity>>> customBreakBlock(World world, BlockPos pos, BlockState blockState, Entity breakingEntity) {
|
||||
if (blockState.get(HALF) != DoubleBlockHalf.LOWER) {
|
||||
return TypedActionResult.pass(null);
|
||||
}
|
||||
RiftData data = ((EntranceRiftBlockEntity) world.getBlockEntity(pos)).getData();
|
||||
return TypedActionResult.success(new Pair<>(ModBlocks.DETACHED_RIFT.getDefaultState().with(WATERLOGGED, blockState.get(WATERLOGGED)), blockEntity -> {
|
||||
((EntranceRiftBlockEntity) blockEntity).setData(data);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,154 +1,39 @@
|
|||
package org.dimdev.dimdoors.mixin;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
|
||||
import net.minecraft.block.AbstractFireBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.loot.context.LootContext;
|
||||
import net.minecraft.loot.context.LootContextParameters;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.explosion.Explosion;
|
||||
import net.minecraft.world.explosion.ExplosionBehavior;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dimdev.dimdoors.block.RiftProvider;
|
||||
import org.dimdev.dimdoors.block.door.DimensionalDoorBlock;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.dimdev.dimdoors.api.block.ExplosionConvertibleBlock;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
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;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.dimdev.dimdoors.block.ModBlocks.DETACHED_RIFT;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(Explosion.class)
|
||||
public class ExplosionMixin {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
@Shadow
|
||||
private static final int field_30960 = 16;
|
||||
@Shadow
|
||||
private boolean createFire;
|
||||
@Shadow
|
||||
private Explosion.DestructionType destructionType;
|
||||
@Shadow
|
||||
private Random random;
|
||||
@Shadow
|
||||
private World world;
|
||||
@Shadow
|
||||
private double x;
|
||||
@Shadow
|
||||
private double y;
|
||||
@Shadow
|
||||
private double z;
|
||||
@Shadow
|
||||
@Nullable
|
||||
private Entity entity;
|
||||
@Shadow
|
||||
private float power;
|
||||
@Shadow
|
||||
private DamageSource damageSource;
|
||||
@Shadow
|
||||
private ExplosionBehavior behavior;
|
||||
@Shadow
|
||||
@Mutable @Shadow
|
||||
private List<BlockPos> affectedBlocks;
|
||||
@Shadow
|
||||
private Map<PlayerEntity, Vec3d> affectedPlayers;
|
||||
|
||||
@Shadow
|
||||
private static void tryMergeStack(ObjectArrayList<Pair<ItemStack, BlockPos>> stacks, ItemStack stack, BlockPos pos) {
|
||||
|
||||
}
|
||||
/**
|
||||
* @author - MalekiRe
|
||||
*/
|
||||
@Overwrite
|
||||
public void affectWorld(boolean particles) {
|
||||
if (this.world.isClient) {
|
||||
this.world.playSound(this.x, this.y, this.z, SoundEvents.ENTITY_GENERIC_EXPLODE, SoundCategory.BLOCKS, 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F, false);
|
||||
}
|
||||
|
||||
boolean bl = this.destructionType != Explosion.DestructionType.NONE;
|
||||
if (particles) {
|
||||
if (!(this.power < 2.0F) && bl) {
|
||||
this.world.addParticle(ParticleTypes.EXPLOSION_EMITTER, this.x, this.y, this.z, 1.0D, 0.0D, 0.0D);
|
||||
} else {
|
||||
this.world.addParticle(ParticleTypes.EXPLOSION, this.x, this.y, this.z, 1.0D, 0.0D, 0.0D);
|
||||
@Inject(method = "affectWorld", at = @At(value = "INVOKE", target = "Ljava/util/Collections;shuffle(Ljava/util/List;Ljava/util/Random;)V", ordinal = 0, shift = At.Shift.AFTER))
|
||||
private void handleExplosionConvertibleBlocks(boolean b1, CallbackInfo ci) {
|
||||
this.affectedBlocks = this.affectedBlocks.stream().filter(blockPos -> {
|
||||
BlockState state = this.world.getBlockState(blockPos);
|
||||
Block block = state.getBlock();
|
||||
if (!(block instanceof ExplosionConvertibleBlock)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bl) {
|
||||
ObjectArrayList<Pair<ItemStack, BlockPos>> objectArrayList = new ObjectArrayList();
|
||||
Collections.shuffle(this.affectedBlocks, this.world.random);
|
||||
|
||||
for (BlockPos blockPos : this.affectedBlocks) {
|
||||
BlockState blockState = this.world.getBlockState(blockPos);
|
||||
Block block = blockState.getBlock();
|
||||
if (!blockState.isAir()) {
|
||||
BlockPos blockPos2 = blockPos.toImmutable();
|
||||
this.world.getProfiler().push("explosion_blocks");
|
||||
if (block.shouldDropItemsOnExplosion((Explosion) (Object) this) && this.world instanceof ServerWorld) {
|
||||
//TODO: Change this to work with trapdoors as well, when we implement trapdoors.
|
||||
//This is the only changed code
|
||||
if (block instanceof DimensionalDoorBlock) {
|
||||
LOGGER.log(Level.INFO, "Creating Detached Rift From Explosion of Door");
|
||||
((DimensionalDoorBlock) block).createDetachedRift(this.world, blockPos2);
|
||||
continue;
|
||||
}
|
||||
else if(world.getBlockState(blockPos2).getBlock() == DETACHED_RIFT) {
|
||||
continue;
|
||||
}
|
||||
//Normal code below
|
||||
BlockEntity blockEntity = blockState.hasBlockEntity() ? this.world.getBlockEntity(blockPos) : null;
|
||||
LootContext.Builder builder = (new LootContext.Builder((ServerWorld) this.world)).random(this.world.random).parameter(LootContextParameters.ORIGIN, Vec3d.ofCenter(blockPos)).parameter(LootContextParameters.TOOL, ItemStack.EMPTY).optionalParameter(LootContextParameters.BLOCK_ENTITY, blockEntity).optionalParameter(LootContextParameters.THIS_ENTITY, this.entity);
|
||||
if (this.destructionType == Explosion.DestructionType.DESTROY) {
|
||||
builder.parameter(LootContextParameters.EXPLOSION_RADIUS, this.power);
|
||||
}
|
||||
|
||||
blockState.getDroppedStacks(builder).forEach((stack) -> {
|
||||
tryMergeStack(objectArrayList, stack, blockPos2);
|
||||
});
|
||||
}
|
||||
|
||||
this.world.setBlockState(blockPos, Blocks.AIR.getDefaultState(), 3);
|
||||
block.onDestroyedByExplosion(this.world, blockPos, (Explosion) (Object) this);
|
||||
this.world.getProfiler().pop();
|
||||
}
|
||||
}
|
||||
|
||||
ObjectListIterator var12 = objectArrayList.iterator();
|
||||
|
||||
while(var12.hasNext()) {
|
||||
Pair<ItemStack, BlockPos> pair = (Pair)var12.next();
|
||||
Block.dropStack(this.world, (BlockPos)pair.getSecond(), (ItemStack)pair.getFirst());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.createFire) {
|
||||
Iterator var11 = this.affectedBlocks.iterator();
|
||||
|
||||
while(var11.hasNext()) {
|
||||
BlockPos blockPos3 = (BlockPos)var11.next();
|
||||
if (this.random.nextInt(3) == 0 && this.world.getBlockState(blockPos3).isAir() && this.world.getBlockState(blockPos3.down()).isOpaqueFullCube(this.world, blockPos3.down())) {
|
||||
this.world.setBlockState(blockPos3, AbstractFireBlock.getState(this.world, blockPos3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionResult result = ((ExplosionConvertibleBlock) block).explode(this.world, blockPos, state, state.hasBlockEntity() ? this.world.getBlockEntity(blockPos) : null);
|
||||
return result == ActionResult.PASS;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
85
src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java
Normal file
85
src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
package org.dimdev.dimdoors.mixin;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.util.Pair;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.dimdev.dimdoors.api.block.CustomBreakBlock;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(World.class)
|
||||
public abstract class WorldMixin {
|
||||
|
||||
/*
|
||||
I thought about redirecting the entire break method to be handled by the block itself,
|
||||
but I am not quite sure what that would mean for compatibility with other mixins,
|
||||
since then a large part of the method would need to be canceled. This is rather hacky, but it should fulfill the purpose best
|
||||
~CreepyCre
|
||||
*/
|
||||
@ModifyVariable(method = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;I)Z",
|
||||
at = @At(value = "INVOKE_ASSIGN",
|
||||
target = "Lnet/minecraft/world/World;getFluidState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/fluid/FluidState;",
|
||||
ordinal = 0))
|
||||
private FluidState replaceFluidStateWithCustomHackyFluidState(FluidState original, BlockPos pos, boolean drop, @Nullable Entity breakingEntity, int maxUpdateDepth) {
|
||||
World world = (World) (Object) this;
|
||||
BlockState blockState = world.getBlockState(pos);
|
||||
Block block = blockState.getBlock();
|
||||
if (!(block instanceof CustomBreakBlock)) {
|
||||
return original;
|
||||
}
|
||||
TypedActionResult<Pair<BlockState, Consumer<BlockEntity>>> result = ((CustomBreakBlock) block).customBreakBlock(world, pos, blockState, breakingEntity);
|
||||
if (!result.getResult().isAccepted()) {
|
||||
return original;
|
||||
}
|
||||
Pair<BlockState, Consumer<BlockEntity>> pair = result.getValue();
|
||||
return new CustomBreakBlock.HackyFluidState(pair.getLeft(), pair.getRight());
|
||||
}
|
||||
|
||||
@Inject(method = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;I)Z",
|
||||
locals = LocalCapture.CAPTURE_FAILHARD,
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;II)Z",
|
||||
ordinal = 0))
|
||||
private void applyBlockEntityModification(BlockPos pos, boolean drop, Entity breakingEntity, int maxUpdateDepth, CallbackInfoReturnable<Boolean> cir, BlockState blockState, FluidState fluidState) {
|
||||
if (!(fluidState instanceof CustomBreakBlock.HackyFluidState)) {
|
||||
return;
|
||||
}
|
||||
Consumer<BlockEntity> blockEntityConsumer = ((CustomBreakBlock.HackyFluidState) fluidState).getBlockEntityConsumer();
|
||||
if (blockEntityConsumer == null) {
|
||||
return;
|
||||
}
|
||||
BlockEntity blockEntity = ((World) (Object) this).getBlockEntity(pos);
|
||||
if (blockEntity != null) {
|
||||
blockEntityConsumer.accept(blockEntity);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This is where I'd inject if it turns out the method used above does actually have an issue
|
||||
*/
|
||||
// @Inject(method = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;I)Z",
|
||||
// cancellable = true,
|
||||
// at = @At(
|
||||
// value = "INVOKE",
|
||||
// target = "Lnet/minecraft/world/WorldAccess;syncWorldEvent(ILnet/minecraft/util/math/BlockPos;I)V",
|
||||
// ordinal = 0,
|
||||
// shift = At.Shift.BY,
|
||||
// by = 2
|
||||
// )
|
||||
// )
|
||||
|
||||
|
||||
}
|
|
@ -10,3 +10,8 @@ accessible method net/minecraft/entity/Entity setRotation (FF)V
|
|||
|
||||
# for MutableBlockEntityType
|
||||
extendable class net/minecraft/block/entity/BlockEntityType$BlockEntityFactory
|
||||
|
||||
# for HackyFluidState
|
||||
extendable class net/minecraft/fluid/FluidState
|
||||
accessible field net/minecraft/state/State codec Lcom/mojang/serialization/MapCodec;
|
||||
#Lnet/minecraft/state/State;codec:Lcom/mojang/serialization/MapCodec;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"ServerPlayerEntityMixin",
|
||||
"ServerPlayerInteractionManagerMixin",
|
||||
"StructurePoolMixin",
|
||||
"WorldMixin",
|
||||
"accessor.BuiltinBiomesAccessor",
|
||||
"accessor.ChunkGeneratorAccessor",
|
||||
"accessor.DefaultParticleTypeAccessor",
|
||||
|
@ -33,9 +34,9 @@
|
|||
"client.ExtendedClientPlayNetworkHandlerMixin",
|
||||
"client.GameRendererMixin",
|
||||
"client.InGameHudMixin",
|
||||
"client.PostProcessShaderMixin",
|
||||
"client.WorldRendererMixin",
|
||||
"client.accessor.RenderLayerAccessor",
|
||||
"client.PostProcessShaderMixin"
|
||||
"client.accessor.RenderLayerAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
Loading…
Reference in a new issue