added firebase and rudimentary leaderboard support

This commit is contained in:
derek
2025-04-09 11:19:02 -05:00
parent ce08df66e6
commit 25eb9e725a
121 changed files with 4987 additions and 4 deletions

View File

@@ -0,0 +1,219 @@
## @meta-authors Nicolò 'fenix' Santilio,
## @meta-version 2.5
##
## (source: [url=https://firebase.google.com/docs/functions]Functions[/url])
##
## @tutorial https://github.com/GodotNuts/GodotFirebase/wiki/Functions
@tool
class_name FirebaseFunctions
extends Node
## Emitted when a [code]query()[/code] request is successfully completed. [code]error()[/code] signal will be emitted otherwise.
## @arg-types Array
## Emitted when a [code]list()[/code] or [code]query()[/code] request is [b]not[/b] successfully completed.
signal task_error(code,status,message)
# TODO: Implement cache size limit
const CACHE_SIZE_UNLIMITED = -1
const _CACHE_EXTENSION : String = ".fscache"
const _CACHE_RECORD_FILE : String = "RmlyZXN0b3JlIGNhY2hlLXJlY29yZHMu.fscache"
const _AUTHORIZATION_HEADER : String = "Authorization: Bearer "
const _MAX_POOLED_REQUEST_AGE = 30
## The code indicating the request Firestore is processing.
## See @[enum FirebaseFirestore.Requests] to get a full list of codes identifiers.
## @enum Requests
var request : int = -1
## Whether cache files can be used and generated.
## @default true
var persistence_enabled : bool = false
## Whether an internet connection can be used.
## @default true
var networking: bool = true : set = set_networking
## A Dictionary containing all authentication fields for the current logged user.
## @type Dictionary
var auth : Dictionary
var _config : Dictionary = {}
var _cache_loc: String
var _encrypt_key: String = "" if Utilities.is_web() else OS.get_unique_id()
var _base_url : String = ""
var _http_request_pool : Array = []
var _offline: bool = false : set = _set_offline
func _ready() -> void:
set_process(false)
func _process(delta : float) -> void:
for i in range(_http_request_pool.size() - 1, -1, -1):
var request = _http_request_pool[i]
if not request.get_meta("requesting"):
var lifetime: float = request.get_meta("lifetime") + delta
if lifetime > _MAX_POOLED_REQUEST_AGE:
request.queue_free()
_http_request_pool.remove_at(i)
return # Prevent setting a value on request after it's already been queue_freed
request.set_meta("lifetime", lifetime)
## @args
## @return FunctionTask
func execute(function: String, method: int, params: Dictionary = {}, body: Dictionary = {}) -> FunctionTask:
set_process(true)
var function_task : FunctionTask = FunctionTask.new()
function_task.task_error.connect(_on_task_error)
function_task.task_finished.connect(_on_task_finished)
function_task.function_executed.connect(_on_function_executed)
function_task._method = method
var url : String = _base_url + ("/" if not _base_url.ends_with("/") else "") + function
function_task._url = url
if not params.is_empty():
url += "?"
for key in params.keys():
url += key + "=" + params[key] + "&"
if not body.is_empty():
function_task._fields = JSON.stringify(body)
_pooled_request(function_task)
return function_task
func set_networking(value: bool) -> void:
if value:
enable_networking()
else:
disable_networking()
func enable_networking() -> void:
if networking:
return
networking = true
_base_url = _base_url.replace("storeoffline", "functions")
func disable_networking() -> void:
if not networking:
return
networking = false
# Pointing to an invalid url should do the trick.
_base_url = _base_url.replace("functions", "storeoffline")
func _set_offline(value: bool) -> void:
if value == _offline:
return
_offline = value
if not persistence_enabled:
return
return
func _set_config(config_json : Dictionary) -> void:
_config = config_json
_cache_loc = _config["cacheLocation"]
if _encrypt_key == "": _encrypt_key = _config.apiKey
_check_emulating()
func _check_emulating() -> void :
## Check emulating
if not Firebase.emulating:
_base_url = "https://{zone}-{projectId}.cloudfunctions.net/".format({ zone = _config.functionsGeoZone, projectId = _config.projectId })
else:
var port : String = _config.emulators.ports.functions
if port == "":
Firebase._printerr("You are in 'emulated' mode, but the port for Cloud Functions has not been configured.")
else:
_base_url = "http://localhost:{port}/{projectId}/{zone}/".format({ port = port, zone = _config.functionsGeoZone, projectId = _config.projectId })
func _pooled_request(task : FunctionTask) -> void:
if _offline:
task._on_request_completed(HTTPRequest.RESULT_CANT_CONNECT, 404, PackedStringArray(), PackedByteArray())
return
if auth == null or auth.is_empty():
Firebase._print("Unauthenticated request issued...")
Firebase.Auth.login_anonymous()
var result : Array = await Firebase.Auth.auth_request
if result[0] != 1:
_check_auth_error(result[0], result[1])
Firebase._print("Client connected as Anonymous")
task._headers = ["Content-Type: application/json", _AUTHORIZATION_HEADER + auth.idtoken]
var http_request : HTTPRequest
for request in _http_request_pool:
if not request.get_meta("requesting"):
http_request = request
break
if not http_request:
http_request = HTTPRequest.new()
Utilities.fix_http_request(http_request)
http_request.accept_gzip = false
_http_request_pool.append(http_request)
add_child(http_request)
http_request.request_completed.connect(_on_pooled_request_completed.bind(http_request))
http_request.set_meta("requesting", true)
http_request.set_meta("lifetime", 0.0)
http_request.set_meta("task", task)
http_request.request(task._url, task._headers, task._method, task._fields)
# -------------
func _on_task_finished(data : Dictionary) :
pass
func _on_function_executed(result : int, data : Dictionary) :
pass
func _on_task_error(code : int, status : int, message : String):
task_error.emit(code, status, message)
Firebase._printerr(message)
func _on_FirebaseAuth_login_succeeded(auth_result : Dictionary) -> void:
auth = auth_result
func _on_FirebaseAuth_token_refresh_succeeded(auth_result : Dictionary) -> void:
auth = auth_result
func _on_pooled_request_completed(result : int, response_code : int, headers : PackedStringArray, body : PackedByteArray, request : HTTPRequest) -> void:
request.get_meta("task")._on_request_completed(result, response_code, headers, body)
request.set_meta("requesting", false)
func _on_connect_check_request_completed(result : int, _response_code, _headers, _body) -> void:
_set_offline(result != HTTPRequest.RESULT_SUCCESS)
func _on_FirebaseAuth_logout() -> void:
auth = {}
func _check_auth_error(code : int, message : String) -> void:
var err : String
match code:
400: err = "Please, enable Anonymous Sign-in method or Authenticate the Client before issuing a request (best option)"
Firebase._printerr(err)