Prevent mods from causing deadlocks in BlockState.getOffset
If this method is called with a ServerLevel, we switch the BlockGetter for a safe wrapper that will only work on loaded chunks Related: https://github.com/N1nn1/twigs/issues/6 Related: https://github.com/N1nn1/etcetera/issues/28
This commit is contained in:
parent
33e43f5b8f
commit
d7b2f5b75b
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.embeddedt.modernfix.chunk;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class SafeBlockGetter implements BlockGetter {
|
||||||
|
private final ServerLevel wrapped;
|
||||||
|
private final Thread mainThread;
|
||||||
|
|
||||||
|
public SafeBlockGetter(ServerLevel wrapped) {
|
||||||
|
this.wrapped = wrapped;
|
||||||
|
this.mainThread = Thread.currentThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldUse() {
|
||||||
|
return Thread.currentThread() != this.mainThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BlockGetter getChunkSafe(BlockPos pos) {
|
||||||
|
// can safely call getChunkForLighting off-thread
|
||||||
|
BlockGetter access = this.wrapped.getChunkSource().getChunkForLighting(pos.getX() >> 4, pos.getZ() >> 4);
|
||||||
|
if(!(access instanceof ChunkAccess))
|
||||||
|
return null;
|
||||||
|
ChunkAccess chunk = (ChunkAccess)access;
|
||||||
|
if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL))
|
||||||
|
return null;
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxBuildHeight() {
|
||||||
|
return this.wrapped.getMaxBuildHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxLightLevel() {
|
||||||
|
return this.wrapped.getMaxLightLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BlockEntity getBlockEntity(BlockPos pos) {
|
||||||
|
BlockGetter g = getChunkSafe(pos);
|
||||||
|
return g == null ? null : g.getBlockEntity(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getBlockState(BlockPos pos) {
|
||||||
|
BlockGetter g = getChunkSafe(pos);
|
||||||
|
return g == null ? Blocks.AIR.defaultBlockState() : g.getBlockState(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidState getFluidState(BlockPos pos) {
|
||||||
|
BlockGetter g = getChunkSafe(pos);
|
||||||
|
return g == null ? Fluids.EMPTY.defaultFluidState() : g.getFluidState(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||||
|
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
|
||||||
|
@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 100)
|
||||||
|
public class BlockStateBaseMixin {
|
||||||
|
@ModifyVariable(method = "getOffset", at = @At("HEAD"), argsOnly = true, index = 1)
|
||||||
|
private BlockGetter useSafeGetter(BlockGetter g) {
|
||||||
|
if(g instanceof ISafeBlockGetter) {
|
||||||
|
SafeBlockGetter replacement = ((ISafeBlockGetter) g).mfix$getSafeBlockGetter();
|
||||||
|
if(replacement.shouldUse())
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
|
||||||
|
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||||
|
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
@Mixin(ServerLevel.class)
|
||||||
|
public class ServerLevelMixin implements ISafeBlockGetter {
|
||||||
|
@Unique
|
||||||
|
private final SafeBlockGetter mfix$safeBlockGetter = new SafeBlockGetter((ServerLevel)(Object)this);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SafeBlockGetter mfix$getSafeBlockGetter() {
|
||||||
|
return mfix$safeBlockGetter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.embeddedt.modernfix.duck;
|
||||||
|
|
||||||
|
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||||
|
|
||||||
|
public interface ISafeBlockGetter {
|
||||||
|
SafeBlockGetter mfix$getSafeBlockGetter();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user