diff --git a/.gitignore b/.gitignore index 86148ec..fb85297 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .import/ -build/ \ No newline at end of file +build/ +ngio.ini \ No newline at end of file diff --git a/autoloads/ngio.gd b/autoloads/ngio.gd new file mode 100644 index 0000000..61dcc5e --- /dev/null +++ b/autoloads/ngio.gd @@ -0,0 +1,123 @@ +extends Node + + +const GATEWAY_URI: String = "https://newgrounds.io/gateway_v3.php" + + +var app_id: String = "" # app id on newgrounds +var aes_key := PoolByteArray([]) # AES-128/Base64 encryption key +var keys_loaded: bool = false # whether id and key have been loaded from ini file + +var http := HTTPRequest.new() # http request node +var aes := AESContext.new() # aes encryption +var rng := RandomNumberGenerator.new() + + +var _response: Dictionary = {} + + +func _ready() -> void: + # load app id from ngio.ini + var ini = ConfigFile.new() + var err = ini.load("res://ngio.ini") + if err != OK: + push_error("Failed to load ngio data. Will not be able to access scoreboards.") + return + app_id = ini.get_value("ngio", "id", "") + if app_id == "": + push_error("Failed to load ngio data. Will not be able to access scoreboards.") + keys_loaded = false + else: + keys_loaded = true + + # attempt to load aes key + var key = Marshalls.base64_to_raw(ini.get_value("ngio", "key", "")) + if key.size() == 16: + aes_key = key + + # initialize HTTPRequest + add_child(http) + http.connect("request_completed", self, "_http_request_completed") + + # initialize rng for encryption + rng.randomize() + + +func _test_http() -> void: + print(yield(request_execute("Gateway.getDatetime"), "completed")) + print(yield(request_execute(["Gateway.getDatetime", "Gateway.getVersion"], {}, "", ["blep", "bleepo"]), "completed")) + + +## requests the provided component be executed, do not call async :/ +# may call with either single or multiple components +func request_execute(component, parameters = {}, session_id: String = "", echo = null, encrypt: bool = false) -> Dictionary: + # build request headers + var headers = [ + "Content-Type: application/x-www-form-urlencoded", + ] + # build execute object + var execute + if component is Array: + execute = [] + for i in component.size(): + if component[i] is String: + execute.append({ + component = component[i], + }) + if parameters is Array and parameters[i] is Dictionary: + execute[-1].parameters = parameters[i] + if echo is Array and echo[i] is String: + execute[-1].echo = echo[i] + else: + execute = { + component = component, + parameters = parameters, + } + if echo is String: + execute.echo = echo + # use encryption if a valid key is available + # TODO: implementation messed up in some way. encryption is fine, but + # formatting is wrong in some way that newgrounds can not understand +# if encrypt and aes_key.size() == 16: + if false: + # convert to bytes + var data = to_json(execute).to_utf8() + # pad to 16 bytes alignment + var padding = PoolByteArray([]) + padding.resize((16 - data.size() % 16) % 16) + padding.fill(0) + data.append_array(padding) + # generate random initialization vector + var iv = PoolByteArray([]) + iv.resize(16) + for i in iv.size(): + iv[i] = rng.randi() % 0xFF + # encrypt data + aes.start(AESContext.MODE_CBC_ENCRYPT, aes_key, iv) + var encrypted = aes.update(data) + aes.finish() + # compose secure execute object + execute = { + secure = Marshalls.raw_to_base64(iv + encrypted) + } + # compose request body + var request = { + app_id = app_id, + session_id = session_id, + execute = execute, + } + var body = "input=" + to_json(request).percent_encode() + # make request + var err = http.request(GATEWAY_URI, headers, true, HTTPClient.METHOD_POST, body) + # yield response + yield(http, "request_completed") + return _response + +## called when the HTTPRequest gets a responce +func _http_request_completed(result: int, response_code: int, headers: PoolStringArray, body: PoolByteArray) -> void: + if response_code == 200: + var dict = parse_json(body.get_string_from_utf8()) + if typeof(dict) == TYPE_DICTIONARY: + _response = dict + return + _response = {success = false} diff --git a/project.godot b/project.godot index f46092b..ba50c22 100644 --- a/project.godot +++ b/project.godot @@ -82,6 +82,7 @@ SceneManager="*res://autoloads/scene_manager.tscn" Console="*res://autoloads/console.tscn" Fade="*res://autoloads/fade.tscn" StainLayer="*res://autoloads/stain_layer.tscn" +Ngio="*res://autoloads/ngio.gd" [debug]