From 48bad77396c4c79da6f8cdc958872e70d3fce907 Mon Sep 17 00:00:00 2001 From: Yoshman29 <81191621+YoshiCrafter29@users.noreply.github.com> Date: Fri, 30 Dec 2022 18:26:39 +0100 Subject: [PATCH] Conductor Multithreading + in-game frame drops fix --- Project.xml | 3 ++ buildnumber.txt | 2 +- source/funkin/game/PlayState.hx | 22 +++++----- source/funkin/system/Conductor.hx | 70 ++++++++++++++++++++++-------- source/funkin/system/CoolUtil.hx | 9 ++++ source/funkin/system/Main.hx | 10 +++-- source/funkin/system/ThreadUtil.hx | 32 ++++++++++++++ 7 files changed, 115 insertions(+), 33 deletions(-) create mode 100644 source/funkin/system/ThreadUtil.hx diff --git a/Project.xml b/Project.xml index 11abc4e3..ce38208f 100644 --- a/Project.xml +++ b/Project.xml @@ -111,6 +111,9 @@ Reduces compilation time at the cost of limited HScript flexibility. !--> + + + diff --git a/buildnumber.txt b/buildnumber.txt index e259f689..af27f188 100644 --- a/buildnumber.txt +++ b/buildnumber.txt @@ -1 +1 @@ -1863 \ No newline at end of file +1873 \ No newline at end of file diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 3480f9ce..00066b90 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1516,7 +1516,9 @@ class PlayState extends MusicBeatState if (event.showRating || (event.showRating == null && event.player && !note.isSustainNote)) { - var rating:FlxSprite = new FlxSprite(-40, -60); + var rating:FlxSprite = comboGroup.recycle(FlxSprite); + rating.resetSprite(-40, -60); + comboGroup.remove(rating, true); songScore += score; @@ -1525,7 +1527,9 @@ class PlayState extends MusicBeatState rating.velocity.y -= FlxG.random.int(140, 175); rating.velocity.x -= FlxG.random.int(0, 10); - var comboSpr:FlxSprite = new FlxSprite().loadAnimatedGraphic(Paths.image('${event.ratingPrefix}combo${event.ratingSuffix}')); + var comboSpr:FlxSprite = comboGroup.recycle(FlxSprite).loadAnimatedGraphic(Paths.image('${event.ratingPrefix}combo${event.ratingSuffix}')); + comboSpr.resetSprite(0, 0); + comboGroup.remove(comboSpr, true); comboSpr.acceleration.y = 600; comboSpr.velocity.y -= 150; comboSpr.velocity.x += FlxG.random.int(1, 10); @@ -1548,7 +1552,9 @@ class PlayState extends MusicBeatState { var e = separatedScore.charAt(i); - var numScore:FlxSprite = new FlxSprite((43 * i) - 90, 80).loadAnimatedGraphic(Paths.image('${event.ratingPrefix}num$e${event.ratingSuffix}')); + var numScore:FlxSprite = comboGroup.recycle(FlxSprite).loadAnimatedGraphic(Paths.image('${event.ratingPrefix}num$e${event.ratingSuffix}')); + numScore.resetSprite((43 * i) - 90, 80); + comboGroup.remove(numScore, true); numScore.antialiasing = event.numAntialiasing; numScore.scale.set(event.numScale, event.numScale); numScore.updateHitbox(); @@ -1562,8 +1568,7 @@ class PlayState extends MusicBeatState FlxTween.tween(numScore, {alpha: 0}, 0.2, { onComplete: function(tween:FlxTween) { - comboGroup.remove(numScore, true); - numScore.destroy(); + numScore.exists = false; }, startDelay: Conductor.crochet * 0.002 }); @@ -1578,11 +1583,8 @@ class PlayState extends MusicBeatState FlxTween.tween(comboSpr, {alpha: 0}, 0.2, { onComplete: function(tween:FlxTween) { - comboGroup.remove(comboSpr, true); - comboGroup.remove(rating, true); - comboSpr.destroy(); - - rating.destroy(); + rating.exists = false; + comboSpr.exists = false; }, startDelay: Conductor.crochet * 0.001 }); diff --git a/source/funkin/system/Conductor.hx b/source/funkin/system/Conductor.hx index cdbbd5c9..17e29a91 100644 --- a/source/funkin/system/Conductor.hx +++ b/source/funkin/system/Conductor.hx @@ -76,6 +76,7 @@ class Conductor @:dox(hide) public static var lastSongPos:Float = 0; @:dox(hide) public static var lastSongPosTime:Float = 0; @:dox(hide) public static var speed:Float = 0; + @:dox(hide) public static var destSpeed:Float = 0; @:dox(hide) public static var offset:Float = 0; @:dox(hide) public static var safeZoneOffset:Float = 175; // is calculated in create(), is safeFrames in milliseconds @@ -85,6 +86,14 @@ class Conductor */ public static var bpmChangeMap:Array = []; + /** + * Thread for multi-threaded audio syncing. + */ + #if ALLOW_MULTITHREADING + public static var syncThread:sys.thread.Thread; + public static var syncThreadTime:Null = null; + #end + @:dox(hide) public function new() {} public static function reset() { @@ -130,10 +139,50 @@ class Conductor } } + private static var elapsed:Float; + public static function init() { FlxG.signals.preUpdate.add(update); FlxG.signals.preStateCreate.add(onStateSwitch); reset(); + + #if ALLOW_MULTITHREADING + syncThread = ThreadUtil.createSafe(function() { + while(true) { + if (syncThreadTime == null) + syncThreadTime = Sys.time(); + + // if (FlxG.state != null && FlxG.state is MusicBeatState && cast(FlxG.state, MusicBeatState).cancelConductorUpdate) continue; + + elapsed = -(syncThreadTime - (syncThreadTime = Sys.time())); + + if (elapsed == 0) continue; + + __updateSongPos(elapsed); + } + }, true); + #end + } + + private static function __updateSongPos(elapsed:Float) { + if (FlxG.sound.music == null || !FlxG.sound.music.playing) { + speed = destSpeed = 1; + lastSongPos = FlxG.sound.music != null ? FlxG.sound.music.time : 0; + lastSongPosTime = Main.time; + return; + } + + var lastPos = lastSongPos; + if (lastSongPos != (lastSongPos = FlxG.sound.music.time)) { + // update conductor + var timeUntilUpdate = -(lastSongPosTime - (lastSongPosTime = Main.time)); + var elapsedAL = (lastSongPos - lastPos); + destSpeed = FlxMath.bound(timeUntilUpdate / elapsedAL, 0.925, 1.075); + songPosition = lastSongPos; + } else { + songPosition += elapsed * 1000 * speed; + } + speed = FlxMath.lerp(speed, destSpeed, FlxMath.bound(elapsed, 0, 1)); } private static function onStateSwitch(newState:FlxState) { @@ -141,26 +190,11 @@ class Conductor reset(); } private static function update() { - var elapsed = FlxG.elapsed; - - if (FlxG.sound.music == null || !FlxG.sound.music.playing) { - speed = 1; - lastSongPos = FlxG.sound.music != null ? FlxG.sound.music.time : 0; - lastSongPosTime = Main.time; - return; - } if (FlxG.state != null && FlxG.state is MusicBeatState && cast(FlxG.state, MusicBeatState).cancelConductorUpdate) return; - var lastPos = lastSongPos; - if (lastSongPos != (lastSongPos = FlxG.sound.music.time)) { - // update conductor - var timeUntilUpdate = -(lastSongPosTime - (lastSongPosTime = Main.time)); - var elapsedAL = (lastSongPos - lastPos); - speed = FlxMath.bound(FlxMath.lerp(speed, timeUntilUpdate / elapsedAL, FlxMath.bound(timeUntilUpdate / 1000, 0, 1)), 0.925, 1.075); - songPosition = lastSongPos; - } else { - songPosition += elapsed * 1000 * speed; - } + #if !ALLOW_MULTITHREADING + __updateSongPos(FlxG.elapsed); + #end if (bpm > 0) { // updates curbeat and stuff diff --git a/source/funkin/system/CoolUtil.hx b/source/funkin/system/CoolUtil.hx index 12411a77..aab15e70 100644 --- a/source/funkin/system/CoolUtil.hx +++ b/source/funkin/system/CoolUtil.hx @@ -342,4 +342,13 @@ class CoolUtil color1.redMultiplier = color2.redMultiplier; color1.redOffset = color2.redOffset; } + + public static function resetSprite(spr:FlxSprite, x:Float, y:Float) { + spr.reset(x, y); + spr.alpha = 1; + spr.visible = true; + spr.active = true; + spr.antialiasing = FlxSprite.defaultAntialiasing; + spr.rotOffset.set(); + } } \ No newline at end of file diff --git a/source/funkin/system/Main.hx b/source/funkin/system/Main.hx index 7236f89f..f7e01136 100644 --- a/source/funkin/system/Main.hx +++ b/source/funkin/system/Main.hx @@ -28,8 +28,10 @@ import funkin.system.Discord.DiscordClient; #end import lime.app.Application; -#if sys +#if ALLOW_MULTITHREADING import sys.thread.Thread; +#end +#if sys import sys.io.File; #end // TODO: REMOVE TEST @@ -51,7 +53,7 @@ class Main extends Sprite // You can pretty much ignore everything from here on - your code should go in your states. - #if sys + #if ALLOW_MULTITHREADING public static var gameThreads:Array = []; #end @@ -120,7 +122,7 @@ class Main extends Sprite private static var __threadCycle:Int = 0; public static function execAsync(func:Void->Void) { - #if sys + #if ALLOW_MULTITHREADING var thread = gameThreads[(__threadCycle++) % gameThreads.length]; thread.events.run(func); #else @@ -135,7 +137,7 @@ class Main extends Sprite public function loadGameSettings() { @:privateAccess FlxG.game.getTimer = getTimer; - #if sys + #if ALLOW_MULTITHREADING for(i in 0...4) gameThreads.push(Thread.createWithEventLoop(function() {Thread.current().events.promise();})); #end diff --git a/source/funkin/system/ThreadUtil.hx b/source/funkin/system/ThreadUtil.hx new file mode 100644 index 00000000..6906d97f --- /dev/null +++ b/source/funkin/system/ThreadUtil.hx @@ -0,0 +1,32 @@ +package funkin.system; + +#if ALLOW_MULTITHREADING +class ThreadUtil { + /** + * Creates a new Thread with an error handler. + * @param func Function to execute + * @param autoRestart Whenever the thread should auto restart itself after crashing. + */ + public static function createSafe(func:Void->Void, autoRestart:Bool = false) { + if (autoRestart) { + return sys.thread.Thread.create(function() { + while(true) { + try { + func(); + } catch(e) { + trace(e.details()); + } + } + }); + } else { + return sys.thread.Thread.create(function() { + try { + func(); + } catch(e) { + trace(e.details()); + } + }); + } + } +} +#end \ No newline at end of file