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