Join destination scheduling to the canonical reveal generation, protect its share across every typed frame-budget dimension, and prevent stale work from clearing a replacement reservation. Remove forced incomplete materialization and project retail's centered portal wait cue while the authored tunnel remains active. Tests: Release build clean; 91 focused reservation/reveal tests; full solution 8,158 passed, 5 skipped. Co-authored-by: Codex <noreply@openai.com>
215 lines
6.8 KiB
Markdown
215 lines
6.8 KiB
Markdown
# Retail landblock/object retirement and portal blocking — pseudocode
|
|
|
|
**Purpose:** Oracle boundary for Modern Runtime Slice E.
|
|
|
|
## Named-retail sources
|
|
|
|
- `CLandBlock::destroy_static_objects @ 0x0052FA50`
|
|
- `CLandBlock::Destroy @ 0x0052FAA0`
|
|
- `CObjectMaint::DestroyObjects @ 0x00508C30`
|
|
- `CObjectMaint::AddObjectToBeDestroyed @ 0x00508F70`
|
|
- `SmartBox::UseTime @ 0x00455410`
|
|
- `gmSmartBoxUI::UseTime @ 0x004D6E30`
|
|
|
|
The Sept 2013 named pseudo-C is the behavioral oracle. WorldBuilder has no
|
|
equivalent live-client teardown scheduler; its extracted mesh/cache owners are
|
|
implementation references only.
|
|
|
|
## `CLandBlock::destroy_static_objects`
|
|
|
|
```text
|
|
for i = 0 .. num_static_objects - 1:
|
|
object = static_objects[i]
|
|
if object != null:
|
|
object.leave_world()
|
|
delete object
|
|
|
|
num_static_objects = 0
|
|
```
|
|
|
|
The operation is synchronous in retail. Each object leaves the world before
|
|
its memory is destroyed.
|
|
|
|
## `CLandBlock::Destroy`
|
|
|
|
```text
|
|
destroy_static_objects()
|
|
destroy_buildings()
|
|
|
|
if landblock_info != null:
|
|
landblock_info.Release()
|
|
landblock_info = null
|
|
|
|
closest = invalid
|
|
lbi_exists = false
|
|
direction = unknown
|
|
|
|
if draw_array != null:
|
|
delete[] draw_array
|
|
draw_array = null
|
|
|
|
draw_array_size = 0
|
|
```
|
|
|
|
Retail does not expose a partially active destroyed landblock. The visible,
|
|
spatial, ticking, and memory lifetimes end in one call.
|
|
|
|
## `CObjectMaint::DestroyObjects`
|
|
|
|
```text
|
|
for every physics object:
|
|
object.exit_world()
|
|
object.leave_world()
|
|
remove object from object_table
|
|
remove object from null_object_table
|
|
remove object from destruction schedule
|
|
object.unset_parent()
|
|
object.unparent_children()
|
|
delete object
|
|
|
|
for every weenie object:
|
|
remove from weenie tables
|
|
delete object
|
|
|
|
destroy all object/null/weenie/inventory tables
|
|
destroy lost-cell table
|
|
```
|
|
|
|
Again, execution is complete once begun. Parent/child and spatial withdrawal
|
|
precede physical deletion.
|
|
|
|
## `CObjectMaint::AddObjectToBeDestroyed`
|
|
|
|
```text
|
|
remove any prior destruction schedule for object_id
|
|
deadline = Timer.cur_time + 25 seconds
|
|
destruction_table[object_id] = deadline
|
|
destruction_priority_queue.Insert(deadline, object_id)
|
|
```
|
|
|
|
This queue delays *when* complete object destruction begins. It is not
|
|
precedent for leaving an object active while destroying it one field at a
|
|
time. Acdream's 25-second nonresident live-object lifetime already ports this
|
|
separate behavior.
|
|
|
|
## `SmartBox::UseTime`
|
|
|
|
```text
|
|
if cell_manager.blocking_for_cells == false:
|
|
if not all_cells_available and CheckPrefetchStatus():
|
|
CellManager.UpdateLoadPoint()
|
|
|
|
if player exists and player has a cell:
|
|
CellManager.ChangePosition(player.position)
|
|
|
|
if player exists and not waiting_for_teleport
|
|
and not position_update_complete:
|
|
position_update_complete = true
|
|
has_been_teleported = true
|
|
|
|
ObjectMaint.UseTime()
|
|
Physics.UseTime()
|
|
GameTime.UseTime()
|
|
LScape.UseTime()
|
|
Ambient.UseTime()
|
|
else:
|
|
CellManager.CheckPrefetchStatus()
|
|
|
|
SceneTool.Think()
|
|
drain inbound SmartBox event queue
|
|
CommandInterpreter.UseTime()
|
|
Render.CalcDegLevel()
|
|
```
|
|
|
|
While retail is blocked for destination cells, the old world does not advance
|
|
object maintenance, physics, landscape, game time, or ambient sound. UI/event
|
|
dispatch and the portal presentation continue. This is the retail basis for
|
|
Slice E's immediate old-generation quiesce.
|
|
|
|
## `gmSmartBoxUI::UseTime` portal wait presentation
|
|
|
|
The portal `CreatureMode` remains live while `SmartBox` is blocked. Its camera
|
|
continues one random roll segment at a time:
|
|
|
|
```text
|
|
if now >= rotationStart + rotationDuration:
|
|
currentAngle = rotationEndAngle
|
|
rotationStart = now
|
|
rotationDuration = RandomUniform(0.6, 1.8) seconds
|
|
rotationStartAngle = currentAngle
|
|
rotationEndAngle = RandomUniform(0, 360) degrees
|
|
ECM_UI.SendNotice_DisplayStringInfo(
|
|
channel = 0x1A,
|
|
text = "In Portal Space - Please Wait...")
|
|
else:
|
|
level = UIGlobals.GetAnimLevel(
|
|
(now - rotationStart) / rotationDuration)
|
|
currentAngle = Lerp(
|
|
rotationStartAngle,
|
|
rotationEndAngle,
|
|
level / 1024)
|
|
|
|
CreatureMode.SetCameraDirection_Degrees((0, currentAngle, 0))
|
|
```
|
|
|
|
The notice is a centered UI display string, not chat. Slice E's asynchronous
|
|
adaptation delays its first projection until the recorded five-second reveal
|
|
stall threshold, then repeats it on the same rotation-segment boundary while
|
|
the authored tunnel keeps animating.
|
|
|
|
## Acdream asynchronous adaptation
|
|
|
|
Retail performs each teardown synchronously and can synchronously block cell
|
|
loading. Acdream prepares content on a worker and must not spend an unbounded
|
|
render/update frame reproducing that transaction. The faithful observable
|
|
contract is therefore split at a different boundary:
|
|
|
|
```text
|
|
begin destination generation:
|
|
mark prior world generation unavailable
|
|
replace normal world viewport with portal presentation
|
|
freeze old-generation object/static/effect simulation
|
|
silence old-generation audio
|
|
reject old generation from collision, picking, radar, and targeting
|
|
|
|
over later update/resource-maintenance frames:
|
|
detach old landblocks through exact retry receipts
|
|
release scripts/effects/lights/plugin projections
|
|
release CPU owners
|
|
admit renderer retirement; physical GL release remains fence delayed
|
|
|
|
in parallel, under reserved destination budgets:
|
|
admit destination completions
|
|
publish collision and required Near render state
|
|
request/upload required assets
|
|
|
|
reveal only when the one destination-generation readiness state proves:
|
|
collision root ready
|
|
required Near scene ready
|
|
required composite textures ready
|
|
camera/player identity belongs to destination generation
|
|
|
|
if the portal reveal is still incomplete after five seconds:
|
|
keep the DAT-authored portal scene animating
|
|
display the centered retail wait notice
|
|
emit one generation-scoped diagnostic
|
|
do not materialize early
|
|
```
|
|
|
|
Release-later never means run-later: an old owner may retain memory until its
|
|
cursor runs, but it cannot keep simulating, colliding, rendering, targeting, or
|
|
emitting audible work after the generation-quiesce edge.
|
|
|
|
## Port constraints
|
|
|
|
1. Preserve existing exact retry receipts; successful stages never replay.
|
|
2. FIFO is stable within priority and generation.
|
|
3. A time limit is checked between atomic owner operations. If one atomic
|
|
operation itself exceeds its ceiling, diagnostics name that operation; it
|
|
is then split at its natural cursor rather than interrupted mid-mutation.
|
|
4. No deferred callback may mutate GL from a worker.
|
|
5. Destination-critical work receives a reserved share of each frame; it does
|
|
not bypass all budgets.
|
|
6. A reveal delay beyond retail's five-second fade ceiling keeps the portal
|
|
tunnel active and displays the retail wait cue. It never reveals an
|
|
incomplete world.
|