Перейти к содержимому

SDK для Godot 4 (GDScript)

Автозагружаемый (autoload) синглтон-модуль. Использует встроенный HTTPRequest и await — без сторонних плагинов и внешних зависимостей.

Установка

  1. Сохраните скрипт ниже как res://bluestudz/BlueStudz.gd.
  2. Project Settings → Autoload → добавьте файл с именем узла BlueStudz.
  3. Заполните secret_token в инспекторе автозагрузки.
  4. Вызовите await BlueStudz.init() из вашей стартовой сцены.

Справочник методов GDScript

МетодПараметрыВозвращаетОписание
init(app_id, secret_token)app_id = "", secret_token = ""boolАсинхронный корутин инициализации. Выполняет handshake и сохраняет токен игрока.
Achievements.unlock(id)achievement_id: StringboolРазблокировка достижения игрока с верификацией секретного токена.
Presence.set_status(active)active: boolboolПереключение статуса активности игрока в сети лаунчера.
is_authenticated—boolСвойство-флаг успешной аутентификации текущей игровой сессии.

Исходный код синглтона (GDScript)

BlueStudz.gd
extends Node
## BlueStudz.gd - autoload singleton for Godot 4.
## Project Settings -> Autoload -> add this script as "BlueStudz".
## Reads BLUESTUDZ_API / BLUESTUDZ_GAME_ID / BLUESTUDZ_LAUNCH_TOKEN from the
## environment (set automatically by the BlueStudz Launcher).

signal auth_completed(success: bool)

@export var secret_token: String = ""

var _api_base: String
var _game_id: String
var _user_token: String
var _user_id: String
var is_authenticated: bool = false

func init() -> bool:
	_api_base = OS.get_environment("BLUESTUDZ_API")
	if _api_base.is_empty():
		_api_base = "https://api.bluestudz.ru"
	_game_id = OS.get_environment("BLUESTUDZ_GAME_ID")
	var launch_token := OS.get_environment("BLUESTUDZ_LAUNCH_TOKEN")

	if _game_id.is_empty() or launch_token.is_empty():
		push_warning("[BlueStudz] Not launched via BlueStudz Launcher - running unauthenticated.")
		return false

	var verify_result = await _post_json("/auth/game/verify", {"launchToken": launch_token}, false)
	if verify_result == null:
		return false
	_user_token = verify_result.get("gameToken", "")
	_user_id = verify_result.get("user", {}).get("id", "")

	var auth_result = await _post_json("/sdk/auth/verify", {
		"gameId": _game_id,
		"secretToken": secret_token,
		"userId": _user_id,
		"userToken": _user_token,
	}, false)
	is_authenticated = auth_result != null and auth_result.get("success", false)
	auth_completed.emit(is_authenticated)
	return is_authenticated

class Achievements:
	static func unlock(achievement_id: String) -> bool:
		var result = await Engine.get_main_loop().root.get_node("/root/BlueStudz")._post_json(
			"/sdk/achievements/unlock", {"achievementId": achievement_id}, true, true
		)
		return result != null and result.get("success", false)

class CloudSaves:
	## sha256/size_bytes/storage_key come from your own upload of the save
	## archive to the presigned URL from POST /storage/presign; this call
	## only registers the resulting file with BlueStudz's conflict detection.
	static func sync(sha256: String, size_bytes: int, storage_key: String, base_version) -> Dictionary:
		var result = await Engine.get_main_loop().root.get_node("/root/BlueStudz")._post_json(
			"/sdk/cloudsaves/sync",
			{"sha256": sha256, "sizeBytes": size_bytes, "storageKey": storage_key, "baseVersion": base_version},
			true
		)
		return result if result != null else {"success": false}

class Presence:
	static func set_status(active: bool) -> bool:
		var result = await Engine.get_main_loop().root.get_node("/root/BlueStudz")._post_json(
			"/sdk/presence/status", {"active": active}, true
		)
		return result != null and result.get("success", false)

func _post_json(path: String, body: Dictionary, with_auth: bool, with_secret: bool = false) -> Variant:
	var http := HTTPRequest.new()
	add_child(http)
	var headers := ["Content-Type: application/json"]
	if with_auth:
		headers.append("Authorization: Bearer %s" % _user_token)
		headers.append("x-bs-gameid: %s" % _game_id)
	if with_secret:
		headers.append("x-bs-secrettoken: %s" % secret_token)

	var err := http.request(_api_base + path, headers, HTTPClient.METHOD_POST, JSON.stringify(body))
	if err != OK:
		http.queue_free()
		return null

	var response = await http.request_completed
	http.queue_free()
	var response_code: int = response[1]
	var body_bytes: PackedByteArray = response[3]
	if response_code < 200 or response_code >= 300:
		push_error("[BlueStudz] %s failed with HTTP %d" % [path, response_code])
		return null

	var json := JSON.new()
	if json.parse(body_bytes.get_string_from_utf8()) != OK:
		return null
	return json.get_data()

Пример использования

gdscript
await BlueStudz.init()
if BlueStudz.is_authenticated:
    await BlueStudz.Achievements.unlock("first_blood")
    await BlueStudz.Presence.set_status(true)