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")) ## requests the provided component be executed, do not call async :/ # may call with either single or multiple components func request_execute(component: String, parameters: Dictionary = {}, session_id: String = "", echo: String = "", encrypt: bool = false) -> Dictionary: # build request headers var headers = [ "Content-Type: application/x-www-form-urlencoded", ] # build execute object var execute = { component = component, parameters = parameters, 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}