Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ public boolean shouldTriggerFor(final BlockState state) {
}

public CollisionResult onHit(final ServerLevel level, final BlockPos pos, final BlockState state, final Vector3d hitPos) {
if (SubLevelPhysicsSystem.IN_PHYSICS_STEP) {
// Breaking the block here would run its neighbour updates inside the native physics step, which can call back
// into Rapier (e.g. building a collider for a block state it hasn't seen yet) and hang the server thread.
// Queue the break until the step has finished instead.
SubLevelPhysicsSystem.getCurrentlySteppingSystem().queueFragileBlockBreak(pos, state);
} else {
breakFragileBlock(level, pos, state);
}

return new CollisionResult(JOMLConversion.ZERO, true);
}

/**
* Breaks a fragile block, melting ice on destruction
*
* @param level the level the block is in
* @param pos the position of the block
* @param state the state of the block being broken
*/
public static void breakFragileBlock(final ServerLevel level, final BlockPos pos, final BlockState state) {
level.destroyBlock(pos, true);

// Melt ice on destruction
Expand All @@ -62,7 +82,5 @@ public CollisionResult onHit(final ServerLevel level, final BlockPos pos, final
level.setBlockAndUpdate(pos, IceBlock.meltsInto());
}
}

return new CollisionResult(JOMLConversion.ZERO, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import dev.ryanhcode.sable.companion.math.Pose3d;
import dev.ryanhcode.sable.mixinterface.plot.SubLevelContainerHolder;
import dev.ryanhcode.sable.mixinterface.toast.SableToastableServer;
import dev.ryanhcode.sable.physics.callback.FragileBlockCallback;
import dev.ryanhcode.sable.physics.config.PhysicsConfigData;
import dev.ryanhcode.sable.physics.config.block_properties.PhysicsBlockPropertyHelper;
import dev.ryanhcode.sable.physics.config.dimension_physics.DimensionPhysicsData;
Expand All @@ -27,6 +28,8 @@
import dev.ryanhcode.sable.sublevel.plot.ServerLevelPlot;
import dev.ryanhcode.sable.sublevel.storage.SubLevelRemovalReason;
import dev.ryanhcode.sable.sublevel.system.ticket.PhysicsChunkTicketManager;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
Expand Down Expand Up @@ -118,6 +121,11 @@ public class SubLevelPhysicsSystem implements SubLevelObserver {
*/
private final Collection<ArbitraryPhysicsObject> queuedWakeUps = new ReferenceOpenHashSet<>();

/**
* Fragile block breaks queued during the physics step, keyed by block position
*/
private final Long2ObjectLinkedOpenHashMap<BlockState> queuedFragileBlockBreaks = new Long2ObjectLinkedOpenHashMap<>();

/**
* Creates a new physics system.
*/
Expand Down Expand Up @@ -278,6 +286,7 @@ private void tickPipelinePhysics(final ServerSubLevelContainer container) {
IN_PHYSICS_STEP = true;
this.pipeline.physicsTick(substepTimeStep);
IN_PHYSICS_STEP = false;
this.breakQueuedFragileBlocks();

// if any blocks were modified due to, say, fragile blocks breaking
// sub-levels could have been removed during the physics tick
Expand Down Expand Up @@ -514,6 +523,35 @@ public void wakeUpObjectsAt(final int x, final int y, final int z) {
}
}

/**
* Queues a fragile block to be broken once the current physics step has finished
*
* @param pos the position of the block
* @param state the state of the block when it was hit
*/
public void queueFragileBlockBreak(final BlockPos pos, final BlockState state) {
this.queuedFragileBlockBreaks.putIfAbsent(pos.asLong(), state);
}

/**
* Breaks the fragile blocks queued during the physics step
*/
private void breakQueuedFragileBlocks() {
if (this.queuedFragileBlockBreaks.isEmpty())
return;

for (final Long2ObjectMap.Entry<BlockState> entry : this.queuedFragileBlockBreaks.long2ObjectEntrySet()) {
final BlockPos pos = BlockPos.of(entry.getLongKey());

// the block could have been changed since it was hit, e.g. by an earlier break
if (this.level.getBlockState(pos) == entry.getValue()) {
FragileBlockCallback.breakFragileBlock(this.level, pos, entry.getValue());
}
}

this.queuedFragileBlockBreaks.clear();
}

public void updateMassDataFromBlockChange(final SubLevel subLevel, final BlockPos globalBlockPos, final BlockState oldState, final BlockState newState, final boolean notifyPipeline) {
if ((subLevel instanceof final ServerSubLevel serverSubLevel)) {

Expand Down