Coordinating Overlay Permissions


In my project, I’ve got overlays—things like movement paths, elevation indicators, and token’s boundary—that need to appear only when the viewer has permission to see them. To manage this, I’ve built a system called the OverlayPermissionCoordinator
.
It bridges Foundry VTT’s infrastructure with my domain logic, keeping permissions clean, isolated, and easy to reason about.
Here’s a breakdown of how the whole thing works.
Entry Points: Where the Flow Begins
Overlay view permission checks kick off in a few key methods:
graph TD
A[UI/Rendering System] --> B{Which Method?}
B --> C[canViewOverlayOnToken - Single Check]
B --> D[canViewOverlaysOnToken - Batch Check]
B --> E[getViewableOverlaysForToken - Get All Viewable]
Single Overlay Check: canViewOverlayOnToken
This is the core function that checks one overlay on one token. Here's what it does under the hood:
// Check if a single overlay is viewable by the user
canViewOverlayOnToken(
overlayId: string,
targetToken: Token,
userId: string,
isGM: boolean,
viewerTokens: Token[] = []
): boolean {
const config = this.registry.get(overlayId);
if (!config) {
this.logger.warn(`Overlay "${overlayId}" isn’t registered.`);
return false;
}
// Quick exit if the overlay doesn't require permission checks
if (!config.usePermissionSystem) {
return true;
}
// Gather the necessary data for a proper check
const targetData = this.getCachedTokenData(targetToken);
const viewerData = this.getCachedTokensData(viewerTokens);
const allVisibleTokens = this.getAllVisibleTokensData();
// Let the domain service handle the actual decision
return this.permissionService.canViewOverlaysOnToken(
targetData,
userId,
isGM,
viewerData,
allVisibleTokens
);
}
Step-by-step:
Registry Lookup
Pulls the overlay config from the registry
If not found, returns
false
Fast Path Check
If
usePermissionSystem === false
, returnstrue
immediatelyThis skips heavy logic for overlays that don’t need it
Data Conversion (Cached)
Converts
targetToken
andviewerTokens[]
intoTokenSightData
Fetches all visible tokens if needed
Domain Permission Check
Calls
permissionService.canViewOverlays()
This service lives in the pure domain layer—no Foundry types involved
Return Result
- Comes back with a
boolean
based on the domain logic
- Comes back with a
Caching Strategy: Keep It Fast, Keep It Simple
To avoid re-converting tokens constantly, I’ve built a lightweight caching layer:
// Token Data Cache Flow
┌─────────────────┐
│ Foundry Token │
└────────┬────────┘
↓
┌────────────────────────┐
│ Generate Cache Key │ → `${token.id}-${cacheGeneration}`
└────────────┬───────────┘
↓
┌────┴────┐
│ Cached? │
└────┬────┘
↓
No ─┴── Yes
↓ ↓
Convert Return
& Store Cached
The key includes a generation number so I can invalidate it whenever needed (e.g. when tokens move or vision updates).
Example Flow: Full Walkthrough
Let’s say a user hovers over a token. I want to know which overlays are visible to them:
getViewableOverlaysForToken(token, userId, isGM, viewerTokens)
Behind the scenes:
Grab all overlays from the registry:
['movement-path', 'elevation-indicator', 'status-effects']
Call the batch check:
canViewOverlaysOnToken([...], token, userId, isGM, viewerTokens)
For each overlay:
movement-path
: needs permission → check domainelevation-indicator
: doesn’t need permission → allowstatus-effects
: needs permission → check domain
Result:
['elevation-indicator', 'status-effects']
(assumingmovement-path
was denied)
Why This Works for Me
This setup gives me:
Separation of concerns
Foundry stays out of my domain logic. My domain doesn’t even know Foundry exists.Performance
The system avoids unnecessary work and reuses data wherever possible.Flexibility
I can add new permission types or overlay rules without touching Foundry code.Testability
Domain services are testable in isolation, no mocks needed.Maintainability
All Foundry-specific stuff lives in one place—the coordinator.
If you’re wrangling overlays in a similar way or building on Foundry and trying to decouple things, I think this structure makes it much easier to reason about and test.
Subscribe to my newsletter
Read articles from stonedtroll directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
