Guard remaining level_colliders lookups against missing keys (extends #1396) - #1
Merged
mynameisgrass merged 1 commit intoJul 31, 2026
Conversation
PR ryanhcode#1396 (ssarg / mynameisgrass) fixed the non-unwinding panic on the rigid_bodies registry (the reported getPose crash), but a second registry, level_colliders, was left fully unguarded. On a production NeoForge 1.21.1 server this reproduced the exact same failure mode (SIGABRT from a Rust panic across an extern "system" JNI boundary) one call further down the same code path, in setCenterOfMass: thread '<unnamed>' panicked at rapier/src/lib.rs:557:14: called `Option::unwrap()` on a `None` value thread caused non-unwinding panic. aborting. This commit audits every JNI entry point that indexes level_colliders (or a rigid_bodies handle later resolved through it) by an id supplied from Java, and replaces unwrap()/expect()/direct indexing with Option-based guards that no-op (or fall back to an unswapped/zero default where the lookup only affects an optimization) when the body has already been removed - mirroring the pattern ryanhcode#1396 applied to rigid_bodies. Sites fixed, none of which were touched by ryanhcode#1396: - lib.rs: setCenterOfMass, setLocalBounds, addChunk - contraptions.rs: get_kinematic_collider_info (now returns Option) and its two callers; createKinematicContraption's mount lookup; removeKinematicContraption against a double-remove - rope.rs: tick() start/end attachment anchor updates, for a rope still attached to a sub-level that has since unloaded - dispatcher.rs: the collision-pair swap heuristic, and both world_vs_world contact-manifold paths - hooks.rs: fake-velocity lookups in both solver-contact hooks Where a guarded lookup only feeds a performance heuristic (the dispatcher swap order) or a purely cosmetic velocity nudge, the fallback is the pre-existing default behavior rather than skipping the tick, since skipping there is not required for correctness. Verified against the production crash: applying ryanhcode#1396 alone traded the original getPose panic for this setCenterOfMass panic on the same server within the same physics tick chain (onStatsChanged calls setCenterOfMass then setLocalBounds back to back). With this commit on top, both crashes are gone under the same reproduction (repeated sub-level load/unload near a player). This patch was written and applied by Claude (Anthropic) at the repository owner's direction - i.e. it is vibecoded: the owner described the crash and asked for a fix, Claude read ryanhcode#1396, found the gap, wrote the guards, cross-compiled sable_rapier for x86_64-unknown-linux-gnu.2.17 to match upstream's release target, and verified the fix in place on the affected server before this commit was prepared. It has not been reviewed by a Rust engineer beyond that runtime verification - please review the unwrap/expect removals accordingly, in particular whether any of the now-silent no-op paths should instead log or clean up related state (e.g. dangling rope attachments after a level_colliders entry disappears from under it).
Owner
|
thanks for the fix man |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Title
Guard remaining
level_colliderslookups against missing keys (extends ryanhcode#1396)Description
This builds on ryanhcode#1396 (@mynameisgrass / "ssarg"), which fixed the non-unwinding
panic on the
rigid_bodiesregistry — the reportedgetPosecrash where asub-level's rigid body handle could be missing after an unload. That PR is a
correct fix as far as it goes, but there is a second, separate registry,
level_colliders, that was left entirely unguarded. It's indexed by the samekind of Java-supplied
id, on the same kind of "the sub-level may have beenunloaded between the Java call and this native call" race, and it panics the
same way: a
panic!/unwrap()/expect()inside anextern "system"JNIfunction is a non-unwinding panic in this build configuration, which means it
aborts the whole process (
SIGABRT), not just the calling thread.We hit this in production immediately after deploying ryanhcode#1396's fix on a
NeoForge 1.21.1 dedicated server: the original
getPosepanic disappeared,and was immediately replaced by this one, one call further down the same
code path (
onStatsChangedcallssetCenterOfMassthensetLocalBoundsback to back):
What this does
Audited every JNI entry point that reads or writes
level_colliders(or arigid_bodieshandle resolved indirectly through it) by an id coming fromJava, and replaced
unwrap()/expect()/ direct map indexing withOption-based guards, following the same pattern ryanhcode#1396 used forrigid_bodies. Where the guarded value only feeds a performance heuristic ora cosmetic detail (see below), the fallback is the pre-existing default
rather than skipping the whole call, since correctness doesn't require it.
Sites fixed (none touched by ryanhcode#1396):
lib.rs—setCenterOfMass,setLocalBounds,addChunkcontraptions.rs—get_kinematic_collider_info(now returnsOption<&mut _>instead of.expect()-ing) and its two callers;createKinematicContraption's mount-body lookup;removeKinematicContraptionguarded against a double-remove
rope.rs—tick()'s start/end attachment anchor updates, for a ropestill attached to a sub-level that has since been unloaded
dispatcher.rs— the collision-pair swap-order heuristic, and bothworld_vs_worldcontact-manifold code pathshooks.rs— fake-velocity lookups in both solver-contact hooksTesting
Cross-compiled
sable_rapierforx86_64-unknown-linux-gnu.2.17(matchingthe release target used for the official builds — verified via
objdump,max required symbol is
GLIBC_2.17), swapped only the native library insidesable_rapier_binaries.zip.l4zin an otherwise-untouched production jar, anddeployed to the affected server. Applying ryanhcode#1396 alone traded the
getPosepanic for the
setCenterOfMasspanic above within the same play session(repeated sub-level load/unload near a player). With this patch on top of
ryanhcode#1396, both crashes are gone under the same reproduction.
This has not been reviewed by a Rust engineer beyond that runtime
verification. In particular I'd like a second opinion on whether any of the
now-silent no-op paths should do more than nothing — e.g. whether a rope
whose attachment sub-level disappears mid-
tick()should also get cleanedup/detached rather than just being left with a stale anchor until the next
dead_start_attachments/dead_end_attachmentssweep catches it via thejoint-handle check.
Attribution
This patch was written and applied by Claude (Anthropic) at my direction
— I described the crash, gave it FTP access to the affected server and its
logs, and asked it to find and fix the underlying cause. It read ryanhcode#1396,
found the
level_collidersgap, wrote these guards, cross-compiled andverified the fix live before I asked it to prepare this PR. I'm submitting
it because it fixed a real production crash for me and the diff looked
correct to me on review, but flagging the authorship clearly so maintainers
weight the review accordingly.