Add explicit stairs and measure elevator congestion with real actors
This commit is contained in:
@@ -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."}
|
||||
|
||||
Reference in New Issue
Block a user