Skip to content

Commit d7b2f5b

Browse files
committed
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: N1nn1/twigs#6 Related: N1nn1/etcetera#28
1 parent 33e43f5 commit d7b2f5b

File tree

4 files changed

+115
-0
lines changed

4 files changed

+115
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.embeddedt.modernfix.chunk;
2+
3+
import net.minecraft.core.BlockPos;
4+
import net.minecraft.server.level.ServerLevel;
5+
import net.minecraft.world.level.BlockGetter;
6+
import net.minecraft.world.level.block.Blocks;
7+
import net.minecraft.world.level.block.entity.BlockEntity;
8+
import net.minecraft.world.level.block.state.BlockState;
9+
import net.minecraft.world.level.chunk.ChunkAccess;
10+
import net.minecraft.world.level.chunk.ChunkStatus;
11+
import net.minecraft.world.level.material.FluidState;
12+
import net.minecraft.world.level.material.Fluids;
13+
import org.jetbrains.annotations.Nullable;
14+
15+
public class SafeBlockGetter implements BlockGetter {
16+
private final ServerLevel wrapped;
17+
private final Thread mainThread;
18+
19+
public SafeBlockGetter(ServerLevel wrapped) {
20+
this.wrapped = wrapped;
21+
this.mainThread = Thread.currentThread();
22+
}
23+
24+
public boolean shouldUse() {
25+
return Thread.currentThread() != this.mainThread;
26+
}
27+
28+
@Nullable
29+
private BlockGetter getChunkSafe(BlockPos pos) {
30+
// can safely call getChunkForLighting off-thread
31+
BlockGetter access = this.wrapped.getChunkSource().getChunkForLighting(pos.getX() >> 4, pos.getZ() >> 4);
32+
if(!(access instanceof ChunkAccess))
33+
return null;
34+
ChunkAccess chunk = (ChunkAccess)access;
35+
if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL))
36+
return null;
37+
return chunk;
38+
}
39+
40+
@Override
41+
public int getMaxBuildHeight() {
42+
return this.wrapped.getMaxBuildHeight();
43+
}
44+
45+
@Override
46+
public int getMaxLightLevel() {
47+
return this.wrapped.getMaxLightLevel();
48+
}
49+
50+
@Nullable
51+
@Override
52+
public BlockEntity getBlockEntity(BlockPos pos) {
53+
BlockGetter g = getChunkSafe(pos);
54+
return g == null ? null : g.getBlockEntity(pos);
55+
}
56+
57+
@Override
58+
public BlockState getBlockState(BlockPos pos) {
59+
BlockGetter g = getChunkSafe(pos);
60+
return g == null ? Blocks.AIR.defaultBlockState() : g.getBlockState(pos);
61+
}
62+
63+
@Override
64+
public FluidState getFluidState(BlockPos pos) {
65+
BlockGetter g = getChunkSafe(pos);
66+
return g == null ? Fluids.EMPTY.defaultFluidState() : g.getFluidState(pos);
67+
}
68+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
2+
3+
import net.minecraft.world.level.BlockGetter;
4+
import net.minecraft.world.level.block.state.BlockBehaviour;
5+
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
6+
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
import org.spongepowered.asm.mixin.injection.At;
9+
import org.spongepowered.asm.mixin.injection.ModifyVariable;
10+
11+
@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 100)
12+
public class BlockStateBaseMixin {
13+
@ModifyVariable(method = "getOffset", at = @At("HEAD"), argsOnly = true, index = 1)
14+
private BlockGetter useSafeGetter(BlockGetter g) {
15+
if(g instanceof ISafeBlockGetter) {
16+
SafeBlockGetter replacement = ((ISafeBlockGetter) g).mfix$getSafeBlockGetter();
17+
if(replacement.shouldUse())
18+
return replacement;
19+
}
20+
return g;
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
2+
3+
import net.minecraft.server.level.ServerLevel;
4+
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
5+
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.Unique;
8+
9+
@Mixin(ServerLevel.class)
10+
public class ServerLevelMixin implements ISafeBlockGetter {
11+
@Unique
12+
private final SafeBlockGetter mfix$safeBlockGetter = new SafeBlockGetter((ServerLevel)(Object)this);
13+
14+
@Override
15+
public SafeBlockGetter mfix$getSafeBlockGetter() {
16+
return mfix$safeBlockGetter;
17+
}
18+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.embeddedt.modernfix.duck;
2+
3+
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
4+
5+
public interface ISafeBlockGetter {
6+
SafeBlockGetter mfix$getSafeBlockGetter();
7+
}

0 commit comments

Comments
 (0)