geoidesic / foundryvtt-actor-studio

A FoundryVTT module for creating Actors
MIT License
2 stars 0 forks source link

[BUG] Donation Tracker initialisation isn't running on TCR server #47

Closed geoidesic closed 1 day ago

geoidesic commented 1 week ago

geoidesic — Today at 9:36 PM I'm having trouble with the ready hook.

I'm creating a module (A) that depends on another module (B). In A's ready hook, I get a value from game.moduleB.value and use that. On my world with only 5 modules installed the value is available. On a larger world with 140 modules installed, the value isn't available in the ready hook (it is available from the inspector console if I go type it in).

So is there a way to ensure that module A loads after module B? (A way that won't break if module B hasn't been installed). mxzf — Today at 9:42 PM The main way I know of is to register your hook in a hook rather than running it in the root, to make sure it's registered (and run) later geoidesic — Today at 9:43 PM Ok but the thing I need it for is to register settings. I'm not sure I can do that later? ( I tried and it didn't work, so assumed it's not possible but maybe I just did it wrong ) mxzf — Today at 9:44 PM Your code still runs in ready regardless.

Registering later than in the document root just puts it onto the stack later on

Hooks.on('ready', func)

becomes

Hooks.on('init', () => {
    Hooks.on('ready', func)
})

Just to add it to the list of ready hooks later than doing it in the file root geoidesic — Today at 9:47 PM um... whut? 😅 sorry that went over my head. I'm not sure what I'm looking at. This is an alien structure I understand the words. I understand the sentences. But the meaning is lost! This is what I currently have:

Hooks.once("ready", (app, html, data) => {
  if (game.modules.get('my-mami')?.active) {
    MyMami.init();
  }
});

So you're saying just wrap it in an init hook? mxzf — Today at 9:54 PM Alright, lemme break it down a bit. This is one of those things where the underlying implications of how code being run and hooks being run comes into play.

When Foundry loads into a world, the underlying code first loads the core API endpoints for basic stuff (which is why Hooks is in the global namespace to be used in your module code), then it runs the system code, then it runs module code. At that point in time, all of the root-level code in your module will run, which is where and how you do your imports and declare your hooks and so on.

Modules are run in a semi-arbitrary order (alphabetically, with library modules and dependencies and such tweaking it some, IIRC)

When Foundry gets to your Hooks.once line, it adds that function to the end of an array of functions to be run whenever that hook is called. Generally speaking, the process of reading through the root level code of all the files adds a bunch of hooks to be run whenever they need to be run.

Once that code loading is all done, Foundry starts initializing Game and actually doing stuff with the code to set up the world, that's when all the init and i18nInit hooks are run and their code does stuff, then setup hooks and so on 'til it does ready hooks just before users actually start doing stuff in the UI

By registering a hook inside another hook, you ensure that it gets put at the end of the array of hooks to run after other modules have had their hooks added to the array, and thus it'll be run after them. init is a good spot to do that because it ensures that your hook is probably at the end of the list, but it still lets someone do stuff in setup if they know they need to be after yours for some reason mxzf — Today at 9:57 PM

Hooks.on('init', () => {
  Hooks.once("ready", (app, html, data) => {
    if (game.modules.get('my-mami')?.active) {
      MyMami.init();
    }
  });
})

This will wait 'til the init hooks fire to stick your ready hook function at the end of the array of ready hooks that Foundry will run, ensuring that it runs last unless someone else uses the same trick too Ultimately, it's just a minor timing shenanigan that revolves around knowing exactly how Foundry initializes itself

geoidesic commented 1 week ago

I tried the above but it didn't work. The output from the logs:

ACTOR STUDIO | Checking for Donation Tracker module:  Found
DonationTrackerGameSettings.js:11 ACTOR STUDIO | Registering Actor Studio Donation Tracker integration
DonationTrackerGameSettings.js:31 ACTOR STUDIO | membershipRanks []
DonationTrackerGameSettings.js:54 ACTOR STUDIO | allSettings []

Which tells us that although DT studio was found, game.membership.RANKS returns empty at that time (it returns the expected array from the inspector console though)

geoidesic commented 1 day ago

Fixed in release 1.0.6