Register

Version 11 Token Changes

The underlying way that unlinked Tokens are represented in the core software has undergone a complete overhaul in Version 11, in order to eliminate a large amount of technical debt that had been burdening it for some time. This article is aimed at Module and System developers that manipulate Tokens and Token data in certain advanced ways, or have previously experienced friction in their codebases and implemented workarounds for unlinked Tokens behaving slightly differently to real Actors.

If your codebase does not manipulate Tokens in any advanced way, or has no special-case code to handle unlinked Tokens, then you can safely skip this migration guide, as the high-level APIs and behaviors have remained the same here.

Glossary of Terms.

Linked Token
A Token with 'Link Actor Data' checked
Unlinked Token
A Token with 'Link Actor Data' unchecked
Base Actor
The Token's 'Represented Actor'
Token Actor
Synthetic Actor
An Actor that is created by merging the Base Actor with changes that are specific to the Token

How Tokens Work

The Token Document is an embedded child of the Scene Document. It first-and-foremost represents an interactable object that can be moved around the Scene, and stores information such as its X and Y co-ordinates, and texture information such as URL, scale, and rotation. It does not have to represent an Actor, though it can. The reference to the World Actor that a Token represents (if it represents one), is stored in actorId. Whether the Token is considered 'linked' to that Actor or not, is determined by the actorLink property.

Linked Tokens

Linked Tokens are the simplest case. The Token on the Scene represents the World Actor exactly. Every instance of that Token points to the same World Actor. Double-clicking a Linked Token will bring up that World Actor's sheet.

Unlinked Tokens

Unlinked Tokens represent a variation on a World Actor. They use the World Actor as a template and then change certain parts of it. For example, having a different name, different hit points, or different Items. The Actor that emerges from applying the changes to the Base Actor is called a Synthetic Actor, or Token Actor, because the full Actor Document does not really exist. You cannot retrieve a Token Actor from the Actors sidebar, it exists only as part of the Token.

Every instance of an Unlinked Token has its own, unique Token Actor instance. Double-clicking an Unlinked Token will bring up the Token Actor's sheet.

The Problem with Actor Data

In V10 and earlier, the set of changes that an Unlinked Token has made to its Base Actor was stored in the actorData property, which was a plain object. This meant that any change to the Token Actor was ultimately a Token update, and not an Actor update.

token.actor.update({ name: 'Furious Goblin' });
// Becomes:
token.update({ actorData: { name: 'Furious Goblin' } });

This meant that any Systems that had overridden Actor#_preUpdate or Actor#_onUpdate, or any System or Module that relied on the preUpdateActor or updateActor hooks would miss these updates entirely, even though they were actually modifying an 'Actor', as far as the tabletop was concerned.

In order to address this, we created an emulation layer that intercepts these actorData updates and fires the appropriate Actor events instead. However, the problem was not solved there, as updates to embedded Items and ActiveEffects also needed to be emulated. Because of this emulation, and because the underlying update was targeting a property of a Document, rather than an actual Document, the abstraction would break down in places, and would not behave like an normal Actor or Item update, forcing System and Module developers to account for and work around these discrepancies.

One particularly problematic result of the emulation layer, was that any change to a Token Actor's Items would result in the entire Item collection being copied into the Token's actorData. This was necessary in order to feasibly emulate the Item events. This resulted in a huge amount of bloat, where a single Scene might have several Tokens, each storing hundreds of duplicated Items in their actorData properties.

Actor Deltas

With the improved capabilities afforded us by the switch to a different database architecture, we were able to remove the emulation layer almost entirely and institute the ActorDelta Document in its place. The ActorDelta document is a direct child of a Token Document, and resembles an Actor Document very closely. In particular, it has its own embedded Items and ActiveEffects collections. The ActorDelta stores the differences between the Token Actor and the Base Actor, and importantly this includes differences to the embedded collections also.

Updates to Token Actors are considered real Actor updates for all intents and purposes now, and are translated into ActorDelta updates server-side. The same is true for any updates to embedded Items or ActiveEffects (or embedded ActiveEffects on embedded Items). It is difficult to provide specific migration guidance here without being able to know what workarounds may have been in place already. Suffice it to say that these workarounds should no longer be necessary, and if you find that any still are, please make us aware of the details.

Embedded Collections

One place where the behavior has changed at a high level is with the embedded collections of Token Actors. Previously any update to any embedded Item on a Token Actor would cause the entire collection to be copied to the Token Actor, effectively 'detaching' it from the Base Actor. If you were relying on this behavior, be aware that is has changed. Creations, deletions, and updates of embedded Items on Token Actors now only affect those Items, and do not impact the rest of the embedded collection at all.

Version 10 (Before)

// A Token Actor with three Items.
// Breastplate, Longsword, Shield.
token.actor.items.getName('Shield').delete();
const baseActor = game.actors.get(token.actorId);
baseActor.items.getName('Breastplate').update({
  name: 'Magic Breastplate'
});

// This logs nothing as all the Items on the Token Actor
// are considered different to the Base Actor's Items
// after the Shield was deleted.
console.log(token.actor.items.getName('Magic Breastplate'));

Version 11 (After)

// A Token Actor with three Items.
// Breastplate, Longsword, Shield.
token.actor.items.getName('Shield').delete();
token.baseActor.items.getName('Breastplate').update({
  name: 'Magic Breastplate'
});

// This logs the renamed Magic Breastplate, because the
// Breastplate Item is still considered 'linked' to the
// one in the Base Actor.
console.log(token.actor.items.getName('Magic Breastplate'));

In the example above, the ActorDelta retains the minimal number of changes that have actually been made to it. So it retains the fact that the Shield was deleted, and therefore its Token Actor will not have a Shield. But since the Breastplate was not changed at all, it is still 'linked' to the Base Actor's Breastplate Item, so any changes to it are reflected in the Token Actor's Breastplate Item too. Whether this behavior is desirable or not will depend a lot on your use case for Token Actors, however it significantly reduces bloat in the Scene data, so it is the default we have opted for.

Operations on ActorDeltas

In the vast majority of cases, you should not need to ever operate on an ActorDelta Document directly. Any changes that need to be made to a Token Actor should be done via the Token Actor instance itself.

// Don't do this:
token.delta.update({ name: 'Furious Goblin' });

// Do this instead:
token.actor.update({ name: 'Furious Goblin' });

For some advanced use cases, you may need to perform operations directly on the ActorDelta, however. For example, you might want to reset some change on the Token Actor and 're-link' it back to following the state of the Base Actor.

// Restore the Token Actor's name
token.delta.update({ name: null });

// Restore the Token Actor's HP
token.delta.update({ 'system.attributes.-=hp': null });

You can even restore embedded Documents back to the Base Actor's state with the EmbeddedCollectionDelta#restoreDocument method.

// Change a Token Actor's Item:
token.actor.items.getName('Breastplate').update({ name: 'Magic Breastplate' });
// Undo the change:
const breastplateID = token.baseActor.items.getName('Breastplate');
token.actor.items.restoreDocument(breastplateID);

// Delete a Token Actor's Item:
token.actor.items.getName('Shield').delete();
// Un-delete the Item:
const shieldID = token.baseActor.items.getName('Shield');
token.actor.items.restoreDocument(shieldID);

Direct Actor Data Updates

In cases where actorData is supplied directly, such as to create a Token with some differences pre-applied, it is enough to substitute actorData for delta to achieve the same effect:

Version 10 (Before)

const createData = {
  x, y,
  actorData: { name: 'Furious Goblin' }
};
TokenDocument.implementation.create(createData, {
  parent: canvas.scene
});

Version 11 (After)

const createData = {
  x, y,
  delta: { name: 'Furious Goblin' }
};
TokenDocument.implementation.create(createData, {
  parent: canvas.scene
});

As mentioned before, however, there should be no need to perform direct updates in the majority of cases, and they should instead be performed directly to on Token Actor.

Version 10 (Before)

token.update({
  actorData: {
    name: 'Furious Goblin',
    system: { details: { cr: 10 } }
  }
});

Version 11 (After)

token.actor.update({
  name: 'Furious Goblin',
  system: { details: { cr: 10 } }
});

One exception to this is when performing bulk update operations on many Tokens in a Scene, such as part of a System migration. In those cases, it is better to perform the update directly on the delta property, rather than updating each Token Actor individually:

const updates = scene.tokens.map(token => {
  return { _id: token.id, delta: { name: 'Furious Hobgoblin' } };
});
scene.updateEmbeddedDocuments('Token', updates);