fix(portal): commit destination projection before reveal
Retail installs the destination CObjCell before hidden world simulation resumes. Rebucket the retained local-player projection inside the Place transaction so Hidden/UnHide scripts and particles settle behind the portal viewport instead of firing after reveal. Preserve the existing unloaded-cell scheduler gate and pin the ordering with a cross-landblock placement test. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
1a14812c44
commit
7aa9618136
4 changed files with 173 additions and 1 deletions
|
|
@ -204,6 +204,62 @@ WorldBuilder cross-check: its upload queue similarly distinguishes generated
|
|||
CPU scenery from render-thread GPU publication (`GameScene.ProcessUploads`),
|
||||
which confirms that worker completion alone is not draw readiness.
|
||||
|
||||
### 2.1. Destination placement enters the spatial cell before simulation resumes
|
||||
|
||||
Named retail references:
|
||||
|
||||
- `CPhysicsObj::change_cell` at `0x00513390`
|
||||
- `CPhysicsObj::update_object` at `0x00515D10`
|
||||
- `CPhysicsObj::enter_world` at `0x00516170`
|
||||
- `CPhysicsObj::prepare_to_enter_world` at `0x00511FA0`
|
||||
- `CPhysicsObj::set_hidden` at `0x00514C60`
|
||||
|
||||
Retail does not separate an accepted destination Position from the object's
|
||||
live cell pointer. `enter_world` runs `SetPosition`, which installs the object
|
||||
in its destination `CObjCell`, before the PartArray and MovementManager
|
||||
enter-world boundaries complete. `update_object` then rejects only a parented
|
||||
object, a null `cell`, or a Frozen object; Hidden is not a reason to skip the
|
||||
ScriptManager/ParticleManager tail.
|
||||
|
||||
```text
|
||||
accepted portal destination becomes ready:
|
||||
player.enter_world(destination)
|
||||
SetPosition(destination)
|
||||
change_cell(destination CObjCell)
|
||||
PartArray.HandleEnterWorld()
|
||||
MovementManager.HandleEnterWorld()
|
||||
|
||||
destination simulation resumes behind the portal viewport
|
||||
update_object()
|
||||
require cell != null and not Frozen
|
||||
UpdateObjectInternal()
|
||||
advance ScriptManager and ParticleManager
|
||||
```
|
||||
|
||||
acdream keeps authoritative full-cell identity and render-bucket residency as
|
||||
separate facts because its world streams asynchronously. The portal arrival
|
||||
transaction must therefore commit both facts before releasing destination
|
||||
simulation:
|
||||
|
||||
```text
|
||||
LocalPlayerTeleportPlacement.Place:
|
||||
resolve and set controller/body position
|
||||
update retained WorldEntity root transform
|
||||
RebucketLiveEntity(destination full cell)
|
||||
reconcile child/effect/light poses
|
||||
signal materialized and release world simulation
|
||||
```
|
||||
|
||||
A 2026-07-25 connected trace proved the missing rebucket was the cause of the
|
||||
spell-recall exit regression. The local record had destination `FullCellId`,
|
||||
resources, and a logical projection, but remained non-resident in its old or
|
||||
pending GPU bucket. The queued Hidden PES `0x33000331` consequently stayed
|
||||
paused until the normal world was already visible, then fired immediately
|
||||
before UnHide stopped it. That produced both the sudden opaque character and
|
||||
the late purple/recall tail. The correct fix is the missing destination spatial
|
||||
commit, not a recall classifier, timer, or relaxation of the legitimate
|
||||
unloaded-cell script gate.
|
||||
|
||||
## 3. Successful indoor transitions commit the canonical outbound Position
|
||||
|
||||
Named retail references:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue