179 lines
4.9 KiB
GDScript
179 lines
4.9 KiB
GDScript
class_name ApiClient
|
|
extends Node
|
|
|
|
const DEFAULT_TIMEOUT_SECONDS := 12.0
|
|
|
|
var base_url: String = ""
|
|
var timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS
|
|
|
|
|
|
func _ready() -> void:
|
|
base_url = str(ProjectSettings.get_setting("farm_game/api/base_url", ""))
|
|
|
|
|
|
func post_json(path: String, payload: Dictionary) -> Dictionary:
|
|
var base_error := _ensure_base_url()
|
|
if not base_error.is_empty():
|
|
return base_error
|
|
|
|
var request := HTTPRequest.new()
|
|
request.timeout = timeout_seconds
|
|
add_child(request)
|
|
|
|
var url := _join_url(base_url, path)
|
|
var body := _to_egret_msg_body(payload)
|
|
var headers := PackedStringArray(["Content-Type: application/x-www-form-urlencoded"])
|
|
var error := request.request(url, headers, HTTPClient.METHOD_POST, body)
|
|
if error != OK:
|
|
request.queue_free()
|
|
return {
|
|
"ok": false,
|
|
"offline": false,
|
|
"status": 0,
|
|
"body": {},
|
|
"text": "HTTPRequest failed to start: %s" % error
|
|
}
|
|
|
|
var response: Array = await request.request_completed
|
|
request.queue_free()
|
|
return _to_result(response)
|
|
|
|
|
|
func get_query(path: String, params: Dictionary = {}) -> Dictionary:
|
|
var base_error := _ensure_base_url()
|
|
if not base_error.is_empty():
|
|
return base_error
|
|
|
|
var request := HTTPRequest.new()
|
|
request.timeout = timeout_seconds
|
|
add_child(request)
|
|
|
|
var url := _join_url(base_url, path)
|
|
var query := _to_form_body(params)
|
|
if not query.is_empty():
|
|
url = "%s%s%s" % [url, "&" if url.contains("?") else "?", query]
|
|
var error := request.request(url, PackedStringArray(), HTTPClient.METHOD_GET)
|
|
if error != OK:
|
|
request.queue_free()
|
|
return {
|
|
"ok": false,
|
|
"offline": false,
|
|
"status": 0,
|
|
"body": {},
|
|
"text": "HTTPRequest failed to start: %s" % error
|
|
}
|
|
|
|
var response: Array = await request.request_completed
|
|
request.queue_free()
|
|
return _to_result(response)
|
|
|
|
|
|
func post_form(path: String, payload: Dictionary) -> Dictionary:
|
|
var base_error := _ensure_base_url()
|
|
if not base_error.is_empty():
|
|
return base_error
|
|
|
|
var request := HTTPRequest.new()
|
|
request.timeout = timeout_seconds
|
|
add_child(request)
|
|
|
|
var url := _join_url(base_url, path)
|
|
var headers := PackedStringArray(["Content-Type: application/x-www-form-urlencoded"])
|
|
var error := request.request(url, headers, HTTPClient.METHOD_POST, _to_form_body(payload))
|
|
if error != OK:
|
|
request.queue_free()
|
|
return {
|
|
"ok": false,
|
|
"offline": false,
|
|
"status": 0,
|
|
"body": {},
|
|
"text": "HTTPRequest failed to start: %s" % error
|
|
}
|
|
|
|
var response: Array = await request.request_completed
|
|
request.queue_free()
|
|
return _to_result(response)
|
|
|
|
|
|
func _ensure_base_url() -> Dictionary:
|
|
if base_url.strip_edges().is_empty():
|
|
base_url = str(ProjectSettings.get_setting("farm_game/api/base_url", ""))
|
|
if not base_url.strip_edges().is_empty():
|
|
return {}
|
|
return {
|
|
"ok": false,
|
|
"offline": true,
|
|
"status": 0,
|
|
"body": {},
|
|
"text": "API base URL is not configured."
|
|
}
|
|
|
|
|
|
func _join_url(root: String, path: String) -> String:
|
|
var normalized_root := root.strip_edges().trim_suffix("/")
|
|
var normalized_path := path.strip_edges()
|
|
if normalized_root.ends_with("=") or normalized_root.ends_with("?") or normalized_root.ends_with("&"):
|
|
return normalized_root + normalized_path.trim_prefix("/")
|
|
if not normalized_path.begins_with("/"):
|
|
normalized_path = "/" + normalized_path
|
|
return normalized_root + normalized_path
|
|
|
|
|
|
func _to_egret_msg_body(payload: Dictionary) -> String:
|
|
var json := JSON.stringify(payload)
|
|
var encoded := Marshalls.raw_to_base64(json.to_utf8_buffer()).uri_encode()
|
|
return "msg=%s" % encoded
|
|
|
|
|
|
func _to_form_body(payload: Dictionary) -> String:
|
|
var parts: Array[String] = []
|
|
for key in payload.keys():
|
|
var encoded_key := str(key).uri_encode()
|
|
var encoded_value := str(payload.get(key, "")).uri_encode()
|
|
parts.append("%s=%s" % [encoded_key, encoded_value])
|
|
return "&".join(parts)
|
|
|
|
|
|
func _to_result(response: Array) -> Dictionary:
|
|
var result: int = response[0]
|
|
var status: int = response[1]
|
|
var headers: PackedStringArray = response[2]
|
|
var body: PackedByteArray = response[3]
|
|
var text := body.get_string_from_utf8()
|
|
var parsed = JSON.parse_string(text)
|
|
if parsed == null:
|
|
parsed = {}
|
|
_decode_embedded_data(parsed)
|
|
|
|
var http_ok := result == HTTPRequest.RESULT_SUCCESS and status >= 200 and status < 300
|
|
var game_ok := true
|
|
var game_status = null
|
|
if parsed is Dictionary and parsed.has("status"):
|
|
game_status = parsed.get("status")
|
|
game_ok = int(game_status) == 0
|
|
elif parsed is Dictionary and parsed.has("code"):
|
|
game_status = parsed.get("code")
|
|
game_ok = int(game_status) == 0
|
|
|
|
return {
|
|
"ok": http_ok and game_ok,
|
|
"offline": false,
|
|
"status": status,
|
|
"game_status": game_status,
|
|
"headers": headers,
|
|
"body": parsed,
|
|
"text": text
|
|
}
|
|
|
|
|
|
func _decode_embedded_data(parsed: Variant) -> void:
|
|
if not parsed is Dictionary:
|
|
return
|
|
if not parsed.has("data") or not parsed["data"] is String:
|
|
return
|
|
|
|
var decoded_text := Marshalls.base64_to_utf8(str(parsed["data"]).strip_edges())
|
|
var decoded = JSON.parse_string(decoded_text)
|
|
if decoded != null:
|
|
parsed["data"] = decoded
|