Reference Devices compiles

Reuse Your Modules Across Islands (What Epic Doesn't Teach)

Every module you built on this island — the Shell Hunt kit, the Codewood helpers, the volcano save system — is just a `.verse` file, and files travel. This lesson covers what Epic's docs never spell out: how to package a module so it drops into a completely different island project and compiles on the first build. The rules are few (make it `<public>`, keep `@editable` out, carry your own imports) but getting them right is the difference between reusing proven parts and rewriting them every time you start a new island.

Updated Examples verified on the live UEFN compiler

Professor AweShucks taps the master blueprint one last time. You built the Shell Hunt kit on the South Shores, the Codewood helpers in the North Jungle, the save module on the East Volcano — and in the last lesson the hub island imported every one of them. Here is the part Epic's documentation never quite says out loud: those modules are not welded to this island. A Verse module is just a file, and a file can board a boat. This lesson shows you how to package a zone kit so it drops into a DIFFERENT island project and compiles on the first build — which is how experienced creators ship island number two in a fraction of the time island number one took.

What you will build

The portable zone-kit trio — three modules packaged for travel, plus the proof device that runs them on a fresh island:

  • ShellHuntKit (from South Shores L9) — the game_phase enum and PhaseLabel announcer, unchanged from the beach.
  • CodewoodKit (from North Jungle L21/L28) — the ScoreLine helper and the kit_bus custom-event bus.
  • VolcanoSaveKit (from East Volcano) — the <persistable> save record and its RecordScore/BestScoreFor API.
  • kit_reuse_demo — a device on the NEW island that imports all three with three using lines and wires them to devices that exist only there.

This is the capstone's closing argument: the hub island (previous lesson) imports every zone kit, and now you can make ANY island do the same. The curriculum compounds — every module you finish is a part you never build again.

Walkthrough

Step 1 — the export checklist (what makes a module portable)

A module travels cleanly between island projects when four things are true. Pin this list above your desk:

  1. <public> on the module AND on every member the other island calls. Access specifiers are the customs office. A module member without <public> is internal — visible inside its own module, invisible to the importing file. The import line will work, but the first call to a hidden member is a compile error on the new island.
  2. No @editable fields inside the module. Modules are not devices; they have no Details panel and nothing to wire. Device references enter as function parameters — the importing island passes in ITS button, ITS HUD device (see Common patterns below).
  3. No project-local asset references. A creative_prop_asset dragged from THIS project's Content Browser does not exist in the next project. Keep asset references in the device layer; keep the kit pure logic.
  4. The kit carries its own using { } lines. If the module body calls Print or touches player, the module block imports /Verse.org/Simulation (and friends) itself. Never assume the host file imported them for you.

Step 2 — moving the file between projects

Epic gives you no package manager, but the mechanics are honest file plumbing:

  1. In the SOURCE project, open Verse Explorer, right-click ShellHuntKit.verseShow in Explorer to find it on disk.
  2. Copy the file into the NEW project's Verse folder (open the new project's Verse Explorer and use its folder on disk — same trick).
  3. Back in UEFN, the file appears in Verse Explorer. Run Build Verse Code. If the checklist above holds, it compiles with zero edits.
  4. In any script on the new island, write using { ShellHuntKit } and start calling.

One habit worth stealing: keep a Kits/ folder in every project and a master copy of each kit in source control. When a kit improves, the fix travels to every island that carries it.

Step 3 — the complete file

For this lesson the three kits and the demo device sit in ONE file so you can read the whole reuse story top to bottom — in a real project each := module: block lives in its own .verse file, and only the using lines and the device remain here.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# One localizable text helper for HUD devices.
KitMsg<localizes>(Text : string) : message = "{Text}"

# ============================================================
# ShellHuntKit -- packaged from SOUTH SHORES (L9).
# In your real project this whole module lives in its own file:
# ShellHuntKit.verse. That file is the thing you carry between islands.
# ============================================================
ShellHuntKit<public> := module:

    # The phase enum the Shell Hunt shipped with.
    game_phase<public> := enum:
        Waiting
        Ready
        Launch
        Reset

    # One readable announcer line per phase.
    PhaseLabel<public>(Phase : game_phase) : string =
        case (Phase):
            game_phase.Waiting => "Waiting for islanders to wash ashore..."
            game_phase.Ready => "Round starting - take your pads!"
            game_phase.Launch => "GO!"
            game_phase.Reset => "Resetting the beach..."

# ============================================================
# CodewoodKit -- packaged from NORTH JUNGLE (L21 modules + L28 events).
# File on disk: CodewoodKit.verse.
# ============================================================
CodewoodKit<public> := module:
    using { /Verse.org/Simulation }

    # HUD text helper the Mod Rally used for its scoreboard lines.
    ScoreLine<public>(Label : string, Value : int) : string =
        "{Label}: {Value}"

    # The custom event bus -- decouples devices from the brain
    # on ANY island that imports this kit.
    kit_bus<public> := class:
        PhaseChangedEvent<public> : event(ShellHuntKit.game_phase) = event(ShellHuntKit.game_phase){}

# ============================================================
# VolcanoSaveKit -- packaged from EAST VOLCANO (persistence module).
# File on disk: VolcanoSaveKit.verse.
# ============================================================
VolcanoSaveKit<public> := module:
    using { /Verse.org/Simulation }

    # One persistable record per player. Fields may be ADDED later
    # (with defaults) but NEVER removed or retyped once published.
    zone_save<public> := class<final><persistable>:
        Version<public> : int = 1
        BestScore<public> : int = 0

    # Module-scoped weak_map: loads on join, saves on every write.
    var SaveMap<public> : weak_map(player, zone_save) = map{}

    # Persistable records are snapshots: rebuild, then write back.
    RecordScore<public>(Player : player, Score : int)<transacts> : void =
        if (Existing := SaveMap[Player]):
            if (Score > Existing.BestScore):
                if (set SaveMap[Player] = zone_save{Version := Existing.Version, BestScore := Score}) {}
        else:
            if (set SaveMap[Player] = zone_save{BestScore := Score}) {}

    # Reads a player's best score, or 0 for a fresh castaway.
    BestScoreFor<public>(Player : player)<transacts> : int =
        if (Existing := SaveMap[Player]):
            Existing.BestScore
        else:
            0

# Import the kits -- the SAME three lines you write on any island
# that carries these files. This is the whole reuse story.
using { ShellHuntKit }
using { CodewoodKit }
using { VolcanoSaveKit }

# The proof device: a brand-new island running three zones' proven parts.
kit_reuse_demo := class(creative_device):

    # Wire both in the Details panel of the NEW island.
    @editable
    StartButton : button_device = button_device{}

    @editable
    TownCrier : hud_message_device = hud_message_device{}

    # The North Jungle event bus, instantiated fresh on this island.
    Bus : kit_bus = kit_bus{}

    var Phase : game_phase = game_phase.Waiting
    var Launches : int = 0

    OnBegin<override>()<suspends>:void =
        TownCrier.SetText(KitMsg(PhaseLabel(Phase)))
        TownCrier.Show()
        StartButton.InteractedWithEvent.Subscribe(OnStart)
        spawn { WatchPhase() }

    # Awaits kit_bus signals -- the CodewoodKit pattern, unchanged.
    WatchPhase()<suspends>:void =
        loop:
            NewPhase := Bus.PhaseChangedEvent.Await()
            TownCrier.SetText(KitMsg(PhaseLabel(NewPhase)))
            Print("Phase now: {PhaseLabel(NewPhase)}")

    OnStart(Agent : agent):void =
        set Phase = game_phase.Launch
        Bus.PhaseChangedEvent.Signal(Phase)
        set Launches += 1
        Print(ScoreLine("Launches", Launches))
        if (Player := player[Agent]):
            VolcanoSaveKit.RecordScore(Player, Launches * 100)
            Print(ScoreLine("Best score", VolcanoSaveKit.BestScoreFor(Player)))

Line-by-line: where each rule shows up

Lines What's happening
ShellHuntKit<public> := module: The module itself is <public> — rule 1, half one.
game_phase<public> := enum: Every exported member is <public> — rule 1, half two. Drop the specifier and the new island's var Phase : game_phase line fails to compile.
CodewoodKit ... using { /Verse.org/Simulation } The kit imports its OWN dependencies (rule 4) — it never leans on the host file's imports.
event(ShellHuntKit.game_phase) Kits may depend on each other — by full module name, so the dependency survives the boat ride. Ship them together.
zone_save<public> := class<final><persistable>: The volcano save record. Persistable classes must be <final>, with defaults on every field.
var SaveMap<public> : weak_map(player, zone_save) = map{} Module-scoped persistence: the engine loads a player's record on join and saves on every write — on whichever island the module lands.
using { ShellHuntKit } (×3) The entire import ceremony on the destination island. Three lines, three zones of proven parts.
@editable StartButton / TownCrier Device references live on the DEVICE, never in the kit (rule 2). The new island wires its own hardware.
Bus.PhaseChangedEvent.Signal(Phase) / .Await() The North Jungle event bus running unmodified in a new postcode.
VolcanoSaveKit.RecordScore(Player, ...) Calling a kit by qualified name also works — handy when two kits export the same function name.

Common patterns

Pattern 1 — devices enter as parameters, never as fields

The single most common porting failure is an @editable buried in a module. The fix is always the same shape — the kit declares WHAT it needs, the island provides WHICH one:

# PORTABLE: the kit takes the device as a parameter...
AnnouncerKit<public> := module:
    using { /Fortnite.com/Devices }

    KitText<localizes><public>(Text : string) : message = "{Text}"

    # The importing island hands us ITS hud_message_device.
    Announce<public>(Crier : hud_message_device, Line : string) : void =
        Crier.SetText(KitText(Line))
        Crier.Show()

using { AnnouncerKit }

# ...and only the device that LIVES on this island holds the @editable.
announcer_demo := class(creative_device):
    @editable
    Crier : hud_message_device = hud_message_device{}

    OnBegin<override>()<suspends>:void =
        Announce(Crier, "This line came from a kit that owns no devices.")

Pattern 2 — stamp every kit with a version

Two islands carrying different vintages of the same kit is a debugging swamp. One public constant drains it:

# Every kit carries a version constant -- one Print tells you
# which vintage of the kit a published island is actually running.
StampedKit<public> := module:
    KitVersion<public> : string = "1.2.0"

    Greet<public>() : string =
        "StampedKit v{KitVersion} reporting for duty"

using { StampedKit }

kit_version_probe := class(creative_device):
    OnBegin<override>()<suspends>:void =
        Print(Greet())

Where this goes next

This is the final Center Village lesson, and the proof was one lesson back: AweShucks Town (awe-shucks-town-hub) is itself an island that imports ShellHuntKit, CodewoodKit, IslandProgress, and the portal_gatekeeper — every zone's capstone piece, nailed into one bridge. From here, every new island you start begins with a Kits/ folder full of parts you already trust: the West Coves' RaceHelpers (shared-helper-module) slots in the same way, and the badge schema from cross-zone-progress-persistence gives any sequel island a shared player profile on day one. Build the part once, sail it everywhere. Professor AweShucks would call that graduating.

Build your own lesson with reuse_modules_across_islands

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →