bacommontools package¶
Tools functionality shared by all Ballistica components.
Submodules¶
bacommontools.bacloud module¶
A tool for interacting with ballistica’s cloud services. This facilitates workflows such as creating asset-packages, etc.
- class bacommontools.bacloud.StateData(login_token: str | None = None)[source]¶
Bases:
objectPersistent state data stored to disk.
bacommontools.bacloudsession module¶
bacloud’s live session to a basn node.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.
One SmartSocket session replaces the request-per-HTTPS-handshake
conversation bacloud used to have. There is no mint step and no HTTPS
request at all: the client dials /bacloudsession on the node it
already resolved, and the handshake itself creates the channel. Every
command after that rides the session – including each
end_command continuation and every streamed command’s output.
What the session buys, measured rather than assumed (2026-08-15): streamed output pushed as produced instead of on a 0.25-5s poll cadence, one connection instead of two for a streamed command, one recovery implementation instead of several (reconnects are invisible here; deaths are not), and a far end that finds out when we quit. Notably not per-command latency – that measured the same as the request-per-connection path it replaced, because urllib3 pooling had already amortized the TLS handshake across a run.
Sequential by contract. Send a request, read until its response, repeat. No correlation ids and no multiplexing: bulk transfers were never on this channel – uploads and downloads go direct to storage on their own connections – so nothing here needs to overlap.
Threading. bacloud is synchronous and stays that way. The session
runs an asyncio loop on its own thread and BacloudSession.request()
is an ordinary blocking call, so nothing above it has to know a socket
is involved.
Credentials and resume. Our bearer rides the WS upgrade’s
Authorization header on every attach, exactly as it rode every
HTTPS request before. The node answers a freshly created channel with
a SessionHandleResponse carrying a resume token, which we
hold and present as X-WS-Token if we ever have to reconnect. It
needs no refreshing – it is minted to outlive the session itself.
That response also tells us where to reconnect, and we must use it rather than re-dialing the host we opened with. In prod that host is a regional endpoint which routes each connection to some node, and a session lives in exactly one node’s process – so re-dialing it would usually reach a node with nothing to resume. (This is the one place bacloud is node-bound; streamcall is not, because its state is bamaster’s.)
Canonical design: efrohome:docs/global_design/
streamcall-smartsocket.md (“Consumer #2”).
bacommontools.meshcompile module¶
Compilers for Ballistica’s binary mesh formats.
Covers display meshes (.bob) and collision meshes (.cob).
This module is intentionally stdlib-only and side-effect free so it can run anywhere it gets efrosynced to (game repo asset builds now, master-server cloud-build recipes later).
- class bacommontools.meshcompile.BobCompileResult(corner_count: int, vertex_count: int, tri_count: int, index_size: int)[source]¶
Bases:
objectStats from a display-mesh compile.
- class bacommontools.meshcompile.BobData(mesh_format: int, vertices: list[tuple[float, float, float, int, int, int, int, int]], indices: list[int])[source]¶
Bases:
objectParsed contents of a
.bobfile.
- class bacommontools.meshcompile.CobCompileResult(vertex_count_in: int, vertex_count_out: int, tri_count_in: int, tri_count_out: int)[source]¶
Bases:
objectStats from a collision-mesh compile.
- class bacommontools.meshcompile.CobData(file_id: int, positions: list[float], indices: list[int], normals: list[float] | None)[source]¶
Bases:
objectParsed contents of a
.cobfile.
- bacommontools.meshcompile.compile_collision_mesh(src: str | Path, dst: str | Path) CobCompileResult[source]¶
Compile a wavefront
.objfile to a binary.cobfile.Reads a constrained subset of the obj format:
vrecords andfrecords (v,v/t,v//n, andv/t/ncorner forms are all accepted; texture-coordinate and normal references are ignored). Faces with more than 3 corners are fan-triangulated.Output is deterministic for a given input, which matters for content-addressed asset storage.
Beyond straight conversion this applies a few optimizations:
Exact-duplicate vertex positions are welded (compared at float32 precision, matching what gets written).
Degenerate triangles (two or more corners sharing a vertex) are dropped.
Triangles are sorted along a Morton curve of their centroids and vertices are then ordered by first use, so triangles that are near each other in space are also near each other in memory. ODE/OPCODE reads these arrays in place during collision queries; spatially-local queries thus touch fewer cache lines. (Tree shape is unaffected; OPCODE splits on geometry, not input order.)
Unreferenced vertices are pruned (they would otherwise inflate both memory use and ODE’s model-space AABB).
- bacommontools.meshcompile.compile_mesh(src: str | Path, dst: str | Path) BobCompileResult[source]¶
Compile a wavefront
.objfile to a binary.bobfile.Reads the obj subset our exporters produce:
v/vt/vnrecords plusfrecords with fullv/t/ncorners. Faces with more than 3 corners are fan-triangulated. The obj V texture coordinate is flipped (1 - v) per GL convention. UVs and normal components meaningfully outside their encodable ranges ([0, 1] / [-1, 1]) are errors; values within a 0.05 tolerance are treated as authoring noise and clamped silently by the quantization.Output is deterministic for a given input, which matters for content-addressed asset storage.
Optimizations applied:
Corners are quantized to the final vertex encoding and welded (exact-match on all attributes), so identical corners share one vertex.
Degenerate triangles (two or more corners welding to the same vertex) are dropped.
Triangle order is optimized for the GPU post-transform vertex cache (Forsyth’s linear-speed algorithm), then vertices are renumbered by first use for fetch locality. This also prunes unreferenced vertices.
Index width is chosen per mesh: u16 when vertices fit, u32 otherwise (the engine supports both; this removes the old make_bob 21845-face limit).
bacommontools.pcommands module¶
Pcommands for bacommontools.
- bacommontools.pcommands.bacurl() None[source]¶
Run curl with the Ballistica API key injected.
Usage: bacurl [curl-args…] <url>
Reads
ballistica_api_keyfrompconfig/localconfig.jsonand passes it as a Bearer token in the Authorization header. All arguments are forwarded to curl. The-s(silent) flag is added automatically. HTTP errors exit non-zero (–fail-with-body) so piped JSON parsing fails loudly instead of KeyError-ing on error payloads; the error body still prints.Examples:
bacurl https://dev.ballistica.net/api/v1/admin/stats/catalog bacurl -X POST -H 'Content-Type: application/json' \ -d '{"dry_run":true}' \ https://dev.ballistica.net/api/v1/admin/stats/flush
- bacommontools.pcommands.compile_collision_mesh() None[source]¶
Compile a collision mesh from .obj to our binary .cob format.
Usage: compile_collision_mesh <src.obj> <dst.cob>