Sync fork to upstream and land surface-relative entity orientation - #1
Open
minerguy341 wants to merge 17 commits into
Open
Sync fork to upstream and land surface-relative entity orientation#1minerguy341 wants to merge 17 commits into
minerguy341 wants to merge 17 commits into
Conversation
Mostly inconsequential except for some heavy-use methods such as in WorldBorder or ServerLevel/ClientLevel
Instead of checking the current actor origin against the current block being broken, we check the current actor origin against the origin when it started breaking the block. This ensures that no matter how large of an area the actor is breaking (IE. rollers, drills from addons that break in an absurd 10x10 area, etc), breaking will still occur until the actor is moved suffeciently far away.
…o Create contraptions (sorry for all the buggy builds)
The entity's OBB yaw was computed from subLevelPose before that variable is assigned for the current substep and sub-level, so the collision box was oriented to the previous substep's pose - or to a different sub-level entirely when several are loaded. On rotated sub-levels this makes entities catch on walls and doorframes that their (correctly oriented) box should clear. Compute the orientation inside the per-sub-level loop, right after the substep pose lerp. The custom-orientation composition matches transformEntityBoundingBox; the up-direction and bounds-center handling that also performs depends only on the custom orientation, not the yaw, so it does not need to be redone here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fill in the stubbed EntitySubLevelUtil.getCustomEntityOrientation: entities standing on (tracking) a tilted sub-level adopt the tilt (swing) component of its orientation, keeping their own yaw, between 1 and 45 degrees of tilt. Below the lower bound the sub-level is treated as flat and vanilla behavior applies; above the upper bound the entity stays upright rather than being pitched onto its side. The orientation is decomposed swing-twist about world Y so the twist - the sub-level's own heading - is discarded: a deck rotating underfoot must not drag the entity's heading with it. All existing custom-orientation plumbing (rendering, shadows, camera, collision OBB, jumping, effects) engages through this stub, so filling it in is what makes surface orientation live. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three fixes that together make standing, walking and sneaking on a tilted sub-level behave the way flat ground does. Gravity press (LivingEntityMixin): grounded on a tilted surface, world-frame gravity leaves a tangential remainder every tick (~g*sin(tilt)) that vanilla never absorbs, because vanilla floors are always horizontal - entities creep downhill with no input. Simply not applying gravity breaks contact: the constant press into the floor is what keeps ground detection engaged, and without it motion decays to zero, contact is lost, and the entity enters a fall-catch-slide cycle. Aim the press along the surface normal instead, so contact stays engaged and the ground resolution absorbs it exactly. Rising jump ticks keep plain world gravity, detected by the velocity component along the SURFACE normal rather than world Y - walking uphill has positive world-Y and would otherwise be misread as a jump, starving the contact press for a tick and letting world gravity stutter in. An earlier iteration of this instead absorbed sub-threshold tangential motion in Block.updateEntityAfterFallOn. That only ran on landing, so it could not hold a standing entity in place, and it is superseded by the press model above. Position reconstruction (SubLevelEntityCollision): reconstruct the entity position as the exact inverse of how entityBoundsCenter was derived from it. The previous reconstruction subtracted half the body height along entityUp, which under a custom orientation displaces the entity laterally by ~eyeHeight*sin(tilt) every tick - a phantom conveyor on any tilted surface. Pose fitting (PlayerMixin): vanilla's canPlayerFitWithinBlocksAndEntities check tests the upright, world-aligned box, but a surface-oriented player's real box is tilted with the surface and clears geometry the upright box cannot, so the check spuriously forces the crawling pose in doorways. Skip the pose demotion while surface-oriented; physical obstruction is still enforced by the oriented collision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Picking (ProjectileUtilMixin): entity picking clips the look ray against the target's world-aligned AABB, but a surface-oriented entity renders tilted, so aiming at the visible model can miss the box. AABBs cannot rotate, so counter-rotate the ray into the entity's frame around its feet pivot (matching the render pivot), clip against the plain box, and transform the hit back - geometrically identical to picking an oriented box. Entities without a custom orientation take the original call unchanged. The candidate loop hands a null entity through ProjectileUtil, so the orientation lookup is null-guarded both here and at the API; without it this NPEs on the render thread. Lighting (EntityRendererMixin): probe from the oriented body center rather than the oriented eye - on tilted decks the tilted eye lever can lean the probe into adjacent geometry, sampling zero light and rendering the entity black. The body center is inside the entity's own volume and so is always in open space. Also require the probe to land in a sub-level plot's AIR before letting that sub-level darken the entity's sky light. When a neighboring contraption's world bounds overlap the entity, the inverse transform drops the probe inside that sub-level's solid geometry - a position the entity cannot really occupy - and its sky=0 would otherwise min() the entity to pitch black. Landing in air is the genuine interior-shadow case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The per-tick [collide dbg] logging in SubLevelEntityCollision.collide is not shippable (fires every tick, client-side, per surface-oriented player), but it is the fastest way to inspect the surface-orientation force model when that work resumes. Record the snippet, its insertion point, the meaning of each field and the locals it depends on, so it can be dropped back in without digging through the feature branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit afec269)
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f0e88183-e4ba-431e-b7fa-7e41c5cad6eb) |
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.
Brings the fork up to
ryanhcode/sable@main(76b67ea) and rebases the surface-orientation work onto it as a reviewable history.Why
mainwas 12 commits / 37 days behind upstream (v2.0.3 → v2.0.4), and the feature work sat onfeature/entity-surface-orientation, whose history was not in a shape that could be offered upstream: it contained aWIPcommit, an abandoned implementation of the force model that was added and then reverted (BlockMixinis touched twice and ends net-zero), and a per-tick debug logging block.The code delta here is byte-for-byte identical to
feature/entity-surface-orientation, with exactly one exception: the[collide dbg]logging block is removed. Verified by diffing the two deltas against their respective bases.What changes
Upstream,
EntitySubLevelUtil.getCustomEntityOrientationis a stub returningnull, so entities on a rotated sub-level stay world-upright — a player on a tilted deck stands vertical regardless of the surface. Filling that stub in is what makes surface orientation live; the rendering, shadow, camera, collision-OBB, jumping and effect plumbing is already wired to it.Four commits, each independently reviewable:
ce976eb96609c22c6c0a2a10074dce976ebis a standalone bug fix and does not depend on the rest. The OBB yaw was read fromsubLevelPosebefore that variable was assigned for the current substep, so the box was oriented to the previous substep's pose — or to a different sub-level entirely when several are loaded. In game: entities catch on walls and doorframes of rotated sub-levels that their correctly-oriented box should clear. This bites any rotated sub-level whether or not surface orientation is enabled.96609c2decomposes the tracked sub-level's interpolated orientation swing-twist about world Y, discarding the twist so a deck rotating underfoot does not drag the entity's heading with it. Gated to 1°–45°: below, treated as flat with vanilla behaviour; above, the entity stays upright rather than being pitched onto its side.2c6c0a2covers the three ways a tilted floor breaks assumptions that only hold for horizontal ground:g*sin(tilt)tangential remainder each tick that vanilla never absorbs, so entities creep downhill with no input. The press is aimed along the surface normal instead — simply zeroing gravity breaks contact, since the constant press is what keeps ground detection engaged. Jump detection uses velocity along the surface normal, not world Y, because walking uphill has positive world-Y and would otherwise be misread as a jump.entityUp, which is not the inverse of howentityBoundsCenterwas derived; under a custom orientation that displaces the entity laterally by ~eyeHeight*sin(tilt)every tick — a phantom conveyor on any tilted surface.a10074dcounter-rotates the pick ray into the entity's frame about its feet pivot so aiming at a tilted model actually hits it, and fixes two rendering faults: the light probe now samples from the oriented body center (the tilted eye lever could lean it into adjacent geometry and render the entity black), and sky-light darkening from a sub-level now requires the probe to land in that plot's air, so a neighbouring contraption whose bounds merely overlap the entity can no longermin()it to black.4d276d6archives the removed debug ledger under.dev/debug-snippets/with its insertion point and field meanings, so it can be dropped back in when this work resumes.Review notes
ProjectileUtilinjector interaction.entity_interaction.ProjectileUtilMixinalready@Redirects the sameAABB.clipcall sites in the same twogetEntityHitResultoverloads that the newentity_rotations_and_riding.ProjectileUtilMixintargets with@WrapOperation. The two demonstrably coexist at runtime — the NPE fixed in the original branch was observed on the render thread, so both applied — but the composition order is priority-dependent and worth a look. The pre-existing redirect maps the ray between sub-level frames for cross-sub-level picking, while the new wrapper counter-rotates around a world-frame pivot; if the wrapper runs inside the redirect it would be counter-rotating already-local coordinates. Untested for the cross-sub-level case specifically.Camera.setup, which is what desynced two earlier attempts. Not addressed here.Not an upstream PR
This targets the fork's own
main. A PR toryanhcode/sablecould not be opened from this session, which is scoped to theminerguy341owner. Note also that upstream landed76b67ea("Further clarify the CLA") two days ago, and the project is PolyForm Shield 1.0.0 — worth reading before offeringce976ebupstream on its own, which is the piece most likely to be accepted independently.🤖 Generated with Claude Code
https://claude.ai/code/session_01NBzRL3ez8kpd7KjWMyb9HG
Generated by Claude Code
Note
Cursor Bugbot is generating a summary for commit 4d276d6. Configure here.