diff --git a/source/funkin/backend/utils/HttpUtil.hx b/source/funkin/backend/utils/HttpUtil.hx index be9d8ac3..1fc78f50 100644 --- a/source/funkin/backend/utils/HttpUtil.hx +++ b/source/funkin/backend/utils/HttpUtil.hx @@ -5,56 +5,104 @@ import haxe.Http; final class HttpUtil { public static var userAgent:String = Flags.USER_AGENT; - public static function requestText(url:String) + + public static function requestText(url:String):String { - var r = null; + var result:String = null; + var error:HttpError = null; + var redirected:Bool = false; + var h = new Http(url); h.setHeader("User-Agent", userAgent); - h.onStatus = function(s) + h.onStatus = function(status) { - if (isRedirect(s)) - r = requestText(h.responseHeaders.get("Location")); + redirected = isRedirect(status); + if (redirected) + { + var loc = h.responseHeaders.get("Location"); + if (loc != null) + result = requestText(loc); + else + error = new HttpError("Missing Location header in redirect", url, status, true); + } }; - h.onData = function(d) + h.onData = function(data) { - if (r == null) - r = d; - } - h.onError = function(e) + if (result == null) + result = data; + }; + + h.onError = function(msg) { - throw e; - } + error = new HttpError(msg, url); + }; h.request(false); - return r; + + if (error != null) + throw error; + + if (result == null) + throw new HttpError("Unknown error or empty response", url); + + return result; } - public static function requestBytes(url:String) + public static function requestBytes(url:String):haxe.io.Bytes { - var r = null; + var result:haxe.io.Bytes = null; + var error:HttpError = null; + var redirected:Bool = false; + var h = new Http(url); h.setHeader("User-Agent", userAgent); - h.onStatus = function(s) + h.onStatus = function(status) { - if (isRedirect(s)) - r = requestBytes(h.responseHeaders.get("Location")); + redirected = isRedirect(status); + if (redirected) + { + var loc = h.responseHeaders.get("Location"); + if (loc != null) + result = requestBytes(loc); + else + error = new HttpError("Missing Location header in redirect", url, status, true); + } }; - h.onBytes = function(d) + h.onBytes = function(data) { - if (r == null) - r = d; - } - h.onError = function(e) + if (result == null) + result = data; + }; + + h.onError = function(msg) { - throw e; - } + error = new HttpError(msg, url); + }; h.request(false); - return r; + + if (error != null) + throw error; + + if (result == null) + throw new HttpError("Unknown error or empty byte response", url); + + return result; + } + + public static function hasInternet():Bool + { + try { + var r = requestText("https://www.google.com/"); + return true; + } catch (e:HttpError) { + Logs.trace('[HttpUtil.hasInternet] Failed: ${e.toString()}', WARNING); + return false; + } } private static function isRedirect(status:Int):Bool @@ -69,3 +117,32 @@ final class HttpUtil return false; } } + +private class HttpError { + public var message:String; + public var url:String; + public var status:Int; + public var redirected:Bool; + + public function new(message:String, url:String, ?status:Int = -1, ?redirected:Bool = false) { + this.message = message; + this.url = url; + this.status = status; + this.redirected = redirected; + } + + public function toString():String { + var parts:Array = ['[HttpError]']; + + if (status != -1) + parts.push('Status: $status'); + + if (redirected) + parts.push('(Redirected)'); + + parts.push('URL: $url'); + parts.push('Message: $message'); + + return parts.join(' | '); + } +} diff --git a/source/funkin/editors/character/CharacterAnimButton.hx b/source/funkin/editors/character/CharacterAnimButton.hx index 065a0d85..80dbb480 100644 --- a/source/funkin/editors/character/CharacterAnimButton.hx +++ b/source/funkin/editors/character/CharacterAnimButton.hx @@ -367,18 +367,39 @@ class CharacterAnimButton extends UIButton { var animData:AnimData = parent.character.animDatas[anim]; var oldIndices:Array = animData.indices.copy(); + var maxFrameIndex = parent.character.frames.frames.length - 1; + for (index in indicies) { + if (index < 0 || index > maxFrameIndex) { + Logs.trace('[changeIndicies] Invalid frame index $index (max allowed: $maxFrameIndex). Change aborted.', ERROR, RED); + return; + } + } + animData.indices = indicies; - __refreshAnimation(); + try { + __refreshAnimation(); + + if (parent.character.getAnimName() == anim) + CharacterEditor.instance.playAnimation(anim); + } catch (e:Dynamic) { + Logs.trace('[changeIndicies] Error occurred: $e. Reverting to old indices.', WARNING, YELLOW); + + animData.indices = oldIndices; + __refreshAnimation(); + if (parent.character.getAnimName() == anim) + CharacterEditor.instance.playAnimation(anim); + + return; + } - if (parent.character.getAnimName() == anim) - CharacterEditor.instance.playAnimation(anim); - indicesTextBox.label.text = animData.indices.join(", "); - if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditIndices(ID, oldIndices, indicies)); + if (addToUndo) + CharacterEditor.undos.addToUndo(CAnimEditIndices(ID, oldIndices, indicies)); } + public inline function refreshFlxAnimationFrames(flxAnimation:FlxAnimation, animData:AnimData) @:privateAccess { try { if (animData.indices.length > 0) {