Reference Verse compiles

rush: Run Async Tasks Side-by-Side

When your island needs two things to happen at the same time — a countdown timer ticking while a cinematic plays on the clifftop dock — `rush` is your tool. It launches two or more async expressions concurrently and waits until ALL of them finish. Think of it as the async equivalent of "go, go, go" shouted at your whole crew at once.

Updated Examples verified on the live UEFN compiler
Watch the Knotrush in ~90 seconds.

Overview

rush is a Verse concurrent-flow expression that runs two or more <suspends> (async) blocks simultaneously and completes only when every branch has finished. It is the counterpart to race (which completes when the first branch finishes).

Use rush when:

  • You need a timer counting down while a cinematic sequence plays, and the game should only continue once both are done.
  • You want to award points to multiple players in parallel rather than one after another.
  • You need to teleport a player and start a score animation at the same moment, waiting for both to settle before moving on.

Key rule: every branch of a rush must be an async expression (a call to a <suspends> function, or an Await on a listenable). The whole rush block itself is <suspends>, so it must live inside another async context such as OnBegin.

API Reference

(API surface could not be resolved for this device.)

Walkthrough

Scenario — Clifftop Dock Sprint Challenge

A player steps onto a pressure plate at the top of a sun-drenched clifftop. Two things must happen at the same time:

  1. A countdown timer starts (the player has 30 seconds to reach the dock below).
  2. A cinematic sequence plays — a dramatic cel-shaded swooping camera over the cove.

Only after both the cinematic finishes and the timer resolves (success or failure) does the game award points and teleport the player to the respawn pad.

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

# CliftopRushDevice — place this Verse device on your island alongside
# a timer_device, cinematic_sequence_device, score_manager_device,
# teleporter_device, and player_spawner_device.
cliftop_rush_device := class(creative_device):

    # Wire these up in the UEFN Details panel
    @editable
    RaceTimer : timer_device = timer_device{}

    @editable
    IntroSequence : cinematic_sequence_device = cinematic_sequence_device{}

    @editable
    ScoreManager : score_manager_device = score_manager_device{}

    @editable
    DockTeleporter : teleporter_device = teleporter_device{}

    @editable
    RespawnPad : player_spawner_device = player_spawner_device{}

    # Called once when the island starts
    OnBegin<override>()<suspends>:void =
        # Wait for the timer's SuccessEvent — that fires when a player
        # reaches the dock trigger that calls RaceTimer.Stop(Agent)
        RaceTimer.SuccessEvent.Await()
        # (In a real island you'd subscribe to a trigger at the dock
        #  that calls HandleDockReached; here we demo the rush pattern.)

    # Called externally (e.g. from a trigger_device subscription) when
    # a player steps onto the clifftop pressure plate.
    StartChallenge(Agent : agent) : void =
        spawn { RunChallengeAsync(Agent) }

    # The core async routine — this is where rush lives.
    RunChallengeAsync(Agent : agent)<suspends> : void =
        # Cast agent → player so we can pass it to devices that need player
        if (P := player[Agent]):
            # ── RUSH: timer + cinematic run concurrently ──────────────────
            # The rush block suspends until BOTH branches complete.
            rush:
                # Branch 1 — start the 30-second countdown for this player.
                # We await either SuccessEvent or FailureEvent so the branch
                # ends as soon as the timer resolves.
                AwaitTimerResult(Agent)

                # Branch 2 — play the swooping clifftop cinematic.
                # cinematic_sequence_device.Play() is fire-and-forget, so we
                # pair it with an Await on StoppedEvent so the branch suspends
                # until the sequence ends naturally.
                AwaitCinematic(Agent)

            # ── Both branches done — now award points and teleport ────────
            ScoreManager.Activate(Agent)
            DockTeleporter.Activate(Agent)

    # Async helper: starts the timer for Agent and waits for it to finish.
    AwaitTimerResult(Agent : agent)<suspends> : void =
        RaceTimer.Start(Agent)
        # SuccessEvent and FailureEvent both send ?agent; we await either.
        rush:
            RaceTimer.SuccessEvent.Await()
            RaceTimer.FailureEvent.Await()
        # Timer has resolved (success or failure) — branch is done.

    # Async helper: plays the cinematic for Agent and waits until it stops.
    AwaitCinematic(Agent : agent)<suspends> : void =
        IntroSequence.Play(Agent)
        # StoppedEvent sends tuple() — just await it.
        IntroSequence.StoppedEvent.Await()

Line-by-line explanation

Lines What's happening
@editable fields Each UEFN device is wired in the Details panel — mandatory for creative_device references.
StartChallenge A plain (non-async) entry point; it spawns the async work so it doesn't block the caller.
rush: block Launches AwaitTimerResult and AwaitCinematic simultaneously. Neither blocks the other. The rush suspends RunChallengeAsync until both helpers return.
AwaitTimerResult Calls RaceTimer.Start(Agent) then itself uses a nested rush to await whichever of SuccessEvent/FailureEvent fires first — because only one will fire, the inner rush finishes as soon as either resolves.
AwaitCinematic Calls IntroSequence.Play(Agent) (fire-and-forget), then Awaits StoppedEvent so the branch doesn't end prematurely.
Post-rush lines ScoreManager.Activate(Agent) and DockTeleporter.Activate(Agent) run only after both branches are complete — guaranteed ordering.

Common patterns

Pattern 1 — Parallel score increments for two players

Award a bonus to two players at the same instant rather than sequentially.

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

dual_score_device := class(creative_device):

    @editable
    BonusScorer : score_manager_device = score_manager_device{}

    @editable
    PlayerRefA : player_reference_device = player_reference_device{}

    @editable
    PlayerRefB : player_reference_device = player_reference_device{}

    OnBegin<override>()<suspends>:void =
        # Wait for both player references to be activated before awarding.
        rush:
            PlayerRefA.ActivatedEvent.Await()
            PlayerRefB.ActivatedEvent.Await()
        # Both players are now registered — award each a bonus point.
        AwardBothAsync()<br>

    AwardBothAsync()<suspends>:void =
        # Fire Increment for each player concurrently, then Activate.
        # (Increment + Activate is the score_manager pattern for bonus points.)
        rush:
            GrantBonus(PlayerRefA)
            GrantBonus(PlayerRefB)

    GrantBonus(Ref : player_reference_device)<suspends>:void =
        Ref.Activate()          # triggers the device's relay logic
        Sleep(0.0)              # yield so both branches truly overlap

Pattern 2 — Teleport + respawn simultaneously

Send a player through a rift while a fresh spawn pad activates, so they land immediately.

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

rift_respawn_device := class(creative_device):

    @editable
    ShoreRift : teleporter_device = teleporter_device{}

    @editable
    DockSpawn : player_spawner_device = player_spawner_device{}

    OnBegin<override>()<suspends>:void =
        # Subscribe to the rift's EnterEvent so we react each time someone steps in.
        ShoreRift.EnterEvent.Subscribe(OnPlayerEnterRift)

    OnPlayerEnterRift(Agent : agent) : void =
        spawn { DoRiftAndSpawn(Agent) }

    DoRiftAndSpawn(Agent : agent)<suspends>:void =
        if (P := player[Agent]):
            rush:
                # Branch 1: teleport the player to the destination group.
                TeleportAsync(Agent)
                # Branch 2: enable the dock spawn pad so it's ready on arrival.
                EnableSpawnAsync(P)

    TeleportAsync(Agent : agent)<suspends>:void =
        ShoreRift.Activate(Agent)
        # Await confirmation the player emerged from the rift.
        ShoreRift.TeleportedEvent.Await()

    EnableSpawnAsync(P : player)<suspends>:void =
        DockSpawn.Enable()
        DockSpawn.SpawnPlayer(P)
        Sleep(0.0)   # yield so branch lifetime overlaps with TeleportAsync

Pattern 3 — Timer urgency + cinematic pause, then score

When the timer enters urgency mode, pause the cinematic and increment the score at the same time.

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

urgency_pause_device := class(creative_device):

    @editable
    CoveTimer : timer_device = timer_device{}

    @editable
    CoveSequence : cinematic_sequence_device = cinematic_sequence_device{}

    @editable
    ThrillScore : score_manager_device = score_manager_device{}

    OnBegin<override>()<suspends>:void =
        CoveTimer.StartUrgencyModeEvent.Subscribe(OnUrgency)

    OnUrgency(MaybeAgent : ?agent) : void =
        if (A := MaybeAgent?):
            spawn { HandleUrgencyAsync(A) }

    HandleUrgencyAsync(Agent : agent)<suspends>:void =
        # Pause the cinematic AND increment the score at the exact same moment.
        rush:
            PauseCinematicAsync(Agent)
            IncrementScoreAsync(Agent)

    PauseCinematicAsync(Agent : agent)<suspends>:void =
        CoveSequence.Pause(Agent)
        Sleep(0.0)

    IncrementScoreAsync(Agent : agent)<suspends>:void =
        ThrillScore.Increment(Agent)
        ThrillScore.Activate(Agent)
        Sleep(0.0)

Gotchas

1. rush vs race — know which you need

rush waits for all branches to finish. race waits for the first branch to finish and cancels the rest. Use race when you want "whichever happens first wins" (e.g., player reaches goal OR timer expires). Use rush when every branch must complete before you proceed.

2. Every branch must be async

Each expression inside a rush block must be a <suspends> call or an Await. A plain void call compiles but returns instantly, making that branch a no-op for concurrency purposes. If you need a synchronous side-effect in a branch, wrap it in a tiny helper that calls Sleep(0.0) to yield.

3. rush itself is <suspends>

The entire rush block is an async expression. It must live inside a <suspends> function (like OnBegin or a helper you spawn). You cannot call rush at the top level of a non-suspending function.

4. Unwrap ?agent before device calls

SuccessEvent and FailureEvent on timer_device send ?agent (optional agent). Always unwrap: if (A := MaybeAgent?): before passing to device methods that expect agent.

5. listenable(?agent) — Await returns the optional

When you Await an event typed listenable(?agent), the result is ?agent. Store it and unwrap:

MaybeAgent := CoveTimer.SuccessEvent.Await()
    if (A := MaybeAgent?):
        ScoreManager.Activate(A)

6. Localized text for message params

If any device you call takes a message parameter (e.g., display text on a player_reference_device), you cannot pass a raw string. Declare a localizer:

MyLabel<localizes>(S:string):message = "{S}"

and pass MyLabel("Dock Sprint!"). There is no StringToMessage function.

7. intfloat is not automatic

timer_device.Start and score operations use typed values. If you compute a duration as int, cast explicitly: Float := float(MyInt). Verse will not silently coerce numeric types.

Guides & scripts that use rush

Step-by-step tutorials that put this object to work.

Build your own lesson with rush

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 →