class_name ActivityActionCommandService extends ActionCommandService const HANDLER_METRIC_DELTA := SimulationIds.ACTION_HANDLER_ACTIVITY_METRIC_DELTA const ACTOR_KIND_PLAYER := SimulationIds.ENTITY_PLAYER const ACTOR_KIND_PERSON := SimulationIds.ENTITY_PERSON const REASON_INVALID_COMMAND := &"invalid_command" const REASON_STALE_TARGET := &"stale_target" const REASON_STALE_REVISION := &"stale_revision" const REASON_UNSUPPORTED_ACTION := &"unsupported_action" const REASON_UNSUPPORTED_ACTOR := &"unsupported_actor" const REASON_TARGET_CAPABILITY := &"target_capability_missing" const REASON_OUT_OF_RANGE := &"out_of_range" const REASON_INVALID_EFFECT := &"invalid_effect" const REASON_COMPLETION_FAILED := &"completion_failed" var _registry: WorldTargetRegistry var _definition_lookup: Callable var _actor_position_lookup: Callable var _state_revision_lookup: Callable var _completion_handler: Callable var _effect_planner := ActionEffectPlanner.new() var _require_target_provider := false var _enforce_spatial_range := true func _init( registry: WorldTargetRegistry = null, definition_lookup: Callable = Callable(), actor_position_lookup: Callable = Callable(), state_revision_lookup: Callable = Callable(), completion_handler: Callable = Callable(), require_target_provider: bool = false, enforce_spatial_range: bool = true ) -> void: _registry = registry _definition_lookup = definition_lookup _actor_position_lookup = actor_position_lookup _state_revision_lookup = state_revision_lookup _completion_handler = completion_handler _require_target_provider = require_target_provider _enforce_spatial_range = enforce_spatial_range func try_execute(command: ActionCommand) -> ActionResult: var result := _execute(command) command_resolved.emit(result) return result func _execute(command: ActionCommand) -> ActionResult: if command == null or not command.is_valid() or not _services_are_valid(): return _reject(command, REASON_INVALID_COMMAND, "Command or service wiring is invalid") var definition := _definition_lookup.call(command.get_action_id()) as ActionDefinition if ( definition == null or definition.target_type != SimulationIds.TARGET_ACTIVITY or definition.completion_handler_id != HANDLER_METRIC_DELTA ): return _reject(command, REASON_UNSUPPORTED_ACTION, "Action has no activity metric handler") var actor := command.get_actor() if actor.get_entity_type() not in [ACTOR_KIND_PLAYER, ACTOR_KIND_PERSON]: return _reject(command, REASON_UNSUPPORTED_ACTOR, "Actor kind cannot perform activity work") var effect_plan := _effect_planner.plan(definition, actor) var metric_operation := _metric_operation(effect_plan) if metric_operation.is_empty(): return _reject(command, REASON_INVALID_EFFECT, "Action metric effect plan is invalid") var handle := command.get_target() if not _registry.is_handle_valid(handle, _require_target_provider): return _reject(command, REASON_STALE_TARGET, "Activity target is no longer available") var descriptor := _registry.resolve_handle(handle) if ( handle.get_target_kind() != SimulationIds.TARGET_ACTIVITY or descriptor == null or descriptor.get_target_kind() != SimulationIds.TARGET_ACTIVITY or not descriptor.has_capability(command.get_action_id()) ): return _reject(command, REASON_TARGET_CAPABILITY, "Activity target lacks the action") var current_revision := int(_state_revision_lookup.call()) if ( command.get_expected_state_revision() >= 0 and command.get_expected_state_revision() != current_revision ): return _reject(command, REASON_STALE_REVISION, "Authoritative state revision changed") var capability := descriptor.get_capability(command.get_action_id()) var actor_position: Variant = _actor_position_lookup.call(actor) if actor_position is not Vector3 or not actor_position.is_finite(): return _reject(command, REASON_UNSUPPORTED_ACTOR, "Actor has no valid world position") if _enforce_spatial_range: var raw_maximum_range: Variant = capability.get_attribute(&"interaction_range", 0.0) if raw_maximum_range is not int and raw_maximum_range is not float: return _reject(command, REASON_INVALID_EFFECT, "Activity range metadata is invalid") var maximum_range := float(raw_maximum_range) if ( not is_finite(maximum_range) or maximum_range <= 0.0 or actor_position.distance_to(descriptor.get_local_position()) > maximum_range ): return _reject(command, REASON_OUT_OF_RANGE, "Actor is outside the activity range") var effect_payload: Dictionary = metric_operation["payload"] var metric_id := StringName(effect_payload["subject_key"]) var amount := float(effect_payload["delta"]) var completion: Variant = _completion_handler.call( actor, command.get_action_id(), descriptor.get_target_id(), metric_id, amount, (effect_payload["parameters"] as Dictionary).duplicate(true), actor_position ) if not _completion_shape_is_valid(completion): return ActionResult.failed( command.get_command_id(), REASON_COMPLETION_FAILED, "Authoritative activity completion returned an invalid result" ) var completion_result := completion as Dictionary if not bool(completion_result["succeeded"]): var failure_reason := StringName(completion_result.get("reason_code", "")) var failure_message := String(completion_result.get("message", "")) if failure_reason.is_empty() or failure_message.is_empty(): return ActionResult.failed( command.get_command_id(), REASON_COMPLETION_FAILED, "Authoritative activity completion was rejected" ) var failure_event_id := int(completion_result["event_id"]) var failure_revision := int(completion_result["state_revision"]) var failure_payload := {} if failure_event_id >= 0: if failure_event_id != current_revision or failure_revision < current_revision + 1: return ActionResult.failed( command.get_command_id(), REASON_COMPLETION_FAILED, "Authoritative failure fact did not advance state" ) failure_payload = { "event_id": failure_event_id, "state_revision": failure_revision, "reason_trace": ["command_rejected", "authoritative_failure_fact_recorded"], } elif failure_revision != current_revision: return ActionResult.failed( command.get_command_id(), REASON_COMPLETION_FAILED, "Rejected completion mutated state without a failure fact" ) return ActionResult.rejected( command.get_command_id(), failure_reason, failure_message, failure_payload ) var event_id := int(completion_result["event_id"]) var resulting_revision := int(completion_result["state_revision"]) var applied_amount := float(completion_result.get("applied_amount", amount)) if ( event_id != current_revision or resulting_revision < current_revision + 1 or not is_finite(applied_amount) ): return ActionResult.failed( command.get_command_id(), REASON_COMPLETION_FAILED, "Authoritative activity completion did not advance state" ) var spatial_trace := ( "range_revalidated" if _enforce_spatial_range else "abstract_completion_authorized" ) return ( ActionResult . completed( command.get_command_id(), { "action_id": String(command.get_action_id()), "target_id": String(descriptor.get_target_id()), "metric_id": String(metric_id), "amount": applied_amount, "event_id": event_id, "state_revision": resulting_revision, "reason_trace": [ "command_shape_valid", "actor_authorized", "target_handle_live", "target_capability_matched", "revision_matched", spatial_trace, "authoritative_metric_mutated", "completion_fact_recorded", "causal_followup_facts_committed", ], } ) ) func _services_are_valid() -> bool: return ( _registry != null and _definition_lookup.is_valid() and _actor_position_lookup.is_valid() and _state_revision_lookup.is_valid() and _completion_handler.is_valid() ) static func _metric_effect(definition: ActionDefinition) -> ActionEffect: if definition == null or definition.effects.size() != 1: return null var selected: ActionEffect for resource in definition.effects: var effect := resource as ActionEffect if effect == null or effect.effect_kind != ActionEffect.KIND_METRIC_DELTA: continue if selected != null: return null selected = effect if selected == null: return null if ( selected.subject_key not in [&"safety", &"knowledge"] or not is_finite(selected.value) or selected.value <= 0.0 ): return null for raw_key in selected.parameters: if String(raw_key) not in ["player_multiplier", "npc_productivity"]: return null var player_multiplier: Variant = selected.parameters.get("player_multiplier", 1.0) if ( (player_multiplier is not int and player_multiplier is not float) or not is_finite(float(player_multiplier)) or float(player_multiplier) <= 0.0 ): return null if selected.parameters.get("npc_productivity", false) is not bool: return null return selected static func _metric_operation(plan: ActionEffectPlan) -> Dictionary: if plan == null or not plan.is_valid() or plan.get_operation_count() != 1: return {} var operation: Dictionary = plan.get_operations()[0] if StringName(operation.get("operation_kind", &"")) != ActionEffect.KIND_METRIC_DELTA: return {} return operation.duplicate(true) static func _completion_shape_is_valid(completion: Variant) -> bool: if completion is not Dictionary: return false var result := completion as Dictionary if not result.has_all(["succeeded", "event_id", "state_revision"]): return false return ( result["succeeded"] is bool and result["event_id"] is int and result["state_revision"] is int and not WorldTargetCapability._contains_object(result) ) static func _reject(command: ActionCommand, reason: StringName, message: String) -> ActionResult: var command_id := command.get_command_id() if command != null else &"invalid_command" return ActionResult.rejected(command_id, reason, message)