Larkinabout / fvtt-token-action-hud-core

Token Action HUD is a repositionable HUD of actions for a selected token.
13 stars 18 forks source link

[FEATURE] Use Hooks for Registering System Module #32

Closed Larkinabout closed 1 year ago

Larkinabout commented 1 year ago

Describe your idea Use Foundry VTT's Hooks mechanism to allow Token Action HUD system modules to register with the Token Action HUD Core module, e.g., Hooks.once('token-action-hud-register', (core) => core.register(myVersionData, mySystemManager))

Versions

Additional context There are three main interactions to include when implementing a hook method:

  1. Token Action HUD Core currently checks the module version required by the system module via a dynamic import of a file on the system module side. Compatibility data does not seem to be currently available in the module data held by Foundry VTT.
  2. Token Action HUD Core uses the system module's SystemManager class to override its own class methods.
  3. Token Action HUD system modules extend core classes and use a utility class for various functions. These could be passed back to the system module via a hook or made available under game.tokenActionHud instead.
Larkinabout commented 1 year ago

Partially added in Token Action HUD Core 1.1.0.

The following code example calls the hook Token Action HUD Core waits for and passes the required core module version and SystemManager class:

Hooks.once('ready', async () => {
    const module = game.modules.get(MODULE.ID)
    module.api = {
        requiredCoreModuleVersion: REQUIRED_CORE_MODULE_VERSION,
        SystemManager
    }
    Hooks.call('tokenActionHudSystemReady', module)
})

Dynamic imports of Token Action HUD Core classes into the Token Action HUD system module are still required due to the unknown order modules are loaded. If anyone has a better solution for this, please let me know. Here is the current method and required classes:

const coreModulePath = '../../token-action-hud-core/scripts/token-action-hud-core.min.js'
const coreModule = await import(coreModulePath)
export const CoreActionHandler = coreModule.ActionHandler
export const CoreActionListExtender = coreModule.ActionListExtender
export const CoreCategoryManager = coreModule.CategoryManager
export const CorePreRollHandler = coreModule.PreRollHandler
export const CoreRollHandler = coreModule.RollHandler
export const CoreSystemManager = coreModule.SystemManager
export const CoreUtils = coreModule.Utils
export const Logger = coreModule.Logger
farling42 commented 1 year ago

You could make these available directly from one of the global objects within Foundry. For my PDF Pager module, I make some functions available as ui.pdfpager.<functionname>. In your core module you could do:

ui.tokenactionhud = { ActionHandler, ActionListExtender, CategoryManager <the rest> }

It might(?) then be possible to do

   class MyClass extends ui.tokenactionhud.CategoryManager {
   }

I'm not 100% if the above works, otherwise something like?

const CoreCategoryManager = ui.tokenactionhud.CategoryManager;
   class MyClass extends CoreCategoryManager {
   }

Your hook would still be required, so that users know that ui.tokenactionhud exists.

Larkinabout commented 1 year ago

I gave that a go, but it threw undefined errors due to the class registering(?) itself before the core module had created the global object. This is before a new instance of the class was initialised.

I think I'd need to somehow put a wait on the registering of the system module classes until the global object is available, though not sure how to do that as yet and it seemed messy when trying to make it as simple as possible for developers on the system module side.

The other way might be to somehow ensure the core module is fully loaded before the system module. Swapping the classes out for functions would also probably work, as it seemed to only be because class extensions are established on module load, though an ordeal to switch over to just for this purpose.

Larkinabout commented 1 year ago

There's also the possibility of setting core up as a library module as I believe they are guaranteed to load beforehand to make them available to non-library modules. Although I've avoided that route because I don't think TAH Core is really a library in the strictest sense.

farling42 commented 1 year ago

Well, it is a library in that it doesn't work alone, and provides functionality to the system specific module.

Larkinabout commented 1 year ago

Good point. I'll explore the option.

farling42 commented 1 year ago

Not rolling up your course module into a minified version would also help with debugging. Is your module big enough to deserve filling up?

Larkinabout commented 1 year ago

Yes, there are noticeable improvements for slower machines when clicking between tokens. At some point I'd like to look at focussed updating, which may mitigate the need, but that's quite a big task.

If I can get these core classes available by a global object, it'll be easy enough to make a non-minified version available for system module developers to use. It's what I use currently for development by switching out the esmodule reference to ./scripts/init.js. It's just that it currently also requires changing the references to those core classes on the system module side.

Larkinabout commented 1 year ago

Added the tokenActionHudCoreApiReady hook in Token Action HUD Core 1.3.0 to provide TAH system modules with TAH Core classes without the need to import them. See Core Changes for System Module Developers for details.