bacommon package¶
Functionality and data shared by all Ballistica components.
This includes clients, various servers, tools, etc.
Subpackages¶
Submodules¶
bacommon.analytics module¶
Analytics support.
- class bacommon.analytics.AnalyticsEvent[source]¶
Bases:
IOMultiType[AnalyticsEventTypeID]Top level class for our multitype.
- classmethod get_type(type_id: AnalyticsEventTypeID) type[AnalyticsEvent][source]¶
Return the subclass for each of our type-ids.
- classmethod get_type_id() AnalyticsEventTypeID[source]¶
Return the type-id for this subclass.
- classmethod get_type_id_storage_name() str[source]¶
Return the key used to store type id in serialized data.
The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).
- class bacommon.analytics.AnalyticsEventTypeID(*values)[source]¶
Bases:
EnumType ID for each of our subclasses.
- CLASSIC = 'c'¶
- class bacommon.analytics.ClassicAnalyticsEvent(eventtype: EventType, extra: str | None = None)[source]¶
Bases:
AnalyticsEventAnalytics event related to classic.
- class EventType(*values)[source]¶
Bases:
EnumTypes of classic events.
- JOIN_NEARBY_PARTY = 'jn'¶
- JOIN_PARTY_BY_ADDRESS = 'ja'¶
- JOIN_PRIVATE_PARTY = 'jpr'¶
- JOIN_PUBLIC_PARTY = 'jpb'¶
- START_COOP_SESSION = 'sc'¶
- START_FFA_SESSION = 'sf'¶
- START_TEAMS_SESSION = 'st'¶
- START_TOURNEY_COOP_SESSION = 'stc'¶
- classmethod get_type_id() AnalyticsEventTypeID[source]¶
Return the type-id for this subclass.
bacommon.app module¶
Common high level values/functionality related to Ballistica apps.
- class bacommon.app.AppArchitecture(*values)[source]¶
Bases:
EnumProcessor architecture an app can be running on.
- ARM = 'arm'¶
- ARM64 = 'arm64'¶
- UNKNOWN = 'unknown'¶
- X86 = 'x86'¶
- X86_64 = 'x86_64'¶
- class bacommon.app.AppInterfaceIdiom(*values)[source]¶
Bases:
EnumA general form-factor or method of experiencing a Ballistica app.
Note that it may be possible for a running app to switch idioms (for instance if a mobile device or computer is connected to a TV).
- CONSOLE = 'console'¶
Screen with medium amount of detail visible; assumed to have game controller(s) as primary input. Note that this covers handheld or arcade cabinet scenarios in addition to tv-connected consoles.
- DESKTOP = 'desktop'¶
Screen with high amount of detail visible; assumed to have keyboard/mouse as primary input.
- HEADLESS = 'headless'¶
The app has no interface (generally is acting as a server).
- PHONE = 'phone'¶
Small screen; assumed to have touch as primary input.
- TABLET = 'tablet'¶
Medium size screen; assumed to have touch as primary input.
- XR_HEADSET = 'xr_headset'¶
Displayed over or in place of of the real world on a headset; assumed to have hand tracking or spatial controllers as primary input.
- XR_PHONE = 'xr_phone'¶
Displayed over or instead of the real world on a small screen; assumed to have device movement augmented by physical or touchscreen controls as primary input.
- XR_TABLET = 'xr_tablet'¶
Displayed over or instead of the real world on a medium size screen; assumed to have device movement augmented by physical or touchscreen controls as primary input.
- class bacommon.app.AppPlatform(*values)[source]¶
Bases:
EnumOverall platform a build can target.
Each distinct flavor of an app has a unique combination of AppPlatform and AppVariant. Generally platform describes a set of hardware, while variant describes a destination or purpose for the build.
- ANDROID = 'android'¶
- IOS = 'ios'¶
- LINUX = 'linux'¶
- MACOS = 'macos'¶
- TVOS = 'tvos'¶
- UNKNOWN = 'unknown'¶
- WINDOWS = 'windows'¶
- class bacommon.app.AppVariant(*values)[source]¶
Bases:
EnumA unique Ballistica build variation within a single platform.
Each distinct permutation of an app has a unique combination of
AppPlatformandAppVariant. Generally platform describes a set of hardware, while variant describes a destination or purpose for the build.- AMAZON_APPSTORE = 'amazon_appstore'¶
- APPLE_APP_STORE = 'apple_app_store'¶
- ARCADE = 'arcade'¶
- CARDBOARD = 'cardboard'¶
- DEMO = 'demo'¶
- EPIC_GAMES_STORE = 'epic_games_store'¶
- GENERIC = 'generic'¶
Default builds.
- GOOGLE_PLAY = 'google_play'¶
- META = 'meta'¶
- STEAM = 'steam'¶
- TEST_BUILD = 'test_build'¶
Particular builds intended for public testing (may have some extra checks or logging enabled).
- WINDOWS_STORE = 'windows_store'¶
- class bacommon.app.ExitCode(*values)[source]¶
Bases:
EnumProcess exit codes a Ballistica app may return on a clean shutdown.
These accompany a clean (non-crash) shutdown: the app tears down gracefully via the normal shutdown sequence and simply returns one of these as its process exit code. This is distinct from a fatal-error/abort exit (which routes through crash reporting); a nonzero value here means “we shut down cleanly, but the run was a failure”. Supervisors such as the headless server-wrapper script (and BASN task orchestration) interpret nonzero values to decide whether restarting is worthwhile.
- ASSET_BRINGUP_FAILED = 10¶
A headless app could not bring up assets it requires to run (e.g. a construct-mode asset resolve failed terminally). Not transient — a restart with the same inputs would fail the same way — so supervisors should treat it as terminal and not auto-restart.
- SUCCESS = 0¶
Normal successful exit.
bacommon.assetcas module¶
Shared content-addressed asset-blob download primitive.
Used by both the game client’s asset subsystem and the bacloud CLI to
fetch content-addressed blobs from a basn node’s /casblob endpoint,
verify them, and write them into a local content-addressed cache. The
logic here is engine-agnostic: the caller supplies the urllib3 pool, the
node base URL, the encoded capability token, and the destination cache
root, and owns concurrency, retry, and progress reporting itself.
- exception bacommon.assetcas.CasDownloadError[source]¶
Bases:
ExceptionA CAS-blob fetch, hash-verify, or write failed.
- bacommon.assetcas.blob_present(roots: list[str], filehash: str, size: int) bool[source]¶
Is this CAS blob already present at the expected size in any root?
Present-but-wrong-size counts as absent (so it gets refetched and overwritten). The free
st_sizecheck is a cheap catch for external truncation or tampering, not a content proof.
- bacommon.assetcas.cas_blob_path(root: str, filehash: str) str[source]¶
Return the path a CAS blob occupies under a cache root.
Blobs are sharded 256 ways by the first two hex chars of the hash to keep per-directory counts low for cache scanning.
- bacommon.assetcas.cas_write(dest_root: str, filehash: str, data: bytes) None[source]¶
sha256-verify
datathen atomically write it intodest_root.Verify, write to a temp file in the destination directory,
fsync, thenos.replace(atomic on the same filesystem). A file at its CAS path is therefore always whole-and-correct: a crash mid-write leaves only a temp file, never a partial blob at the final path. RaisesCasDownloadErroron a hash mismatch.
- bacommon.assetcas.download_cas_blob(pool: PoolManager, base_url: str, filehash: str, size: int, *, token_header: str, dest_root: str, timeout_seconds: float) None[source]¶
Fetch one CAS blob from a node and atomically write it.
Issues
GET {base_url}/casblob/{filehash}?size={size}with the capability token in theX-Asset-Tokenheader and anX-Accept-Compressionheader advertising every encoding this build can decode, so the node fulfills the request in whichever supported encoding is smallest/available.filehashandsizeare the blob’s canonical (uncompressed) identity. The encoding of the received bytes is taken solely from the node’sX-Cas-Compressionresponse header – it is authoritative, so whatever the node actually served is decoded correctly regardless of how it was cached. (The flavor-manifest no longer carries compression; encoding is purely a transfer-layer concern negotiated here.) The bytes are decompressed back to canonical beforecas_write()sha256-verifies them againstfilehashand writes them – so the local cache always holds uncompressed blobs and the hash check still validates content. One attempt only; the caller owns concurrency and retry. RaisesCasDownloadErroron a non-200 response, a missing/unknown served-encoding header, a decompress failure, or a verify failure.
- bacommon.assetcas.encode_asset_token(token: securedata.Archive) str[source]¶
Encode a capability token for the
X-Asset-Tokenheader.Returns base64-urlsafe of the token’s canonical JSON (HTTP headers don’t carry raw JSON cleanly).
bacommon.bacloud module¶
Functionality related to the bacloud tool.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.
bacommon.build module¶
Functionality related to game builds.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.
bacommon.clienteffect module¶
ClientEffect related functionality.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.
bacommon.cloud module¶
Cloud related functionality.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.
bacommon.cloudfilecodec module¶
zstd (de)compression for cloud-file blobs.
Holds the CompressionType enum (how a stored blob is encoded)
plus the codec dispatch mapping each type to a concrete zstd operation –
including which pre-shared dictionary a dict-based type uses. Lives in
bacommon so both server components (which compress on store) and
client components (which read the compression type from an asset manifest
and decompress on load) share one source of truth for the type->codec
mapping and load identical dictionary bytes.
Compression is always expressed relative to a cloud-file’s canonical
(uncompressed) content: a blob’s stored bytes are the canonical content
run through its CompressionType. UNCOMPRESSED means the
stored bytes are the canonical content.
- bacommon.cloudfilecodec.CAS_ACCEPT_COMPRESSION_HEADER = 'X-Accept-Compression'¶
a comma-separated list of
CompressionTypevalues the client can decode (e.g.'zb1,z,u'). Lets the server fulfill the request in any encoding the client supports – picking the smallest available – rather than assuming a fixed one. A client always includesu(it can always handle uncompressed). Absent ⇒ a legacy client that decodes per its manifest (back-compat).- Type:
Request header on a CAS
/casblobGET
- bacommon.cloudfilecodec.CAS_COMPRESSION_HEADER = 'X-Cas-Compression'¶
the single
CompressionTypevalue the served bytes are encoded in. The client decodes per THIS header (not its manifest), so whatever the node actually serves – even a re-negotiated or differently-cached encoding – is always decoded correctly. Absent ⇒ a legacy node; the client falls back to its manifest’s compression.- Type:
Response header on a CAS
/casblobGET
- class bacommon.cloudfilecodec.CompressionType(*values)[source]¶
Bases:
EnumHow a blob’s stored bytes are compressed.
New members can be added as we see fit. Each member must encode enough to decompress deterministically – dictionary-based members bake in the exact dictionary and version, so a stored blob is always decodable by whatever holds that member. The enum value is a stable wire string: it travels in asset manifests for the client to read, so values must never change.
- UNCOMPRESSED = 'u'¶
Stored bytes are the canonical content verbatim.
- ZSTD = 'z'¶
Stored bytes are the canonical content run through plain zstd.
- ZSTD_DICT_BOB_V1 = 'zb1'¶
Stored bytes are the canonical content run through zstd using the v1 display-mesh (
.bob) dictionary (bacommon.meshzstddict.display_mesh_dict_v1()). The dict and its version are fixed by this member.
- bacommon.cloudfilecodec.all_compression_types() set[CompressionType][source]¶
All
CompressionTypemembers this build can decode.What a client advertises in
CAS_ACCEPT_COMPRESSION_HEADER– every member is decodable here because the codecs (and any bundled dictionaries) ship with the build.
- bacommon.cloudfilecodec.compress_for_type(canonical: bytes, ctype: CompressionType, *, level: int) bytes[source]¶
Encode
canonicalbytes perctype.The canonical (uncompressed) content identified by a cloud-file’s
cloud_file_id. Inverse ofdecompress_for_type(). This is the single source of truth for which dictionary a dict-based type uses.
- bacommon.cloudfilecodec.decompress_for_type(stored: bytes, ctype: CompressionType) bytes[source]¶
Decode
storedbytes perctypeback to canonical content.Inverse of
compress_for_type().
- bacommon.cloudfilecodec.format_compression_accept(types: Iterable[CompressionType]) str[source]¶
Encode a set of supported types for the accept header.
- bacommon.cloudfilecodec.parse_compression_accept(value: str | None) set[CompressionType][source]¶
Decode the accept header into a set of known types.
Tolerant of unknown/empty entries (an unrecognized future value is skipped, not an error) so an old server reading a newer client’s header simply ignores types it doesn’t know.
- bacommon.cloudfilecodec.zstd_compress(data: bytes, level: int) bytes[source]¶
Compress
datawith plain zstd at the given level.
- bacommon.cloudfilecodec.zstd_compress_with_dict(data: bytes, dict_bytes: bytes, level: int) bytes[source]¶
Compress
datawith zstd using a pre-shared dictionary.
- bacommon.cloudfilecodec.zstd_decompress(data: bytes) bytes[source]¶
Decompress plain-zstd
dataproduced byzstd_compress().
bacommon.displayitem module¶
Functionality for displaying currencies, prizes, owned items, etc.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.
bacommon.locale module¶
Functionality for wrangling locale info.
- class bacommon.locale.Locale(*values)[source]¶
Bases:
EnumA distinct grouping of language, cultural norms, etc.
This list of locales is considered ‘sacred’ - we assume any values (and associated long values) added here remain in use out in the wild indefinitely. If a locale is superseded by a newer or more specific one, the new locale should be added and both new and old should map to the same
LocaleResolved.- ARABIC = 'arabc'¶
- BELARUSSIAN = 'blrs'¶
- CHINESE = 'chn'¶
- CHINESE_SIMPLIFIED = 'chn_sim'¶
- CHINESE_TRADITIONAL = 'chn_tr'¶
- CROATIAN = 'croat'¶
- CZECH = 'czch'¶
- DANISH = 'dnsh'¶
- DUTCH = 'dtch'¶
- ENGLISH = 'eng'¶
- ESPERANTO = 'esprnto'¶
- FILIPINO = 'filp'¶
- FRENCH = 'frnch'¶
- GERMAN = 'grmn'¶
- GIBBERISH = 'gibber'¶
- GREEK = 'greek'¶
- HINDI = 'hndi'¶
- HUNGARIAN = 'hngr'¶
- INDONESIAN = 'indnsn'¶
- ITALIAN = 'italn'¶
- JAPANESE = 'jpn'¶
- KAZAKH = 'kazk'¶
- KOREAN = 'kor'¶
- MALAY = 'mlay'¶
- PERSIAN = 'pers'¶
- PIRATE_SPEAK = 'pirate'¶
- POLISH = 'pol'¶
- PORTUGUESE = 'prtg'¶
- PORTUGUESE_BRAZIL = 'prtg_brz'¶
- PORTUGUESE_PORTUGAL = 'prtg_pr'¶
- ROMANIAN = 'rom'¶
- RUSSIAN = 'rusn'¶
- SERBIAN = 'srbn'¶
- SLOVAK = 'slvk'¶
- SPANISH = 'spn'¶
- SPANISH_LATIN_AMERICA = 'spn_lat'¶
- SPANISH_SPAIN = 'spn_spn'¶
- SWEDISH = 'swed'¶
- TAMIL = 'taml'¶
- THAI = 'thai'¶
- TURKISH = 'turk'¶
- UKRAINIAN = 'ukrn'¶
- VENETIAN = 'venetn'¶
- VIETNAMESE = 'viet'¶
- property description: str[source]¶
A human readable description for the locale.
Intended as instructions to humans or AI for translating. For most locales this is simply the language name, but for special ones like pirate-speak it may include instructions.
- property long_value: str[source]¶
A longer more human readable alternative to value.
Like the regular enum values, these values will never change and can be used for persistent storage/etc.
- property resolved: LocaleResolved[source]¶
Return the associated resolved locale.
- class bacommon.locale.LocaleResolved(*values)[source]¶
Bases:
EnumA resolved
Localefor use in logic.These values should never be stored or transmitted and should always come from resolving a
Localewhich can be stored/transmitted. This gives us the freedom to revise this list as needed to keep our actual list of implemented resolved-locales as trim as possible.- ARABIC = 'arabc'¶
- BELARUSSIAN = 'blrs'¶
- CHINESE_SIMPLIFIED = 'chn_sim'¶
- CHINESE_TRADITIONAL = 'chn_tr'¶
- CROATIAN = 'croat'¶
- CZECH = 'czch'¶
- DANISH = 'dnsh'¶
- DUTCH = 'dtch'¶
- ENGLISH = 'eng'¶
- ESPERANTO = 'esprnto'¶
- FILIPINO = 'filp'¶
- FRENCH = 'frnch'¶
- GERMAN = 'grmn'¶
- GIBBERISH = 'gibber'¶
- GREEK = 'greek'¶
- HINDI = 'hndi'¶
- HUNGARIAN = 'hngr'¶
- INDONESIAN = 'indnsn'¶
- ITALIAN = 'italn'¶
- JAPANESE = 'jpn'¶
- KAZAKH = 'kazk'¶
- KOREAN = 'kor'¶
- MALAY = 'mlay'¶
- PERSIAN = 'pers'¶
- PIRATE_SPEAK = 'pirate'¶
- POLISH = 'pol'¶
- PORTUGUESE_BRAZIL = 'prtg_brz'¶
- PORTUGUESE_PORTUGAL = 'prtg_pr'¶
- ROMANIAN = 'rom'¶
- RUSSIAN = 'rusn'¶
- SERBIAN = 'srbn'¶
- SLOVAK = 'slvk'¶
- SPANISH_LATIN_AMERICA = 'spn_lat'¶
- SPANISH_SPAIN = 'spn_spn'¶
- SWEDISH = 'swed'¶
- TAMIL = 'taml'¶
- THAI = 'thai'¶
- TURKISH = 'turk'¶
- UKRAINIAN = 'ukrn'¶
- VENETIAN = 'venetn'¶
- VIETNAMESE = 'viet'¶
- static from_tag(tag: str) LocaleResolved[source]¶
Return a locale for a given string tag.
Tags can be provided in BCP 47 form (‘en-US’) or POSIX locale string form (‘en_US.UTF-8’).
- property locale: Locale[source]¶
Return a locale that resolves to this resolved locale.
In some cases, such as when presenting locale options to the user, it makes sense to iterate over resolved locale values, as regular locales may include obsolete or redundant values. When storing locale values to disk or transmitting them, however, it is important to use plain locales. This method can be used to get back to a plain locale from a resolved one.
bacommon.loctext module¶
Localized-text evaluation (structured plural/select + substitutions).
The value delivered for a locale is either a plain str or a
StringSelector. evaluate() resolves either against a locale
and caller-supplied arguments to a final str:
A plain
strhas its{name}placeholders replaced bystr(args['name']).A
StringSelectorpicks one leaf string at render time – by CLDR plural category of an integer arg (kind=PLURAL) or by the string value of an arg (kind=SELECT) – then substitutes{name}and (for plurals)#(the count) in the chosen leaf.
We use our own small structured form rather than parsing a message-format
string: the runtime decisions are few and closed, the data is
self-describing (it can only express what we support), and a struct is far
safer to port than a hand-rolled string parser. This is the prototype of
the native Lstr2 runtime – keeping it in bacommon gives pure-Python
consumers (the client’s embedded Python, server-side resolution in
bamaster/basn) the same evaluator, and the StringSelector
IOAttrs keys are the on-the-wire schema the C++ port implements.
Scope: cardinal plural selection for non-negative integers, string
select, {name} substitution, and # for the plural count.
=N keys match an exact count before the category rule. Not (yet):
ordinals/selectordinal, decimal operands, plural offsets, nested selectors.
- exception bacommon.loctext.LocTextError[source]¶
Bases:
ExceptionA malformed localized value or a missing/bad argument.
- class bacommon.loctext.PluralCategory(*values)[source]¶
Bases:
EnumA CLDR plural category.
- FEW = 'few'¶
- MANY = 'many'¶
- ONE = 'one'¶
- OTHER = 'other'¶
- TWO = 'two'¶
- ZERO = 'zero'¶
- class bacommon.loctext.SelectorKind(*values)[source]¶
Bases:
EnumHow a
StringSelectorchooses among its forms.- PLURAL = 'p'¶
Choose by the CLDR plural category of an integer argument.
- SELECT = 's'¶
Choose by the string value of an argument (e.g. gender).
- class bacommon.loctext.StringSelector(kind: SelectorKind, arg: str, forms: dict[str, str])[source]¶
Bases:
objectA localized string whose final form is chosen at render time.
The structured alternative to a plain
strvalue:formsmaps a form key to its leaf text (which may carry{name}substitutions and, for plurals,#for the count). ForPLURALthe keys are CLDR category names (one/few/…/other) or=Nexact-count matches; forSELECTthey are the possible string values ofarg.otheris the fallback. TheIOAttrskeys are the on-the-wire schema shared with the nativeLstr2port.- kind: SelectorKind¶
- bacommon.loctext.evaluate(value: str | StringSelector, locale: Locale, **args: object) str[source]¶
Resolve a localized
valueto a final string.valueis a plainstr({name}substitutions only) or aStringSelector(plural/select choice, then substitution).argssupplies the named arguments; plural selection useslocale’s CLDR rule (seeplural_category()). RaisesLocTextErroron a missing/ill-typed argument or a selector with no matching form and noother.
- bacommon.loctext.plural_category(locale: Locale, n: int) PluralCategory[source]¶
Return the CLDR cardinal plural category for an integer in a locale.
nis treated as a non-negative integer count (its absolute value is used). Uses the locale’s resolved form so obsolete aliases (e.g. the bareSPANISH) follow their modern locale’s rule. Every shipped locale is mapped explicitly; a genuinely-unknown locale falls back to theone-for-1 rule. See the module note for the integer-only scope.
- bacommon.loctext.required_plural_categories(locale: Locale) list[PluralCategory][source]¶
The plural categories a
pluralmessage must cover for a locale.The set the locale’s rule can produce over the integers, plus the mandatory
otherfallback (ICU requires it), in canonical order. Used to tell a translation model exactly which plural forms to emit for the target locale – derived from the same rulesplural_category()evaluates with, so producer and client agree.The integer sample (0..200) spans every modulo cycle the rules use; under the integer-only scope (see the module note) this is the exact set of categories the locale can select.
bacommon.loggercontrol module¶
System for managing loggers.
- class bacommon.loggercontrol.LoggerControlConfig(levels: dict[str, int]=<factory>)[source]¶
Bases:
objectA logging level configuration that applies to all loggers.
Any loggers not explicitly contained in the configuration will be set to NOTSET.
- apply(*, warn_unexpected_loggers: bool = False, warn_missing_loggers: bool = False, ignore_log_prefixes: list[str] | None = None) None[source]¶
Apply the config to all Python loggers.
If ‘warn_unexpected_loggers’ is True, warnings will be issues for any loggers not explicitly covered by the config. This is useful to help ensure controls for all possible loggers are present in a UI/etc.
If ‘warn_missing_loggers’ is True, warnings will be issued for any loggers present in the config that are not found at apply time. This can be useful for pruning settings for no longer used loggers.
Warnings for any log names beginning with any strings in ‘ignore_log_prefixes’ will be suppressed. This can allow ignoring loggers associated with submodules for a given package and instead presenting only a top level logger (or none at all).
- apply_diff(diffconfig: LoggerControlConfig) LoggerControlConfig[source]¶
Apply a diff config to ourself.
Note that values that resolve to NOTSET are left intact in the output config. This is so all loggers expected by either the base or diff config to exist can be created if desired/etc.
- diff(baseconfig: LoggerControlConfig) LoggerControlConfig[source]¶
Return a config containing only changes compared to a base config.
Note that this omits all NOTSET values that resolve to NOTSET in the base config.
This diffed config can later be used with apply_diff() against the base config to recreate the state represented by self.
- get_effective_level(logname: str) int[source]¶
Given a log name, predict its level if this config is applied.
bacommon.logging module¶
Logging functionality.
- class bacommon.logging.ClientLoggerName(*values)[source]¶
Bases:
EnumLogger names used on the Ballistica client.
- ACCOUNT = 'ba.account'¶
- ACCOUNT_CLIENT_V2 = 'ba.accountclientv2'¶
- APP = 'ba.app'¶
- ASSETS = 'ba.assets'¶
- ASSET_MANAGER = 'ba.assetmanager'¶
- AUDIO = 'ba.audio'¶
- BA = 'ba'¶
- CACHE = 'ba.cache'¶
- CLOUD_SUBSCRIPTION = 'ba.cloudsub'¶
- CONNECTIVITY = 'ba.connectivity'¶
- DISCORD = 'ba.discord'¶
- DISPLAYTIME = 'ba.displaytime'¶
- ENV = 'ba.env'¶
- GARBAGE_COLLECTION = 'ba.gc'¶
- GRAPHICS = 'ba.gfx'¶
- INPUT = 'ba.input'¶
- LIFECYCLE = 'ba.lifecycle'¶
- LOGIN_ADAPTER = 'ba.loginadapter'¶
- NETWORKING = 'ba.net'¶
- PERFORMANCE = 'ba.perf'¶
- UI = 'ba.ui'¶
- V2TRANSPORT = 'ba.v2transport'¶
- WORKSPACE = 'ba.workspace'¶
- bacommon.logging.get_base_logger_control_config_client() LoggerControlConfig[source]¶
Return the logger-control-config used by the Ballistica client.
This should remain consistent since local logger configurations are stored relative to this.
bacommon.login module¶
Functionality related to cloud based assets.
bacommon.meshzstddict module¶
Zstandard dictionaries for compressing display-mesh (.bob) data.
These dictionaries are trained on the corpus of display-mesh blobs and
substantially improve zstd compression of individual meshes (small
files that share a lot of structure). They live in bacommon so that
both server components (which compress meshes on store) and client
components (which decompress them on load) load identical dictionary
bytes.
Dictionaries are immutable and versioned: never mutate an existing
.dict file – add a new version instead, so blobs compressed with an
older dictionary remain decompressable. Whatever stores a compressed
blob must record which dictionary version produced it.
bacommon.metascan module¶
Scanner for # ba_meta directives in Python source.
Recognized directive shapes:
# ba_meta require api <N>— module-level API version requirement. Whenexpected_api_versionis supplied, modules whose value doesn’t match are skipped (and listed inScanResults.incorrect_api_modules). When it isNonethe line is parsed for validity but no filtering occurs.# ba_meta export <TYPE>— export the class defined on the next non-blank source line under the export-type<TYPE>.# ba_meta require asset-package <ID>— module declares that it needs the named asset-package at runtime.
Other shapes are reported as malformed.
This module has no dependencies beyond the standard library so it
can run anywhere — in the game runtime (wrapped by
babase._meta.MetadataSubsystem), in build/tooling contexts
(e.g. tools/pcommand assetpins), or in tests.
Higher-level concerns — background-thread scheduling, UI feedback, expansion of legacy export-type shortcuts, deprecation warnings — live in the consumer.
- class bacommon.metascan.DirectoryScan(paths: list[str], expected_api_version: int | None = None, deprecated_export_shortcuts: dict[str, str] | None = None, *, expects_extras: bool = False)[source]¶
Bases:
objectScans directories for
# ba_metadirectives.Pure-Python; no runtime dependencies. Construct with a list of paths (which must already be on PYTHONPATH if discovered modules will be imported by the consumer), then call
run(). Results land inresults.If
expected_api_versionis set, modules whose# ba_meta require apivalue doesn’t match are skipped and listed inScanResults.incorrect_api_modules. PassNoneto scan regardless of api version (the typical tooling-side mode).If
deprecated_export_shortcutsis provided, export-type strings that appear as keys are substituted with the corresponding canonical class path and a deprecation warning is emitted with file:line context. PassNoneto perform no substitution.If
expects_extrasis True,run()will block after finishing the base paths untilset_extras()is called from another thread. This supports the runtime pattern of kicking off the scan immediately and providing extra paths (workspace dirs, etc.) once they are known. Synchronous tooling callers should leave it at the defaultFalse;run()will then finish after the base paths.
bacommon.net module¶
Network related data and functionality.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.
bacommon.securedata module¶
Functionality related to verifying server generated data.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.
- class bacommon.securedata.Archive(data: bytes, signature: bytes, cert: Cert | None = None)[source]¶
Bases:
objectSelf-contained signed payload — verifiable on its own.
Produced by
Writer.write()(delegate-signed) ormake_master_archive()(master-signed;certisNone). Verified viaReader.read(), which returns the payload bytes on success.Embed directly as a field on dataclassio messages and responses (
Annotated[Archive, IOAttrs('x')]); dataclassio nests the bytes-typed fields into the outer JSON without the extra base64-of-base64 round trip a bytes-blob field would incur. For the rare case that genuinely needs bytes (filesystem storage, etc.), serialize explicitly withefro.dataclassio.dataclass_to_json().Per-field IOAttrs short keys are stable; new optional fields get
soft_defaultrather than reusing or renaming existing ones — same as anywhere else dataclassio types travel.
- class bacommon.securedata.Cert(payload: bytes, signature: bytes)[source]¶
Bases:
objectMaster-signed delegation certificate.
Asserts that the public key embedded in
payloadis authorized to sign on the master’s behalf for the validity window in the payload. Verifiable by anyone holding a currentReader(server-side) or the static public keys baked into the client binary (client-side, once a future client version supports the cert path).Carries an opaque
payload(canonical JSON ofCertPayload) plus the master signature over those bytes. Mirrors theInsecureDirectivepattern — re-encoding the dataclass on every verify would risk canonicalization drift.- decoded_payload() CertPayload[source]¶
Decode
payloadto aCertPayload.Does not verify the signature. Callers are expected to be operating on a cert that’s already been chain-verified via
Reader.check().
- payload: bytes¶
Canonical JSON encoding of
CertPayload. Signed as-is and re-decoded by recipients viadecoded_payload().
- class bacommon.securedata.CertPayload(publickey: bytes, starttime: datetime, endtime: datetime, issuer: str)[source]¶
Bases:
objectUnsigned content of a
Cert.Master signs the canonical JSON encoding of this; recipients re-decode after signature verification to access the fields.
- exception bacommon.securedata.Invalid[source]¶
Bases:
ExceptionRaised by
Reader.read()andWriter.write()when an operation cannot complete safely.For
read: the archive bytes are malformed, the master signature does not validate, the cert is expired or signed by an unknown master, or the delegate signature does not match.For
write: the writer’s cert is past its validity window and would produce a signed archive a verifier would refuse.str(exc)carries a short reason suitable for logging.
- bacommon.securedata.MIN_CLIENT_BUILD_FOR_DELEGATE_VERIFY: int = 22843¶
Minimum ballistica-internal client build number known to support cert-bearing verification via
Reader.check(). Older clients only have the legacycheck(data, signature)path and would silently fail to verify(data, signature, cert)triples — server code must therefore skip the delegate-signed path for clients reporting a build below this threshold.Bump in lockstep with the ballistica-internal release that ships the matching client code. Tracks the build at the moment the cert path was added; ratchets upward only when a protocol change requires it.
- class bacommon.securedata.Reader(starttime: datetime, endtime: datetime, publickeys: list[bytes])[source]¶
Bases:
objectVerifies data as being signed by our master server.
- check(data: bytes, signature: bytes, cert: Cert | None = None) bool[source]¶
Verify data, returning True if successful.
When
certisNone(the default),signatureis verified directly against the master public keys — used for anything bamaster signed itself.When
certis given, the chain is verified end-to-end:The cert’s signature must validate against a master public key (proving the master authorized this delegate).
The cert’s validity window must include now.
signaturemust then validate against the delegate public key embedded in the cert.
Used for data signed by a delegate (typically a basn node) acting on the master’s behalf. Older clients that predate the delegate-signing rollout will only ever be sent
cert=Nonetriples; seebacommon/securedata.pydesign notes for the rollout discipline.Uses the ballistica native
_babase.verify_ed25519when available (inside the app binary). On contexts without it (server, pytest, tools scripts) falls back to thecryptographypackage, which those contexts already have.
- read(archive: Archive) bytes[source]¶
Verify
archiveand return its data on success.archiveis theArchiveproduced byWriter.write()ormake_master_archive(). Routes automatically: an archive without a cert is verified directly against this reader’s master public keys; an archive with a cert is chain-verified (cert against master keys, then data against the cert’s delegate pubkey).Raises
Invalidfor any failure — expired/forged cert, bad signature.
- bacommon.securedata.STATIC_DATA_PUBLIC_KEYS: tuple[bytes, ...] = (b'\x98\xc0\xb3{\xea\n\t\x0f\xfb\xcbN\x1c\x03A\xd7\xd6d\x95{.\xdc\xda\x9b\xf6\xe0\x7f\x0bM\x84^\x15b',)¶
Public halves of long-lived Ed25519 keypairs the master server uses to sign static data that clients must verify offline — without any network round trip to fetch a
Reader. Compiled into the client binary, so a MITM cannot swap them.Multiple entries let us rotate: add the new key (as a second entry) in a client release, wait for that release to propagate to the installed base, switch the server to sign with the new key, then later drop the old key in a subsequent client release. On verify, a signature is considered valid if ANY listed key verifies it.
The matching private halves live on the master server in svals under
static_data_private_key(current signing key). basn nodes fetch the current private key from the master at startup over the secure inter-node channel.
- class bacommon.securedata.Writer(starttime: datetime, endtime: datetime, privatekey: bytes, cert: Cert)[source]¶
Bases:
objectDelegated signing capability.
Issued by the master to a delegate (typically a basn node) over a secure channel. The delegate calls
write()on data and distributes the resulting archive bytes; recipients chain-verify and recover the original bytes viaReader.read().Sensitive: contains a private key. Never traverses untrusted channels and should not be persisted to disk.
The top-level
starttime/endtimemirror the cert’s validity window for ergonomic refresh-decision code (see basn’s_update_secure_data_signer). They are NOT authoritative for verification —certis.Servers must consult
MIN_CLIENT_BUILD_FOR_DELEGATE_VERIFYbefore sending delegate-signed archives to a client; clients below the threshold do not understand the cert path and would reject the archive.- cert: Cert¶
Master-signed cert authorizing the public counterpart of
privatekey.
- sign(data: bytes) bytes[source]¶
Low-level: produce a raw signature over
data.Most callers should use
write()instead — it returns a self-contained archive that includes the signature, the cert, and the data, all verifiable in a singleReader.read()call.
- write(data: bytes) Archive[source]¶
Sign
dataand return anArchive.The archive carries the signature + the writer’s cert; recipients pass it to
Reader.read()to verify and recover the original bytes.Raises
Invalidif the writer’s cert has expired — an archive produced past expiry would be rejected by every verifier, so we fail fast at write time rather than at verify time.
- bacommon.securedata.make_master_archive(data: bytes, master_priv_bytes: bytes) Archive[source]¶
Pack data + a master-key signature into an
Archive.Server-only — clients do not have access to a master private key. Verifiers (with master public keys) accept the resulting archive via
Reader.read()exactly the same way they accept Writer-produced archives, except the master path carries no cert.master_priv_bytesis the raw 32-byte Ed25519 seed. On bamaster, preferUniversalGlobals.secure_data_archive_masterwhich sources the key from svals.
bacommon.servermanager module¶
Functionality related to the server manager script.
- class bacommon.servermanager.ChatMessageCommand(message: str, clients: list[int] | None)[source]¶
Bases:
ServerCommandChat message from the server.
- class bacommon.servermanager.ClientListCommand[source]¶
Bases:
ServerCommandPrint a list of clients.
- class bacommon.servermanager.KickCommand(client_id: int, ban_time: int | None)[source]¶
Bases:
ServerCommandKick a client.
- class bacommon.servermanager.ScreenMessageCommand(message: str, color: tuple[float, float, float] | None, clients: list[int] | None)[source]¶
Bases:
ServerCommandScreen-message from the server.
- class bacommon.servermanager.ServerCommand[source]¶
Bases:
objectBase class for commands that can be sent to the server.
- class bacommon.servermanager.ServerConfig(party_name: str = 'FFA', party_is_public: bool = True, authenticate_clients: bool = True, admins: list[str] = <factory>, enable_default_kick_voting: bool = True, public_ipv4_address: str | None = None, public_ipv6_address: str | None = None, port: int = 43210, max_party_size: int = 6, session_max_players_override: int | None = None, session_type: str = 'ffa', playlist_code: int | None = None, playlist_inline: list[dict[str, ~typing.Any]] | None = None, playlist_shuffle: bool = True, auto_balance_teams: bool = True, coop_campaign: str = 'Easy', coop_level: str = 'Onslaught Training', enable_telnet: bool = False, teams_series_length: int = 7, ffa_series_length: int = 24, stats_url: str | None = None, clean_exit_minutes: float | None = None, unclean_exit_minutes: float | None = None, idle_exit_minutes: float | None = None, show_tutorial: bool = False, team_names: tuple[str, str] | None = None, team_colors: tuple[tuple[float, float, float], tuple[float, float, float]] | None = None, enable_queue: bool = True, protocol_version: int | None = None, stress_test_players: int | None = None, player_rejoin_cooldown: float = 10.0, log_levels: dict[str, str] | None = None, dont_write_bytecode: bool = False)[source]¶
Bases:
objectConfiguration for the server manager app (<appname>_server).
- class bacommon.servermanager.ShutdownCommand(reason: ShutdownReason, immediate: bool)[source]¶
Bases:
ServerCommandTells the server to shut down.
- reason: ShutdownReason¶
- class bacommon.servermanager.ShutdownReason(*values)[source]¶
Bases:
EnumReason a server is shutting down.
- NONE = 'none'¶
- RESTARTING = 'restarting'¶
- class bacommon.servermanager.StartServerModeCommand(config: ServerConfig)[source]¶
Bases:
ServerCommandTells the app to switch into ‘server’ mode.
- config: ServerConfig¶
bacommon.text module¶
Text related bits.
- class bacommon.text.SpecialChar(*values)[source]¶
Bases:
EnumCustom unicode characters the engine can display.
Keep this in sync with babase._generated.enums.SpecialChar.
- BACK = '\ue00b'¶
- BOTTOM_BUTTON = '\ue008'¶
- BOXING_GLOVE = '\ue067'¶
- CLOSE = '\ue017'¶
- CROWN = '\ue043'¶
- DELETE = '\ue009'¶
- DICE_BUTTON1 = '\ue022'¶
- DICE_BUTTON2 = '\ue023'¶
- DICE_BUTTON3 = '\ue024'¶
- DICE_BUTTON4 = '\ue025'¶
- DISCORD_LOGO = '\ue026'¶
- DOWN_ARROW = '\ue004'¶
- DPAD_CENTER_BUTTON = '\ue010'¶
- DRAGON = '\ue048'¶
- EXPLODINARY_LOGO = '\ue031'¶
- EYE_BALL = '\ue045'¶
- FAST_FORWARD_BUTTON = '\ue00f'¶
- FEDORA = '\ue041'¶
- FIREBALL = '\ue04f'¶
- FLAG_ALGERIA = '\ue054'¶
- FLAG_ARGENTINA = '\ue05f'¶
- FLAG_AUSTRALIA = '\ue058'¶
- FLAG_BRAZIL = '\ue035'¶
- FLAG_CANADA = '\ue039'¶
- FLAG_CHILE = '\ue061'¶
- FLAG_CHINA = '\ue037'¶
- FLAG_CZECH_REPUBLIC = '\ue057'¶
- FLAG_EGYPT = '\ue052'¶
- FLAG_FRANCE = '\ue03c'¶
- FLAG_GERMANY = '\ue034'¶
- FLAG_INDIA = '\ue03a'¶
- FLAG_INDONESIA = '\ue03d'¶
- FLAG_IRAN = '\ue05d'¶
- FLAG_ITALY = '\ue03e'¶
- FLAG_JAPAN = '\ue03b'¶
- FLAG_KUWAIT = '\ue053'¶
- FLAG_MALAYSIA = '\ue056'¶
- FLAG_MEXICO = '\ue033'¶
- FLAG_NETHERLANDS = '\ue040'¶
- FLAG_PHILIPPINES = '\ue060'¶
- FLAG_POLAND = '\ue05e'¶
- FLAG_QATAR = '\ue051'¶
- FLAG_RUSSIA = '\ue036'¶
- FLAG_SAUDI_ARABIA = '\ue055'¶
- FLAG_SINGAPORE = '\ue059'¶
- FLAG_SOUTH_KOREA = '\ue03f'¶
- FLAG_UNITED_ARAB_EMIRATES = '\ue050'¶
- FLAG_UNITED_KINGDOM = '\ue038'¶
- FLAG_UNITED_STATES = '\ue032'¶
- GAME_CENTER_LOGO = '\ue021'¶
- GOOGLE_PLAY_GAMES_LOGO = '\ue020'¶
- HAL = '\ue042'¶
- HEART = '\ue047'¶
- HELMET = '\ue049'¶
- LEFT_ARROW = '\ue001'¶
- LEFT_BUTTON = '\ue005'¶
- LOCAL_ACCOUNT = '\ue030'¶
- LOGO = '\ue01e'¶
- LOGO_FLAT = '\ue00c'¶
- MIKIROG = '\ue062'¶
- MOON = '\ue04d'¶
- MUSHROOM = '\ue04a'¶
- NINJA_STAR = '\ue04b'¶
- NVIDIA_LOGO = '\ue05c'¶
- OCULUS_LOGO = '\ue05a'¶
- OUYA_BUTTON_A = '\ue01c'¶
- OUYA_BUTTON_O = '\ue019'¶
- OUYA_BUTTON_U = '\ue01a'¶
- OUYA_BUTTON_Y = '\ue01b'¶
- PALM_TREE = '\ue066'¶
- PARTY_ICON = '\ue027'¶
- PAUSE_BUTTON = '\ue016'¶
- PLAY_BUTTON = '\ue015'¶
- PLAY_PAUSE_BUTTON = '\ue00e'¶
- PLAY_STATION_CIRCLE_BUTTON = '\ue012'¶
- PLAY_STATION_CROSS_BUTTON = '\ue011'¶
- PLAY_STATION_SQUARE_BUTTON = '\ue014'¶
- PLAY_STATION_TRIANGLE_BUTTON = '\ue013'¶
- POTATO = '\ue065'¶
- REWIND_BUTTON = '\ue00d'¶
- RIGHT_ARROW = '\ue002'¶
- RIGHT_BUTTON = '\ue007'¶
- SANTA_HAT = '\ue064'¶
- SHIFT = '\ue00a'¶
- SKULL = '\ue046'¶
- SPIDER = '\ue04e'¶
- STEAM_LOGO = '\ue05b'¶
- TEST_ACCOUNT = '\ue028'¶
- TICKET = '\ue01f'¶
- TICKET_BACKING = '\ue029'¶
- TOKEN = '\ue01d'¶
- TOP_BUTTON = '\ue006'¶
- TROPHY0A = '\ue02d'¶
- TROPHY0B = '\ue02e'¶
- TROPHY1 = '\ue02a'¶
- TROPHY2 = '\ue02b'¶
- TROPHY3 = '\ue02c'¶
- TROPHY4 = '\ue02f'¶
- UP_ARROW = '\ue003'¶
- V2_LOGO = '\ue063'¶
- VIKING_HELMET = '\ue04c'¶
- YIN_YANG = '\ue044'¶
bacommon.transfer module¶
Functionality related to transferring files/data.
Warning
This is an internal api and subject to change at any time. Do not use it in mod code.