Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

CrowdyPy

The official Python SDK for Crowded Kingdoms: typed clients for the platform's one GraphQL API (identity, studio administration, world data, ck-exec) and a native UDP replication core.

CrowdyPy follows the CrowdyJS API surface (the same domains, the same two-token model, the same error codes) the way CrowdyCPP does, with Python names (mintAppToken is mint_app_token). Its realtime path is CrowdyCPP's native replication client, bound with nanobind and shipped inside the wheel. Python never touches a datagram.

v0.4.0: headless Crowdy Studio, and full parity with CrowdyJS.

  • crowdypy.studio.CrowdyStudioController is the Studio editor without a browser: open projects, edit with autosave and revision conflicts, bind GitHub, apply agent patches with checkpoints, and build, deploy and run mods. StudioLayoutController and parse_rustc_diagnostics match CrowdyJS byte for byte.
  • The roots CrowdyCPP sends and CrowdyJS does not are wrapped too: email confirmation, usage projections, compute budgets, listing versions, the narrow Studio mutations, and client.crowdy_studio_agent.
  • crowdypy.player_host is the typed observation contract a game implements so tooling can read the player it controls, validated exactly as CrowdyJS validates it.
  • The parity gate is strict: docs/parity-matrix.md has no gaps.
  • 0.3.0 brought the World Stores, the Game Kit, ck-exec and subscriptions; 0.2.0 native UDP replication (client.udp); 0.1.0 the GraphQL client.

See MIGRATION.md.

Install

pip install crowdypy

Wheels: CPython 3.12+ (one abi3 wheel per platform) and free-threaded CPython 3.14, on Linux x86_64 / aarch64, macOS arm64 / x86_64 and Windows x64. OpenSSL is linked statically; nothing else native is needed. docs/compatibility.md has the details, including the tier versions below.

Each tier of the platform has its own release line, and a build defaults to its own tier's API origin (crowdypy.CROWDY_DEFAULT_TIER says which):

Tier PyPI version Pin
dev X.Y.Z.devN crowdypy==0.1.0.dev1
test X.Y.ZrcN crowdypy==0.1.0rc1
prod X.Y.Z crowdypy==0.1.0

pip skips pre-releases unless a requirement names one, so a prod range never picks up a dev or test build. The same wheels are attached to each GitHub release.

Quick start

import asyncio

import crowdypy

API = "https://ck.dev.crowdedkingdoms.com"


async def main() -> None:
    # The identity client: sign-in, account and studio calls, minting.
    async with crowdypy.AsyncCrowdyClient(http_url=API) as identity:
        await identity.auth.login("player@example.com", "correct-horse-battery")
        minted = await identity.portal.mint_app_token("42")

    # The game client: one per app, at the app's own datacenter.
    async with crowdypy.AsyncCrowdyClient(
        http_url=minted.game_api_url or API, discovery_url=minted.discovery_url
    ) as game:
        game.set_app_token(minted)
        print(await game.server_status.server_with_least_clients())


asyncio.run(main())

Without an event loop, crowdypy.sync.CrowdyClient has the same methods, without await:

from crowdypy.sync import CrowdyClient

with CrowdyClient(http_url=API) as identity:
    identity.auth.login("player@example.com", "correct-horse-battery")
    print(identity.users.me())

Two tokens, two clients

There is one API origin but two tokens, as in every Crowded Kingdoms SDK:

  1. Sign-in (auth.login, a magic link, or social/OIDC) yields an identity session token: account, studio administration and minting. It is not accepted for gameplay.
  2. Gameplay needs a short-lived app-scoped token per app (portal.mint_app_token(app_id)), which is also the HMAC key for native UDP. Refresh it with client.refresh_gameplay_token() (concurrent callers share one refresh).
  3. Build one identity client and one client per game. The game client points at the app's datacenter (game_api_url) with discovery_url set, so it can find the app again if that instance stops answering.

Persist them the same way: FileTokenStore.session_path(directory, api_origin) names the session file (one per origin, shared by every game) and FileTokenStore.app_path(directory, app_id) the gameplay one (one per app):

from crowdypy import FileTokenStore

identity = crowdypy.AsyncCrowdyClient(
    http_url=API, token_store=FileTokenStore(FileTokenStore.session_path(state_dir, API))
)

CrowdyPy sends no Origin header, so direct sign-in is served to it; hosted browser sign-in (portal.signIn in CrowdyJS) is a browser flow and is not part of this SDK.

Errors

Every error is a crowdypy.CrowdyError. A GraphQL refusal is a CrowdyGraphQLError with the server's stable code (error.code == "FORBIDDEN") and extensions; HTTP, network and timeout failures have their own classes. CrowdyAppUnavailableError (the app's datacenter is not serving) and CrowdyUserCodeFaultError (a player module faulted; player_fault_of(error) reads the fault) mirror CrowdyJS. A WRONG_DATACENTER refusal moves the client to the app's datacenter and retries once, by itself.

Replication

minted = await identity.portal.mint_app_token("42")
async with crowdypy.AsyncCrowdyClient(
    http_url=minted.game_api_url or API, discovery_url=minted.discovery_url
) as game:
    await game.udp.connect(minted)  # assign a server, open the socket
    game.udp.subscribe({"actor_update": lambda n: print(n.uuid, n.chunk, n.payload)})
    await game.udp.send_actor_update((0, 0, 0), my_uuid, pose_bytes)
    echo = await game.udp.send_actor_update_and_wait((0, 0, 0), my_uuid, pose_bytes)

For a game loop, use the connection underneath: send a frame's entities in one call, and read whole batches.

conn = game.udp.connection
conn.send_actor_updates(chunks, uuids, poses, stride=88)  # numpy (n, 3) int64, n x 32 octets
async for batch in conn.batches():
    actors = batch.types == crowdypy.wire.MessageType.ACTOR_UPDATE_NOTIFICATION
    positions = batch.chunks[actors]  # a view, not a copy

crowdypy.replication.ReplicationConnection is the same for code without an event loop: wait() and then poll() from your own loop.

World Stores

from crowdypy.codecs import f32, struct_codec, u16
from crowdypy.stores import create_world_session

pose = struct_codec({"x": f32(), "y": f32(), "z": f32(), "yaw": u16()})
session = create_world_session(game, app_id, actor_codec=pose, host=True, save=True)
session.self.join((0, 0, 0), {"x": 0.0, "y": 64.0, "z": 0.0, "yaw": 0})
session.actors.on_join(lambda actor: print("joined", actor.uuid, actor.value))
asyncio.create_task(session.run())  # tick 60 times a second

snapshot = session.actors.snapshot()  # once a frame: every actor at once
states = pose.decode_many(snapshot.state_offsets, snapshot.state_data)  # numpy, no loop

Crowdy Studio

from crowdypy.studio import CrowdyStudioController

studio = CrowdyStudioController.for_client(game, app_id=app_id, grid_id=grid_id)
await studio.initialize()  # opens the first project
studio.update_file("SERVER", "src/lib.rs", source)  # autosaves
result = await studio.test_draft()  # build, deploy and switch on the mod
print(result.status, studio.get_state().build_output)

A CLIENT target runs in a host you pass as broker_factory; the browser Studio runs it in a Web Worker.

Performance

Python does no work per datagram. Receive, HMAC verification and decoding happen on CrowdyCPP's network thread, which never takes the GIL; Python sees one batch object per poll. Sends release the GIL, and a batch releases it once for the whole batch. An asyncio loop is woken through a socket, not by polling. Measured against CrowdyCPP on the same machine (benchmarks/README.md): a 200-entity batch costs 1.06x CrowdyCPP's own send path per entity, Python takes in two million verified notifications a second, a notification reaches an asyncio handler 47 µs after it hits the wire (p50), and a 60 Hz loop stays within 0.2 ms of its schedule while receiving 60,000 a second.

Development

uv sync                       # builds the extension (CMake, a C++20 compiler) and installs dev tools
uv run pytest tests/unit
uv run ruff check . && uv run ruff format --check . && uv run mypy

The live suites in tests/e2e/ run against a deployment when the CROWDY_E2E_* variables are set; docs/e2e-coverage.md lists them, how to configure them and the last live runs.

The gates CI runs, all from a clean checkout:

Gate Command
Vendored CrowdyCPP is the pinned commit's tree python scripts/vendor_crowdycpp.py --check
schema.gql and operations are CrowdyJS's at the pin python scripts/schema_sync.py --check
Generated code is current python scripts/codegen.py --check
The blocking client is generated from the async one python scripts/unasync.py --check
Parity with CrowdyJS python tools/parity/parity.py --crowdyjs ../CrowdyJS --check docs/parity-matrix.md
No internal names in the tree or the artifacts python scripts/ci/check_content_policy.py

AGENTS.md covers maintenance: re-pinning CrowdyJS and CrowdyCPP, the tier branches and the release path.

License

MIT. Wheels also contain CrowdyCPP (MIT), yyjson (MIT) and OpenSSL (Apache-2.0).

Metadata

Release files for crowdypy 0.4.0.dev1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for crowdypy 0.4.0.dev1
File Size Uploaded
crowdypy-0.4.0.dev1.tar.gz 1.1 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for crowdypy 0.4.0.dev1
File
crowdypy-0.4.0.dev1-cp314-cp314t-win_amd64.whl CPython 3.14 CPython 3.14 free-threading Windows x86-64 Details
crowdypy-0.4.0.dev1-cp314-cp314t-musllinux_1_2_x86_64.whl CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ x86-64 Details
crowdypy-0.4.0.dev1-cp314-cp314t-musllinux_1_2_aarch64.whl CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ ARM64 Details
crowdypy-0.4.0.dev1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 free-threading Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
crowdypy-0.4.0.dev1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl CPython 3.14 CPython 3.14 free-threading Linux glibc 2.26+ ARM64, Linux glibc 2.28+ ARM64 Details
crowdypy-0.4.0.dev1-cp314-cp314t-macosx_11_0_x86_64.whl CPython 3.14 CPython 3.14 free-threading macOS 11.0+ x86-64 Details
crowdypy-0.4.0.dev1-cp314-cp314t-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 free-threading macOS 11.0+ ARM64 Details
crowdypy-0.4.0.dev1-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details
crowdypy-0.4.0.dev1-cp312-abi3-musllinux_1_2_x86_64.whl CPython 3.12 abi3 Linux musl 1.2+ x86-64 Details
crowdypy-0.4.0.dev1-cp312-abi3-musllinux_1_2_aarch64.whl CPython 3.12 abi3 Linux musl 1.2+ ARM64 Details
crowdypy-0.4.0.dev1-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 abi3 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details
crowdypy-0.4.0.dev1-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl CPython 3.12 abi3 Linux glibc 2.26+ ARM64, Linux glibc 2.28+ ARM64 Details
crowdypy-0.4.0.dev1-cp312-abi3-macosx_11_0_x86_64.whl CPython 3.12 abi3 macOS 11.0+ x86-64 Details
crowdypy-0.4.0.dev1-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details

Total release size: 39.5 MB

Release files / crowdypy-0.4.0.dev1.tar.gz

Download URL crowdypy-0.4.0.dev1.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
116fc8f42a451f4b15ee81ff0f90c0b208106c03c0928d446a58f1522221812e
BLAKE2b-256 checksum
How to use checksums
1bd770ee513f8e8e7ac5eb32f9c9871598a3fe69396d84c3de191b93cd3aca77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp314-cp314t-win_amd64.whl

Download URL crowdypy-0.4.0.dev1-cp314-cp314t-win_amd64.whl
Size 2.4 MB
Tags CPython 3.14 CPython 3.14 free-threading Windows x86-64
SHA-256 checksum
How to use checksums
345c055cd0033eb8a007fb6e629721b0c5dfe3df4a013fee6dfb94cb360db85b
BLAKE2b-256 checksum
How to use checksums
e375310c01ade3902b6c83f72d8bc16904535a1a64a1a05b87c64141e9e20b23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp314-cp314t-musllinux_1_2_x86_64.whl

Download URL crowdypy-0.4.0.dev1-cp314-cp314t-musllinux_1_2_x86_64.whl
Size 2.8 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
9b41ec9bd144207faca4b5eaa5de9fbf37657500304cfd8da6b164ad6ebc6f82
BLAKE2b-256 checksum
How to use checksums
a421578bc1644c90a97187d66fd89af7a100df431326e9f40c53b13f68682525
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp314-cp314t-musllinux_1_2_aarch64.whl

Download URL crowdypy-0.4.0.dev1-cp314-cp314t-musllinux_1_2_aarch64.whl
Size 3.1 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
6bb6ccc1c5520c981178c45af4a63227e0f0aa38a3ab6cf6f77c299152a8cfbf
BLAKE2b-256 checksum
How to use checksums
054d55fff1a1ec577d2bd80e990ab855be903e173fe43f07ade26a6da2a73fbc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL crowdypy-0.4.0.dev1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 2.7 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
15687cbb7c1ff6081934d9ca3bb2aef7879b17a93e42906c0c67d9c131ac09e6
BLAKE2b-256 checksum
How to use checksums
c13988b0f7a03b2eb24b61fd466ddded2097c58f6b5c688fe499f7822e8ae20e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl

Download URL crowdypy-0.4.0.dev1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl
Size 3.0 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux glibc 2.26+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
d96ed68629a6c4e06947791e650207b316bc81d2396188230ca0ee64cec35a91
BLAKE2b-256 checksum
How to use checksums
f0465e178e20df5528e8311a6ebb3c954932082b8fc8b76ae72d1a927430df5b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp314-cp314t-macosx_11_0_x86_64.whl

Download URL crowdypy-0.4.0.dev1-cp314-cp314t-macosx_11_0_x86_64.whl
Size 2.4 MB
Tags CPython 3.14 CPython 3.14 free-threading macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
7f0902d4ab9c6e1709bf4ee43023a233b8e466aa903cd0832a6393e0dfb01051
BLAKE2b-256 checksum
How to use checksums
73994b041d13c491d0ff1be40414172fdea623c00047e84b7f222e53c4e2bf1d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp314-cp314t-macosx_11_0_arm64.whl

Download URL crowdypy-0.4.0.dev1-cp314-cp314t-macosx_11_0_arm64.whl
Size 2.7 MB
Tags CPython 3.14 CPython 3.14 free-threading macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
57eafb4e3e38b3b4cb25fa50428666cdd9bab10f66ad7d017a063626e2764af6
BLAKE2b-256 checksum
How to use checksums
c98ccb3a744220a5a64d507f5ba9b31f8e995edd11d42702862a4ce2f945e5db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp312-abi3-win_amd64.whl

Download URL crowdypy-0.4.0.dev1-cp312-abi3-win_amd64.whl
Size 2.4 MB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
eade5f9737da901b3db051fcc8f6a4230a9e7da8c86988421b55b789cb076aa2
BLAKE2b-256 checksum
How to use checksums
5b133d80d5c88db4b9d248f10099579ea0a80eb18fce6c830de197bc87cec633
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp312-abi3-musllinux_1_2_x86_64.whl

Download URL crowdypy-0.4.0.dev1-cp312-abi3-musllinux_1_2_x86_64.whl
Size 2.8 MB
Tags CPython 3.12 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
e577c027c84449450353cd274d6f6f44b74f174397a3fb6621211d6263b71a0f
BLAKE2b-256 checksum
How to use checksums
0f57868458f940f1d1f0e8fe9534e0492bdb89eb85a87041387b045273697de7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp312-abi3-musllinux_1_2_aarch64.whl

Download URL crowdypy-0.4.0.dev1-cp312-abi3-musllinux_1_2_aarch64.whl
Size 3.1 MB
Tags CPython 3.12 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
3ad65016cbece32d7d528cfe0ef641735d8950455bad4e238737baa0c7c3951e
BLAKE2b-256 checksum
How to use checksums
5fa32dd5ecf3f166f173de26eb16d327f058a684a8e918000750a10d57a585c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL crowdypy-0.4.0.dev1-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 2.7 MB
Tags CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
560834e5262b78fe15ea70b94a347abdaca6cf01a791539c8d3d0dbab45b37c3
BLAKE2b-256 checksum
How to use checksums
2ef651d6678376e20454f581ed97bf42126342e18513068e89d678c566be00d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl

Download URL crowdypy-0.4.0.dev1-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl
Size 3.0 MB
Tags CPython 3.12 Linux glibc 2.26+ ARM64 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
a7522d447da8540457c4256b4188edc530c3c11ad2f697d72ec24ad261d4ca0a
BLAKE2b-256 checksum
How to use checksums
37897db038fd278158ce5920e0eea69e8e3e35ea845ece0260b9a94ef369e2e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp312-abi3-macosx_11_0_x86_64.whl

Download URL crowdypy-0.4.0.dev1-cp312-abi3-macosx_11_0_x86_64.whl
Size 2.4 MB
Tags CPython 3.12 abi3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
31685d1855c4dfb3f23ca27f740f0911b7d3c090e0029f547a42b624fc8e1c63
BLAKE2b-256 checksum
How to use checksums
d1b7f99edc86fd83fd6673088e72076d0482cfd1829c8128c11ca8b6c2476573
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / crowdypy-0.4.0.dev1-cp312-abi3-macosx_11_0_arm64.whl

Download URL crowdypy-0.4.0.dev1-cp312-abi3-macosx_11_0_arm64.whl
Size 2.7 MB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
5ba9882b55915796419c1e41d5aac8bcb1361f6a1138fd3c31a64c6d391e1dfd
BLAKE2b-256 checksum
How to use checksums
9827ec36d86a36ddf2e0c963be90f84a40a90394ca744697cdbbb839111cb419
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page