New crash handler + Additional null safety for ChartEditorState (#130)

* A bunch of smaller syntax tweaks.

* New crash handler catches and logs critical errors!

* Chart editor now has null safety enabled.

* Fix -W build issue.

* Actually update hmm.json to use the crash handling branch

* Fix issues causing crash handler to trigger
This commit is contained in:
Eric 2023-08-28 15:03:29 -04:00 committed by GitHub
parent 74dac9d830
commit 21f44edf1d
43 changed files with 629 additions and 345 deletions

View File

@ -20,6 +20,7 @@
<!--Mobile-specific--> <!--Mobile-specific-->
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false" /> <window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false" />
<!-- _____________________________ Path Settings ____________________________ --> <!-- _____________________________ Path Settings ____________________________ -->
<set name="BUILD_DIR" value="export/debug" if="debug" /> <set name="BUILD_DIR" value="export/debug" if="debug" />
<set name="BUILD_DIR" value="export/release" unless="debug" /> <set name="BUILD_DIR" value="export/release" unless="debug" />
<set name="BUILD_DIR" value="export/32bit" if="32bit" /> <set name="BUILD_DIR" value="export/32bit" if="32bit" />
@ -96,8 +97,9 @@
<haxelib name="lime" /> <!-- Game engine backend --> <haxelib name="lime" /> <!-- Game engine backend -->
<haxelib name="openfl" /> <!-- Game engine backend --> <haxelib name="openfl" /> <!-- Game engine backend -->
<haxelib name="flixel" /> <!-- Game engine --> <haxelib name="flixel" /> <!-- Game engine -->
<haxedev set="webgl" /> <haxedev set="webgl" />
<!--In case you want to use the addons package-->
<haxelib name="flixel-addons" /> <!-- Additional utilities for Flixel --> <haxelib name="flixel-addons" /> <!-- Additional utilities for Flixel -->
<haxelib name="hscript" /> <!-- Scripting --> <haxelib name="hscript" /> <!-- Scripting -->
<haxelib name="flixel-ui" /> <!-- UI framework (deprecate this? --> <haxelib name="flixel-ui" /> <!-- UI framework (deprecate this? -->
@ -150,8 +152,11 @@
<haxeflag name="--macro" value="include('flixel', true, [ 'flixel.addons.editors.spine.*', 'flixel.addons.nape.*', 'flixel.system.macros.*' ])" /> <haxeflag name="--macro" value="include('flixel', true, [ 'flixel.addons.editors.spine.*', 'flixel.addons.nape.*', 'flixel.system.macros.*' ])" />
<!-- Necessary to provide stack traces for HScript. --> <!-- Necessary to provide stack traces for HScript. -->
<haxedef name="hscriptPos" /> <haxedef name="hscriptPos" />
<haxedef name="safeMode"/>
<haxedef name="HXCPP_CHECK_POINTER" /> <haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" /> <haxedef name="HXCPP_STACK_LINE" />
<haxedef name="HXCPP_STACK_TRACE" />
<haxedef name="openfl-enable-handle-error" />
<!-- This macro allows addition of new functionality to existing Flixel. --> <!-- This macro allows addition of new functionality to existing Flixel. -->
<haxeflag name="--macro" value="addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" /> <haxeflag name="--macro" value="addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" />
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)--> <!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
@ -159,7 +164,6 @@
<icon path="art/icon32.png" size="32" /> <icon path="art/icon32.png" size="32" />
<icon path="art/icon64.png" size="64" /> <icon path="art/icon64.png" size="64" />
<icon path="art/iconOG.png" /> <icon path="art/iconOG.png" />
<!-- <haxedef name="SKIP_TO_PLAYSTATE" if="debug" /> -->
<haxedef name="CAN_OPEN_LINKS" unless="switch" /> <haxedef name="CAN_OPEN_LINKS" unless="switch" />
<haxedef name="CAN_CHEAT" if="switch debug" /> <haxedef name="CAN_CHEAT" if="switch debug" />
<haxedef name="haxeui_no_mouse_reset" /> <haxedef name="haxeui_no_mouse_reset" />
@ -172,7 +176,6 @@
<!-- Difficulty, only used for week or song, defaults to 1 --> <!-- Difficulty, only used for week or song, defaults to 1 -->
<!-- <haxedef name="dif" value="2" if="debug"/> --> <!-- <haxedef name="dif" value="2" if="debug"/> -->
</section> </section>
<!-- <haxedef name="CLEAR_INPUT_SAVE"/> -->
<section if="newgrounds"> <section if="newgrounds">
<!-- Enables Ng.core.verbose --> <!-- Enables Ng.core.verbose -->
<!-- <haxedef name="NG_VERBOSE" /> --> <!-- <haxedef name="NG_VERBOSE" /> -->
@ -182,18 +185,21 @@
<!-- <haxedef name="NG_FORCE_EXPIRED_SESSION" if="debug" /> --> <!-- <haxedef name="NG_FORCE_EXPIRED_SESSION" if="debug" /> -->
</section> </section>
<!-- Uncomment this to wipe your input settings. -->
<!-- <haxedef name="CLEAR_INPUT_SAVE"/> -->
<section if="debug" unless="NO_REDIRECT_ASSETS_FOLDER || html5"> <section if="debug" unless="NO_REDIRECT_ASSETS_FOLDER || html5">
<!-- <!--
Use the parent assets folder rather than the exported one Use the parent assets folder rather than the exported one
No more will we accidentally undo our changes! No more will we accidentally undo our changes!
TODO: Add a thing to disable this on builds meant for itch.io.
--> -->
<haxedef name="REDIRECT_ASSETS_FOLDER" /> <haxedef name="REDIRECT_ASSETS_FOLDER" />
</section> </section>
<!-- <prebuild haxe="trace('prebuilding');"/> --> <!-- Run a script before and after building. -->
<!-- <postbuild haxe="art/Postbuild.hx"/> --> <postbuild haxe="source/Prebuild.hx"/> -->
<!-- <config:ios allow-provisioning-updates="true" team-id="" /> --> <postbuild haxe="source/Postbuild.hx"/> -->
<!-- Options for Polymod --> <!-- Options for Polymod -->
<section if="polymod"> <section if="polymod">
<!-- Turns on additional debug logging. --> <!-- Turns on additional debug logging. -->
@ -213,12 +219,4 @@
<!-- Determines the file in the mod folder used for the icon. --> <!-- Determines the file in the mod folder used for the icon. -->
<haxedef name="POLYMOD_MOD_ICON_FILE" value="_polymod_icon.png" /> <haxedef name="POLYMOD_MOD_ICON_FILE" value="_polymod_icon.png" />
</section> </section>
<section if="TOOLS">
<!-- Compiles tool for old song conversion shit -->
<!-- Assumes you use it on windows/desktop!!!! -->
<postbuild command="haxe -main art/SongConverter.hx --cs export/songShit" />
<assets path="export/songShit/bin/SongConverter.exe" rename="SongConverter.exe" />
<!-- <postbuild command='ren export/songShit/bin export/songShit/tools '/> -->
<!-- <postbuild command='move export/songShit/tools export/release/windows/bin'/> -->
</section>
</project> </project>

View File

@ -47,7 +47,7 @@
"name": "haxeui-core", "name": "haxeui-core",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "3590c94858fc6dbcf9b4d522cd644ad571269677", "ref": "f5daafe93bdfa957538f199294a54e0476c805b7",
"url": "https://github.com/haxeui/haxeui-core/" "url": "https://github.com/haxeui/haxeui-core/"
}, },
{ {
@ -128,7 +128,7 @@
"name": "openfl", "name": "openfl",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "d33d489a137ff8fdece4994cf1302f0b6334ed08", "ref": "1591a6c5f1f72e65d711f7e17e8055df41424d94",
"url": "https://github.com/EliteMasterEric/openfl" "url": "https://github.com/EliteMasterEric/openfl"
}, },
{ {

View File

@ -2,12 +2,13 @@ package;
import flixel.FlxGame; import flixel.FlxGame;
import flixel.FlxState; import flixel.FlxState;
import funkin.util.logging.CrashHandler;
import funkin.MemoryCounter; import funkin.MemoryCounter;
import haxe.ui.Toolkit; import haxe.ui.Toolkit;
import openfl.Lib;
import openfl.display.FPS; import openfl.display.FPS;
import openfl.display.Sprite; import openfl.display.Sprite;
import openfl.events.Event; import openfl.events.Event;
import openfl.Lib;
import openfl.media.Video; import openfl.media.Video;
import openfl.net.NetStream; import openfl.net.NetStream;
@ -77,10 +78,18 @@ class Main extends Sprite
* -Eric * -Eric
*/ */
CrashHandler.initialize();
CrashHandler.queryStatus();
initHaxeUI(); initHaxeUI();
addChild(new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen)); addChild(new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen));
#if hxcpp_debug_server
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
#end
#if debug #if debug
fpsCounter = new FPS(10, 3, 0xFFFFFF); fpsCounter = new FPS(10, 3, 0xFFFFFF);
addChild(fpsCounter); addChild(fpsCounter);

11
source/Postbuild.hx Normal file
View File

@ -0,0 +1,11 @@
package source; // Yeah, I know...
class Postbuild
{
static function main()
{
trace('Postbuild');
// TODO: Maybe put a 'Build took X seconds' message here?
}
}

9
source/Prebuild.hx Normal file
View File

@ -0,0 +1,9 @@
package source; // Yeah, I know...
class Prebuild
{
static function main()
{
trace('Prebuild');
}
}

View File

@ -38,7 +38,7 @@ class Alphabet extends FlxSpriteGroup
var isBold:Bool = false; var isBold:Bool = false;
public function new(x:Float = 0.0, y:Float = 0.0, text:String = "", ?bold:Bool = false, typed:Bool = false) public function new(x:Float = 0.0, y:Float = 0.0, text:String = "", bold:Bool = false, typed:Bool = false)
{ {
super(x, y); super(x, y);

View File

@ -12,7 +12,6 @@ import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween; import flixel.tweens.FlxTween;
import funkin.play.PlayState; import funkin.play.PlayState;
import funkin.shaderslmfao.ScreenWipeShader; import funkin.shaderslmfao.ScreenWipeShader;
import haxe.Json;
import haxe.format.JsonParser; import haxe.format.JsonParser;
import lime.math.Rectangle; import lime.math.Rectangle;
import lime.utils.Assets; import lime.utils.Assets;
@ -120,31 +119,6 @@ class CoolUtil
FlxG.camera.setFilters([new ShaderFilter(screenWipeShit)]); FlxG.camera.setFilters([new ShaderFilter(screenWipeShit)]);
} }
/**
* Just saves the json with some default values hehe
* @param json
* @return String
*/
public static inline function jsonStringify(data:Dynamic):String
{
return Json.stringify(data, null, "\t");
}
/**
* Hashlink json encoding fix for some wacky bullshit
* https://github.com/HaxeFoundation/haxe/issues/6930#issuecomment-384570392
*/
public static function coolJSON(fileData:String)
{
var cont = fileData;
function is(n:Int, what:Int)
return cont.charCodeAt(n) == what;
return JsonParser.parse(cont.substr(if (is(0, 65279)) /// looks like a HL target, skipping only first character here:
1 else if (is(0, 239) && is(1, 187) && is(2, 191)) /// it seems to be Neko or PHP, start from position 3:
3 else /// all other targets, that prepare the UTF string correctly
0));
}
/* /*
* frame dependant lerp kinda lol * frame dependant lerp kinda lol
*/ */

View File

@ -64,7 +64,7 @@ class DiscordClient
trace("Discord Client initialized"); trace("Discord Client initialized");
} }
public static function changePresence(details:String, state:Null<String>, ?smallImageKey:String, ?hasStartTimestamp:Bool, ?endTimestamp:Float) public static function changePresence(details:String, ?state:String, ?smallImageKey:String, ?hasStartTimestamp:Bool, ?endTimestamp:Float)
{ {
var startTimestamp:Float = if (hasStartTimestamp) Date.now().getTime() else 0; var startTimestamp:Float = if (hasStartTimestamp) Date.now().getTime() else 0;

View File

@ -464,7 +464,7 @@ class FreeplayState extends MusicBeatSubState
}); });
} }
public function generateSongList(?filterStuff:SongFilter, ?force:Bool = false) public function generateSongList(?filterStuff:SongFilter, force:Bool = false)
{ {
curSelected = 0; curSelected = 0;
@ -1045,7 +1045,7 @@ class FreeplaySongData
public var songCharacter:String = ""; public var songCharacter:String = "";
public var isFav:Bool = false; public var isFav:Bool = false;
public function new(song:String, levelId:String, songCharacter:String, ?isFav:Bool = false) public function new(song:String, levelId:String, songCharacter:String, isFav:Bool = false)
{ {
this.songName = song; this.songName = song;
this.levelId = levelId; this.levelId = levelId;

View File

@ -41,7 +41,7 @@ class PauseSubState extends MusicBeatSubState
var isChartingMode:Bool; var isChartingMode:Bool;
public function new(?isChartingMode:Bool = false) public function new(isChartingMode:Bool = false)
{ {
super(); super();

View File

@ -82,7 +82,7 @@ class FlxAtlasSprite extends FlxAnimate
* @param restart Whether to restart the animation if it is already playing. * @param restart Whether to restart the animation if it is already playing.
* @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing * @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing
*/ */
public function playAnimation(id:String, ?restart:Bool = false, ?ignoreOther:Bool = false):Void public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false):Void
{ {
// Skip if not allowed to play animations. // Skip if not allowed to play animations.
if ((!canPlayOtherAnims && !ignoreOther)) return; if ((!canPlayOtherAnims && !ignoreOther)) return;

View File

@ -255,7 +255,7 @@ class GameOverSubState extends MusicBeatSubState
* Starts the death music at the appropriate volume. * Starts the death music at the appropriate volume.
* @param startingVolume * @param startingVolume
*/ */
function startDeathMusic(?startingVolume:Float = 1, ?force:Bool = false):Void function startDeathMusic(?startingVolume:Float = 1, force:Bool = false):Void
{ {
var musicPath = Paths.music('gameOver' + musicSuffix); var musicPath = Paths.music('gameOver' + musicSuffix);
if (isEnding) if (isEnding)

View File

@ -81,7 +81,7 @@ class AnimateAtlasCharacter extends BaseCharacter
super.onCreate(event); super.onCreate(event);
} }
public override function playAnimation(name:String, restart:Bool = false, ?ignoreOther:Bool = false, ?reverse:Bool = false):Void public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reverse:Bool = false):Void
{ {
if ((!canPlayOtherAnims && !ignoreOther)) return; if ((!canPlayOtherAnims && !ignoreOther)) return;

View File

@ -570,7 +570,7 @@ class BaseCharacter extends Bopper
* @param miss If true, play the miss animation instead of the sing animation. * @param miss If true, play the miss animation instead of the sing animation.
* @param suffix A suffix to append to the animation name, like `alt`. * @param suffix A suffix to append to the animation name, like `alt`.
*/ */
public function playSingAnimation(dir:NoteDirection, ?miss:Bool = false, ?suffix:String = ''):Void public function playSingAnimation(dir:NoteDirection, miss:Bool = false, ?suffix:String = ''):Void
{ {
var anim:String = 'sing${dir.nameUpper}${miss ? 'miss' : ''}${suffix != '' ? '-${suffix}' : ''}'; var anim:String = 'sing${dir.nameUpper}${miss ? 'miss' : ''}${suffix != '' ? '-${suffix}' : ''}';
@ -578,7 +578,7 @@ class BaseCharacter extends Bopper
playAnimation(anim, true); playAnimation(anim, true);
} }
public override function playAnimation(name:String, restart:Bool = false, ?ignoreOther:Bool = false, ?reversed:Bool = false):Void public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void
{ {
FlxG.watch.addQuick('playAnim(${characterName})', name); FlxG.watch.addQuick('playAnim(${characterName})', name);
// trace('playAnim(${characterName}): ${name}'); // trace('playAnim(${characterName}): ${name}');

View File

@ -190,7 +190,7 @@ class CharacterDataParser
* @param charId The character ID to fetch. * @param charId The character ID to fetch.
* @return The character instance, or null if the character was not found. * @return The character instance, or null if the character was not found.
*/ */
public static function fetchCharacter(charId:String, ?debug:Bool = false):Null<BaseCharacter> public static function fetchCharacter(charId:String, debug:Bool = false):Null<BaseCharacter>
{ {
if (charId == null || charId == '' || !characterCache.exists(charId)) if (charId == null || charId == '' || !characterCache.exists(charId))
{ {

View File

@ -181,7 +181,7 @@ class MultiSparrowCharacter extends BaseCharacter
trace('[MULTISPARROWCHAR] Successfully loaded ${animNames.length} animations for ${characterId}'); trace('[MULTISPARROWCHAR] Successfully loaded ${animNames.length} animations for ${characterId}');
} }
public override function playAnimation(name:String, restart:Bool = false, ?ignoreOther:Bool = false, ?reverse:Bool = false):Void public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reverse:Bool = false):Void
{ {
// Make sure we ignore other animations if we're currently playing a forced one, // Make sure we ignore other animations if we're currently playing a forced one,
// unless we're forcing a new animation. // unless we're forcing a new animation.

View File

@ -208,7 +208,7 @@ class OutroData
public var type:OutroType; public var type:OutroType;
public var data:Dynamic; public var data:Dynamic;
public function new(typeStr:Null<String>, data:Dynamic) public function new(?typeStr:String, data:Dynamic)
{ {
this.type = typeStr ?? OutroType.NONE; this.type = typeStr ?? OutroType.NONE;
this.data = data; this.data = data;

View File

@ -172,7 +172,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass
/** /**
* Set the sprite scale to the appropriate value. * Set the sprite scale to the appropriate value.
* @param scale * @param scale
*/ */
public function setScale(scale:Null<Float>):Void public function setScale(scale:Null<Float>):Void
{ {
@ -218,7 +218,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass
* @param name The name of the current animation. * @param name The name of the current animation.
* @param frameNumber The number of the current frame. * @param frameNumber The number of the current frame.
* @param frameIndex The index of the current frame. * @param frameIndex The index of the current frame.
* *
* For example, if an animation was defined as having the indexes [3, 0, 1, 2], * For example, if an animation was defined as having the indexes [3, 0, 1, 2],
* then the first callback would have frameNumber = 0 and frameIndex = 3. * then the first callback would have frameNumber = 0 and frameIndex = 3.
*/ */
@ -253,7 +253,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass
* @param restart Whether to restart the animation if it is already playing. * @param restart Whether to restart the animation if it is already playing.
* @param reversed If true, play the animation backwards, from the last frame to the first. * @param reversed If true, play the animation backwards, from the last frame to the first.
*/ */
public function playAnimation(name:String, restart:Bool = false, ?reversed:Bool = false):Void public function playAnimation(name:String, restart:Bool = false, reversed:Bool = false):Void
{ {
var correctName:String = correctAnimationName(name); var correctName:String = correctAnimationName(name);
if (correctName == null) return; if (correctName == null) return;
@ -266,7 +266,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass
/** /**
* Ensure that a given animation exists before playing it. * Ensure that a given animation exists before playing it.
* Will gracefully check for name, then name with stripped suffixes, then 'idle', then fail to play. * Will gracefully check for name, then name with stripped suffixes, then 'idle', then fail to play.
* @param name * @param name
*/ */
function correctAnimationName(name:String):String function correctAnimationName(name:String):String
{ {

View File

@ -93,7 +93,7 @@ class DialogueBoxTextData
public var shadowColor:Null<String>; public var shadowColor:Null<String>;
public var shadowWidth:Null<Int>; public var shadowWidth:Null<Int>;
public function new(offsets:Null<Array<Float>>, width:Null<Int>, size:Null<Int>, color:String, shadowColor:Null<String>, shadowWidth:Null<Int>) public function new(offsets:Null<Array<Float>>, ?width:Int, ?size:Int, color:String, ?shadowColor:String, shadowWidth:Null<Int>)
{ {
this.offsets = offsets ?? [0, 0]; this.offsets = offsets ?? [0, 0];
this.width = width ?? 300; this.width = width ?? 300;

View File

@ -19,8 +19,8 @@ class SpeakerData
public var scale:Float; public var scale:Float;
public var animations:Array<AnimationData>; public var animations:Array<AnimationData>;
public function new(version:String, name:String, assetPath:String, animations:Array<AnimationData>, ?offsets:Array<Float>, ?flipX:Bool = false, public function new(version:String, name:String, assetPath:String, animations:Array<AnimationData>, ?offsets:Array<Float>, flipX:Bool = false,
?isPixel:Bool = false, ?scale:Float = 1.0) isPixel:Bool = false, ?scale:Float = 1.0)
{ {
this.version = version; this.version = version;
this.name = name; this.name = name;

View File

@ -271,7 +271,7 @@ class Strumline extends FlxSpriteGroup
* @param strumTime * @param strumTime
* @return Float * @return Float
*/ */
static function calculateNoteYPos(strumTime:Float, ?vwoosh:Bool = true):Float static function calculateNoteYPos(strumTime:Float, vwoosh:Bool = true):Float
{ {
// Make the note move faster visually as it moves offscreen. // Make the note move faster visually as it moves offscreen.
var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 1.0; var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 1.0;

View File

@ -113,7 +113,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
return noteFrames; return noteFrames;
} }
function getNoteAssetPath(?raw:Bool = false):String function getNoteAssetPath(raw:Bool = false):String
{ {
if (raw) if (raw)
{ {
@ -161,7 +161,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
return (result == null) ? fallback.fetchNoteAnimationData(dir) : result; return (result == null) ? fallback.fetchNoteAnimationData(dir) : result;
} }
public function getHoldNoteAssetPath(?raw:Bool = false):String public function getHoldNoteAssetPath(raw:Bool = false):String
{ {
if (raw) if (raw)
{ {
@ -209,7 +209,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
target.antialiasing = !_data.assets.noteStrumline.isPixel; target.antialiasing = !_data.assets.noteStrumline.isPixel;
} }
function getStrumlineAssetPath(?raw:Bool = false):String function getStrumlineAssetPath(raw:Bool = false):String
{ {
if (raw) if (raw)
{ {

View File

@ -72,7 +72,7 @@ class Song implements IPlayStateScriptedClass
@:allow(funkin.play.song.Song) @:allow(funkin.play.song.Song)
public static function buildRaw(songId:String, metadata:Array<SongMetadata>, variations:Array<String>, charts:Map<String, SongChartData>, public static function buildRaw(songId:String, metadata:Array<SongMetadata>, variations:Array<String>, charts:Map<String, SongChartData>,
?validScore:Bool = false):Song validScore:Bool = false):Song
{ {
var result:Song = new Song(songId, true); var result:Song = new Song(songId, true);
@ -150,7 +150,7 @@ class Song implements IPlayStateScriptedClass
/** /**
* Parse and cache the chart for all difficulties of this song. * Parse and cache the chart for all difficulties of this song.
*/ */
public function cacheCharts(?force:Bool = false):Void public function cacheCharts(force:Bool = false):Void
{ {
if (force) if (force)
{ {

View File

@ -920,7 +920,7 @@ typedef RawSongTimeChange =
*/ */
abstract SongTimeChange(RawSongTimeChange) from RawSongTimeChange abstract SongTimeChange(RawSongTimeChange) from RawSongTimeChange
{ {
public function new(timeStamp:Float, beatTime:Null<Float>, bpm:Float, timeSignatureNum:Int = 4, timeSignatureDen:Int = 4, beatTuplets:Array<Int>) public function new(timeStamp:Float, ?beatTime:Float, bpm:Float, timeSignatureNum:Int = 4, timeSignatureDen:Int = 4, beatTuplets:Array<Int>)
{ {
this = this =
{ {

View File

@ -123,7 +123,7 @@ class SongDataUtils
/** /**
* Sort an array of notes by strum time. * Sort an array of notes by strum time.
*/ */
public static function sortNotes(notes:Array<SongNoteData>, ?desc:Bool = false):Array<SongNoteData> public static function sortNotes(notes:Array<SongNoteData>, desc:Bool = false):Array<SongNoteData>
{ {
// TODO: Modifies the array in place. Is this okay? // TODO: Modifies the array in place. Is this okay?
notes.sort(function(a:SongNoteData, b:SongNoteData):Int { notes.sort(function(a:SongNoteData, b:SongNoteData):Int {
@ -135,7 +135,7 @@ class SongDataUtils
/** /**
* Sort an array of events by strum time. * Sort an array of events by strum time.
*/ */
public static function sortEvents(events:Array<SongEventData>, ?desc:Bool = false):Array<SongEventData> public static function sortEvents(events:Array<SongEventData>, desc:Bool = false):Array<SongEventData>
{ {
// TODO: Modifies the array in place. Is this okay? // TODO: Modifies the array in place. Is this okay?
events.sort(function(a:SongEventData, b:SongEventData):Int { events.sort(function(a:SongEventData, b:SongEventData):Int {

View File

@ -63,9 +63,15 @@ class SongValidator
} }
input.timeChanges = validateTimeChanges(input.timeChanges, songId); input.timeChanges = validateTimeChanges(input.timeChanges, songId);
if (input.timeChanges == null)
{
trace('[SONGDATA] Song ${songId} is missing a timeChanges field. ');
return null;
}
input.playData = validatePlayData(input.playData, songId); input.playData = validatePlayData(input.playData, songId);
input.variation = ''; if (input.variation == null) input.variation = '';
return input; return input;
} }
@ -79,6 +85,12 @@ class SongValidator
*/ */
public static function validatePlayData(input:SongPlayData, songId:String = 'unknown'):SongPlayData public static function validatePlayData(input:SongPlayData, songId:String = 'unknown'):SongPlayData
{ {
if (input == null)
{
trace('[SONGDATA] Could not parse metadata.playData for song ${songId}');
return null;
}
return input; return input;
} }
@ -91,6 +103,12 @@ class SongValidator
*/ */
public static function validateTimeChange(input:SongTimeChange, songId:String = 'unknown'):SongTimeChange public static function validateTimeChange(input:SongTimeChange, songId:String = 'unknown'):SongTimeChange
{ {
if (input == null)
{
trace('[SONGDATA] Could not parse metadata.timeChange for song ${songId}');
return null;
}
return input; return input;
} }
@ -101,8 +119,8 @@ class SongValidator
{ {
if (input == null) if (input == null)
{ {
trace('[SONGDATA] Song ${songId} is missing a timeChanges field. '); trace('[SONGDATA] Could not parse metadata.timeChange for song ${songId}');
return []; return null;
} }
input = input.map((timeChange) -> validateTimeChange(timeChange, songId)); input = input.map((timeChange) -> validateTimeChange(timeChange, songId));

View File

@ -268,7 +268,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
* @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing * @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing
* @param reversed If true, play the animation backwards, from the last frame to the first. * @param reversed If true, play the animation backwards, from the last frame to the first.
*/ */
public function playAnimation(name:String, restart:Bool = false, ?ignoreOther:Bool = false, ?reversed:Bool = false):Void public function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void
{ {
if (!canPlayOtherAnims && !ignoreOther) return; if (!canPlayOtherAnims && !ignoreOther) return;

View File

@ -450,7 +450,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
* @param pop If true, the character will be removed from the stage as well. * @param pop If true, the character will be removed from the stage as well.
* @return The Boyfriend character. * @return The Boyfriend character.
*/ */
public function getBoyfriend(?pop:Bool = false):BaseCharacter public function getBoyfriend(pop:Bool = false):BaseCharacter
{ {
if (pop) if (pop)
{ {
@ -473,7 +473,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
* @param pop If true, the character will be removed from the stage as well. * @param pop If true, the character will be removed from the stage as well.
* @return The player/Boyfriend character. * @return The player/Boyfriend character.
*/ */
public function getPlayer(?pop:Bool = false):BaseCharacter public function getPlayer(pop:Bool = false):BaseCharacter
{ {
return getBoyfriend(pop); return getBoyfriend(pop);
} }
@ -483,7 +483,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
* @param pop If true, the character will be removed from the stage as well. * @param pop If true, the character will be removed from the stage as well.
* @return The Girlfriend character. * @return The Girlfriend character.
*/ */
public function getGirlfriend(?pop:Bool = false):BaseCharacter public function getGirlfriend(pop:Bool = false):BaseCharacter
{ {
if (pop) if (pop)
{ {
@ -506,7 +506,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
* @param pop If true, the character will be removed from the stage as well. * @param pop If true, the character will be removed from the stage as well.
* @return The Dad character. * @return The Dad character.
*/ */
public function getDad(?pop:Bool = false):BaseCharacter public function getDad(pop:Bool = false):BaseCharacter
{ {
if (pop) if (pop)
{ {
@ -529,7 +529,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
* @param pop If true, the character will be removed from the stage as well. * @param pop If true, the character will be removed from the stage as well.
* @return The opponent character. * @return The opponent character.
*/ */
public function getOpponent(?pop:Bool = false):BaseCharacter public function getOpponent(pop:Bool = false):BaseCharacter
{ {
return getDad(pop); return getDad(pop);
} }

View File

@ -503,7 +503,7 @@ typedef StageDataCharacter =
* Again, just like CSS. * Again, just like CSS.
* @default 0 * @default 0
*/ */
zIndex:Null<Int>, ?zIndex:Int,
/** /**
* The position to render the character at. * The position to render the character at.

View File

@ -225,7 +225,7 @@ class AddEventsCommand implements ChartEditorCommand
var events:Array<SongEventData>; var events:Array<SongEventData>;
var appendToSelection:Bool; var appendToSelection:Bool;
public function new(events:Array<SongEventData>, ?appendToSelection:Bool = false) public function new(events:Array<SongEventData>, appendToSelection:Bool = false)
{ {
this.events = events; this.events = events;
this.appendToSelection = appendToSelection; this.appendToSelection = appendToSelection;

View File

@ -258,7 +258,7 @@ class ChartEditorDialogHandler
* @return The dialog that was opened. * @return The dialog that was opened.
*/ */
@:haxe.warning("-WVarInit") @:haxe.warning("-WVarInit")
public static function openUploadInstDialog(state:ChartEditorState, ?closable:Bool = true):Dialog public static function openUploadInstDialog(state:ChartEditorState, closable:Bool = true):Dialog
{ {
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_UPLOAD_INST_LAYOUT, true, closable); var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_UPLOAD_INST_LAYOUT, true, closable);
@ -578,7 +578,7 @@ class ChartEditorDialogHandler
* @param closable Whether the dialog can be closed by the user. * @param closable Whether the dialog can be closed by the user.
* @return The dialog that was opened. * @return The dialog that was opened.
*/ */
public static function openUploadVocalsDialog(state:ChartEditorState, ?closable:Bool = true):Dialog public static function openUploadVocalsDialog(state:ChartEditorState, closable:Bool = true):Dialog
{ {
var charIdsForVocals:Array<String> = []; var charIdsForVocals:Array<String> = [];
@ -692,7 +692,7 @@ class ChartEditorDialogHandler
* @return The dialog that was opened. * @return The dialog that was opened.
*/ */
@:haxe.warning('-WVarInit') @:haxe.warning('-WVarInit')
public static function openChartDialog(state:ChartEditorState, ?closable:Bool = true):Dialog public static function openChartDialog(state:ChartEditorState, closable:Bool = true):Dialog
{ {
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_OPEN_CHART_LAYOUT, true, closable); var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_OPEN_CHART_LAYOUT, true, closable);
@ -765,6 +765,19 @@ class ChartEditorDialogHandler
var songMetadataVariation:SongMetadata = SongMigrator.migrateSongMetadata(songMetadataJson, 'import'); var songMetadataVariation:SongMetadata = SongMigrator.migrateSongMetadata(songMetadataJson, 'import');
songMetadataVariation = SongValidator.validateSongMetadata(songMetadataVariation, 'import'); songMetadataVariation = SongValidator.validateSongMetadata(songMetadataVariation, 'import');
if (songMetadataVariation == null)
{
// Tell the user the load was not successful.
NotificationManager.instance.addNotification(
{
title: 'Failure',
body: 'Could not load metadata file (${path.file}.${path.ext})',
type: NotificationType.Error,
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
});
return;
}
songMetadata.set(variation, songMetadataVariation); songMetadata.set(variation, songMetadataVariation);
// Tell the user the load was successful. // Tell the user the load was successful.
@ -879,7 +892,7 @@ class ChartEditorDialogHandler
* @param closable * @param closable
* @return Dialog * @return Dialog
*/ */
public static function openImportChartDialog(state:ChartEditorState, format:String, ?closable:Bool = true):Dialog public static function openImportChartDialog(state:ChartEditorState, format:String, closable:Bool = true):Dialog
{ {
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_IMPORT_CHART_LAYOUT, true, closable); var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_IMPORT_CHART_LAYOUT, true, closable);

View File

@ -26,12 +26,12 @@ class ChartEditorEventSprite extends FlxSprite
* The note data that this sprite represents. * The note data that this sprite represents.
* You can set this to null to kill the sprite and flag it for recycling. * You can set this to null to kill the sprite and flag it for recycling.
*/ */
public var eventData(default, set):SongEventData; public var eventData(default, set):Null<SongEventData> = null;
/** /**
* The image used for all song events. Cached for performance. * The image used for all song events. Cached for performance.
*/ */
static var eventSpriteBasic:BitmapData; static var eventSpriteBasic:Null<BitmapData> = null;
public function new(parent:ChartEditorState) public function new(parent:ChartEditorState)
{ {
@ -49,7 +49,7 @@ class ChartEditorEventSprite extends FlxSprite
* Build a set of animations to allow displaying different types of chart events. * Build a set of animations to allow displaying different types of chart events.
* @param force `true` to force rebuilding the frames. * @param force `true` to force rebuilding the frames.
*/ */
static function buildFrames(?force:Bool = false):FlxFramesCollection static function buildFrames(force:Bool = false):FlxFramesCollection
{ {
static var eventFrames:FlxFramesCollection = null; static var eventFrames:FlxFramesCollection = null;
@ -112,7 +112,7 @@ class ChartEditorEventSprite extends FlxSprite
this.updateHitbox(); this.updateHitbox();
} }
function set_eventData(value:SongEventData):SongEventData function set_eventData(value:Null<SongEventData>):Null<SongEventData>
{ {
this.eventData = value; this.eventData = value;

View File

@ -27,7 +27,7 @@ class ChartEditorNoteSprite extends FlxSprite
* The note data that this sprite represents. * The note data that this sprite represents.
* You can set this to null to kill the sprite and flag it for recycling. * You can set this to null to kill the sprite and flag it for recycling.
*/ */
public var noteData(default, set):SongNoteData; public var noteData(default, set):Null<SongNoteData>;
/** /**
* The name of the note style currently in use. * The name of the note style currently in use.
@ -70,7 +70,7 @@ class ChartEditorNoteSprite extends FlxSprite
this.animation.addByPrefix('tapRightPixel', 'pixel7'); this.animation.addByPrefix('tapRightPixel', 'pixel7');
} }
static var noteFrameCollection:FlxFramesCollection = null; static var noteFrameCollection:Null<FlxFramesCollection> = null;
/** /**
* We load all the note frames once, then reuse them. * We load all the note frames once, then reuse them.
@ -108,7 +108,7 @@ class ChartEditorNoteSprite extends FlxSprite
} }
} }
function set_noteData(value:SongNoteData):SongNoteData function set_noteData(value:Null<SongNoteData>):Null<SongNoteData>
{ {
this.noteData = value; this.noteData = value;

File diff suppressed because it is too large Load Diff

View File

@ -168,9 +168,9 @@ class ChartEditorToolboxHandler
case ChartEditorState.CHART_EDITOR_TOOLBOX_CHARACTERS_LAYOUT: case ChartEditorState.CHART_EDITOR_TOOLBOX_CHARACTERS_LAYOUT:
toolbox = buildToolboxCharactersLayout(state); toolbox = buildToolboxCharactersLayout(state);
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT: case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
toolbox = null; // buildToolboxPlayerPreviewLayout(state); toolbox = buildToolboxPlayerPreviewLayout(state);
case ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT: case ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:
toolbox = null; // buildToolboxOpponentPreviewLayout(state); toolbox = buildToolboxOpponentPreviewLayout(state);
default: default:
// This happens if you try to load an unknown layout. // This happens if you try to load an unknown layout.
trace('ChartEditorToolboxHandler.initToolbox() - Unknown toolbox ID: $id'); trace('ChartEditorToolboxHandler.initToolbox() - Unknown toolbox ID: $id');
@ -200,6 +200,8 @@ class ChartEditorToolboxHandler
// Initialize the toolbox without showing it. // Initialize the toolbox without showing it.
if (toolbox == null) toolbox = initToolbox(state, id); if (toolbox == null) toolbox = initToolbox(state, id);
if (toolbox == null) throw 'ChartEditorToolboxHandler.getToolbox() - Could not retrieve or build toolbox: $id';
return toolbox; return toolbox;
} }

View File

@ -32,7 +32,7 @@ class CharacterPlayer extends Box
{ {
var character:BaseCharacter; var character:BaseCharacter;
public function new(?defaultToBf:Bool = true) public function new(defaultToBf:Bool = true)
{ {
super(); super();
_overrideSkipTransformChildren = false; _overrideSkipTransformChildren = false;

View File

@ -21,7 +21,7 @@ class MovePropCommand implements StageEditorCommand
var yDiff:Float; var yDiff:Float;
var realMove:Bool; // if needs a move! var realMove:Bool; // if needs a move!
public function new(xDiff:Float = 0, yDiff:Float = 0, ?realMove:Bool = true) public function new(xDiff:Float = 0, yDiff:Float = 0, realMove:Bool = true)
{ {
this.xDiff = xDiff; this.xDiff = xDiff;
this.yDiff = yDiff; this.yDiff = yDiff;

View File

@ -3,17 +3,18 @@ package funkin.ui.stageBuildShit;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.input.mouse.FlxMouseEvent; import flixel.input.mouse.FlxMouseEvent;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
import funkin.play.PlayState;
import funkin.play.character.BaseCharacter; import funkin.play.character.BaseCharacter;
import funkin.play.PlayState;
import funkin.play.stage.StageData; import funkin.play.stage.StageData;
import funkin.play.stage.StageProp; import funkin.play.stage.StageProp;
import funkin.shaderslmfao.StrokeShader; import funkin.shaderslmfao.StrokeShader;
import funkin.ui.haxeui.HaxeUISubState; import funkin.ui.haxeui.HaxeUISubState;
import funkin.ui.stageBuildShit.StageEditorCommand; import funkin.ui.stageBuildShit.StageEditorCommand;
import haxe.ui.RuntimeComponentBuilder; import funkin.util.SerializerUtil;
import haxe.ui.containers.ListView; import haxe.ui.containers.ListView;
import haxe.ui.core.Component; import haxe.ui.core.Component;
import haxe.ui.events.UIEvent; import haxe.ui.events.UIEvent;
import haxe.ui.RuntimeComponentBuilder;
import openfl.events.Event; import openfl.events.Event;
import openfl.events.IOErrorEvent; import openfl.events.IOErrorEvent;
import openfl.net.FileReference; import openfl.net.FileReference;
@ -376,6 +377,6 @@ class StageOffsetSubState extends HaxeUISubState
stageLol.characters.gf.position[0] = Std.int(GF_FEET_SNIIIIIIIIIIIIIFFFF.x); stageLol.characters.gf.position[0] = Std.int(GF_FEET_SNIIIIIIIIIIIIIFFFF.x);
stageLol.characters.gf.position[1] = Std.int(GF_FEET_SNIIIIIIIIIIIIIFFFF.y); stageLol.characters.gf.position[1] = Std.int(GF_FEET_SNIIIIIIIIIIIIIFFFF.y);
return CoolUtil.jsonStringify(stageLol); return SerializerUtil.toJSON(stageLol);
} }
} }

View File

@ -14,7 +14,7 @@ class ClipboardUtil
* @param once If true, the callback will only execute once and then be deleted. * @param once If true, the callback will only execute once and then be deleted.
* @param priority Set the priority at which the callback will be executed. Higher values execute first. * @param priority Set the priority at which the callback will be executed. Higher values execute first.
*/ */
public static function addListener(callback:Void->Void, ?once:Bool = false, ?priority:Int = 0):Void public static function addListener(callback:Void->Void, once:Bool = false, ?priority:Int = 0):Void
{ {
lime.system.Clipboard.onUpdate.add(callback, once, priority); lime.system.Clipboard.onUpdate.add(callback, once, priority);
} }

View File

@ -209,7 +209,7 @@ class FileUtil
* @return Whether the file dialog was opened successfully. * @return Whether the file dialog was opened successfully.
*/ */
public static function saveMultipleFiles(resources:Array<Entry>, ?onSaveAll:Array<String>->Void, ?onCancel:Void->Void, ?defaultPath:String, public static function saveMultipleFiles(resources:Array<Entry>, ?onSaveAll:Array<String>->Void, ?onCancel:Void->Void, ?defaultPath:String,
?force:Bool = false):Bool force:Bool = false):Bool
{ {
#if desktop #if desktop
// Prompt the user for a directory, then write all of the files to there. // Prompt the user for a directory, then write all of the files to there.
@ -257,7 +257,7 @@ class FileUtil
* Takes an array of file entries and prompts the user to save them as a ZIP file. * Takes an array of file entries and prompts the user to save them as a ZIP file.
*/ */
public static function saveFilesAsZIP(resources:Array<Entry>, ?onSave:Array<String>->Void, ?onCancel:Void->Void, ?defaultPath:String, public static function saveFilesAsZIP(resources:Array<Entry>, ?onSave:Array<String>->Void, ?onCancel:Void->Void, ?defaultPath:String,
?force:Bool = false):Bool force:Bool = false):Bool
{ {
// Create a ZIP file. // Create a ZIP file.
var zipBytes:Bytes = createZIPFromEntries(resources); var zipBytes:Bytes = createZIPFromEntries(resources);
@ -278,7 +278,7 @@ class FileUtil
* Use `saveFilesAsZIP` instead. * Use `saveFilesAsZIP` instead.
* @param force Whether to force overwrite an existing file. * @param force Whether to force overwrite an existing file.
*/ */
public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, ?force:Bool = false):Bool public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, force:Bool = false):Bool
{ {
#if desktop #if desktop
// Create a ZIP file. // Create a ZIP file.

View File

@ -21,7 +21,7 @@ class SerializerUtil
/** /**
* Convert a Haxe object to a JSON string. * Convert a Haxe object to a JSON string.
*/ */
public static function toJSON(input:Dynamic, ?pretty:Bool = true):String public static function toJSON(input:Dynamic, pretty:Bool = true):String
{ {
return Json.stringify(input, replacer, pretty ? INDENT_CHAR : null); return Json.stringify(input, replacer, pretty ? INDENT_CHAR : null);
} }

View File

@ -0,0 +1,143 @@
package funkin.util.logging;
import openfl.Lib;
import openfl.events.UncaughtErrorEvent;
/**
* A custom crash handler that writes to a log file and displays a message box.
*/
@:nullSafety
class CrashHandler
{
static final LOG_FOLDER = 'logs';
/**
* Initializes
*/
public static function initialize():Void
{
trace('[LOG] Enabling standard uncaught error handler...');
Lib.current.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtError);
#if cpp
trace('[LOG] Enabling C++ critical error handler...');
untyped __global__.__hxcpp_set_critical_error_handler(onCriticalError);
#end
}
/**
* Called when an uncaught error occurs.
* This handles most thrown errors, and is sufficient to handle everything alone on HTML5.
* @param error Information on the error that was thrown.
*/
static function onUncaughtError(error:UncaughtErrorEvent):Void
{
try
{
#if sys
logError(error);
#end
displayError(error);
}
catch (e:Dynamic)
{
trace('Error while handling crash: ' + e);
}
}
static function onCriticalError(message:String):Void
{
try
{
#if sys
logErrorMessage(message, true);
#end
displayErrorMessage(message);
}
catch (e:Dynamic)
{
trace('Error while handling crash: $e');
trace('Message: $message');
}
}
static function displayError(error:UncaughtErrorEvent):Void
{
displayErrorMessage(generateErrorMessage(error));
}
static function displayErrorMessage(message:String):Void
{
lime.app.Application.current.window.alert(message, "Fatal Uncaught Exception");
}
#if sys
static function logError(error:UncaughtErrorEvent):Void
{
logErrorMessage(generateErrorMessage(error));
}
static function logErrorMessage(message:String, critical:Bool = false):Void
{
FileUtil.createDirIfNotExists(LOG_FOLDER);
sys.io.File.saveContent('$LOG_FOLDER/crash${critical ? '-critical' : ''}-${DateUtil.generateTimestamp()}.log', message);
}
#end
static function generateErrorMessage(error:UncaughtErrorEvent):String
{
var errorMessage:String = "";
var callStack:Array<haxe.CallStack.StackItem> = haxe.CallStack.exceptionStack(true);
errorMessage += '${error.error}\n';
for (stackItem in callStack)
{
switch (stackItem)
{
case FilePos(innerStackItem, file, line, column):
errorMessage += ' in ${file}#${line}';
if (column != null) errorMessage += ':${column}';
case CFunction:
errorMessage += '[Function] ';
case Module(m):
errorMessage += '[Module(${m})] ';
case Method(classname, method):
errorMessage += '[Function(${classname}.${method})] ';
case LocalFunction(v):
errorMessage += '[LocalFunction(${v})] ';
}
errorMessage += '\n';
}
return errorMessage;
}
public static function queryStatus():Void
{
@:privateAccess
var currentStatus = Lib.current.stage.__uncaughtErrorEvents.__enabled;
trace('ERROR HANDLER STATUS: ' + currentStatus);
#if openfl_enable_handle_error
trace('Define: openfl_enable_handle_error is enabled');
#else
trace('Define: openfl_enable_handle_error is disabled');
#end
#if openfl_disable_handle_error
trace('Define: openfl_disable_handle_error is enabled');
#else
trace('Define: openfl_disable_handle_error is disabled');
#end
}
public static function induceBasicCrash():Void
{
throw "This is an example of an uncaught exception.";
}
}

View File

@ -22,7 +22,7 @@ class ClassMacro
* @param includeSubPackages Whether to include classes located in sub-packages of the target package. * @param includeSubPackages Whether to include classes located in sub-packages of the target package.
* @return A list of classes matching the specified criteria. * @return A list of classes matching the specified criteria.
*/ */
public static macro function listClassesInPackage(targetPackage:String, ?includeSubPackages:Bool = true):ExprOf<Iterable<Class<Dynamic>>> public static macro function listClassesInPackage(targetPackage:String, includeSubPackages:Bool = true):ExprOf<Iterable<Class<Dynamic>>>
{ {
if (!onGenerateCallbackRegistered) if (!onGenerateCallbackRegistered)
{ {