diff --git a/README.md b/README.md index d09c559a..ac87a79c 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,5 @@ In the future (when the engine won't be a WIP anymore) we're gonna also publish - Credits to [Ne_Eo](https://twitter.com/Ne_Eo_Twitch) and the [3D-HaxeFlixel](https://github.com/lunarcleint/3D-HaxeFlixel) repository for Away3D Flixel support - Credits to the [FlxAnimate](https://github.com/Dot-Stuff/flxanimate) team for the Animate Atlas support - Credits to Smokey555 for the backup Animate Atlas to spritesheet code -- Credits to MAJigsaw77 for [hxvlc](https://github.com/MAJigsaw77/hxvlc) for video cutscene/mp4 support -- Credits to Aidan63 for [discord-rpc](https://github.com/Aidan63/linc_discord-rpc) for discord rpc integration +- Credits to MAJigsaw77 for [hxvlc](https://github.com/MAJigsaw77/hxvlc) (video cutscene/mp4 support) and [hxdiscord_rpc](https://github.com/MAJigsaw77/hxdiscord_rpc) (discord rpc integration) diff --git a/assets/shaders/engine/editorWaveforms.frag b/assets/shaders/engine/editorWaveforms.frag index 7a424adb..53fc9fdf 100644 --- a/assets/shaders/engine/editorWaveforms.frag +++ b/assets/shaders/engine/editorWaveforms.frag @@ -2,11 +2,11 @@ // Used in charter by waveforms -const vec3 gradient1 = vec3(114./255., 81./255., 135./255); -const vec3 gradient2 = vec3(144./255., 80./255., 186./255); +const vec3 gradient1 = vec3(114.0/255.0, 81.0/255.0, 135.0/255.0); +const vec3 gradient2 = vec3(144.0/255.0, 80.0/255.0, 186.0/255.0); -const vec4 outlinecolor = vec4(vec3(.1), .1); -const vec3 lowdetailcol = vec3(0., 0., 1.); +const vec4 outlinecolor = vec4(0.1); +const vec3 lowdetailcol = vec3(0.0, 0.0, 1.0); uniform vec2 textureRes; uniform float pixelOffset; @@ -19,18 +19,17 @@ uniform ivec2 waveformSize; uniform bool lowDetail; float getAmplitude(vec2 pixel) { - float pixelID = floor((pixel.y+pixelOffset)/3.); - + float pixelID = floor((pixel.y+pixelOffset)/3.0); + // TODO: INVESTIGATE THE 1.+ AND WHY IT WORKS (SRSLY I GOT NO CLUE) -lunar - vec2 wavePixel = vec2(mod(pixelID,waveformSize.x), 1.+floor(pixelID/waveformSize.x)); + vec2 wavePixel = vec2(mod(pixelID,waveformSize.x), 1.0+floor(pixelID/waveformSize.x)); vec4 waveData = texture2D(waveformTexture, wavePixel / waveformSize); - switch (int(mod(wavePixel.x, 3.))) { - case 0: return waveData.r; break; - case 1: return waveData.g; break; - case 2: return waveData.b; break; - } - return 0.; + int id = int(mod(wavePixel.x, 3.0)); + if(id == 0) return waveData.r; + if(id == 1) return waveData.g; + if(id == 2) return waveData.b; + return 0.0; } bool inWaveForm(vec2 pixel, float width) { @@ -41,40 +40,40 @@ bool inWaveForm(vec2 pixel, float width) { void highDetailWaveform(vec2 pixel, float amplitude, float ampwidth) { if (inWaveForm(pixel, ampwidth)) { vec3 gradientColor = mix(gradient1, gradient2, openfl_TextureCoordv.x); - gradientColor *= pixel.y+pixelOffset>playerPosition ? .7 : 1.; + gradientColor *= pixel.y+pixelOffset>playerPosition ? 0.7 : 1.0; - float remapCoord = (amplitude/textureRes.x)/2.; + float remapCoord = (amplitude/textureRes.x)/2.0; - float mappedCoord = map(openfl_TextureCoordv.x, remapCoord, 1.-remapCoord, 0., 1.); - float hightlightAmount = 1.-(abs(mappedCoord-0.5)*2.); + float mappedCoord = map(openfl_TextureCoordv.x, remapCoord, 1.0-remapCoord, 0.0, 1.0); + float hightlightAmount = 1.0-(abs(mappedCoord-0.5)*2.0); - gradientColor = mix(gradientColor, gradientColor * vec3(1.8), map(hightlightAmount, 0., 1., .5, 1.)); + gradientColor = mix(gradientColor, gradientColor * vec3(1.8), map(hightlightAmount, 0.0, 1.0, 0.5, 1.0)); - float ampwidthHighlight = (1.-(amplitude*(1./3.))) * textureRes.x; + float ampwidthHighlight = (1.0-(amplitude*(1.0/3.0))) * textureRes.x; if (inWaveForm(pixel, ampwidthHighlight)) { - float remapCoord = (ampwidthHighlight/textureRes.x)/2.; + float remapCoord = (ampwidthHighlight/textureRes.x)/2.0; - float mappedCoord = map(openfl_TextureCoordv.x, remapCoord, 1.-remapCoord, 0., 1.); - float hightlightAmount = 1.-(abs(mappedCoord-0.5)*2.); + float mappedCoord = map(openfl_TextureCoordv.x, remapCoord, 1.0-remapCoord, 0.0, 1.0); + float hightlightAmount = 1.0-(abs(mappedCoord-0.5)*2.0); - gradientColor = mix(gradientColor, gradientColor * vec3(1.8), map(hightlightAmount, 0., 1., .5, 1.)); + gradientColor = mix(gradientColor, gradientColor * vec3(1.8), map(hightlightAmount, 0.0, 1.0, 0.5, 1.0)); } - - gl_FragColor = vec4(vec3(gradientColor * mix(0.5, .8, amplitude))*openfl_Alphav, openfl_Alphav); - float lastAmplitude = getAmplitude(pixel - vec2(0., -1.)); - float lastAmpwidth = (1.-lastAmplitude) * textureRes.x; + gl_FragColor = vec4(vec3(gradientColor * mix(0.5, 0.8, amplitude))*openfl_Alphav, openfl_Alphav); + + float lastAmplitude = getAmplitude(pixel - vec2(0.0, -1.0)); + float lastAmpwidth = (1.0-lastAmplitude) * textureRes.x; if (!inWaveForm(pixel, lastAmpwidth)) - gl_FragColor -= outlinecolor*mix(0.5, .8, amplitude); + gl_FragColor -= outlinecolor*mix(0.5, 0.8, amplitude); - float nextAmplitude = getAmplitude(pixel - vec2(0., 1.)); - float nextAmpwidth = (1.-nextAmplitude) * textureRes.x; + float nextAmplitude = getAmplitude(pixel - vec2(0.0, 1.0)); + float nextAmpwidth = (1.0-nextAmplitude) * textureRes.x; if (!inWaveForm(pixel, nextAmpwidth)) - gl_FragColor -= outlinecolor*mix(0.5, .8, amplitude); + gl_FragColor -= outlinecolor*mix(0.5, 0.8, amplitude); - if (!inWaveForm(pixel, ampwidth+2.) && amplitude > 0.) + if (!inWaveForm(pixel, ampwidth+2.0) && amplitude > 0.0) gl_FragColor -= outlinecolor; } } @@ -82,9 +81,9 @@ void highDetailWaveform(vec2 pixel, float amplitude, float ampwidth) { void lowDetailWaveform(vec2 pixel, float amplitude, float ampwidth) { if (inWaveForm(pixel, ampwidth)) { vec3 color = lowdetailcol; - color *= pixel.y+pixelOffset>playerPosition ? .7 : 1.; + color *= pixel.y+pixelOffset>playerPosition ? 0.7 : 1.0; - gl_FragColor = vec4(color, 1.); + gl_FragColor = vec4(color, 1.0); } } @@ -93,10 +92,10 @@ void main() vec2 pixel = openfl_TextureCoordv * textureRes; float amplitude = getAmplitude(pixel); - float ampwidth = (1.-amplitude) * textureRes.x; + float ampwidth = (1.0-amplitude) * textureRes.x; if (lowDetail) lowDetailWaveform(pixel, amplitude, ampwidth); - else + else highDetailWaveform(pixel, amplitude, ampwidth); } \ No newline at end of file diff --git a/libs.xml b/libs.xml index 81618ce1..5afacfc3 100644 --- a/libs.xml +++ b/libs.xml @@ -10,12 +10,12 @@ - + - + diff --git a/project.xml b/project.xml index aee55825..6f26ce30 100644 --- a/project.xml +++ b/project.xml @@ -170,10 +170,11 @@ - + + diff --git a/source/funkin/backend/scripting/events/DiscordPresenceUpdateEvent.hx b/source/funkin/backend/scripting/events/DiscordPresenceUpdateEvent.hx index 782d70a0..c17cbb68 100644 --- a/source/funkin/backend/scripting/events/DiscordPresenceUpdateEvent.hx +++ b/source/funkin/backend/scripting/events/DiscordPresenceUpdateEvent.hx @@ -1,12 +1,8 @@ package funkin.backend.scripting.events; -#if DISCORD_RPC -import discord_rpc.DiscordRpc.DiscordPresenceOptions; -#end - final class DiscordPresenceUpdateEvent extends CancellableEvent { /** * Object containing all of the data for the presence. Can be altered. */ - public var presenceData:#if DISCORD_RPC DiscordPresenceOptions #else Dynamic #end; + public var presence:funkin.backend.utils.DiscordUtil.DPresence; } \ No newline at end of file diff --git a/source/funkin/backend/system/github/GitHub.hx b/source/funkin/backend/system/github/GitHub.hx index fa664112..27ba825b 100644 --- a/source/funkin/backend/system/github/GitHub.hx +++ b/source/funkin/backend/system/github/GitHub.hx @@ -3,7 +3,6 @@ package funkin.backend.system.github; #if GITHUB_API import haxe.Json; import haxe.Exception; -import haxe.Http; #end // TODO: Document further and perhaps make this a Haxelib. @@ -18,7 +17,7 @@ class GitHub { public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array { #if GITHUB_API try { - var data = Json.parse(__requestOnGitHubServers('https://api.github.com/repos/${user}/${repository}/releases')); + var data = Json.parse(HttpUtil.requestText('https://api.github.com/repos/${user}/${repository}/releases')); if (!(data is Array)) throw __parseGitHubException(data); @@ -41,7 +40,7 @@ class GitHub { public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array { #if GITHUB_API try { - var data = Json.parse(__requestOnGitHubServers('https://api.github.com/repos/${user}/${repository}/contributors')); + var data = Json.parse(HttpUtil.requestText('https://api.github.com/repos/${user}/${repository}/contributors')); if (!(data is Array)) throw __parseGitHubException(data); @@ -63,7 +62,7 @@ class GitHub { public static function getOrganization(org:String, ?onError:Exception->Void):GitHubOrganization { #if GITHUB_API try { - var data = Json.parse(__requestOnGitHubServers('https://api.github.com/orgs/$org')); + var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org')); if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data); @@ -86,7 +85,7 @@ class GitHub { public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array { #if GITHUB_API try { - var data = Json.parse(__requestOnGitHubServers('https://api.github.com/orgs/$org/members')); + var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org/members')); if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data); @@ -111,7 +110,7 @@ class GitHub { try { var url = 'https://api.github.com/users/$user'; - var data = Json.parse(__requestOnGitHubServers(url)); + var data = Json.parse(HttpUtil.requestText(url)); if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data); @@ -134,59 +133,6 @@ class GitHub { public static inline function filterReleases(releases:Array, keepPrereleases:Bool = true, keepDrafts:Bool = false) return #if GITHUB_API [for(release in releases) if (release != null && (!release.prerelease || (release.prerelease && keepPrereleases)) && (!release.draft || (release.draft && keepDrafts))) release] #else releases #end; - public static function __requestOnGitHubServers(url:String) { - var r = null; - #if GITHUB_API - var h = new Http(url); - h.setHeader("User-Agent", "request"); - - h.onStatus = function(s) { - if(isRedirect(s)) - r = __requestOnGitHubServers(h.responseHeaders.get("Location")); - }; - - h.onData = function(d) { - if(r == null) r = d; - } - h.onError = function(e) { - throw e; - } - - h.request(false); - #end - return r; - } - public static function __requestBytesOnGitHubServers(url:String) { - var r = null; - #if GITHUB_API - var h = new Http(url); - h.setHeader("User-Agent", "request"); - - h.onStatus = function(s) { - if(isRedirect(s)) - r = __requestBytesOnGitHubServers(h.responseHeaders.get("Location")); - }; - - h.onBytes = function(d) { - if(r == null) r = d; - } - h.onError = function(e) { - throw e; - } - - h.request(false); - #end - return r; - } - private static function isRedirect(status:Int):Bool { - switch (status) { - // 301: Moved Permanently, 302: Found (Moved Temporarily), 307: Temporary Redirect, 308: Permanent Redirect - Nex - case 301 | 302 | 307 | 308 : - trace("Redirected with status code: " + status); - return true; - } - return false; - } private static function __parseGitHubException(obj:Dynamic):GitHubException { #if GITHUB_API var msg:String = "(No message)"; diff --git a/source/funkin/backend/system/macros/Utils.hx b/source/funkin/backend/system/macros/Utils.hx index df25030c..1d4873f4 100644 --- a/source/funkin/backend/system/macros/Utils.hx +++ b/source/funkin/backend/system/macros/Utils.hx @@ -37,4 +37,21 @@ class Utils { return expr; #end } + + public static macro function safeSet(variable:Null, value:Null):Null { + #if macro + return macro if (${value} != null) ${variable} = ${value}; + #end + } + public static macro function safeSetWrapper(variable:Null, value:Null, wrapper:Null):Null { + #if macro + return macro if (${value} != null) ${variable} = ${wrapper}(${value}); + #end + } + + public static macro function safeReflection(variable:Null, value:Null, field:Null):Null { + #if macro + return macro if (Reflect.hasField(${value}, ${field})) ${variable} = Reflect.field(${value}, ${field}); + #end + } } \ No newline at end of file diff --git a/source/funkin/backend/utils/DiscordUtil.hx b/source/funkin/backend/utils/DiscordUtil.hx index cdd24b6c..733790fa 100644 --- a/source/funkin/backend/utils/DiscordUtil.hx +++ b/source/funkin/backend/utils/DiscordUtil.hx @@ -1,62 +1,77 @@ package funkin.backend.utils; +import flixel.util.typeLimit.OneOfTwo; +import openfl.display.BitmapData; +import funkin.backend.system.macros.Utils; import funkin.backend.scripting.events.DiscordPresenceUpdateEvent; import haxe.Json; import flixel.sound.FlxSound; #if DISCORD_RPC -import discord_rpc.DiscordRpc; +import hxdiscord_rpc.Discord; +import hxdiscord_rpc.Types; import sys.thread.Thread; import Sys; import lime.app.Application; #end -class DiscordUtil { - public static var currentID:String = null; +class DiscordUtil +{ + public static var currentID(default, set):String = null; public static var discordThread:#if DISCORD_RPC Thread #else Dynamic #end = null; public static var ready:Bool = false; - public static var data:DiscordJson = null; - public static function init() { + public static var user:#if DISCORD_RPC DUser #else Dynamic #end = null; + public static var lastPresence:#if DISCORD_RPC DPresence #else Dynamic #end = null; + public static var config:#if DISCORD_RPC DiscordJson #else Dynamic #end = null; + + public static function init() + { #if DISCORD_RPC reloadJsonData(); - discordThread = Thread.create(function() { + + discordThread = Thread.create(function() + { while (true) { - while(!ready) { - Sys.sleep(1/60); + while (ready) + { + #if DISCORD_DISABLE_IO_THREAD + Discord.UpdateConnection(); + #end + Discord.RunCallbacks(); + + Sys.sleep(2); } - trace("Processing Discord RPC..."); - DiscordRpc.process(); - Sys.sleep(2); + + Sys.sleep(1); // to reduce cpu } }); - Application.current.onExit.add(function(exitCode) { - shutdown(); - }); + Application.current.onExit.add(function(_) shutdown()); #end } - public static function reloadJsonData() { + public static function reloadJsonData() + { #if DISCORD_RPC - data = {}; var jsonPath = Paths.json("config/discord"); - if (Assets.exists(jsonPath)) { - try { - data = Json.parse(Assets.getText(jsonPath)); - } catch(e) { + if (Assets.exists(jsonPath)) + { + try + config = Json.parse(Assets.getText(jsonPath)) + catch (e) Logs.trace('Couldn\'t load Discord RPC configuration: ${e.toString()}', ERROR); - } } - data.setFieldDefault("clientID", "1027994136193810442"); - data.setFieldDefault("logoKey", "icon"); - data.setFieldDefault("logoText", Application.current.meta.get('title')); - changeClientID(data.clientID); + config.logoKey = config.logoKey.getDefault("icon"); + config.logoText = config.logoText.getDefault(Application.current.meta.get('title')); + config.clientID = config.clientID.getDefault("1027994136193810442"); + currentID = config.clientID; #end } - public static function changePresence(details:String, state:String, ?smallImageKey : String) { + public static function changePresence(details:String, state:String, ?smallImageKey:String) + { #if DISCORD_RPC changePresenceAdvanced({ state: state, @@ -66,12 +81,14 @@ class DiscordUtil { #end } - public static function changeSongPresence(details:String, state:String, audio:FlxSound, ?smallImageKey : String) { + public static function changeSongPresence(details:String, state:String, audio:FlxSound, ?smallImageKey:String) + { #if DISCORD_RPC var start:Float = 0; var end:Float = 0; - if (audio != null && audio.playing) { + if (audio != null && audio.playing) + { start = Date.now().getTime(); end = start + (audio.length - audio.time); } @@ -86,66 +103,203 @@ class DiscordUtil { #end } - public static function changePresenceAdvanced(data:#if DISCORD_RPC DiscordPresenceOptions #else Dynamic #end) { - #if DISCORD_RPC - if (data == null) return; + @:noCompletion public static function fixString(str:String) + { + return new cpp.ConstCharStar(cast(str, String)); + } + @:noCompletion public static function toString(str:cpp.ConstCharStar) + { + return cast(str, String); + } + + public static function changePresenceAdvanced(data:DPresence) + { + #if DISCORD_RPC + if (data == null) + return; + + // copy last presence if (data.largeImageKey == null) - data.largeImageKey = DiscordUtil.data.logoKey; + data.largeImageKey = config.logoKey; if (data.largeImageText == null) - data.largeImageText = DiscordUtil.data.logoText; + data.largeImageText = config.logoText; #if GLOBAL_SCRIPT var event = funkin.backend.scripting.GlobalScript.event("onDiscordPresenceUpdate", EventManager.get(DiscordPresenceUpdateEvent).recycle(data)); - if (event.cancelled) return; + if (event.cancelled) + return; + data = event.presence; #end + lastPresence = data; - DiscordRpc.presence(data); + var dp:DiscordRichPresence = DiscordRichPresence.create(); + // TODO: make this use a reflection-like macro + // strings + /*if(data.state != null) dp.state = fixString(data.state); + if(data.details != null) dp.details = fixString(data.details); + if(data.largeImageKey != null) dp.largeImageKey = fixString(data.largeImageKey); + if(data.largeImageText != null) dp.largeImageText = fixString(data.largeImageText); + if(data.smallImageKey != null) dp.smallImageKey = fixString(data.smallImageKey); + if(data.smallImageText != null) dp.smallImageText = fixString(data.smallImageText); + if(data.partyId != null) dp.partyId = fixString(data.partyId); + if(data.matchSecret != null) dp.matchSecret = fixString(data.matchSecret); + if(data.joinSecret != null) dp.joinSecret = fixString(data.joinSecret); + if(data.spectateSecret != null) dp.spectateSecret = fixString(data.spectateSecret); + + // other + if(data.startTimestamp != null) dp.startTimestamp = data.startTimestamp; + if(data.endTimestamp != null) dp.endTimestamp = data.endTimestamp; + if(data.partySize != null) dp.partySize = data.partySize; + if(data.partyMax != null) dp.partyMax = data.partyMax; + if(data.partyPrivacy != null) dp.partyPrivacy = data.partyPrivacy; + if(data.instance != null) dp.instance = data.instance; */ + + Utils.safeSetWrapper(dp.state, data.state, fixString); + Utils.safeSetWrapper(dp.details, data.details, fixString); + Utils.safeSet(dp.startTimestamp, data.startTimestamp); + Utils.safeSet(dp.endTimestamp, data.endTimestamp); + Utils.safeSetWrapper(dp.largeImageKey, data.largeImageKey, fixString); + Utils.safeSetWrapper(dp.largeImageText, data.largeImageText, fixString); + Utils.safeSetWrapper(dp.smallImageKey, data.smallImageKey, fixString); + Utils.safeSetWrapper(dp.smallImageText, data.smallImageText, fixString); + Utils.safeSetWrapper(dp.partyId, data.partyId, fixString); + Utils.safeSet(dp.partySize, data.partySize); + Utils.safeSet(dp.partyMax, data.partyMax); + Utils.safeSet(dp.partyPrivacy, data.partyPrivacy); + Utils.safeSetWrapper(dp.matchSecret, data.matchSecret, fixString); + Utils.safeSetWrapper(dp.joinSecret, data.joinSecret, fixString); + Utils.safeSetWrapper(dp.spectateSecret, data.spectateSecret, fixString); + Utils.safeSet(dp.instance, data.instance); + + Discord.UpdatePresence(cpp.RawConstPointer.addressOf(dp)); #end } - public static function changeClientID(id:String) { - #if DISCORD_RPC - if (currentID != null) { - DiscordRpc.shutdown(); - } + private static function set_currentID(id:String):String + { + if (currentID == id) + return id; + #if DISCORD_RPC + if (currentID != null) + shutdown(); + + var handlers:DiscordEventHandlers = DiscordEventHandlers.create(); + handlers.ready = cpp.Function.fromStaticFunction(onReady); + handlers.disconnected = cpp.Function.fromStaticFunction(onDisconnected); + handlers.errored = cpp.Function.fromStaticFunction(onError); + Discord.Initialize(id, cpp.RawPointer.addressOf(handlers), 1, null); + #end + + return currentID = id; + } + + public static function shutdown() + { ready = false; - - DiscordRpc.start({ - clientID: id, - onReady: function() { - Logs.trace('Discord RPC started'); - ready = true; - }, - onError: onError, - onDisconnected: onDisconnected - }); - currentID = id; - - #end - } - - public static function shutdown() { #if DISCORD_RPC - DiscordRpc.shutdown(); + Discord.Shutdown(); #end } - // HANDLERS #if DISCORD_RPC - static function onError(_code:Int, _message:String) { - Logs.trace('Discord RPC Error: ${_message} (Code: $_code)', ERROR); + private static function onReady(request:cpp.RawConstPointer):Void + { + var requestPtr:cpp.Star = cpp.ConstPointer.fromRaw(request).ptr; + + user = new DUser(); + user.init(requestPtr); + + Logs.trace('(Discord) Connected to User (${user.tag})'); + + ready = true; } - static function onDisconnected(_code:Int, _message:String) { - Logs.trace('Discord RPC Disconnected: ${_message} (Code: $_code)', WARNING); + private static function onDisconnected(errorCode:Int, message:cpp.ConstCharStar):Void + { + Sys.println('Discord: Disconnected ($errorCode: ${cast (message, String)})'); + } + + private static function onError(errorCode:Int, message:cpp.ConstCharStar):Void + { + Sys.println('Discord: Error ($errorCode: ${cast (message, String)})'); } #end } -typedef DiscordJson = { +typedef DiscordJson = +{ var ?clientID:String; var ?logoKey:String; var ?logoText:String; -} \ No newline at end of file +} + +@:noCustomClass +final class DUser +{ + /** + * The username + discriminator if they have it + **/ + public var tag:String; + + /** + * The user id, aka 860561967383445535 + **/ + public var userId:String; + + /** + * The user's username + **/ + public var username:String; + + /** + * The #number from before discord changed to usernames only, if the user has changed to a username them its just a 0 + **/ + public var discriminator:Int; + + /** + * The user's avatar filename + **/ + public var avatar:String; + + public function new() {} + public function init(userData:cpp.Star) + { + userId = userData.userId; + username = userData.username; + discriminator = Std.parseInt(userData.discriminator); + avatar = userData.avatar; + + if (discriminator != 0) + tag = '${username}#${discriminator}'; + else + tag = '${username}'; + } + + /** + * Calling this function gets the BitmapData of the user + **/ + public function getAvatar(size:Int = 256):BitmapData + return BitmapData.fromBytes(HttpUtil.requestBytes('https://cdn.discordapp.com/avatars/$userId/$avatar.png?size=$size')); +} + +typedef DPresence = +{ + var ?state:String; /* max 128 bytes */ + var ?details:String; /* max 128 bytes */ + var ?startTimestamp:OneOfTwo; + var ?endTimestamp:OneOfTwo; + var ?largeImageKey:String; /* max 32 bytes */ + var ?largeImageText:String; /* max 128 bytes */ + var ?smallImageKey:String; /* max 32 bytes */ + var ?smallImageText:String; /* max 128 bytes */ + var ?partyId:String; /* max 128 bytes */ + var ?partySize:Int; + var ?partyMax:Int; + var ?partyPrivacy:Int; + var ?matchSecret:String; /* max 128 bytes */ + var ?joinSecret:String; /* max 128 bytes */ + var ?spectateSecret:String; /* max 128 bytes */ + var ?instance:OneOfTwo; +} diff --git a/source/funkin/backend/utils/HttpUtil.hx b/source/funkin/backend/utils/HttpUtil.hx new file mode 100644 index 00000000..1edbbfd2 --- /dev/null +++ b/source/funkin/backend/utils/HttpUtil.hx @@ -0,0 +1,71 @@ +package funkin.backend.utils; + +import haxe.Http; + +class HttpUtil +{ + public static var userAgent:String = "request"; + public static function requestText(url:String) + { + var r = null; + var h = new Http(url); + h.setHeader("User-Agent", userAgent); + + h.onStatus = function(s) + { + if (isRedirect(s)) + r = requestText(h.responseHeaders.get("Location")); + }; + + h.onData = function(d) + { + if (r == null) + r = d; + } + h.onError = function(e) + { + throw e; + } + + h.request(false); + return r; + } + + public static function requestBytes(url:String) + { + var r = null; + var h = new Http(url); + h.setHeader("User-Agent", userAgent); + + h.onStatus = function(s) + { + if (isRedirect(s)) + r = requestBytes(h.responseHeaders.get("Location")); + }; + + h.onBytes = function(d) + { + if (r == null) + r = d; + } + h.onError = function(e) + { + throw e; + } + + h.request(false); + return r; + } + + private static function isRedirect(status:Int):Bool + { + switch (status) + { + // 301: Moved Permanently, 302: Found (Moved Temporarily), 307: Temporary Redirect, 308: Permanent Redirect - Nex + case 301 | 302 | 307 | 308: + trace("Redirected with status code: " + status); + return true; + } + return false; + } +} diff --git a/source/funkin/game/NoteGroup.hx b/source/funkin/game/NoteGroup.hx index 97b34da6..0d45a41f 100644 --- a/source/funkin/game/NoteGroup.hx +++ b/source/funkin/game/NoteGroup.hx @@ -106,6 +106,7 @@ class NoteGroup extends FlxTypedGroup { if (members == null) return null; + // TODO: maybe lastIndexOf var index:Int = members.indexOfFromLast(Object); if (index < 0) diff --git a/source/funkin/options/type/GithubIconOption.hx b/source/funkin/options/type/GithubIconOption.hx index cbbef1ed..7f41b8d4 100644 --- a/source/funkin/options/type/GithubIconOption.hx +++ b/source/funkin/options/type/GithubIconOption.hx @@ -57,7 +57,7 @@ class GithubUserIcon extends FlxSprite var bytes = null; if(unfLink) { - try bytes = GitHub.__requestBytesOnGitHubServers('${user.avatar_url}?size=$size') + try bytes = HttpUtil.requestBytes('${user.avatar_url}?size=$size') catch(e) Logs.traceColored([Logs.logText('Failed to download github pfp for ${user.login}: ${CoolUtil.removeIP(e.message)} - (Retrying using the api..)', RED)], ERROR); if(bytes != null) { @@ -68,7 +68,7 @@ class GithubUserIcon extends FlxSprite if(planB) { if(unfLink) user = GitHub.getUser(user.login, function(e) Logs.traceColored([Logs.logText('Failed to download github user info for ${user.login}: ${CoolUtil.removeIP(e.message)}', RED)], ERROR)); // Api part - Nex - try bytes = GitHub.__requestBytesOnGitHubServers('${user.avatar_url}&size=$size') + try bytes = HttpUtil.requestBytes('${user.avatar_url}&size=$size') catch(e) Logs.traceColored([Logs.logText('Failed to download github pfp for ${user.login}: ${CoolUtil.removeIP(e.message)}', RED)], ERROR); if(bytes != null) bmap = BitmapData.fromBytes(bytes);