Skip to content

Fixes #681 | Fix deployer placement orientation on rotated sub-levels - #1569

Open
melon-444 wants to merge 2 commits into
ryanhcode:mainfrom
melon-444:main
Open

melon-444 wants to merge 2 commits into
ryanhcode:mainfrom
melon-444:main

Conversation

@melon-444

@melon-444 melon-444 commented Sep 17, 2026

Copy link
Copy Markdown

Fix deployer placement orientation on rotated sub-levels

Summary

Fix Create Deployer placement orientation when a deployer operates inside a rotated Sable SubLevel.

Create deployers already calculate their FakePlayer position and rotation in the local coordinate system used by the current contraption or block entity. Sable then projects that synthetic player into the target SubLevel once again, causing the orientation to be transformed twice.

This affects both stationary deployers and deployers mounted on moving Create contraptions.

Root cause

Create's DeployerBlockEntity and DeployerMovementBehaviour initialize DeployerFakePlayer using the deployer's current local facing.

Sable's block-placement compatibility layer assumes that every interacting player is expressed in world space and calls SubLevelHelper.pushEntityLocal(...) before reading:

  • UseOnContext#getHorizontalDirection();
  • UseOnContext#getRotation();
  • Direction.getFacingAxis(...);
  • Direction.orderedByNearest(...).

For a Create DeployerFakePlayer operating inside the same SubLevel, this applies the same SubLevel inverse transform a second time.

If Q is the SubLevel rotation and d is the Create-provided local direction, the previous behavior is effectively:

Create direction       = d
Sable inverse transform = Q⁻¹d

The FakePlayer is therefore interpreted in the wrong coordinate system. This produces the original-direction or perpendicular-direction behavior reported in:

Fix

Add a small interaction-space marker for synthetic entities and implement it for Create's DeployerFakePlayer.

Before Sable localizes an entity, it checks whether:

sourceSubLevel == targetSubLevel

When this condition is true, the FakePlayer is already expressed in the target SubLevel's local coordinate system, so Sable leaves it unchanged.

The existing Sable transformation remains active for:

  • real players;
  • interactions from a different SubLevel;
  • SubLevel-to-world interactions;
  • SubLevel A to SubLevel B interactions;
  • non-Create entities.

The fix also applies the same rule to BlockPlaceContext direction selection, preventing player-derived placement calculations from being localized twice.

Why this approach

The fix preserves Create's existing Deployer logic instead of duplicating or replacing DeployerHandler.

This is important because Create has separate execution paths for:

  • DeployerBlockEntity;
  • DeployerMovementBehaviour.

Both paths already calculate the correct current local orientation, including contraption rotation. Modifying Sable's shared localization decision allows both paths to use the same compatibility rule.

The patch does not disable Sable's SubLevel transformation globally. It only skips the transformation when the synthetic player and target are already in the same local coordinate system.

Normal world Deployer behavior is unchanged because no Sable SubLevel is involved.

Testing

The following cases should be tested:

  • stationary Deployer in the main world;
  • stationary Deployer inside a rotated SubLevel;
  • Deployer mounted on a Create moving contraption;
  • Deployer mounted on a gantry or bearing contraption;
  • yaw rotations of 90°, 180° and 270°;
  • pitch and roll rotations;
  • carved pumpkins;
  • pistons;
  • observers;
  • dispensers;
  • stairs;
  • chains;
  • logs;
  • end rods;
  • rails and Create tracks.

The most important regression cases are:

Create Deployer → carved pumpkin
Create Deployer → chain
Create Deployer on moving contraption → carved pumpkin
Create Deployer on moving contraption → chain

The standalone compatibility mod uses the same coordinate-space rule and was used as the local implementation reference. No separate behavioral divergence is expected between the standalone version and this upstream patch.

Compatibility

This patch does not change Create's public API and does not add a dependency on Create to Sable's common module.

The Create-specific implementation remains in the NeoForge compatibility source set. The generic interaction marker is exposed from Sable's API so that other synthetic interaction entities can use the same mechanism.

The patch is intended for Minecraft 1.21.1 and the corresponding Sable/Create compatibility layer.

Related issues

Fixes #681

Related: Creators-of-Aeronautics/Simulated-Project#1145

@CLAassistant

CLAassistant commented Sep 17, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deployer block orientation is perpendicular to intended after contraption is rotated 90° on x-z plane

2 participants