Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
1203 lines
44 KiB
GDScript
1203 lines
44 KiB
GDScript
class_name RouteSubslotPathAdapter
|
|
extends RefCounted
|
|
|
|
## Atomic projection boundary between SpatialSubslotService ROUTE slots and
|
|
## PathService. The adapter owns projection metadata and idempotency evidence;
|
|
## both domain services remain authoritative for their own state.
|
|
|
|
const CommandResultType = preload("res://scripts/core/command_result.gd")
|
|
const SpatialSubslotServiceType = preload("res://scripts/world/spatial_subslot_service.gd")
|
|
const PathServiceType = preload("res://scripts/world/path_service.gd")
|
|
|
|
const SNAPSHOT_SCHEMA_VERSION := 1
|
|
const MAX_REQUEST_ID_LENGTH := 128
|
|
const MAX_PROCESSED_REQUESTS := 4_096
|
|
const MAX_HISTORY_CELL_REFERENCES := 262_144
|
|
const MAX_BASE_WALKABLE_CELLS := 65_536
|
|
const MAX_PROJECTED_CELLS := 131_072
|
|
const MAX_CELLS_PER_ROUTE_REQUEST := 256
|
|
const MAX_COORDINATE_ABS := 100_000
|
|
const MAX_SAFE_INTEGER := 9_007_199_254_740_991
|
|
const MAX_SNAPSHOT_DEPTH := 9
|
|
const MAX_CONTAINER_ITEMS := 131_072
|
|
|
|
const OPERATION_PLACE: StringName = &"PLACE"
|
|
const OPERATION_REPLACE: StringName = &"REPLACE"
|
|
const OPERATION_REMOVE: StringName = &"REMOVE"
|
|
const OPERATION_SYNCHRONIZE: StringName = &"SYNCHRONIZE"
|
|
const OPERATIONS: Array[StringName] = [
|
|
OPERATION_PLACE,
|
|
OPERATION_REPLACE,
|
|
OPERATION_REMOVE,
|
|
OPERATION_SYNCHRONIZE,
|
|
]
|
|
|
|
const QUARANTINE_CONFIGURE_ROLLBACK: StringName = &"CONFIGURE_ROLLBACK_UNCERTAIN"
|
|
const QUARANTINE_TRANSACTION_ROLLBACK: StringName = &"TRANSACTION_ROLLBACK_UNCERTAIN"
|
|
const QUARANTINE_REASONS: Array[StringName] = [
|
|
QUARANTINE_CONFIGURE_ROLLBACK,
|
|
QUARANTINE_TRANSACTION_ROLLBACK,
|
|
]
|
|
|
|
var source_revision := 0
|
|
var projection_revision := 0
|
|
var last_error := ""
|
|
|
|
var _source: SpatialSubslotService
|
|
var _path: PathService
|
|
var _configured := false
|
|
var _base_walkable_cells: Dictionary = {}
|
|
var _projected_route_cells: Array[Vector3i] = []
|
|
var _projected_walkable_cells: Array[Vector3i] = []
|
|
var _processed_requests: Dictionary = {}
|
|
var _quarantined := false
|
|
var _quarantine_reason: StringName = &""
|
|
var _transaction_active := false
|
|
# One private successful parse input; live graph/source checks still run each time.
|
|
var _validated_owned_snapshot: Dictionary = {}
|
|
|
|
|
|
## Binds the two authoritative services once and establishes projection 1.
|
|
## Existing primary World occupancy is intentionally not consulted: ROUTE is
|
|
## an independent subslot and is merged with the supplied base walkable cells.
|
|
func configure(
|
|
source_value: Variant,
|
|
path_value: Variant,
|
|
base_walkable_cells: Variant
|
|
) -> bool:
|
|
if _transaction_active:
|
|
return _fail("Route projection configuration cannot reenter an active transaction.")
|
|
if _configured:
|
|
return _fail("Route projection adapter is already configured.")
|
|
if not source_value is SpatialSubslotService or not path_value is PathService:
|
|
return _fail("Route projection requires SpatialSubslotService and PathService instances.")
|
|
var normalized_base := _normalize_cells(
|
|
base_walkable_cells,
|
|
true,
|
|
MAX_BASE_WALKABLE_CELLS,
|
|
"Base walkable cells"
|
|
)
|
|
if not bool(normalized_base[&"ok"]):
|
|
return _fail(String(normalized_base[&"message"]))
|
|
var source_service: SpatialSubslotService = source_value
|
|
var path_service: PathService = path_value
|
|
var source_audit: Dictionary = source_service.validate_invariants()
|
|
if not bool(source_audit.get(&"ok", false)):
|
|
return _fail("Spatial subslot source invariants are invalid.")
|
|
var route_cells := source_service.get_route_cells()
|
|
var projected := _union_cells(normalized_base[&"cells"], route_cells)
|
|
if projected.size() > MAX_PROJECTED_CELLS:
|
|
return _fail("Projected walkable-cell capacity is exceeded.")
|
|
|
|
var owned_before := _capture_owned_state()
|
|
_transaction_active = true
|
|
var guard_state := _capture_owned_state()
|
|
var source_snapshot := source_service.create_snapshot()
|
|
var path_capture := _capture_path_state(path_service)
|
|
if not bool(path_capture[&"ok"]):
|
|
_restore_owned_state(owned_before)
|
|
return _fail(String(path_capture[&"message"]))
|
|
var apply_ok := path_service.rebuild_graph(projected.duplicate())
|
|
var callback_tampered := not _owned_state_matches(guard_state)
|
|
var source_tampered := source_service.create_snapshot() != source_snapshot
|
|
var path_matches := _path_cells_match(path_service, projected)
|
|
if not apply_ok or callback_tampered or source_tampered or not path_matches:
|
|
var rollback_ok := _restore_dependencies_exact(
|
|
source_service,
|
|
path_service,
|
|
source_snapshot,
|
|
path_capture
|
|
)
|
|
_restore_owned_state(owned_before)
|
|
if not rollback_ok:
|
|
_bind_quarantined_configuration(
|
|
source_service,
|
|
path_service,
|
|
normalized_base[&"cells"],
|
|
route_cells,
|
|
projected,
|
|
QUARANTINE_CONFIGURE_ROLLBACK
|
|
)
|
|
return _fail("Route projection configuration failed and rollback is uncertain.")
|
|
return _fail(
|
|
"Route projection callback tampered with the adapter and was rolled back."
|
|
if callback_tampered or source_tampered
|
|
else "Path graph rejected the initial route projection; state was rolled back."
|
|
)
|
|
|
|
_source = source_service
|
|
_path = path_service
|
|
_configured = true
|
|
_base_walkable_cells = _cell_set(normalized_base[&"cells"])
|
|
_projected_route_cells = route_cells.duplicate()
|
|
_projected_walkable_cells = projected.duplicate()
|
|
source_revision = source_service.get_revision_for_category(source_service.CATEGORY_ROUTE)
|
|
projection_revision = 1
|
|
_processed_requests.clear()
|
|
_quarantined = false
|
|
_quarantine_reason = &""
|
|
_transaction_active = false
|
|
last_error = ""
|
|
return true
|
|
|
|
|
|
func place_route(
|
|
request_id: Variant,
|
|
entity_id: Variant,
|
|
cells: Variant
|
|
) -> CommandResult:
|
|
return _execute_route_request(OPERATION_PLACE, request_id, entity_id, cells)
|
|
|
|
|
|
func replace_route(
|
|
request_id: Variant,
|
|
entity_id: Variant,
|
|
cells: Variant
|
|
) -> CommandResult:
|
|
return _execute_route_request(OPERATION_REPLACE, request_id, entity_id, cells)
|
|
|
|
|
|
func remove_route(request_id: Variant, entity_id: Variant) -> CommandResult:
|
|
return _execute_route_request(OPERATION_REMOVE, request_id, entity_id, [])
|
|
|
|
|
|
## Reconciles direct, out-of-adapter ROUTE changes or Path topology drift. A
|
|
## supplied base set atomically replaces the prior World walkability projection.
|
|
func synchronize_projection(
|
|
request_id: Variant,
|
|
base_walkable_cells: Variant = null
|
|
) -> CommandResult:
|
|
var requested_base: Variant = (
|
|
get_base_walkable_cells() if base_walkable_cells == null else base_walkable_cells
|
|
)
|
|
return _execute_route_request(OPERATION_SYNCHRONIZE, request_id, &"", requested_base)
|
|
|
|
|
|
func is_configured() -> bool:
|
|
return _configured
|
|
|
|
|
|
## Identity-only binding check for higher-level atomic coordinators. The
|
|
## authoritative service references themselves remain encapsulated.
|
|
func is_bound_to(source_value: Variant, path_value: Variant) -> bool:
|
|
return _configured and _source == source_value and _path == path_value
|
|
|
|
|
|
func is_quarantined() -> bool:
|
|
return _quarantined
|
|
|
|
|
|
func get_quarantine_reason() -> StringName:
|
|
return _quarantine_reason
|
|
|
|
|
|
func get_base_walkable_cells(sort_result := true) -> Array[Vector3i]:
|
|
return _sorted_cells(_base_walkable_cells.keys(), sort_result)
|
|
|
|
|
|
func get_projected_route_cells() -> Array[Vector3i]:
|
|
return _projected_route_cells.duplicate()
|
|
|
|
|
|
func get_projected_walkable_cells() -> Array[Vector3i]:
|
|
return _projected_walkable_cells.duplicate()
|
|
|
|
|
|
func get_processed_request_ids() -> Array[StringName]:
|
|
return _sorted_names(_processed_requests)
|
|
|
|
|
|
func get_projection_status() -> Dictionary:
|
|
return _projection_status_internal().duplicate(true)
|
|
|
|
|
|
func is_projection_stale() -> bool:
|
|
return bool(_projection_status_internal()[&"stale"])
|
|
|
|
|
|
func create_snapshot() -> Dictionary:
|
|
if not _configured:
|
|
return {}
|
|
var request_rows: Array[Dictionary] = []
|
|
for request_id: StringName in _sorted_names(_processed_requests):
|
|
var record: Dictionary = _processed_requests[request_id]
|
|
var details: Dictionary = record[&"details"]
|
|
request_rows.append({
|
|
&"request_id": String(request_id),
|
|
&"operation": String(record[&"operation"]),
|
|
&"entity_id": String(record[&"entity_id"]),
|
|
&"input_source_revision": int(record[&"input_source_revision"]),
|
|
&"input_cells": (record[&"input_cells"] as Array).duplicate(),
|
|
&"fingerprint": String(record[&"fingerprint"]),
|
|
&"details": {
|
|
&"request_id": String(details[&"request_id"]),
|
|
&"operation": String(details[&"operation"]),
|
|
&"entity_id": String(details[&"entity_id"]),
|
|
&"cells": (details[&"cells"] as Array).duplicate(),
|
|
&"changed": bool(details[&"changed"]),
|
|
&"source_revision": int(details[&"source_revision"]),
|
|
&"projection_revision": int(details[&"projection_revision"]),
|
|
},
|
|
})
|
|
return {
|
|
&"schema_version": SNAPSHOT_SCHEMA_VERSION,
|
|
&"source_revision": source_revision,
|
|
&"projection_revision": projection_revision,
|
|
&"base_walkable_cells": get_base_walkable_cells(),
|
|
&"projected_route_cells": get_projected_route_cells(),
|
|
&"projected_walkable_cells": get_projected_walkable_cells(),
|
|
&"processed_requests": request_rows,
|
|
&"quarantined": _quarantined,
|
|
&"quarantine_reason": String(_quarantine_reason),
|
|
}
|
|
|
|
|
|
## Restores adapter-owned state only. Non-quarantined snapshots must crosslink
|
|
## exactly with the already-restored SpatialSubslotService and PathService.
|
|
func restore_snapshot(snapshot: Dictionary) -> bool:
|
|
if _transaction_active:
|
|
return _fail("Route projection snapshot cannot restore during a transaction.")
|
|
if not _configured:
|
|
return _fail("Route projection adapter must be configured before restore.")
|
|
var parsed := _parse_snapshot(snapshot)
|
|
if not bool(parsed[&"ok"]):
|
|
return _fail(String(parsed[&"message"]))
|
|
var owned_before := _capture_owned_state()
|
|
_transaction_active = true
|
|
var guard_state := _capture_owned_state()
|
|
if not bool(parsed[&"quarantined"]):
|
|
var live_matches := _live_projection_matches(
|
|
int(parsed[&"source_revision"]),
|
|
parsed[&"projected_route_cells"],
|
|
parsed[&"projected_walkable_cells"]
|
|
)
|
|
if not live_matches or not _owned_state_matches(guard_state):
|
|
_restore_owned_state(owned_before)
|
|
return _fail("Route projection snapshot does not match the live source and Path graph.")
|
|
_source_revision_from_parsed(parsed)
|
|
_transaction_active = false
|
|
last_error = ""
|
|
return true
|
|
|
|
|
|
func validate_invariants() -> Dictionary:
|
|
var errors: Array[String] = []
|
|
if not _configured or _source == null or _path == null:
|
|
errors.append("Route projection adapter is not configured.")
|
|
return {&"ok": false, &"errors": errors}
|
|
var owned_snapshot := create_snapshot()
|
|
if owned_snapshot != _validated_owned_snapshot:
|
|
var parsed := _parse_snapshot(owned_snapshot)
|
|
if not bool(parsed[&"ok"]):
|
|
errors.append(String(parsed[&"message"]))
|
|
else:
|
|
_validated_owned_snapshot = owned_snapshot
|
|
if not _quarantine_state_is_valid(_quarantined, _quarantine_reason):
|
|
errors.append("Route projection quarantine state is inconsistent.")
|
|
if not _quarantined:
|
|
var status := _projection_status_internal()
|
|
if bool(status[&"stale"]):
|
|
errors.append("Route projection crosslinks are stale.")
|
|
return {&"ok": errors.is_empty(), &"errors": errors}
|
|
|
|
|
|
func _execute_route_request(
|
|
operation: StringName,
|
|
request_id_value: Variant,
|
|
entity_id_value: Variant,
|
|
cells_value: Variant
|
|
) -> CommandResult:
|
|
if _transaction_active:
|
|
return _failure(
|
|
CommandResultType.Code.TRANSACTION_CLOSED,
|
|
"Route projection transaction is already active."
|
|
)
|
|
if not _configured:
|
|
return _failure(CommandResultType.Code.TRANSACTION_CLOSED, "Route projection adapter is not configured.")
|
|
if not _stable_id_is_valid(request_id_value):
|
|
return _failure(CommandResultType.Code.INVALID_PAYLOAD, "Route projection request ID is invalid.")
|
|
if not OPERATIONS.has(operation):
|
|
return _failure(CommandResultType.Code.INVALID_COMMAND, "Route projection operation is invalid.")
|
|
var request_id := StringName(request_id_value)
|
|
var entity_id: StringName = &""
|
|
var input_cells: Array[Vector3i] = []
|
|
var input_source_revision := -1
|
|
if operation == OPERATION_SYNCHRONIZE:
|
|
input_source_revision = _source.get_revision_for_category(_source.CATEGORY_ROUTE)
|
|
var normalized_base := _normalize_cells(
|
|
cells_value,
|
|
true,
|
|
MAX_BASE_WALKABLE_CELLS,
|
|
"Synchronization base walkable cells"
|
|
)
|
|
if not bool(normalized_base[&"ok"]):
|
|
return _failure(CommandResultType.Code.INVALID_PAYLOAD, String(normalized_base[&"message"]))
|
|
input_cells = normalized_base[&"cells"]
|
|
else:
|
|
if not _stable_id_is_valid(entity_id_value):
|
|
return _failure(CommandResultType.Code.INVALID_PAYLOAD, "Route entity ID is invalid.")
|
|
entity_id = StringName(entity_id_value)
|
|
if operation in [OPERATION_PLACE, OPERATION_REPLACE]:
|
|
var normalized := _normalize_cells(
|
|
cells_value,
|
|
false,
|
|
MAX_CELLS_PER_ROUTE_REQUEST,
|
|
"Route request cells"
|
|
)
|
|
if not bool(normalized[&"ok"]):
|
|
return _failure(CommandResultType.Code.INVALID_PAYLOAD, String(normalized[&"message"]))
|
|
input_cells = normalized[&"cells"]
|
|
var fingerprint := _request_fingerprint(
|
|
operation,
|
|
entity_id,
|
|
input_source_revision,
|
|
input_cells
|
|
)
|
|
if _processed_requests.has(request_id):
|
|
var previous: Dictionary = _processed_requests[request_id]
|
|
if String(previous[&"fingerprint"]) != fingerprint:
|
|
return _failure(
|
|
CommandResultType.Code.DUPLICATE_IDEMPOTENCY_KEY,
|
|
"Route projection request ID conflicts with earlier input."
|
|
)
|
|
var replay := _success((previous[&"details"] as Dictionary).duplicate(true))
|
|
replay.is_replay = true
|
|
return replay
|
|
if _quarantined:
|
|
return _failure(
|
|
CommandResultType.Code.TRANSACTION_CLOSED,
|
|
"Route projection adapter is quarantined until an exact snapshot is restored.",
|
|
{&"quarantine_reason": _quarantine_reason}
|
|
)
|
|
if _processed_requests.size() >= MAX_PROCESSED_REQUESTS:
|
|
return _failure(CommandResultType.Code.VALIDATION_FAILED, "Route request history capacity is exhausted.")
|
|
if _history_cell_reference_count(_processed_requests) > MAX_HISTORY_CELL_REFERENCES - input_cells.size():
|
|
return _failure(CommandResultType.Code.VALIDATION_FAILED, "Route request history cell budget is exhausted.")
|
|
|
|
var owned_before := _capture_owned_state()
|
|
_transaction_active = true
|
|
var guard_state := _capture_owned_state()
|
|
var status_before := _projection_status_internal()
|
|
if operation != OPERATION_SYNCHRONIZE and bool(status_before[&"stale"]):
|
|
_restore_owned_state(owned_before)
|
|
return _failure(
|
|
CommandResultType.Code.TRANSACTION_CLOSED,
|
|
"Route projection is stale and must be synchronized before mutation.",
|
|
status_before
|
|
)
|
|
var source_audit: Dictionary = _source.validate_invariants()
|
|
if not bool(source_audit.get(&"ok", false)):
|
|
_restore_owned_state(owned_before)
|
|
return _failure(CommandResultType.Code.VALIDATION_FAILED, "Spatial route source invariants are invalid.")
|
|
if operation in [OPERATION_REPLACE, OPERATION_REMOVE]:
|
|
if not _source.has_entity(entity_id):
|
|
_restore_owned_state(owned_before)
|
|
return _failure(CommandResultType.Code.NOT_FOUND, "Route entity was not found.")
|
|
if _source.get_entity_category(entity_id) != _source.CATEGORY_ROUTE:
|
|
_restore_owned_state(owned_before)
|
|
return _failure(CommandResultType.Code.VALIDATION_FAILED, "Adapter may mutate only ROUTE entities.")
|
|
|
|
var source_service := _source
|
|
var path_service := _path
|
|
var source_snapshot := source_service.create_snapshot()
|
|
var path_capture := _capture_path_state(path_service)
|
|
if not bool(path_capture[&"ok"]) or not _owned_state_matches(guard_state):
|
|
var capture_rollback_ok := false
|
|
if bool(path_capture[&"ok"]):
|
|
capture_rollback_ok = _restore_dependencies_exact(
|
|
source_service, path_service, source_snapshot, path_capture
|
|
)
|
|
else:
|
|
capture_rollback_ok = source_service.create_snapshot() == source_snapshot
|
|
_restore_owned_state(owned_before)
|
|
if not capture_rollback_ok:
|
|
_quarantine(QUARANTINE_TRANSACTION_ROLLBACK)
|
|
return _failure(
|
|
CommandResultType.Code.TRANSACTION_CLOSED,
|
|
"Route capture was tampered with and rollback is uncertain.",
|
|
{&"rollback_ok": false, &"quarantined": true}
|
|
)
|
|
return _failure(
|
|
CommandResultType.Code.TRANSACTION_APPLY_FAILED,
|
|
"Route transaction capture failed without changing authoritative state.",
|
|
{&"rollback_ok": true, &"quarantined": false}
|
|
)
|
|
|
|
var result_cells: Array[Vector3i] = []
|
|
var source_result: CommandResult
|
|
match operation:
|
|
OPERATION_PLACE:
|
|
result_cells = input_cells.duplicate()
|
|
source_result = source_service.place(entity_id, source_service.CATEGORY_ROUTE, input_cells)
|
|
OPERATION_REPLACE:
|
|
result_cells = input_cells.duplicate()
|
|
source_result = source_service.replace_entity_cells(entity_id, input_cells)
|
|
OPERATION_REMOVE:
|
|
result_cells = source_service.get_entity_cells(entity_id)
|
|
source_result = source_service.remove(entity_id)
|
|
OPERATION_SYNCHRONIZE:
|
|
source_result = CommandResultType.succeeded()
|
|
_:
|
|
source_result = CommandResultType.failed(CommandResultType.Code.INVALID_COMMAND, 0, "Unknown route operation.")
|
|
|
|
var callback_tampered := not _owned_state_matches(guard_state)
|
|
if not source_result.is_success() or callback_tampered:
|
|
return _rollback_transaction_failure(
|
|
owned_before,
|
|
source_service,
|
|
path_service,
|
|
source_snapshot,
|
|
path_capture,
|
|
"Spatial route apply failed." if not source_result.is_success() else "Spatial callback tampered with the route adapter."
|
|
)
|
|
|
|
var next_route_cells := source_service.get_route_cells()
|
|
var next_base_cells := (
|
|
input_cells.duplicate() if operation == OPERATION_SYNCHRONIZE else get_base_walkable_cells()
|
|
)
|
|
var next_walkable_cells := _union_cells(next_base_cells, next_route_cells)
|
|
if next_walkable_cells.size() > MAX_PROJECTED_CELLS:
|
|
return _rollback_transaction_failure(
|
|
owned_before,
|
|
source_service,
|
|
path_service,
|
|
source_snapshot,
|
|
path_capture,
|
|
"Projected walkable-cell capacity is exceeded."
|
|
)
|
|
var path_apply_ok := path_service.rebuild_graph(next_walkable_cells.duplicate())
|
|
callback_tampered = not _owned_state_matches(guard_state)
|
|
var next_source_revision := source_service.get_revision_for_category(source_service.CATEGORY_ROUTE)
|
|
var post_source_audit: Dictionary = source_service.validate_invariants()
|
|
var postcondition_ok := (
|
|
path_apply_ok
|
|
and not callback_tampered
|
|
and bool(post_source_audit.get(&"ok", false))
|
|
and source_service.get_route_cells() == next_route_cells
|
|
and _path_cells_match(path_service, next_walkable_cells)
|
|
)
|
|
if not postcondition_ok:
|
|
return _rollback_transaction_failure(
|
|
owned_before,
|
|
source_service,
|
|
path_service,
|
|
source_snapshot,
|
|
path_capture,
|
|
"Path projection failed or violated its postcondition."
|
|
)
|
|
|
|
var changed := (
|
|
bool(status_before[&"stale"]) or next_base_cells != get_base_walkable_cells()
|
|
if operation == OPERATION_SYNCHRONIZE
|
|
else bool(source_result.details.get(&"changed", true))
|
|
)
|
|
if changed:
|
|
projection_revision += 1
|
|
source_revision = next_source_revision
|
|
_base_walkable_cells = _cell_set(next_base_cells)
|
|
_projected_route_cells = next_route_cells.duplicate()
|
|
_projected_walkable_cells = next_walkable_cells.duplicate()
|
|
var details := {
|
|
&"request_id": request_id,
|
|
&"operation": operation,
|
|
&"entity_id": entity_id,
|
|
&"cells": result_cells.duplicate(),
|
|
&"changed": changed,
|
|
&"source_revision": source_revision,
|
|
&"projection_revision": projection_revision,
|
|
}
|
|
_processed_requests[request_id] = {
|
|
&"operation": operation,
|
|
&"entity_id": entity_id,
|
|
&"input_source_revision": input_source_revision,
|
|
&"input_cells": input_cells.duplicate(),
|
|
&"fingerprint": fingerprint,
|
|
&"details": details.duplicate(true),
|
|
}
|
|
_transaction_active = false
|
|
last_error = ""
|
|
return _success(details)
|
|
|
|
|
|
func _rollback_transaction_failure(
|
|
owned_before: Dictionary,
|
|
source_service: SpatialSubslotService,
|
|
path_service: PathService,
|
|
source_snapshot: Dictionary,
|
|
path_capture: Dictionary,
|
|
message: String
|
|
) -> CommandResult:
|
|
var rollback_ok := _restore_dependencies_exact(
|
|
source_service,
|
|
path_service,
|
|
source_snapshot,
|
|
path_capture
|
|
)
|
|
_restore_owned_state(owned_before)
|
|
if not rollback_ok:
|
|
_quarantine(QUARANTINE_TRANSACTION_ROLLBACK)
|
|
return _failure(
|
|
CommandResultType.Code.TRANSACTION_CLOSED,
|
|
"%s Rollback is uncertain; adapter was quarantined." % message,
|
|
{&"rollback_ok": false, &"quarantined": true, &"quarantine_reason": _quarantine_reason}
|
|
)
|
|
return _failure(
|
|
CommandResultType.Code.TRANSACTION_APPLY_FAILED,
|
|
"%s Both services were rolled back exactly." % message,
|
|
{&"rollback_ok": true, &"quarantined": false}
|
|
)
|
|
|
|
|
|
func _projection_status_internal() -> Dictionary:
|
|
if not _configured or _source == null or _path == null:
|
|
return {
|
|
&"configured": false,
|
|
&"stale": true,
|
|
&"quarantined": _quarantined,
|
|
&"quarantine_reason": _quarantine_reason,
|
|
&"source_revision": -1,
|
|
&"projected_source_revision": source_revision,
|
|
&"projection_revision": projection_revision,
|
|
&"source_changed": true,
|
|
&"path_changed": true,
|
|
}
|
|
var current_source_revision := _source.get_revision_for_category(_source.CATEGORY_ROUTE)
|
|
var current_route_cells := _source.get_route_cells()
|
|
var path_capture := _capture_path_state(_path, false)
|
|
var source_changed: bool = (
|
|
current_source_revision != source_revision
|
|
or current_route_cells != _projected_route_cells
|
|
)
|
|
var path_changed: bool = (
|
|
not bool(path_capture[&"ok"])
|
|
or path_capture[&"cells"] != _projected_walkable_cells
|
|
)
|
|
var projection_crosslink_ok: bool = (
|
|
_union_cells(get_base_walkable_cells(false), _projected_route_cells)
|
|
== _projected_walkable_cells
|
|
)
|
|
return {
|
|
&"configured": true,
|
|
&"stale": _quarantined or source_changed or path_changed or not projection_crosslink_ok,
|
|
&"quarantined": _quarantined,
|
|
&"quarantine_reason": _quarantine_reason,
|
|
&"source_revision": current_source_revision,
|
|
&"projected_source_revision": source_revision,
|
|
&"projection_revision": projection_revision,
|
|
&"source_changed": source_changed,
|
|
&"path_changed": path_changed,
|
|
}
|
|
|
|
|
|
func _live_projection_matches(
|
|
expected_source_revision: int,
|
|
expected_route_cells: Array[Vector3i],
|
|
expected_walkable_cells: Array[Vector3i]
|
|
) -> bool:
|
|
var audit: Dictionary = _source.validate_invariants()
|
|
return (
|
|
bool(audit.get(&"ok", false))
|
|
and _source.get_revision_for_category(_source.CATEGORY_ROUTE) == expected_source_revision
|
|
and _source.get_route_cells() == expected_route_cells
|
|
and _path_cells_match(_path, expected_walkable_cells)
|
|
)
|
|
|
|
|
|
func _source_revision_from_parsed(parsed: Dictionary) -> void:
|
|
source_revision = int(parsed[&"source_revision"])
|
|
projection_revision = int(parsed[&"projection_revision"])
|
|
_base_walkable_cells = _cell_set(parsed[&"base_walkable_cells"])
|
|
_projected_route_cells = (parsed[&"projected_route_cells"] as Array).duplicate()
|
|
_projected_walkable_cells = (parsed[&"projected_walkable_cells"] as Array).duplicate()
|
|
_processed_requests = (parsed[&"processed_requests"] as Dictionary).duplicate(true)
|
|
_quarantined = bool(parsed[&"quarantined"])
|
|
_quarantine_reason = parsed[&"quarantine_reason"]
|
|
|
|
|
|
func _capture_owned_state() -> Dictionary:
|
|
return {
|
|
&"source": _source,
|
|
&"path": _path,
|
|
&"configured": _configured,
|
|
&"base_walkable_cells": _base_walkable_cells.duplicate(true),
|
|
&"projected_route_cells": _projected_route_cells.duplicate(),
|
|
&"projected_walkable_cells": _projected_walkable_cells.duplicate(),
|
|
&"source_revision": source_revision,
|
|
&"projection_revision": projection_revision,
|
|
&"processed_requests": _processed_requests.duplicate(true),
|
|
&"quarantined": _quarantined,
|
|
&"quarantine_reason": _quarantine_reason,
|
|
&"transaction_active": _transaction_active,
|
|
}
|
|
|
|
|
|
func _owned_state_matches(capture: Dictionary) -> bool:
|
|
return (
|
|
_source == capture[&"source"]
|
|
and _path == capture[&"path"]
|
|
and _configured == bool(capture[&"configured"])
|
|
and _base_walkable_cells == capture[&"base_walkable_cells"]
|
|
and _projected_route_cells == capture[&"projected_route_cells"]
|
|
and _projected_walkable_cells == capture[&"projected_walkable_cells"]
|
|
and source_revision == int(capture[&"source_revision"])
|
|
and projection_revision == int(capture[&"projection_revision"])
|
|
and _processed_requests == capture[&"processed_requests"]
|
|
and _quarantined == bool(capture[&"quarantined"])
|
|
and _quarantine_reason == capture[&"quarantine_reason"]
|
|
and _transaction_active == bool(capture[&"transaction_active"])
|
|
)
|
|
|
|
|
|
func _restore_owned_state(capture: Dictionary) -> void:
|
|
_source = capture[&"source"]
|
|
_path = capture[&"path"]
|
|
_configured = bool(capture[&"configured"])
|
|
_base_walkable_cells = (capture[&"base_walkable_cells"] as Dictionary).duplicate(true)
|
|
_projected_route_cells.assign(capture[&"projected_route_cells"])
|
|
_projected_walkable_cells.assign(capture[&"projected_walkable_cells"])
|
|
source_revision = int(capture[&"source_revision"])
|
|
projection_revision = int(capture[&"projection_revision"])
|
|
_processed_requests = (capture[&"processed_requests"] as Dictionary).duplicate(true)
|
|
_quarantined = bool(capture[&"quarantined"])
|
|
_quarantine_reason = capture[&"quarantine_reason"]
|
|
_transaction_active = bool(capture[&"transaction_active"])
|
|
|
|
|
|
func _bind_quarantined_configuration(
|
|
source_service: SpatialSubslotService,
|
|
path_service: PathService,
|
|
base_cells: Array[Vector3i],
|
|
route_cells: Array[Vector3i],
|
|
projected_cells: Array[Vector3i],
|
|
reason: StringName
|
|
) -> void:
|
|
_source = source_service
|
|
_path = path_service
|
|
_configured = true
|
|
_base_walkable_cells = _cell_set(base_cells)
|
|
_projected_route_cells = route_cells.duplicate()
|
|
_projected_walkable_cells = projected_cells.duplicate()
|
|
source_revision = source_service.get_revision_for_category(source_service.CATEGORY_ROUTE)
|
|
projection_revision = 1
|
|
_processed_requests.clear()
|
|
_transaction_active = false
|
|
_quarantine(reason)
|
|
|
|
|
|
func _quarantine(reason: StringName) -> void:
|
|
_quarantined = true
|
|
_quarantine_reason = reason
|
|
_transaction_active = false
|
|
|
|
|
|
static func _capture_path_state(path_service: PathService, capture_snapshot := true) -> Dictionary:
|
|
var snapshot := path_service.create_snapshot()
|
|
var parsed_cells := _path_cells_from_snapshot(snapshot)
|
|
if not bool(parsed_cells[&"ok"]):
|
|
return {&"ok": false, &"message": parsed_cells[&"message"]}
|
|
for cell: Vector3i in parsed_cells[&"cells"]:
|
|
if not path_service.has_cell(cell):
|
|
return {
|
|
&"ok": false,
|
|
&"message": "Path snapshot topology has not been materialized into the live graph.",
|
|
}
|
|
var result := {&"ok": true}
|
|
if capture_snapshot:
|
|
result[&"snapshot"] = snapshot.duplicate(true)
|
|
result[&"cells"] = (parsed_cells[&"cells"] as Array).duplicate()
|
|
return result
|
|
|
|
|
|
static func _path_cells_from_snapshot(snapshot: Dictionary) -> Dictionary:
|
|
var raw_rows: Variant = snapshot.get(&"cell_traffic", null)
|
|
if not raw_rows is Array or raw_rows.size() > MAX_PROJECTED_CELLS:
|
|
return {&"ok": false, &"message": "Path snapshot does not expose a bounded cell topology."}
|
|
var cells: Array[Vector3i] = []
|
|
var seen: Dictionary = {}
|
|
for raw_row: Variant in raw_rows:
|
|
if not raw_row is Dictionary:
|
|
return {&"ok": false, &"message": "Path snapshot cell topology is malformed."}
|
|
var raw_cell: Variant = raw_row.get(&"cell", raw_row.get("cell", null))
|
|
if not _cell_is_valid(raw_cell) or seen.has(raw_cell):
|
|
return {&"ok": false, &"message": "Path snapshot contains an invalid or duplicate cell."}
|
|
seen[raw_cell] = true
|
|
cells.append(raw_cell)
|
|
cells.sort_custom(_cell_less)
|
|
return {&"ok": true, &"cells": cells}
|
|
|
|
|
|
static func _path_cells_match(path_service: PathService, expected: Array[Vector3i]) -> bool:
|
|
var capture := _capture_path_state(path_service)
|
|
if not bool(capture[&"ok"]) or capture[&"cells"] != expected:
|
|
return false
|
|
for cell: Vector3i in expected:
|
|
if not path_service.has_cell(cell):
|
|
return false
|
|
return true
|
|
|
|
|
|
static func _restore_dependencies_exact(
|
|
source_service: SpatialSubslotService,
|
|
path_service: PathService,
|
|
source_snapshot: Dictionary,
|
|
path_capture: Dictionary
|
|
) -> bool:
|
|
for _attempt: int in range(2):
|
|
source_service.restore_snapshot(source_snapshot.duplicate(true))
|
|
_restore_path_once(path_service, path_capture)
|
|
if (
|
|
source_service.create_snapshot() == source_snapshot
|
|
and _path_state_matches_capture(path_service, path_capture)
|
|
):
|
|
return true
|
|
return false
|
|
|
|
|
|
static func _restore_path_once(path_service: PathService, capture: Dictionary) -> void:
|
|
var cells: Array = capture[&"cells"]
|
|
var snapshot: Dictionary = capture[&"snapshot"]
|
|
# The second rebuild materializes PathService's pending graph revision when
|
|
# the authoritative graph is empty. Calls are intentionally best-effort;
|
|
# exact equality below is the authoritative rollback verdict.
|
|
path_service.rebuild_graph(cells.duplicate())
|
|
path_service.restore_snapshot(snapshot.duplicate(true))
|
|
path_service.rebuild_graph(cells.duplicate())
|
|
|
|
|
|
static func _path_state_matches_capture(path_service: PathService, capture: Dictionary) -> bool:
|
|
return (
|
|
path_service.create_snapshot() == capture[&"snapshot"]
|
|
and _path_cells_match(path_service, capture[&"cells"])
|
|
)
|
|
|
|
|
|
static func _parse_snapshot(snapshot: Dictionary) -> Dictionary:
|
|
if not _variant_depth_is_valid(snapshot, 0):
|
|
return {&"ok": false, &"message": "Route projection snapshot depth or capacity is exceeded."}
|
|
if not _keys_match(snapshot, [
|
|
&"base_walkable_cells",
|
|
&"processed_requests",
|
|
&"projected_route_cells",
|
|
&"projected_walkable_cells",
|
|
&"projection_revision",
|
|
&"quarantine_reason",
|
|
&"quarantined",
|
|
&"schema_version",
|
|
&"source_revision",
|
|
]):
|
|
return {&"ok": false, &"message": "Route projection snapshot root fields are not canonical."}
|
|
if typeof(snapshot.get(&"schema_version", null)) != TYPE_INT \
|
|
or int(snapshot[&"schema_version"]) != SNAPSHOT_SCHEMA_VERSION:
|
|
return {&"ok": false, &"message": "Unsupported RouteSubslotPathAdapter snapshot version."}
|
|
if typeof(snapshot.get(&"source_revision", null)) != TYPE_INT \
|
|
or typeof(snapshot.get(&"projection_revision", null)) != TYPE_INT:
|
|
return {&"ok": false, &"message": "Route projection revisions must be integers."}
|
|
var next_source_revision := int(snapshot[&"source_revision"])
|
|
var next_projection_revision := int(snapshot[&"projection_revision"])
|
|
if next_source_revision < 0 or next_source_revision > MAX_SAFE_INTEGER \
|
|
or next_projection_revision < 1 or next_projection_revision > MAX_SAFE_INTEGER:
|
|
return {&"ok": false, &"message": "Route projection revisions are outside supported bounds."}
|
|
if typeof(snapshot.get(&"quarantined", null)) != TYPE_BOOL \
|
|
or typeof(snapshot.get(&"quarantine_reason", null)) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
|
return {&"ok": false, &"message": "Route projection quarantine state has invalid types."}
|
|
var next_quarantined := bool(snapshot[&"quarantined"])
|
|
var next_quarantine_reason := StringName(snapshot[&"quarantine_reason"])
|
|
if not _quarantine_state_is_valid(next_quarantined, next_quarantine_reason):
|
|
return {&"ok": false, &"message": "Route projection quarantine state is inconsistent."}
|
|
|
|
var base_result := _normalize_cells(
|
|
snapshot.get(&"base_walkable_cells", null),
|
|
true,
|
|
MAX_BASE_WALKABLE_CELLS,
|
|
"Snapshot base walkable cells"
|
|
)
|
|
var route_result := _normalize_cells(
|
|
snapshot.get(&"projected_route_cells", null),
|
|
true,
|
|
SpatialSubslotServiceType.MAX_TOTAL_SLOTS,
|
|
"Snapshot projected route cells"
|
|
)
|
|
var projected_result := _normalize_cells(
|
|
snapshot.get(&"projected_walkable_cells", null),
|
|
true,
|
|
MAX_PROJECTED_CELLS,
|
|
"Snapshot projected walkable cells"
|
|
)
|
|
for result: Dictionary in [base_result, route_result, projected_result]:
|
|
if not bool(result[&"ok"]):
|
|
return result
|
|
if snapshot[&"base_walkable_cells"] != base_result[&"cells"] \
|
|
or snapshot[&"projected_route_cells"] != route_result[&"cells"] \
|
|
or snapshot[&"projected_walkable_cells"] != projected_result[&"cells"]:
|
|
return {&"ok": false, &"message": "Route projection snapshot cells are not canonically ordered."}
|
|
if _union_cells(base_result[&"cells"], route_result[&"cells"]) != projected_result[&"cells"]:
|
|
return {&"ok": false, &"message": "Route projection snapshot union crosslink is invalid."}
|
|
|
|
var raw_requests: Variant = snapshot.get(&"processed_requests", null)
|
|
if not raw_requests is Array or raw_requests.size() > MAX_PROCESSED_REQUESTS:
|
|
return {&"ok": false, &"message": "Route projection request history is malformed or too large."}
|
|
var next_requests: Dictionary = {}
|
|
var previous_request_id := ""
|
|
var history_cell_references := 0
|
|
var latest_recorded_projection_revision := 1
|
|
for raw_record: Variant in raw_requests:
|
|
var record_result := _parse_request_record(
|
|
raw_record,
|
|
next_source_revision,
|
|
next_projection_revision,
|
|
previous_request_id
|
|
)
|
|
if not bool(record_result[&"ok"]):
|
|
return record_result
|
|
var request_id: StringName = record_result[&"request_id"]
|
|
var parsed_record: Dictionary = record_result[&"record"]
|
|
history_cell_references += (parsed_record[&"input_cells"] as Array).size()
|
|
if history_cell_references > MAX_HISTORY_CELL_REFERENCES:
|
|
return {&"ok": false, &"message": "Route projection request history cell budget is exceeded."}
|
|
latest_recorded_projection_revision = max(
|
|
latest_recorded_projection_revision,
|
|
int((parsed_record[&"details"] as Dictionary)[&"projection_revision"])
|
|
)
|
|
next_requests[request_id] = parsed_record
|
|
previous_request_id = String(request_id)
|
|
if latest_recorded_projection_revision != next_projection_revision:
|
|
return {&"ok": false, &"message": "Route projection revision lacks matching request history."}
|
|
return {
|
|
&"ok": true,
|
|
&"source_revision": next_source_revision,
|
|
&"projection_revision": next_projection_revision,
|
|
&"base_walkable_cells": base_result[&"cells"],
|
|
&"projected_route_cells": route_result[&"cells"],
|
|
&"projected_walkable_cells": projected_result[&"cells"],
|
|
&"processed_requests": next_requests,
|
|
&"quarantined": next_quarantined,
|
|
&"quarantine_reason": next_quarantine_reason,
|
|
}
|
|
|
|
|
|
static func _parse_request_record(
|
|
raw_record: Variant,
|
|
current_source_revision: int,
|
|
current_projection_revision: int,
|
|
previous_request_id: String
|
|
) -> Dictionary:
|
|
if not raw_record is Dictionary or not _keys_match(raw_record, [
|
|
&"details", &"entity_id", &"fingerprint", &"input_cells",
|
|
&"input_source_revision", &"operation", &"request_id",
|
|
]):
|
|
return {&"ok": false, &"message": "Route projection request record fields are invalid."}
|
|
for field: StringName in [&"request_id", &"operation", &"entity_id", &"fingerprint"]:
|
|
if typeof(raw_record.get(field, null)) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
|
return {&"ok": false, &"message": "Route projection request record string fields are invalid."}
|
|
if typeof(raw_record.get(&"input_source_revision", null)) != TYPE_INT:
|
|
return {&"ok": false, &"message": "Route projection request source revision is invalid."}
|
|
var request_id := StringName(raw_record[&"request_id"])
|
|
var request_text := String(request_id)
|
|
var operation := StringName(raw_record[&"operation"])
|
|
var entity_id := StringName(raw_record[&"entity_id"])
|
|
var input_source_revision := int(raw_record[&"input_source_revision"])
|
|
if not _stable_id_is_valid(request_id) \
|
|
or (not previous_request_id.is_empty() and request_text <= previous_request_id) \
|
|
or not OPERATIONS.has(operation):
|
|
return {&"ok": false, &"message": "Route projection request identity/order is invalid."}
|
|
if operation == OPERATION_SYNCHRONIZE:
|
|
if not entity_id.is_empty() \
|
|
or input_source_revision < 0 \
|
|
or input_source_revision > current_source_revision:
|
|
return {&"ok": false, &"message": "Synchronization request input is inconsistent."}
|
|
else:
|
|
if not _stable_id_is_valid(entity_id) or input_source_revision != -1:
|
|
return {&"ok": false, &"message": "Route mutation request input is inconsistent."}
|
|
var allow_empty := operation in [OPERATION_REMOVE, OPERATION_SYNCHRONIZE]
|
|
var maximum_cells := (
|
|
MAX_BASE_WALKABLE_CELLS
|
|
if operation == OPERATION_SYNCHRONIZE
|
|
else MAX_CELLS_PER_ROUTE_REQUEST
|
|
)
|
|
var cells_result := _normalize_cells(
|
|
raw_record.get(&"input_cells", null),
|
|
allow_empty,
|
|
maximum_cells,
|
|
"Route projection request input cells"
|
|
)
|
|
if not bool(cells_result[&"ok"]):
|
|
return cells_result
|
|
var input_cells: Array[Vector3i] = cells_result[&"cells"]
|
|
if raw_record[&"input_cells"] != input_cells \
|
|
or (operation == OPERATION_REMOVE and not input_cells.is_empty()):
|
|
return {&"ok": false, &"message": "Route projection request cells are not canonical."}
|
|
var expected_fingerprint := _request_fingerprint(
|
|
operation,
|
|
entity_id,
|
|
input_source_revision,
|
|
input_cells
|
|
)
|
|
if String(raw_record[&"fingerprint"]) != expected_fingerprint:
|
|
return {&"ok": false, &"message": "Route projection request fingerprint is invalid."}
|
|
var details_result := _parse_request_details(
|
|
raw_record.get(&"details", null),
|
|
request_id,
|
|
operation,
|
|
entity_id,
|
|
input_cells,
|
|
current_source_revision,
|
|
current_projection_revision
|
|
)
|
|
if not bool(details_result[&"ok"]):
|
|
return details_result
|
|
return {
|
|
&"ok": true,
|
|
&"request_id": request_id,
|
|
&"record": {
|
|
&"operation": operation,
|
|
&"entity_id": entity_id,
|
|
&"input_source_revision": input_source_revision,
|
|
&"input_cells": input_cells,
|
|
&"fingerprint": expected_fingerprint,
|
|
&"details": details_result[&"details"],
|
|
},
|
|
}
|
|
|
|
|
|
static func _parse_request_details(
|
|
raw_details: Variant,
|
|
request_id: StringName,
|
|
operation: StringName,
|
|
entity_id: StringName,
|
|
input_cells: Array[Vector3i],
|
|
current_source_revision: int,
|
|
current_projection_revision: int
|
|
) -> Dictionary:
|
|
if not raw_details is Dictionary or not _keys_match(raw_details, [
|
|
&"cells", &"changed", &"entity_id", &"operation", &"projection_revision",
|
|
&"request_id", &"source_revision",
|
|
]):
|
|
return {&"ok": false, &"message": "Route projection request result fields are invalid."}
|
|
if typeof(raw_details.get(&"request_id", null)) not in [TYPE_STRING, TYPE_STRING_NAME] \
|
|
or typeof(raw_details.get(&"operation", null)) not in [TYPE_STRING, TYPE_STRING_NAME] \
|
|
or typeof(raw_details.get(&"entity_id", null)) not in [TYPE_STRING, TYPE_STRING_NAME] \
|
|
or typeof(raw_details.get(&"changed", null)) != TYPE_BOOL \
|
|
or typeof(raw_details.get(&"source_revision", null)) != TYPE_INT \
|
|
or typeof(raw_details.get(&"projection_revision", null)) != TYPE_INT:
|
|
return {&"ok": false, &"message": "Route projection request result types are invalid."}
|
|
var detail_source_revision := int(raw_details[&"source_revision"])
|
|
var detail_projection_revision := int(raw_details[&"projection_revision"])
|
|
if StringName(raw_details[&"request_id"]) != request_id \
|
|
or StringName(raw_details[&"operation"]) != operation \
|
|
or StringName(raw_details[&"entity_id"]) != entity_id \
|
|
or detail_source_revision < 0 \
|
|
or detail_source_revision > current_source_revision \
|
|
or detail_projection_revision < 1 \
|
|
or detail_projection_revision > current_projection_revision:
|
|
return {&"ok": false, &"message": "Route projection request result crosslink is invalid."}
|
|
var cells_result := _normalize_cells(
|
|
raw_details.get(&"cells", null),
|
|
operation == OPERATION_SYNCHRONIZE,
|
|
MAX_CELLS_PER_ROUTE_REQUEST,
|
|
"Route projection request result cells"
|
|
)
|
|
if not bool(cells_result[&"ok"]) or raw_details[&"cells"] != cells_result[&"cells"]:
|
|
return {&"ok": false, &"message": "Route projection request result cells are invalid."}
|
|
var result_cells: Array[Vector3i] = cells_result[&"cells"]
|
|
if operation in [OPERATION_PLACE, OPERATION_REPLACE] and result_cells != input_cells:
|
|
return {&"ok": false, &"message": "Route projection request input/result cells diverge."}
|
|
if operation == OPERATION_SYNCHRONIZE and not result_cells.is_empty():
|
|
return {&"ok": false, &"message": "Synchronization result cells must be empty."}
|
|
var changed := bool(raw_details[&"changed"])
|
|
if operation in [OPERATION_PLACE, OPERATION_REMOVE] and not changed:
|
|
return {&"ok": false, &"message": "Place/remove result must record a changed projection."}
|
|
if changed and detail_projection_revision < 2:
|
|
return {&"ok": false, &"message": "Changed projection result has an invalid revision."}
|
|
return {
|
|
&"ok": true,
|
|
&"details": {
|
|
&"request_id": request_id,
|
|
&"operation": operation,
|
|
&"entity_id": entity_id,
|
|
&"cells": result_cells,
|
|
&"changed": changed,
|
|
&"source_revision": detail_source_revision,
|
|
&"projection_revision": detail_projection_revision,
|
|
},
|
|
}
|
|
|
|
|
|
static func _normalize_cells(
|
|
value: Variant,
|
|
allow_empty: bool,
|
|
maximum: int,
|
|
label: String
|
|
) -> Dictionary:
|
|
if not value is Array or value.size() > maximum or (not allow_empty and value.is_empty()):
|
|
return {&"ok": false, &"message": "%s have an invalid collection shape or capacity." % label}
|
|
var seen: Dictionary = {}
|
|
var cells: Array[Vector3i] = []
|
|
for raw_cell: Variant in value:
|
|
if not _cell_is_valid(raw_cell) or seen.has(raw_cell):
|
|
return {&"ok": false, &"message": "%s contain an invalid or duplicate cell." % label}
|
|
seen[raw_cell] = true
|
|
cells.append(raw_cell)
|
|
cells.sort_custom(_cell_less)
|
|
return {&"ok": true, &"cells": cells}
|
|
|
|
|
|
static func _cell_is_valid(value: Variant) -> bool:
|
|
if not value is Vector3i:
|
|
return false
|
|
var cell: Vector3i = value
|
|
return (
|
|
cell.z in [SpatialSubslotServiceType.LAYER_FRONT, SpatialSubslotServiceType.LAYER_BACK]
|
|
and cell.x >= -MAX_COORDINATE_ABS
|
|
and cell.x <= MAX_COORDINATE_ABS
|
|
and cell.y >= -MAX_COORDINATE_ABS
|
|
and cell.y <= MAX_COORDINATE_ABS
|
|
)
|
|
|
|
|
|
static func _cell_set(cells: Array) -> Dictionary:
|
|
var result: Dictionary = {}
|
|
for raw_cell: Variant in cells:
|
|
result[raw_cell] = true
|
|
return result
|
|
|
|
|
|
static func _union_cells(first: Array, second: Array) -> Array[Vector3i]:
|
|
var union: Dictionary = {}
|
|
for raw_cell: Variant in first:
|
|
if raw_cell is Vector3i:
|
|
union[raw_cell] = true
|
|
for raw_cell: Variant in second:
|
|
if raw_cell is Vector3i:
|
|
union[raw_cell] = true
|
|
return _sorted_cells(union.keys())
|
|
|
|
|
|
static func _sorted_cells(source: Variant, sort_result := true) -> Array[Vector3i]:
|
|
var result: Array[Vector3i] = []
|
|
if not source is Array:
|
|
return result
|
|
for raw_cell: Variant in source:
|
|
if raw_cell is Vector3i:
|
|
result.append(raw_cell)
|
|
if sort_result:
|
|
result.sort_custom(_cell_less)
|
|
return result
|
|
|
|
|
|
static func _cell_less(left: Vector3i, right: Vector3i) -> bool:
|
|
if left.y != right.y:
|
|
return left.y < right.y
|
|
if left.x != right.x:
|
|
return left.x < right.x
|
|
return left.z < right.z
|
|
|
|
|
|
static func _sorted_names(source: Dictionary) -> Array[StringName]:
|
|
var result: Array[StringName] = []
|
|
for raw_name: Variant in source:
|
|
result.append(StringName(raw_name))
|
|
result.sort_custom(_name_less)
|
|
return result
|
|
|
|
|
|
static func _history_cell_reference_count(source: Dictionary) -> int:
|
|
var total := 0
|
|
for record_value: Variant in source.values():
|
|
if record_value is Dictionary:
|
|
var cells: Variant = record_value.get(&"input_cells", [])
|
|
if cells is Array:
|
|
total += cells.size()
|
|
return total
|
|
|
|
|
|
static func _name_less(left: StringName, right: StringName) -> bool:
|
|
return String(left) < String(right)
|
|
|
|
|
|
static func _stable_id_is_valid(value: Variant) -> bool:
|
|
if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
|
return false
|
|
var text := String(value)
|
|
if text.is_empty() or text.length() > MAX_REQUEST_ID_LENGTH:
|
|
return false
|
|
for index: int in range(text.length()):
|
|
var code := text.unicode_at(index)
|
|
var allowed := (
|
|
code >= 48 and code <= 57
|
|
or code >= 65 and code <= 90
|
|
or code >= 97 and code <= 122
|
|
or code in [45, 46, 58, 64, 95]
|
|
)
|
|
if not allowed:
|
|
return false
|
|
return true
|
|
|
|
|
|
static func _request_fingerprint(
|
|
operation: StringName,
|
|
entity_id: StringName,
|
|
input_source_revision: int,
|
|
cells: Array[Vector3i]
|
|
) -> String:
|
|
var entity_text := String(entity_id)
|
|
var canonical := "%s|%d:%s|source=%d|cells=%d|" % [
|
|
String(operation), entity_text.length(), entity_text, input_source_revision, cells.size(),
|
|
]
|
|
for cell: Vector3i in cells:
|
|
canonical += "%d,%d,%d;" % [cell.x, cell.y, cell.z]
|
|
return canonical.sha256_text()
|
|
|
|
|
|
static func _keys_match(source: Dictionary, expected: Array) -> bool:
|
|
if source.size() != expected.size():
|
|
return false
|
|
var seen: Dictionary = {}
|
|
for raw_key: Variant in source:
|
|
if typeof(raw_key) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
|
return false
|
|
var key := StringName(raw_key)
|
|
if seen.has(key) or not expected.has(key):
|
|
return false
|
|
seen[key] = true
|
|
return seen.size() == expected.size()
|
|
|
|
|
|
static func _quarantine_state_is_valid(value: bool, reason: StringName) -> bool:
|
|
return (value and QUARANTINE_REASONS.has(reason)) or (not value and reason.is_empty())
|
|
|
|
|
|
static func _variant_depth_is_valid(value: Variant, depth: int) -> bool:
|
|
if depth > MAX_SNAPSHOT_DEPTH:
|
|
return false
|
|
if value is Array:
|
|
if value.size() > MAX_CONTAINER_ITEMS:
|
|
return false
|
|
for child: Variant in value:
|
|
if not _variant_depth_is_valid(child, depth + 1):
|
|
return false
|
|
elif value is Dictionary:
|
|
if value.size() > MAX_CONTAINER_ITEMS:
|
|
return false
|
|
for key: Variant in value:
|
|
if not _variant_depth_is_valid(key, depth + 1) \
|
|
or not _variant_depth_is_valid(value[key], depth + 1):
|
|
return false
|
|
elif typeof(value) in [TYPE_OBJECT, TYPE_CALLABLE, TYPE_SIGNAL, TYPE_RID]:
|
|
return false
|
|
return true
|
|
|
|
|
|
func _success(details: Dictionary = {}) -> CommandResult:
|
|
last_error = ""
|
|
return CommandResultType.succeeded(0, details)
|
|
|
|
|
|
func _failure(code: CommandResult.Code, message: String, details: Dictionary = {}) -> CommandResult:
|
|
last_error = message
|
|
return CommandResultType.failed(code, 0, message, details)
|
|
|
|
|
|
func _fail(message: String) -> bool:
|
|
last_error = message
|
|
return false
|