Skip to main content

For AI Agents

This page is the routing layer for a coding agent working inside a studio's Unreal project. It does not re-explain the SDK; every row links the page that owns the fact, and the text of every page is available as plain-text files, one per section. Read this page, then pull only the pages a task needs.

Machine-readable files sit beside the docs. This page is where they are described. Start at the index, pull one section file, and check the slim symbol index before the full manifest.

FileSizeUse it when
/helpers/unreal-sdk/llms.txtabout 42 KBYou are starting a task. Every page with its URL and a one-line description, grouped into sections, each with the URL and size of its section file.
/helpers/unreal-sdk/llms-<section>.txt: start, concepts, studio, runtime, mass, exec, services, guides, reference20 to 430 KB eachYou know which part of the SDK the task touches. Every page of that section as plain text, with C++ snippets inlined and a pointer line for each Blueprint graph. A section over 200 KB is split by subfolder: exec and mass also have llms-exec-from-blueprint.txt, llms-exec-from-cpp.txt, llms-exec-examples.txt and the same three for mass; llms-exec.txt then holds only the section's top-level pages. The index lists every file with its size.
/helpers/unreal-sdk/sdk-surface-index.jsonlabout 340 KBYou need to know whether a symbol exists, which module and header it lives in, or where to look. One line per symbol: name, kind, module, header, and the first sentence of its help text where the manifest has one (console variables only today). Line one names the columns.
/helpers/unreal-sdk/sdk-surface.jsonabout 3 MBThe index is not enough: you need a function's specifiers and return type, a struct's metadata, an enum's values or a delegate's signature. Too large to read whole; query it with jq or a script. See SDK surface manifest.
/helpers/unreal-sdk/llms-full.txtabout 1.6 MBA tool wants every page in one file. About 400,000 tokens, so it does not fit a context window. Prefer the section files.
/helpers/unreal-sdk/AGENTS.mdunder 8 KBA drop-in for a project root, CLAUDE.md, or .cursorrules: the locked rules and one example per marker.

Where does this state go​

Ask one question: can a malicious client benefit from lying about this value? If yes, it belongs on the truth plane, in your app's server code on ck-exec, which the SDK reaches from C++ or Blueprint through Server Logic. If no, it is view state. The Two Planes owns the rule and every row but the door's; that one applies the same test.

StatePlaneMechanismOwning page
MovementViewDynamic entity mode, continuous state channelContinuous state
Animation blend stateViewmeta=(CrowdyState) property, diffed on changeCrowdy State
A one-shot trigger (muzzle flash, impact)Viewmeta=(CrowdyEvent) RPC, not a state fieldRPC events
Hit points, inventory, currencyTruthYour server codeThe Two Planes
Match scoreTruthA hub for the match in your server codeThe Two Planes
A chat lineViewCrowdyEvent with the Multicast recipient, session channelChannels
A door's open flagViewCrowdyState boolean: a lie is a visual glitch. A placed door is host-owned, so the flag is also CrowdyManualDirty and the host calls MarkStateDirty after each write. If the door gates progress (a lock), the lock state belongs in your server codeThe Two Planes, the host push
A player's display nameViewCrowdyState property; route it through the avatars service if it must persistThe Two Planes, Avatars
Authoritative or cheat-sensitive state never belongs in Crowdy State.

Crowdy State is written by the owning client and believed by everyone else. Low latency is not a reason to move a value to the view plane; predict locally and let the server's confirmed value overwrite the prediction. The Two Planes.

The marker vocabulary​

Two of the three are meta=(...) keys; the entity is a component. One canonical example each, all from the same lantern the Quickstart builds; the snippet id names the file the docs render.

MarkerGoes onCanonical exampleOwning page
CrowdyEntityA UCrowdyEntityComponent on an AActor (the component is the marker; set Ownership)qs-entityEntities, identity, ownership
CrowdyState (+ CrowdyOnRep)A UPROPERTY on an entity actor. On a client-owned entity (Ownership = Local Client, the lantern) the owner assigns and calls the notify itself; on a host-owned entity (Ownership = Host, the default a placed actor keeps) a plain assignment never ships: add CrowdyManualDirty and call MarkStateDirty after each write, or add CrowdyHeartbeatqs-stateCrowdy State, the host push
CrowdyEvent + CrowdyRecipientA UFUNCTION named X_Implementation, plus CROWDY_EVENT(X); recipient is one of SpatialMulticast, Multicast, OwningClient, Hostqs-eventRPC events

Four rules the examples assume: the first thing a client needs is its own pawn as a Dynamic, PlayerDerived, LocalClient entity spawned after On UDP Connection Success (qs-player), because the server knows a client by its actor updates and a SpatialMulticast reaches only clients with a fresh actor; that pawn registers inside its first possession, since the engine possesses a runtime spawn after its BeginPlay, so read its id and IsLocallyOwned() from OnCrowdyOwnershipAssigned, not at BeginPlay; a SpatialMulticast body runs on the caller too, so guard the call with IsLocallyOwned() (RPC events); a CrowdyOnRep function takes no parameters, read the property for the new value (Crowdy State); and the lantern's assign-and-stop is the client-owned pattern, because the Quickstart sets its Ownership to Local Client: a host-owned entity (Ownership = Host, the default a placed actor keeps: Static, Stable, level-placed) never ships a plain CrowdyState write, so mark the property CrowdyManualDirty and call MarkStateDirty after each write, or give it CrowdyHeartbeat (the host push).

Three things that break silently in a cooked build​

Editor and Play in Editor hide all three. Only a packaged build shows them.

BreakWhy it is silentFixOwning page
Reading HasMetaData at runtimeCooked and Shipping builds strip UObject metadata; the check answers false and marker-driven code goes dark with no errorRead UCrowdyBakedRegistry; the SDK already does this for RPC and Crowdy State metadataPackaging
A marker added after the last cookThe baked registry is a snapshot taken at cook time; a new marker is invisible until it is rebakedCook again: the cook rebakes the registry at its start, so the break is a package cooked before the marker existed. Tools > Rebuild Crowdy Registry or Studio's Rebuild (Deep Scan) only refresh the in-editor view of the bakePackaging, Inspector and Registry
A placement guid read at BeginPlayThe engine releases the per-placement guid before BeginPlay in a cooked build; the editor keeps it aliveRead placement data at OnRegister, never at BeginPlayStable identity in a packaged build

A fourth hazard is loud, not silent: Shipping sets WITH_DEV_AUTOMATION_TESTS to 0, and a target that forces dev tests back on can then fail to compile a test file through a !UE_BUILD_SHIPPING region it reaches via a header. What Shipping strips.

Settings never to hand-edit​

Config Sync in Crowdy Studio writes the Network category of UCrowdySDKDeveloperSettings into Config/DefaultGame.ini under [/Script/CrowdyReplication.CrowdySDKDeveloperSettings]. The fields are read-only in Project Settings on purpose; a hand-pasted value is overwritten by the next sync, and a drifted project fails to connect with no obvious error.

Never hand-typeOwned by you (fine to edit)Owning page
Environment, DiscoveryUrl, AppID, OrgId, GameApiHttpUrl, GameApiWsUrl, UDPProtocol, UDPTimeoutSeconds, HostPollIntervalSecondsMapProfiles, DefaultProfile, BakedRegistry, PreloadedEntityClasses, IDOverrides, ClassIDOverridesConfig Sync, Project settings

Do not copy the plugin's [CoreRedirects] into the project's DefaultEngine.ini either; Plugins/CrowdySDK/Config/DefaultCrowdySDK.ini already carries them. Packaging.

Drive the editor without a window​

An agent cannot press editor buttons. Two commandlets do what the Server Compute panel and Crowdy Studio's Project page do, from a shell:

UnrealEditor-Cmd.exe <Project>.uproject -run=CrowdyServerCompute -op=<op> [switches] [-out=result.json]
UnrealEditor-Cmd.exe <Project>.uproject -run=CrowdyStudio -op=<op> [switches] [-out=result.json]

Exit code 0 is success, 1 is a refusal or a failure, and -out= writes the result as JSON (ok, error, and the op's own fields). An op that changes the project or the app needs -yes; without it the op says what it would do and changes nothing. A commandlet run with an unknown or no -op prints every op it has.

Close the Unreal Editor first. It holds the project's Server Object types and settings in memory and saves its own copy over what an op wrote, so an op that writes the project refuses while an editor runs unless you pass -editoropen.

StepOpOwning section
Check the sign-in and the project's app-run=CrowdyServerCompute -op=whoamiFrom the command line
List organizations, apps and datacenters-run=CrowdyStudio -op=appsSync from the command line
Create an app-run=CrowdyStudio -op=createapp -org=<id> -name="My Game" -slug=my-game -datacenter=<code> -yesSync from the command line
Point the project at an app-run=CrowdyStudio -op=sync -app=<id> -yesSync from the command line
Read every entity class's CrowdyEvent routing and CrowdyState flags-run=CrowdyStudio -op=registry [-class=<substring>] [-norebuild] [-out=file]Sync from the command line
Add Game Kit starters-op=addstarter -starter=<name>Add a starter
Define a Server Object type-op=exporttype -spec=type.json (a template), -op=newtype -spec=@type.json, -op=edittype -type=T -spec=@type.json -yesDefine a type from a file
Generate its server code-op=generate -type=T, or -op=generate -yes for every type that is out of dateFrom the command line
Remove or rename a type-op=removetype -type=T -yes, -op=renametype -type=T -to=/Game/Path/NewName -yes (without -yes they only say what they would do)From the command line
Write its logicEdit Server/<Type Name>/src/logic.rsWrite logic.rs
Deploy, call and read back-op=deploy -yes, -op=call -type=T -fn=F -key=K -inputs=@in.json -yes, -op=logs [-level=errors], -op=instancesFrom the command line
Move running Server Objects to the new deploy-op=restart -type=T -yes, or -op=restart -yes for every type with an instance on older codeFrom the command line
Set up access features, teams, channels and grids-run=CrowdyStudio -op=features, -op=definefeature, -op=createteam, -op=createchannel, -op=creategrid, -op=grantgrid and more (comma lists without spaces: -keys=a,b)Access, teams, channels and grids

-op=generate reports, per type, the functions logic.rs is missing and the ones it defines that the type no longer has, so the next edit is known. -op=batch -ops=<file> runs many ops in one editor start.

Who owns which file:

FileOwner
Server/<Type Name>/src/logic.rs, and any src/logic/<name>.rs it declares with mod <name>;You
Cargo.toml, src/lib.rs, src/types.rs in the same folderGenerate rewrites them; never edit
Server/<Type Name>/revisions.jsonServer Compute: every kept deploy's code in full, so a restore is a copy
GameKitWorld, GameKitActorStarters, PreloadedEntityClasses and the map profiles in Config/DefaultGame.iniYou
The Network settings in Config/DefaultGame.iniConfig Sync, or -run=CrowdyStudio -op=sync

Two things still need a person. Crowdy Studio's sign-in is once per machine and again when it expires; -op=whoami says whether it is there. The Blueprint-only editors (the Crowdy Replication variable panel, Crowdy Replicates events) have no text form; write the C++ markers instead. The sbx.* console commands exist only in the SDK's own Sandbox project, not in yours.

Testing without a second client​

Both switches are off by default. Turn them back off before testing real two-client routing. Testing locally.

Console variableWhat it doesOwning section
crowdy.rpc.loopback 1A sent CrowdyEvent is also delivered to your own receive path, once; a call made from inside the replayed body does not loop againSingle-client RPC loopback
crowdy.state.loopback 1A state delta for an owned entity is decoded onto a lazily spawned local mirror (RemoteProxy role, its own NetID), so OnRep fires with one client. The mirror's trace line always says (targeted); that label is cosmeticSingle-client Crowdy State loopback

For a headless smoke test in a development build, crowdy.auth.signin.file <path> signs in a test account from a JSON file ({"email": "...", "password": "..."}; never type a password on a console line), and crowdy.auth.status reports SignedIn=true once it has finished: poll it before player-side commands. Sign in from the console.

Trace console variables​

Every area exposes crowdy.<area>.trace, off by default; warnings and errors print regardless. The four to reach for first, and the two to leave alone:

Console variableShowsOwning page
crowdy.rpc.traceEvery CrowdyEvent send and receive: function, entity, addressing, owned or delegatedTesting locally
crowdy.state.traceOwned-entity diffing, delta emission, datagram sizesTesting locally
crowdy.entity.traceRegistry add and remove, event routing and dispatch, actor trackingTesting locally
crowdy.net.traceUDP transport: socket open and close, sends and receives, pool churnTesting locally
crowdy.serialize.scopes, crowdy.state.scopesCPU trace scopes around every message decode, a real per-message cost; leave off unless reading that timingConsole variables

The full table, including behavior switches and diagnostic commands, is on Console variables; which log category pairs with which trace is on Log categories.

Locked rules in one place​

RuleOwning page
Every id is int64; the app id travels as a JSON string and the SDK encodes it, never youThe Two Planes
The host is a convention, not enforcement; never gate cheat-sensitive state on itHost is a convention, Host authority
Presence is the player's actor: a client with no fresh actor is at no position and receives no spatial eventSessions and presence
A world subsystem null-checks GetGameInstance() in Initialize(); it is null in the transient world at engine startHost authority, Replicated subsystems
An array parameter on a delegate handler is const TArray<T>&; a by-value parameter does not match and AddDynamic will not compileTeams
A Multicast call has a 1024-byte payload budget and is dropped loudly, never truncated; large payloads go on SpatialMulticastRPC events