Engine integration
CrowdyCPP is designed to be the foundation for engine-specific SDKs, including the official Crowdy Unreal SDK (docs: Unreal SDK guide). The design rules that make it wrappable:
- No engine types, ever. The public API uses
std::span,std::string_view, and POD structs. Nothing allocates withnewon hot paths or leaks platform handles, so an engine can marshal at whatever boundary it chooses. - Pluggable platform services. Engines inject their own implementations of the small platform interfaces.
- Threading stays with the engine. Manual-pump mode spawns no SDK threads at all.
- Binary state stays binary. Actor-state payloads are opaque bytes end to end — no base64, no JSON, no intermediate copies.
- The session layer maps to entity systems. The WorldSession stores are exactly the inputs an engine's replication components need.
The Unreal recipe
The expected integration shape for an Unreal plugin:
- ThirdParty static library. CrowdyCPP builds as a static library in a
ThirdPartymodule of the plugin. The plugin's subsystems (connection, entities, voice, teams, persistence) become thin adapters overcrowdy::CrowdyClient,crowdy::replication, andcrowdy::session::WorldSession, keeping the Blueprint-facing API stable. - HTTP via
IHttpTransport. WrapFHttpModuleso all GraphQL traffic uses the engine's HTTP stack, proxies, and certificate handling. (Alternatively link the default libcurl transport — Unreal ships libcurl and OpenSSL in its own ThirdParty tree.) - Crypto via
ICrypto. Bind the engine's bundled OpenSSL for HMAC-SHA256. ILogger/IAllocator/IClock. Adapters ontoUE_LOG,FMemory, and engine time, so SDK activity shows up in engine tooling.- Manual pump on an
FRunnable. Runpump()on anFRunnablenetwork thread (or the task graph) andpoll()on the game thread from a ticker. Callbacks therefore fire on the game thread, whereUObjects are safe to touch — the SDK spawns no threads in this mode. See Replication client → Threading modes. - The 88-byte pose codec.
crowdy::session::UnrealPoseis the 88-byte actor-state layout (version byte, position/rotation/velocity as f64 triples, crouch flag, attachment enum) that interoperates with the current Unreal SDK's pose format — the same layout the open-source cks-loadtest tool uses (see Load testing). An Unreal wrapper maps its entity snapshots directly into the payload span. - WorldSession → entity systems. The remote-actor registry (staleness, sample history) drives owner/proxy entity components and interpolation; the chunk cache backs voxel/terrain streaming; the inboxes back chat and direct messages; host tracking backs host-authority checks.
Other engines
The same recipe applies beyond Unreal:
- Custom C++ engines consume the library directly — link
CrowdyCPP::crowdy, choose owned-thread or manual-pump mode, and feed the session stores into your own entity and terrain systems. - Godot wraps it in a GDExtension: manual pump from a background thread
(or
_process), signals emitted from thepoll()site, and Godot's crypto and HTTP optionally injected through the same interfaces. - Unity goes through a thin C shim: export a C ABI over the client, connection, and session objects, P/Invoke from C#, and keep the state structs blittable so payloads cross the boundary without marshalling.
In every case the division of labor is the same: CrowdyCPP owns protocol, signing, correlation, and state bookkeeping; the engine owns threads, memory visibility, and object lifetimes at the boundary.