Skip to content

Commit a1fa227

Browse files
nykwonoACrazyTownKoloInDaCribMAJigsaw77
authored andcommitted
Fix hot reloading hanging
Co-Authored-By: ACrazyTown <47027981+ACrazyTown@users.noreply.github.com> Co-Authored-By: Kolo <67389779+KoloInDaCrib@users.noreply.github.com> Co-Authored-By: Mihai Alexandru <77043862+MAJigsaw77@users.noreply.github.com>
1 parent a05ba64 commit a1fa227

7 files changed

Lines changed: 92 additions & 22 deletions

File tree

source/funkin/data/character/CharacterData.hx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,8 @@ class CharacterDataParser
279279
{entryId:String, error:Any, ?entryCls:String}> = new SynchronizedArray();
280280

281281
var charIdList:Array<String> = funkin.modding.compat.RegistryData.listEntryIds(DATA_FILE_PATH, true);
282-
var previousScriptedEntryClasses:Array<String> = [];
283-
var scriptedEntryClassNames:Array<String> = [];
282+
var previousScriptedEntryClasses:SynchronizedArray<String> = new SynchronizedArray<String>();
283+
var scriptedEntryClassNames:SynchronizedArray<String> = new SynchronizedArray<String>();
284284
var entryCount:Int = 0;
285285

286286
// Used to track the state we're in while loading the characters. This can either be us loading all character data, or loading each scripted character types.
@@ -507,7 +507,7 @@ class CharacterDataParser
507507
{
508508
var loadScriptedEntriesFuture = TaskHandler.performSimpleTask(() ->
509509
{
510-
scriptedEntryClassNames = switch (entryLoadingState)
510+
var scriptedClsNames:Array<String> = switch (entryLoadingState)
511511
{
512512
case 'sparrow':
513513
SparrowCharacter.listScriptClasses();
@@ -529,12 +529,14 @@ class CharacterDataParser
529529
default:
530530
[];
531531
}
532+
scriptedEntryClassNames.clear();
533+
scriptedEntryClassNames.addAll(scriptedClsNames);
532534

533535
// We concatenate this list so we can use this when checking for BaseCharacter entries.
534-
previousScriptedEntryClasses = previousScriptedEntryClasses.concat(scriptedEntryClassNames);
536+
previousScriptedEntryClasses.addAll(scriptedClsNames);
535537

536-
log('Queuing loading for ${scriptedEntryClassNames.length} $entryLoadingState character scripted entries...');
537-
entryCount += scriptedEntryClassNames.length; // Since this function is called several times, we increment the entry count for each use.
538+
log('Queuing loading for ${scriptedClsNames.length} $entryLoadingState character scripted entries...');
539+
entryCount += scriptedClsNames.length; // Since this function is called several times, we increment the entry count for each use.
538540

539541
return true;
540542
});

source/funkin/data/event/SongEventRegistry.hx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,15 @@ class SongEventRegistry
2626
* Every built-in event class must be added to this list.
2727
* Thankfully, with the power of `ClassMacro`, this is done automatically.
2828
*/
29-
static final BUILTIN_EVENTS:List<Class<SongEvent>> = ClassMacro.listSubclassesOf(SongEvent).filter((cls:Class<SongEvent>) -> ![
30-
'funkin.play.event.SongEvent'
31-
].contains(Type.getClassName(cls)));
32-
29+
#if FEATURE_MULTITHREADING
30+
static final BUILTIN_EVENTS:SynchronizedArray<Class<SongEvent>> = new SynchronizedArray<Class<SongEvent>>(
31+
ClassMacro.listSubclassesOf(SongEvent).filter((cls:Class<SongEvent>) -> !['funkin.play.event.SongEvent'].contains(Type.getClassName(cls)))
32+
);
33+
#else
34+
static final BUILTIN_EVENTS:Array<Class<SongEvent>> = ClassMacro
35+
.listSubclassesOf(SongEvent)
36+
.filter((cls:Class<SongEvent>) -> !['funkin.play.event.SongEvent'].contains(Type.getClassName(cls)));
37+
#end
3338
/**
3439
* Map of internal handlers for song events.
3540
* These may be either `ScriptedSongEvents` or built-in classes extending `SongEvent`.
@@ -94,7 +99,7 @@ class SongEventRegistry
9499
{eventId:String, error:Any, ?eventCls:String}> = new SynchronizedArray();
95100

96101
var entryCount:Int = 0;
97-
var scriptedEventClassNames:Array<String> = [];
102+
var scriptedEventClassNames:SynchronizedArray<String> = new SynchronizedArray<String>();
98103
var loadedBaseEvents:Bool = false;
99104

100105
var loadBaseEventsAsync:Void->Void = () -> {};
@@ -217,7 +222,7 @@ class SongEventRegistry
217222

218223
loadScriptedEventsAsync = () ->
219224
{
220-
scriptedEventClassNames = SongEvent.listScriptClasses();
225+
scriptedEventClassNames = new SynchronizedArray<String>(SongEvent.listScriptClasses());
221226
entryCount = EVENT_CACHE.size() + scriptedEventClassNames.length;
222227

223228
trace('Instantiating ${scriptedEventClassNames.length} scripted song events...');

source/funkin/data/song/SongRegistry.hx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import funkin.play.song.Song;
1010
import funkin.util.VersionUtil;
1111
import funkin.util.tools.ISingleton;
1212
import funkin.data.DefaultRegistryImpl;
13+
#if FEATURE_MULTITHREADING
14+
import hx.concurrent.collection.SynchronizedMap;
15+
#end
1316

1417
using funkin.data.song.migrator.SongDataMigrator;
1518

@@ -22,14 +25,19 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata, SongEntryParams> imp
2225
* and adding migration to the `migrateStageData()` function.
2326
*/
2427
public static final SONG_METADATA_VERSION:thx.semver.Version = '2.2.8';
28+
2529
public static final SONG_METADATA_VERSION_RULE:thx.semver.VersionRule = '2.2.x';
2630
public static final SONG_CHART_DATA_VERSION:thx.semver.Version = '2.0.0';
2731
public static final SONG_CHART_DATA_VERSION_RULE:thx.semver.VersionRule = '2.0.x';
2832
public static final SONG_MUSIC_DATA_VERSION:thx.semver.Version = '2.0.0';
2933
public static final SONG_MUSIC_DATA_VERSION_RULE:thx.semver.VersionRule = '2.0.x';
3034
public static var DEFAULT_GENERATEDBY(get, never):String;
3135

36+
#if FEATURE_MULTITHREADING
37+
public var scriptedSongVariations:SynchronizedMap<String, Song> = SynchronizedMap.newStringMap(); // Use a thread safe map when needed.
38+
#else
3239
public var scriptedSongVariations:Map<String, Song> = new Map<String, Song>();
40+
#end
3341

3442
static function get_DEFAULT_GENERATEDBY():String
3543
{

source/funkin/modding/module/ModuleHandler.hx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,24 @@ import funkin.util.tasks.TaskHandler;
1010
import hx.concurrent.collection.SynchronizedArray;
1111
import lime.app.Future;
1212
import lime.app.Promise;
13+
#if FEATURE_MULTITHREADING
14+
import hx.concurrent.collection.SynchronizedArray;
15+
import hx.concurrent.collection.SynchronizedMap;
16+
#end
1317

1418
/**
1519
* Utility functions for loading and manipulating active modules.
1620
*/
1721
@:nullSafety
1822
class ModuleHandler
1923
{
24+
#if FEATURE_MULTITHREADING
25+
static final moduleCache:SynchronizedMap<String, Module> = SynchronizedMap.newStringMap();
26+
static var modulePriorityOrder:SynchronizedArray<String> = new SynchronizedArray<String>();
27+
#else
2028
static final moduleCache:Map<String, Module> = new Map<String, Module>();
2129
static var modulePriorityOrder:Array<String> = [];
30+
#end
2231

2332
/**
2433
* Parses and preloads the game's stage data and scripts when the game starts.
@@ -57,7 +66,7 @@ class ModuleHandler
5766
// Clear module cache first.
5867
clearModuleCache();
5968

60-
var scriptedModuleClassNames:Array<String> = Module.listScriptClasses();
69+
var scriptedModuleClassNames:SynchronizedArray<String> = new SynchronizedArray(Module.listScriptClasses());
6170
var promise:lime.app.Promise<LoadEntriesResult> = new lime.app.Promise<LoadEntriesResult>();
6271

6372
// We don't have any modules to load so we can just immediately complete the promise.
@@ -194,9 +203,14 @@ class ModuleHandler
194203

195204
static function reorderModuleCache():Void
196205
{
206+
#if FEATURE_MULTITHREADING
207+
var sortedArray:Array<String> = moduleCache.keys().array();
208+
sortedArray.sort(sortByPriority);
209+
modulePriorityOrder = new SynchronizedArray(sortedArray);
210+
#else
197211
modulePriorityOrder = moduleCache.keys().array();
198-
199212
modulePriorityOrder.sort(sortByPriority);
213+
#end
200214
}
201215

202216
/**
@@ -259,9 +273,12 @@ class ModuleHandler
259273
{
260274
ScriptEventDispatcher.callEvent(value, event);
261275
}
262-
263276
moduleCache.clear();
277+
#if FEATURE_MULTITHREADING
278+
modulePriorityOrder.clear();
279+
#else
264280
modulePriorityOrder = [];
281+
#end
265282
event.finish();
266283
}
267284
}

source/funkin/play/notes/notekind/NoteKindManager.hx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ class NoteKindManager
2424
* Every built-in note kind class must be added to this list.
2525
* Thankfully, with the power of `ClassMacro`, this is done automatically.
2626
*/
27-
static final BUILTIN_KINDS:List<Class<NoteKind>> = ClassMacro.listSubclassesOf(NoteKind).filter((cls) ->
27+
#if FEATURE_MULTITHREADING
28+
static final BUILTIN_KINDS:SynchronizedArray<Class<NoteKind>> = new SynchronizedArray<Class<NoteKind>>(ClassMacro.listSubclassesOf(NoteKind).filter((cls) ->
29+
{
30+
!['funkin.play.notes.notekind.NoteKind'].contains(Type.getClassName(cls));
31+
}));
32+
#else
33+
static final BUILTIN_KINDS:Array<Class<NoteKind>> = ClassMacro.listSubclassesOf(NoteKind).filter((cls) ->
2834
{
29-
![
30-
'funkin.play.notes.notekind.NoteKind'
31-
].contains(Type.getClassName(cls));
35+
!['funkin.play.notes.notekind.NoteKind'].contains(Type.getClassName(cls));
3236
});
33-
37+
#end
3438
/**
3539
* A map of all note kinds, keyed by their name.
3640
* This is used to retrieve note kinds by their name.

source/funkin/util/assets/StagedCache.hx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package funkin.util.assets;
22

33
import flixel.util.FlxSignal.FlxTypedSignal;
4+
#if FEATURE_MULTITHREADING
5+
import hx.concurrent.collection.SynchronizedMap;
6+
#end
47

58
/**
69
* Represents a three-stage asset cache, which is useful for managing and tracking assets that are in use by the game.
@@ -12,17 +15,27 @@ class StagedCache<T> implements IStagedCache
1215
/**
1316
* The permanent cache, containing assets which always stay in memory and are never purged.
1417
*/
18+
#if FEATURE_MULTITHREADING
19+
final permanent:SynchronizedMap<String, T>;
20+
#else
1521
final permanent:Map<String, T>;
16-
22+
#end
1723
/**
1824
* The currently cached assets. Won't be purged until the next purge cycle.
1925
*/
26+
#if FEATURE_MULTITHREADING
27+
final current:SynchronizedMap<String, T>;
28+
#else
2029
final current:Map<String, T>;
21-
30+
#end
2231
/**
2332
* The assets that were previously cached. May be re-cached, but if not, they will be purged.
2433
*/
34+
#if FEATURE_MULTITHREADING
35+
final previous:SynchronizedMap<String, T>;
36+
#else
2537
final previous:Map<String, T>;
38+
#end
2639

2740
/**
2841
* An FlxSignal which is dispatched when an asset is removed. Typically used for cleanup of assets that need to be manually destroyed (e.g. FlxGraphics).
@@ -48,9 +61,15 @@ class StagedCache<T> implements IStagedCache
4861
{
4962
// The cache maps are final to prevent them from being overridden,
5063
// but they are still mutable.
64+
#if FEATURE_MULTITHREADING
65+
permanent = SynchronizedMap.newStringMap();
66+
current = SynchronizedMap.newStringMap();
67+
previous = SynchronizedMap.newStringMap();
68+
#else
5169
permanent = [];
5270
current = [];
5371
previous = [];
72+
#end
5473
}
5574

5675
/**
@@ -188,9 +207,16 @@ class StagedCache<T> implements IStagedCache
188207
public function keys():Array<String>
189208
{
190209
var keys:Array<String> = [];
210+
#if FEATURE_MULTITHREADING
211+
// MapTools can't be used for SynchronizedMap.
212+
keys.appendUnique([for (k => v in permanent) k]);
213+
keys.appendUnique([for (k => v in current) k]);
214+
keys.appendUnique([for (k => v in previous) k]);
215+
#else
191216
keys.appendUnique(permanent.keyValues());
192217
keys.appendUnique(current.keyValues());
193218
keys.appendUnique(previous.keyValues());
219+
#end
194220
return keys;
195221
}
196222

source/funkin/util/logging/AnsiTrace.hx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ class AnsiTrace
8989
*/
9090
static function formatOutput(v:Dynamic, ?infos:haxe.PosInfos):String
9191
{
92+
// EReg isn't thread-safe, so we default to the normal function if we're loading asynchronously.
93+
if (funkin.util.plugins.ReloadAssetsDebugPlugin.hotReloadInProgress)
94+
{
95+
if (infos == null) return Std.string(v);
96+
97+
return haxe.Log.formatOutput(v, infos);
98+
}
99+
92100
var str:String = Std.string(v);
93101
if (infos == null) return str;
94102

0 commit comments

Comments
 (0)