Funkin/source/funkin/modding/module/ModuleHandler.hx
Eric 42d8d55067 Unit Test Suite (#119)
* Initial test suite

* Fix some build warnings

* Implemented working unit tests with coverage

* Reduced some warnings

* Fix a mac-specific issue

* Add 2 additional unit test classes.

* Multiple new unit tests

* Some fixins

* Remove auto-generated file

* WIP on hiding ignored tests

* Added list of debug hotkeys

* Remove old website

* Remove empty file

* Add more unit tests

* Fix bug where arrows would nudge BF

* Fix bug where ctrl/alt would flash capsules

* Fixed bug where bf-old easter egg broke

* Remove duplicate lines

* More test-related stuff

* Some code cleanup

* Add mocking and a test assets folder

* More TESTS!

* Update Hmm...

* Update artist on Monster

* More minor fixes to individual functions

* 1.38% unit test coverage!

* Even more tests? :O

* More unit test work

* Rework migration for BaseRegistry

* gameover fix

* Fix an issue with Lime

* Fix issues with version parsing on data files

* 100 total unit tests!

* Added even MORE unit tests!

* Additional test tweaks :3

* Fixed tests on windows by updating libraries.

* Set versions for flixel-ui and hamcrest

---------

Co-authored-by: Cameron Taylor <cameron.taylor.ninja@gmail.com>
2023-08-22 04:27:30 -04:00

153 lines
3.8 KiB
Haxe

package funkin.modding.module;
import funkin.util.SortUtil;
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
import funkin.modding.events.ScriptEvent;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.modding.module.Module;
import funkin.modding.module.ScriptedModule;
/**
* Utility functions for loading and manipulating active modules.
*/
class ModuleHandler
{
static final moduleCache:Map<String, Module> = new Map<String, Module>();
static var modulePriorityOrder:Array<String> = [];
/**
* Parses and preloads the game's stage data and scripts when the game starts.
*
* If you want to force stages to be reloaded, you can just call this function again.
*/
public static function loadModuleCache():Void
{
// Clear any stages that are cached if there were any.
clearModuleCache();
trace("[MODULEHANDLER] Loading module cache...");
var scriptedModuleClassNames:Array<String> = ScriptedModule.listScriptClasses();
trace(' Instantiating ${scriptedModuleClassNames.length} modules...');
for (moduleCls in scriptedModuleClassNames)
{
var module:Module = ScriptedModule.init(moduleCls, moduleCls);
if (module != null)
{
trace(' Loaded module: ${moduleCls}');
// Then store it.
addToModuleCache(module);
}
else
{
trace(' Failed to instantiate module: ${moduleCls}');
}
}
reorderModuleCache();
trace("[MODULEHANDLER] Module cache loaded.");
}
public static function buildModuleCallbacks():Void
{
FlxG.signals.postStateSwitch.add(onStateSwitchComplete);
}
static function onStateSwitchComplete():Void
{
callEvent(new StateChangeScriptEvent(ScriptEvent.STATE_CHANGE_END, FlxG.state, true));
}
static function addToModuleCache(module:Module):Void
{
moduleCache.set(module.moduleId, module);
}
static function reorderModuleCache():Void
{
modulePriorityOrder = moduleCache.keys().array();
modulePriorityOrder.sort(sortByPriority);
}
/**
* Given two module IDs, sort them by priority.
* @return 1 or -1 depending on which module has a higher priority.
*/
static function sortByPriority(a:String, b:String)
{
var aModule:Module = moduleCache.get(a);
var bModule:Module = moduleCache.get(b);
if (aModule.priority != bModule.priority)
{
return aModule.priority - bModule.priority;
}
else
{
return SortUtil.alphabetically(a, b);
}
}
public static function getModule(moduleId:String):Module
{
return moduleCache.get(moduleId);
}
public static function activateModule(moduleId:String):Void
{
var module:Module = getModule(moduleId);
if (module != null)
{
module.active = true;
}
}
public static function deactivateModule(moduleId:String):Void
{
var module:Module = getModule(moduleId);
if (module != null)
{
module.active = false;
}
}
/**
* Clear the module cache, forcing all modules to call shutdown events.
*/
public static function clearModuleCache():Void
{
if (moduleCache != null)
{
var event = new ScriptEvent(ScriptEvent.DESTROY, false);
// Note: Ignore stopPropagation()
for (key => value in moduleCache)
{
ScriptEventDispatcher.callEvent(value, event);
moduleCache.remove(key);
}
moduleCache.clear();
modulePriorityOrder = [];
}
}
public static function callEvent(event:ScriptEvent):Void
{
for (moduleId in modulePriorityOrder)
{
var module:Module = moduleCache.get(moduleId);
// The module needs to be active to receive events.
if (module != null && module.active)
{
ScriptEventDispatcher.callEvent(module, event);
}
}
}
public static inline function callOnCreate():Void
{
callEvent(new ScriptEvent(ScriptEvent.CREATE, false));
}
}