Module-Scoped Variables & Data Persistence: weak_map and <persistable>
The main lesson covered block-level scope; this page adds the outermost layer of the scope onion: the module. Running Verse in UEFN today, module-level vars come with one special rule — they must be weak_map types. That rule answers "why can't I write a global variable", and throws in a cross-session save system while it's at it.
1. Why You Can't Write a Global var
Nearly everyone who finishes Lesson 8 tries it immediately: write var TotalKills:int = 0 at the very top of the module (outside every class) — such a convenient global counter. And promptly collects a Compile error. It's not that you wrote it wrong; it's a hard rule of UEFN today: a mutable variable at the module's outermost layer cannot be a plain integer / string / boolean — it must be a weak_map type. Constants are unrestricted — MaxPlayers:int = 12 at the outermost layer is perfectly legal; only the "mutable" half gets stopped.
Why design it this way? Think about the environment your level runs in: lots of players online at once, joining and leaving at any moment. A bare, naked global mutable variable means "all players share one slot of data" — everyone's kills pile into the same number, and when a player leaves, their data has nowhere to live. UEFN simply seals the problem off at the root: want module-level mutable state? Then say clearly how it splits the ledger. That's exactly the role of weak_map: the most common usage keys it by player — one slot per player, nobody fighting over data; when a player leaves, their slot naturally becomes void, no leaks. This is essentially the embryo of a "per-player SaveGame".
2. The Right Way to Write a Module-Level var
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# A module-level var must be a weak_map type
# Keyed by player: one slot per player, no squabbling
var PlayerScores:weak_map(player, int) = map{}
score_board_device := class(creative_device):
AddTen(Player:player):void =
var NewScore:int = 10
if (Old := PlayerScores[Player]):
set NewScore = Old + 10
if (set PlayerScores[Player] = NewScore) {}
Three things to notice. First, the creation: weak_map(player, int) reads "a weak map keyed by player with integer values", and the initial value map{} is an empty table. Second, reading: PlayerScores[Player] might not go through (this player may not have a record yet), so it must be wrapped in an if (Blueprint's Branch) — the full story of "checks that might not pass" unfolds in a later chapter; for now just remember "reads and writes to this table both go through a Branch". Third, writing: stuffing a value into the weak_map (set) might not go through either, so the official idiom is if (set …[…] = …) {} — an empty branch that spells out "if it doesn't go through, so be it".
3. The Free Gift: Cross-Session Saves
This "annoying" rule ships with a big bonus: data persistence. Per the official docs, a module-level weak_map keyed by player whose value type satisfies the persistable constraint keeps its data across sessions — the score a player banks this round is still there next round: a fully automatic SaveGame, no less. You write no save logic at all: get the shape right, and saving is automatic.
What counts as persistable? The common basic types are mostly all on the list: integers, floats, strings, booleans, and so on. To save a bundle of your own data (say, "level + gold + title"), hang the <persistable> setting on the class or enum asset that carries it, then stuff it into the weak_map as the value. For the exact constraint list and version-upgrade caveats, defer to the official "Using Persistable Data in Verse" page — worth a full read before you run a long-lived project.
Looking back, this is Verse's style through and through: in Lesson 8, "mutability needs an application (var), changes go through a Set"; at the module level, "global mutable state must split the ledger per player (weak_map)". Every layer forces you to spell the dangerous things out plainly — and once you write it right, the language covers the hardest parts (staying safe when several players change things at once, data lifetimes, saves) for you.
4. Quick Quiz
You write var TotalKills:int = 0 at the module's top level. What happens?
Sources
Compiled from Epic's official documentation: Using Persistable Data in Verse (official docs) ↗ · Verse Language Quick Reference (official docs) ↗