Use PlacementUnlocks when multiple players/controllers or progression states share one placement catalog but should not have the same choices.
GridPlacementBundledefines what the game can place.PlacementSession.unlocksdefines what one session can use now.- Unlock filtering changes availability, not committed world occupancy or placement rules.
Filter semantics
Each filter axis supports FilterMode.ALLOWLIST and FilterMode.DENYLIST.
| Mode | Empty list | Non-empty list |
|---|---|---|
ALLOWLIST |
Allow all | Allow only listed ids |
DENYLIST |
Allow all | Block listed ids |
A runtime-only predicate can also be supplied with set_runtime_filter(Callable). Runtime filters are not serialized.
Setup
var session:= PlacementSession.new()
session.grid_placement_bundle= preload("res://config/grid_placement_bundle.tres")
session.catalog= preload("res://catalog.tres")
var unlocks:= PlacementUnlocks.new()
unlocks.unlocked_entry_ids= PackedStringArray(["hut","grass"])
unlocks.unlocked_terrain_ids= PackedStringArray(["grass"])
session.unlocks= unlocks
host.register_session(self, session)Give each player/controller its own session when availability differs.
Read available content
for entryin session.get_available_entries():
populate_palette(entry)
if session.is_entry_available(&"sawmill"):
enable_build_button()PlaceableSelectionUI and TerrainPaletteUI read the active session. Depending on UI settings, locked content can remain visible but disabled through options such as shows_entry_as_locked, shows_terrain_as_locked, and shows_sequence_as_locked.
Boundaries
Per-session unlocks do not provide network authority, ownership permissions, or separate committed worlds. Sessions that build in the same world still share committed occupancy. See Multiplayer & Split-Screen.