The Game class is the upper-most level of Foundry's data architecture and is responsible for initializing the application. It's usually referenced by the singleton game instance.
Official documentation
The Game class relies heavily on read-only properties which are not documented by the official JSDoc, you can see the class in more detail in your local install at foundryInstallPath\resources\app\client\game.js.
Legend
Game.create // `.` indicates static method or property
Game#system // `#` indicates instance method or property
game.system // Lowercase game also indicates instance method or property
When a client connects to a Foundry server, after all relevant system and module code is imported, the Game class is initialized in the globally available instance game. This process instantiates everything else available to Foundry developers, such as loading information from the databases and initializing the canvas.
More information on how data is processed is available in the From Load to Render guide.
The following lays out how the game object acquires its properties.
The basics of how the game object is instantiated and its properties are filled in are as follows
Game.create is called after all code is importedGame.connect establishes the underlying socket connection with the serverGame.getData fetches the live data from the database.new Game constructs the singleton instance which is publicly stashed as gameGame.initialize is called, which progressively builds out the game instance and calls a series of hooks.Note that the globally-available CONST and CONFIG objects are simply initialized as part of loading the relevant javascript module files (common\constants.mjs and client\config.js respectively). However, one should be hesitant to modify them before the init hook, to avoid any issues with module load order.
Developers of all kinds almost always need to work with one or more of the following hooks. This section provides information on what is available at each stage. Each of these hooks is only called once, so there's no functional difference between Hooks.once("init", callback) and Hooks.on("init", callback).
The first Hook available to a Foundry developer. The following are typical actions performed in this hook.
CONFIG object is modifiedThis is called before any of the other processes in Game#initialize; as such, the only properties available are the ones added in the constructor. These are constructed as read-only properties and so do not currently show up in Foundry's documentation.
false)The following properties are technically available but are not yet properly initialized with their data
canvas)Technically called at the end of Localization#initialize, this hook is called after the the following methods:
Game#registerSettings — Registers various core settingsgame.i18n is fully set up, so game.i18n.localize and other similar methods are availableThis hook is less commonly used. It's called after the following are established
Game#registerTours — initializes game.toursGame#activateListeners — initializes game.tooltip, game.videogame.permissions — loaded from world settingGame#initializePacks — initalizes game.packsGame#initializeDocuments — initializes game.collections and instantiates world collections like game.actors for all primary document types.The main point of the setup hook is it's after document data is loaded but before the canvas is initalized.
The final step of Foundry's initialization process, this hook is called after all of these other methods are called.
Game#initializeRTC — initializes game.webrtcGame#initializeMouse — initializes game.mouseGame#initializeGamepads — initializes game.gamepadGame#initializeKeyboard — initializes game.keyboard and game.keybindingsGame#initializeCanvas — initializes game.canvasGame#initializeUI — Renders UI elements like the sidebarDocumentSheetConfig.initializeSheets — processes registered sheet classesGame#activateSocketListeners — Enables various pieces of interactivity and data-sharing that occur over socketsDocumentIndex#index — initializes game.documentIndexNewUserExperience#initialize — initializes game.nuegame.ready = trueThe game object is where the in-world documents are stashed. Each of the primary document types has a WorldCollection, which is sourced from [Class].metadata.collection, e.g. Actor.metadata.collection returns "actors", and so game.actors is a collection of actors.
Unlike Compendium Collections, all documents in world collections are stored in-memory on the client, so access can be performed synchronously. Updates are still asynchronous to allow synchronization between clients.
It's worth noting that game.collections is itself a Collection of these world collections. However, it's basically never necessary to use that.
WorldCollection extends DocumentCollection, which provides the bulk of methods for interacting with the stored documents.
There's two main ways to get documents from a world collection, both of which run synchronously.
WorldCollection#get, which fetches by document id.WorldCollection#getName, which fetches by the document's name property.The global methods, fromUuid and fromUuidSync both work with documents in world collections; fromUuidSync in particular is quite useful because it remains synchrous and has no problem fetching embedded documents.
Invalid Documents: Sometimes, a data model update will mark a document as invalid if it fails to cast data (a common example is Number('30 ft') returns NaN). In this cases, there's a few ways to access invalid documents
DocumentCollection#getInvalid(id) is the primary canonical way.DocumentCollection#invalidDocumentIds is a Set, which can be iterated through with a for loop or accessed with a simple invalidDocumentIds.first().The upstream Collection class provides several ways of accessing the values stored inside: for and forEach iteration, map, reduce, filter, and some.
The DocumentCollection class adds the search function, which will look through all searchable fields based on the data model's metadata.
A third option is to use WorldCollection#folders, but this is generally inferior to just using filter(d => d.folder.id === 'targetFolderID') or some other construction with those basic methods inherited from Collection.
Here are some specific tips and tricks with the game instance
Dumping functions into the global scope with globalThis.myFunc = myFunc can be risky if there's a name collission with another package. One way to deal with this is to add them to your package's instance, at either game.system or game.modules.get('my-module'). By convention, many group them under the api
function myFunction() {}
// Doing it here requires assigning during
Hooks.once("init", () => {
// system example
game.system.api = {
myFunction,
}
// module example
game.modules.get('my-module').api = {
myFunction,
}
})
// a macro could then call the function with
game.system.api.myFunction()
game.modules.get('my-module').api.myFunction()
One important caveat to this method is they won't reliably be available until AFTER the init hook, so this isn't as helpful for anything you expect end users to call either before hooks fire (classes to extend, for examples) or during the init hook. In these cases, just scoping out a globally available object can be preferable, such as using our package name.
Stub
This section is a stub, you can help by contributing to it.