¶ What is this?
This is going to be our FAQ and "common hurdles to overcome" guide to help new package developers. It's going to be perpetually updating and changing.
If you know of some questions or common sticking points for new package developers, please edit this to include those things.¶ Have a question not answered here?
The League of Foundry VTT Developers is a helpful development-focused discord community which aims to help veterans and new developers alike. Please drop by and ask us your questions, the more questions we get the more likely this document is going to be updated with their answers.
¶ Pseudocode
None of the code within this document is guaranteed to work and should be tested before used in a world that you care about.
API article: Documents
From the official docs:
Data in Foundry Virtual Tabletop is organized around the concept of Documents. Each document represents a single coherent piece of data which represents a specific object within the framework. The term Document refers to both the definition of a specific type of data (the Document class) as well as a uniquely identified instance of that data type (an instance of that class).
Main article: Hooks Listening & Calling
API article: Hooks
There is some official documentation about hooks as of 0.8.
There is also a community maintained hook typescript definition and documentation file. But there's an easier way to discover what hook you can use for a specific piece of functionality:
CONFIG.debug.hooks = true;
With this enabled, you'll see every hook that fires as you interact with Foundry's UI and what arguments it is passed.
Bonus Tip: Install Developer Mode and you can explore the hooks debug more easily.
This is Foundry specific and will delete the someKey
in the database entry for someDocument
someDocument.update({
'-=someKey': null
})
Main article: Template Basics
The .html
files in Foundry are actually Handlebars files. In your own packages you can use either .html
or .hbs
.
Handlebars comes built in with a log
helper, which can be used exactly like console.log
can be to log out what data a template sees. It might help to think of a space as a comma within Handlebars {{}}
brackets.
This snippet will log an object with all of the data that the "current scope" has access to:
{{log 'some arbitrary string' this}}
stub
"monkeypatching" definition (maybe in appendix?)
Use libWrapper.
The short and mildly helpful answer is "Use LibWrapper."
LibWrapper is a library module which lets you 'safely' monkeypatch other functions, including class methods. We say "safely" because it has some built in QOL things around conflicting patches and puts the power in the user's hands to prioritize one module over another when said conflicts arise (and they will arise).
To pull this off first you have to find where the method or function is in the global scope which libWrapper can see and affect. For dnd5e's entity class that ends up being game.dnd5e.entities.Actor5e
(assigned here). Once you know what prototype you need to modify, you'll end up with something that looks like this:
libWrapper.register('my-fvtt-module-name', 'game.dnd5e.entities.Actor5e.prototype._prepareSkills', function (wrapped, ...args) {
console.log('game.dnd5e.entities.Actor5e.prototype._prepareSkills was called with', ...args);
const actor = this.actor; // you should have access to `this` in here as well
// ... do things ...
const result = wrapped(...args); // remember to call the original (wrapped) method
// ... do things ...
return result; // this return needs to have the same shape (signature) as the original, or things start breaking
});
API article: Sockets
The easiest way to use Sockets in a module is via Socketlib. This library provides a useful abstraction layer on top of the core socket implementation.
Main article: Library Modules
The convention among Foundry VTT Development community (and the official recommendation from Atropos himself) is to expose module-specific APIs on the module's moduleData located at game.modules.get('my-module-name')?.api
. Additionally, any module can call a custom hook to inform other modules about events.
Stub. There's a KeyboardManager class and a ClientKeybindings class that might be useful here.
Registration of custom Keybindings is possible through the ClientKeybindings.register api.
Main article: Handling Data: Flags, Settings, and Files
API article: Flags
Flags are the safest way that modules can store arbitrary data on existing documents. If you are making a module which allows the user to set a data point which isn't supported normally in Foundry Core or a system's data structure, you should use a flag.
Main article: Handling Data: Flags, Settings, and Files
API article: Settings
Settings, like flags, are a way for modules to store and persist data. Settings are not tied to a specific document however, unlike flags. Also unlike flags they are able to leverage the 'scope' field to keep a set of data specific to a user's localStorage (scope: client
) or put that data in the database (scope: world
).
For system independence, it's often a good idea to allow data paths to be defined by a setting (e.g. if you want to reference an attribute modifier in a dialog, then you could hard-code modifier = actor.attributes.str.mod
, but that path is 5e specific - so allowing a user to define the path to the attribute modifier makes it easy to tweak your module for a specific system).
This can be done with the getProperty(object, key)
utility function, e.g. modifier = getProperty(actor, attributeKey)
.
By far the most common issue when you're brand new to this. Don't panic and go through this checklist
name
matches your directory exactly.Foundry verifies that the package name
and the package directory name match exactly, otherwise it deems them invalid.
{
"name": "my-module-name"
}
With this module.json in the following structure, your module will not appear as installed in Foundry.
/Data
└ /modules
├ /not-my-module
└ /my-module-wrong-name
├ myScript.js
└ module.json
Instead you need to either change your directory or your name
so they match.
Similar to 2, but affects people who have a build step.
/Data
└ /modules
├ /not-my-module
└ /my-module-name
└ /dist
├ myScript.js
└ module.json
In this example, the manifest at modules/my-module-name/dist/module.json
will not be found by Foundry and thus the module will not appear as installed.
Main article: Handling Data: Flags, Settings, and Files
There's basically three options for modules for how to store data: Flags, Settings, and Files.
If you'd like a tutorial that walks through each step one at a time sequentially, this community made tutorial touches on a lot of the basics:
¶ Foundry VTT Module Making for Beginners
This covers everything from "I have no files" to "I have a module which interacts with Flags, Settings, FormApplication, CSS, Localization, Hooks, and more."
Basically means "changing" a piece of data.
let foo = 'bar';
someFunction() {
// does things...
foo = 'bat';
console.log({foo}); // 'bat'
}
someOtherFunction() {
if (foo === 'bar') {
// do things...
}
}
In the example above, someFunction
mutates foo
. Depending on the order in which it is called (before or after someOtherFunction
) this might cause someOtherFunction
to behave unexpectedly.
This isn't necessarily a bad pattern but it can save you some headaches if you avoid mutating things as much as possible. Instead try to make duplicates of your data when you want to change it.
OOP is a coding paradigm that is widely used in the Foundry Development community as it is what Foundry itself is built on. In essence, it values splitting functionality and data mangement into ES2020 Class
es as opposed to a lot of independent functions and variables.
¶ Further reading
stub monkeypatch