Repository navigation
SubLevelAPI functions are not on the main thread #6
Description
Activity
- added a commit that references this issue
on May 4, 2026 Potentially fixed in 4efdef9
- addedbugSomething isn't workingSomething isn't workinghelp wantedExtra attention is neededExtra attention is needed
on May 4, 2026 While testing with version 1.2.2, I observed something interesting: While I had another computer running a bunch of commands, my ship remained stable. When I stopped the command loop, suddenly my ship began losing control and glitching/teleporting to 0, 0, 0.
Not exactly sure what about running extra work on another computer would make the ship stable and the glitches disappear. But I thought I'd bring this up in case it helps in any way.
I think this is less a CC: Sable bug than a Sable bug that CC: Sable exposed
While testing with version 1.2.2, I observed something interesting: While I had another computer running a bunch of commands, my ship remained stable. When I stopped the command loop, suddenly my ship began losing control and glitching/teleporting to 0, 0, 0.
Not exactly sure what about running extra work on another computer would make the ship stable and the glitches disappear. But I thought I'd bring this up in case it helps in any way.
Try this test without CC: Sable
While testing with version 1.2.2, I observed something interesting: While I had another computer running a bunch of commands, my ship remained stable. When I stopped the command loop, suddenly my ship began losing control and glitching/teleporting to 0, 0, 0.
Not exactly sure what about running extra work on another computer would make the ship stable and the glitches disappear. But I thought I'd bring this up in case it helps in any way.
https://youtu.be/7qnNlvJDU88Try this test without CC: Sable
Can't quite test it without CC: Sable since the quadrotor is being stabilized with it lol. On every computer/game tick I call a ton of the 'sublevel' API methods to get the ship's state for the LQR controller.
Thats fair, can I have a world download ZIP that I can test in-dev and can send along to another dev who is also looking into the issue?
Could you please make it so at least the getLogicalPose function returns nil instead of erroring when used while not on a sublevel? With this change it now takes 2 ticks just to get the logical pose since you need to use isInSubGrid first to avoid erroring. pcall doesnt seem to work for it for some reason.
EDIT: nvm pcall works fine was just using it wrongI believe the issue was mainly from using
getVelocity, which was using theSableCompanionto reach into the physics pipeline to get the velocity, which is likely not thread-safe. All the other methods usegetSublevel().*which don't seem to have this issue.There's already a
getLinearVelocitywhich uses exactly this pattern. I thinkgetVelocityis a bit different? ButgetLinearVelocitydoes what I need, so just using it instead ofgetVelocityeliminates the glitches without having the methods run on the main thread.I’ve tried to find the cause of this bug with llm’s help. Here’s what I could find out, maybe that would be helpful:
AI's response
The mainThread = true fix stops the teleportation bug, but it introduces a 1 server tick delay for every single API call, forcing the Lua script to wait for the next server tick.
The core problem is most likely that Rapier runs in native memory (Rust) and its JNI bindings aren't thread-safe. When the main server thread updates physics via pipeline.physicsTick(), it actively mutates coordinates in memory. If a Lua computer tries to read getPose() from a background thread at the exact same millisecond, it triggers a race condition and memory tearing. This perfectly explains why Rapier suddenly returns corrupted data, resulting in those weird (0,0,0) position resets.
A better approach to guarantee thread safety without sacrificing performance would be state snapshotting.
Right after the physics pipeline finishes stepping on the main thread, we can extract the raw position and velocity data from Rapier and copy it into a lightweight, thread-safe Java DTO (like a volatile reference or using a fast concurrent lock). Then, we can remove mainThread = true from the @LuaFunction annotations, allowing them to run off-thread again. Instead of hitting the native Rapier JNI directly, these functions will read the data from this cached Java object.Reacted by SUPERALEX93Reacted by SUPERALEX93Reacted by SUPERALEX93Reacted by SUPERALEX93Reacted by SUPERALEX93asking an AI is not generally helpful. but its response is generally how threading issues like this are solved yes
Reacted by SUPERALEX93Reacted by SUPERALEX93

Every once in a while my ships were being randomly teleported to 0 0 0, turns out it was being caused by CC-Sable. The problem is that your Lua Functions are being executed in the CC Thread instead of the Server Thread, and that does some weird things to Sable.
You can reproduce this error by having a computer in a sub level doing the following:
And then just fly around your world.
It might take a while, but a good way I found it to force it to happen is just setting the /tick rate to something big like 10000 and then throwing the ship around using the creative physics wand thingy.
You can fix it by setting the @LuaFunction annotations to @LuaFunction(mainThread = true)
Tested it on a local build and it seems to work well