Add explicit stairs and measure elevator congestion with real actors

This commit is contained in:
Takeshi
2026-09-09 14:26:38 +09:00
parent e246ce382b
commit fb4ad23190
57 changed files with 3030 additions and 162 deletions
@@ -33,12 +33,16 @@ const KIND_SERVICE_CORRIDOR: StringName = &"service_corridor"
const KIND_STAFF_DOOR: StringName = &"staff_door"
const KIND_GUEST_ELEVATOR: StringName = &"guest_elevator"
const KIND_SERVICE_ELEVATOR: StringName = &"service_elevator"
const KIND_GUEST_STAIRS: StringName = &"guest_stairs"
const KIND_SERVICE_STAIRS: StringName = &"service_stairs"
const SUPPORTED_KINDS: Array[StringName] = [
KIND_GUEST_CORRIDOR,
KIND_SERVICE_CORRIDOR,
KIND_STAFF_DOOR,
KIND_GUEST_ELEVATOR,
KIND_SERVICE_ELEVATOR,
KIND_GUEST_STAIRS,
KIND_SERVICE_STAIRS,
]
const KNOWN_KINDS: Array[StringName] = [
KIND_GUEST_CORRIDOR,
@@ -46,10 +50,14 @@ const KNOWN_KINDS: Array[StringName] = [
KIND_STAFF_DOOR,
KIND_GUEST_ELEVATOR,
KIND_SERVICE_ELEVATOR,
KIND_GUEST_STAIRS,
KIND_SERVICE_STAIRS,
]
const UNSUPPORTED_VERTICAL_KINDS: Array[StringName] = []
const VERTICAL_KINDS: Array[StringName] = [KIND_GUEST_ELEVATOR, KIND_SERVICE_ELEVATOR]
const STAIR_KINDS: Array[StringName] = [KIND_GUEST_STAIRS, KIND_SERVICE_STAIRS]
const CONNECTOR_KINDS: Array[StringName] = [KIND_STAFF_DOOR, KIND_GUEST_STAIRS, KIND_SERVICE_STAIRS]
const REASON_UNSUPPORTED_VERTICAL_TOPOLOGY: StringName = &"UNSUPPORTED_VERTICAL_TOPOLOGY"
const QUARANTINE_TRANSACTION_ROLLBACK: StringName = &"TRANSACTION_ROLLBACK_UNCERTAIN"
const QUARANTINE_RESTORE_ROLLBACK: StringName = &"RESTORE_ROLLBACK_UNCERTAIN"
@@ -65,6 +73,7 @@ const ADAPTER_PROJECT_REQUEST_PREFIX := "infra.project:"
const ADAPTER_UNPROJECT_REQUEST_PREFIX := "infra.unproject:"
const STAFF_ACCESS_ID: StringName = &"staff"
const STAFF_DOOR_CONNECTOR_COST := 120
const STAIR_CONNECTOR_COST := 150
const _INSTANCE_KEYS: Array[StringName] = [
&"instance_id", &"definition_id", &"origin", &"rotation", &"layer",
@@ -193,6 +202,8 @@ func project_facility(
return _failure(CommandResultType.Code.DUPLICATE_COMMAND_ID, "Stable infrastructure ID collides with existing authoritative state.")
if not _dependencies_match_current_state():
return _failure(CommandResultType.Code.TRANSACTION_CLOSED, "Facility infrastructure dependencies are stale or invalid.")
if kind in STAIR_KINDS and not _path.has_cell(_stair_lower_landing(record)):
return _failure(CommandResultType.Code.VALIDATION_FAILED, "Stairs require an existing passable landing directly below their origin.")
var owned_before := _capture_owned_state()
_transaction_active = true
@@ -218,23 +229,22 @@ func project_facility(
dependency_capture,
"ROUTE projection failed or reentrant callback tampering was detected."
)
if kind == KIND_STAFF_DOOR:
var back_cell: Vector3i = record[&"cells"][0]
var front_cell := Vector3i(back_cell.x, back_cell.y, PathType.FRONT)
if kind in CONNECTOR_KINDS:
var connector := _expected_stair_connector(record) if kind in STAIR_KINDS else _expected_staff_connector(record)
var connector_ok := _path.configure_connector(
StringName(record[&"connector_id"]),
front_cell,
back_cell,
STAFF_DOOR_CONNECTOR_COST,
connector[&"from"],
connector[&"to"],
connector[&"cost"],
true,
STAFF_ACCESS_ID,
connector[&"access_id"],
true
)
if not connector_ok or not _owned_state_matches(guard_state):
return _rollback_failure(
owned_before,
dependency_capture,
"Staff-door connector projection failed or callback tampering was detected."
"Infrastructure connector projection failed or callback tampering was detected."
)
if kind in VERTICAL_KINDS:
var shaft_cells: Array[Vector3i] = []
@@ -327,7 +337,7 @@ func unproject_facility(request_id_value: Variant, instance_id_value: Variant) -
return _rollback_failure(
owned_before,
dependency_capture,
"Staff-door connector removal failed or callback tampering was detected."
"Infrastructure connector removal failed or callback tampering was detected."
)
if record[&"kind"] in VERTICAL_KINDS:
if not _path.remove_vertical_walk_blockers(StringName(SHAFT_OWNER_PREFIX + String(instance_id))) or not _owned_state_matches(guard_state):
@@ -396,6 +406,21 @@ func get_projection_ids() -> Array[StringName]:
return _sorted_names(_projections)
## Before removing a ROUTE/base cell, the owner can reject demolition that
## would leave a staircase without a lower landing. Overlapping remaining
## passable sources may still keep that landing valid.
func get_stair_dependents(cells: Array[Vector3i]) -> Array[StringName]:
var selected: Dictionary = {}
for cell: Vector3i in cells:
selected[cell] = true
var result: Array[StringName] = []
for instance_id: StringName in _sorted_names(_projections):
var record: Dictionary = _projections[instance_id]
if record[&"kind"] in STAIR_KINDS and selected.has(_stair_lower_landing(record)):
result.append(instance_id)
return result
func get_processed_request_ids() -> Array[StringName]:
return _sorted_names(_processed_requests)
@@ -511,6 +536,7 @@ func _canonicalize_live_project_input(definition_value: Variant, instance_value:
or definition.footprint != schema[&"footprint"]
or definition.pass_through != bool(schema[&"pass_through"])
or canonical_tags != schema[&"tags"]
or (definition.id in STAIR_KINDS and definition.allowed_rotations != [0])
):
return {&"ok": false, &"message": "Facility definition layer, footprint, pass-through flag, or tags diverge from its infrastructure schema."}
var canonical_definition := {
@@ -565,6 +591,7 @@ func _canonicalize_live_instance(
if not _cell_is_valid(origin) \
or rotation not in [0, 90, 180, 270] \
or rotation not in allowed_rotations \
or (definition[&"id"] in STAIR_KINDS and (rotation != 0 or not _cell_is_valid(origin + Vector3i(0, -1, 0)))) \
or layer != int(definition[&"layer"]) \
or origin.z != layer \
or completed_at < 0 \
@@ -601,6 +628,17 @@ func _canonicalize_live_instance(
static func _kind_schema(kind: StringName) -> Dictionary:
match kind:
KIND_GUEST_STAIRS, KIND_SERVICE_STAIRS:
var staff_only := kind == KIND_SERVICE_STAIRS
var tags: Array[StringName] = [&"movement", &"route_subslot", &"schema_facility_proxy", &"stairs", &"vertical_connector"]
tags.append(&"staff_only" if staff_only else &"guest_access")
return {
&"layer": PathType.BACK if staff_only else PathType.FRONT,
&"footprint": Vector2i.ONE,
&"pass_through": true,
&"staff_only": staff_only,
&"tags": _sorted_name_array(tags),
}
KIND_GUEST_CORRIDOR:
return {
&"layer": PathType.FRONT,
@@ -664,7 +702,7 @@ static func _projection_from_project_input(input: Dictionary) -> Dictionary:
&"definition_id": kind,
&"kind": kind,
&"route_entity_id": StringName(ROUTE_ENTITY_PREFIX + String(instance_id)),
&"connector_id": StringName(CONNECTOR_PREFIX + String(instance_id)) if kind == KIND_STAFF_DOOR else &"",
&"connector_id": StringName(CONNECTOR_PREFIX + String(instance_id)) if kind in CONNECTOR_KINDS else &"",
&"layer": int(definition[&"layer"]),
&"cells": (instance[&"occupied_cells"] as Array).duplicate(),
&"staff_only": bool(_kind_schema(kind)[&"staff_only"]),
@@ -973,6 +1011,11 @@ func _projection_live_matches(record: Dictionary) -> bool:
or _source.get_entity_cells(route_entity_id) != record[&"cells"]:
return false
var connector_id: StringName = record[&"connector_id"]
if record[&"kind"] in STAIR_KINDS:
return _path.has_cell(_stair_lower_landing(record)) \
and not connector_id.is_empty() \
and _path.has_connector(connector_id) \
and _path.get_connector(connector_id) == _expected_stair_connector(record)
if record[&"kind"] == KIND_STAFF_DOOR:
if connector_id.is_empty() or not _path.has_connector(connector_id):
return false
@@ -1010,6 +1053,21 @@ static func _expected_staff_connector(record: Dictionary) -> Dictionary:
}
static func _stair_lower_landing(record: Dictionary) -> Vector3i:
return (record[&"origin"] as Vector3i) + Vector3i(0, -1, 0)
static func _expected_stair_connector(record: Dictionary) -> Dictionary:
var connector := _expected_staff_connector(record)
var upper_cell: Vector3i = record[&"origin"]
connector[&"from"] = upper_cell
connector[&"to"] = _stair_lower_landing(record)
connector[&"cost"] = STAIR_CONNECTOR_COST
connector[&"access_id"] = STAFF_ACCESS_ID if record[&"kind"] == KIND_SERVICE_STAIRS else &""
connector[&"current_floor"] = upper_cell.y
return connector
static func _has_managed_namespace_collision(
source_service: SpatialSubslotService,
path_service: PathService,
@@ -1257,7 +1315,7 @@ static func _parse_projection(raw: Variant) -> Dictionary:
or kind not in SUPPORTED_KINDS \
or definition_id != kind \
or route_entity_id != StringName(ROUTE_ENTITY_PREFIX + String(instance_id)) \
or connector_id != (StringName(CONNECTOR_PREFIX + String(instance_id)) if kind == KIND_STAFF_DOOR else &"") \
or connector_id != (StringName(CONNECTOR_PREFIX + String(instance_id)) if kind in CONNECTOR_KINDS else &"") \
or not _name_is_valid(source_job_id, MAX_INSTANCE_ID_LENGTH):
return {&"ok": false, &"message": "Facility infrastructure projection stable-ID crosslink is invalid."}
var schema := _kind_schema(kind)
@@ -1297,6 +1355,7 @@ static func _parse_projection(raw: Variant) -> Dictionary:
or not _cell_is_valid(origin)
or origin.z != int(record[&"layer"])
or rotation not in [0, 90, 180, 270]
or (kind in STAIR_KINDS and (rotation != 0 or not _cell_is_valid(origin + Vector3i(0, -1, 0))))
or completed_at < 0
or completed_at > MAX_SAFE_INTEGER
or record[&"cells"] != _footprint_cells(origin, record[&"footprint"], rotation)
@@ -1461,6 +1520,7 @@ static func _parse_snapshot_project_input(raw: Variant) -> Dictionary:
or origin.z != layer \
or layer != int(definition[&"layer"]) \
or rotation not in [0, 90, 180, 270] \
or (definition_id in STAIR_KINDS and (rotation != 0 or not _cell_is_valid(origin + Vector3i(0, -1, 0)))) \
or completed_at < 0 \
or completed_at > MAX_SAFE_INTEGER:
return {&"ok": false, &"message": "Snapshot completed-facility semantic fields are invalid."}