class_name AgonesSDK extends Node # Dependency-free REST bridge for the Agones sidecar. The Go supervisor owns # the process-ready probe and /ready transition; this node owns the game # process's periodic Health pings and terminal Shutdown/annotation calls. const HEALTH_INTERVAL_SECONDS := 2.0 const REQUEST_TIMEOUT_SECONDS := 2.0 const MAX_ANNOTATION_VALUE_LENGTH := 4096 var _base_url := "" var _health_timer: Timer = null var _health_in_flight := false func configure_from_environment() -> bool: var port := OS.get_environment("AGONES_SDK_HTTP_PORT") if port.is_empty() or not port.is_valid_int() or int(port) < 1 or int(port) > 65535: return false _base_url = "http://127.0.0.1:%d" % int(port) return true func configure_for_testing(base_url: String) -> bool: if not base_url.begins_with("http://127.0.0.1:") and not base_url.begins_with("http://localhost:"): return false _base_url = base_url.trim_suffix("/") return true func is_available() -> bool: return not _base_url.is_empty() func start_health() -> void: if not is_available() or _health_timer != null: return _health_timer = Timer.new() _health_timer.name = "AgonesHealth" _health_timer.wait_time = HEALTH_INTERVAL_SECONDS _health_timer.one_shot = false _health_timer.timeout.connect(_send_health) add_child(_health_timer) _health_timer.start() _send_health() func stop_health() -> void: if _health_timer != null: _health_timer.stop() _health_timer.queue_free() _health_timer = null func health() -> int: return await _request(HTTPClient.METHOD_POST, "/health", {}) func mark_ready() -> int: return await _request(HTTPClient.METHOD_POST, "/ready", {}) func shutdown() -> int: return await _request(HTTPClient.METHOD_POST, "/shutdown", {}) func set_annotation(key: String, value: String) -> int: if not annotation_is_valid(key, value): return 400 return await _request(HTTPClient.METHOD_PUT, "/metadata/annotation", {"key": key, "value": value}) static func annotation_is_valid(key: String, value: String) -> bool: return not (key.is_empty() or value.is_empty() or value.length() > MAX_ANNOTATION_VALUE_LENGTH or "\n" in key or "\r" in key or "\n" in value or "\r" in value) func _send_health() -> void: if _health_in_flight or not is_available(): return _health_in_flight = true var status := await health() _health_in_flight = false if status < 200 or status >= 300: push_warning("Agones health ping failed (%d)" % status) func _request(method: int, path: String, payload: Dictionary) -> int: if not is_available() or not path.begins_with("/"): return 408 var request := HTTPRequest.new() request.timeout = REQUEST_TIMEOUT_SECONDS add_child(request) var body := JSON.stringify(payload) var err := request.request(_base_url + path, PackedStringArray(["Content-Type: application/json"]), method, body) if err != OK: request.queue_free() return 599 var result = await request.request_completed request.queue_free() return int(result[1])