The ship_data module
Load and query the ship-definition database, and let an add-on declare ships of its own.
Overview
Cosmos ships are defined in data/shipData.yaml, keyed by art ID (e.g.
"tsn_battle_cruiser"). This module loads that database, merges anything a mission
or add-on has contributed, and answers queries against the result.
get_ship_data() returns the whole merged database (a dict with a #ship-list).
get_ship_data_for(key) returns one ship's entry. get_ship_name(key) is the display
name, and filter_ship_data_by_side is how a prefab finds "a Kralien warship".
The database is loaded lazily and cached. Mission scripts do not normally need this module directly - spawn functions look ship data up for you.
Quick example
== pick_ship ==
entry = ship_data_get_ship_data_for("tsn_battle_cruiser")
display_name = entry.get("name", "Unknown")
log(f"Spawning a {display_name}")
->END
from sbs_utils.procedural.ship_data import (
get_ship_data, get_ship_data_for, get_ship_name,
)
entry = get_ship_data_for("tsn_battle_cruiser")
name = entry.get("name", "Unknown")
# every ship the database knows
for ship in get_ship_data().get("#ship-list", []):
print(ship.get("key"), ship.get("name"))
The MAST names carry a ship_data_ prefix
The prelude registers this module with a prefix, so Python's get_ship_data_for
is ship_data_get_ship_data_for in MAST, and add_extra is
ship_data_add_extra.
Adding ships from an add-on
An add-on ships a ship-data file and names it. Nothing is written:
ship_data_add_extra("turrets/extraShipData_turrets", mod="MyMod")
from sbs_utils.procedural.ship_data import add_extra
add_extra("turrets/extraShipData_turrets", mod="MyMod")
add_extra points both readers at the one file: sbs_utils' own table (which is
what filter_ship_data_by_side and get_ship_name read) and the engine (which
is what resolves artfileroot, and what makes a behav_station fire at all).
The name takes no extension - the engine tries .yaml then .json itself - and
may include a logical folder. With no path it is looked for where the media system
already looks: this mission, then each media pack it pinned.
Put the file in a media pack, not a mastlib
A mastlib is a zip, and the engine cannot read inside one. A media pack is unpacked to disk once, so the engine can be handed a real folder. That is the whole reason this used to involve writing a file.
The older generating route is broken
ship_data_merge_mod reaches the engine by generating extraShipData.json in the
mission folder. get_ship_data() then prepends that file whole on the next run -
#mod entries and all - while the add-on declares the same entries again. Measured
at 51 hulls becoming 102 from run 2 onward. Use add_extra.
extra_enable(False) turns off only the engine half, leaving the library merge in
place - useful while the engine side is in flux, since headless and the mock keep
behaving identically.
API
add_extra(name, path=None, mod=None)
Load another ship-data file for this mission.
name has no extension - .yaml or .json is found here, so a mod can
change format without the caller changing. It may include a logical folder
("turrets/extraShipData_turrets"). The engine now wants the fully-pathed
file WITH its suffix, so the extension search that used to be the engine's job
happens in _read_extra_ship_data and its answer is what the engine is handed
- one decision, not two that can disagree.
With no path, the file is looked for where the media system already looks:
this mission's folder first, then each media pack it pinned. That matters
because an ADD-ON cannot put a file where the engine can read it - a mastlib
is a zip - while a media pack is unpacked to disk once. So an addon ships its
hulls in its media pack and names them here, and neither it nor the library
has to write anything.
Returns True when the engine was told, False when only the library was. Missing files are not fatal, matching the engine's habit: a mod with a broken path should be a ship with no stats, not a dead mission.
add_ship_data(entry, mod=None, prepend=True)
Add a single entry to the in-memory ship data.
Inserts entry into the #ship-list so it is returned by
:func:get_ship_data, :func:get_ship_index, :func:get_ship_data_for,
:func:filter_ship_data_by_side, and the *_keys helpers -- letting a
script register a ship/terrain/pickup type at runtime without editing
shipData or shipping an extraShipData.json.
The entry is prepended by default, matching how extraShipData.json is
merged (script data ahead of built-in data). The derived caches
(ship_index and every *_keys cache) are cleared so the new entry shows
up on the next lookup; the loaded ship_data_cache itself is preserved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entry
|
dict
|
A ship data dict. Must include a |
required |
mod
|
str
|
Name of the mod/addon this entry comes from; stamped on
the entry as |
None
|
prepend
|
bool
|
Insert at the front of the list (script
priority) when |
True
|
Returns:
| Type | Description |
|---|---|
|
dict | None: The updated ship data cache, or |
alien_keys()
Return all pickup keys containing "alien" (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Alien pickup type keys. |
arvonian_ship_keys()
Return all Arvonian ship keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Arvonian ship type keys. |
arvonian_starbase_keys()
Return all Arvonian starbase keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Arvonian starbase type keys. |
asteroid_keys()
Return all asteroid ship keys from the ship data (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Asteroid type keys. |
container_keys()
Return all pickup keys containing "container" (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Container pickup type keys. |
crystal_asteroid_keys()
Return all crystal asteroid keys, excluding plain asteroids (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Crystal asteroid type keys. |
danger_keys()
Return all pickup keys containing "danger" (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Danger pickup type keys. |
extra_enable(enabled=True)
Allow or forbid the ENGINE side of add_extra.
Off, the ships are still merged into sbs_utils, so headless runs and every library lookup behave the same; only the engine is not told. Use it to take the engine path out of play without touching any caller.
extra_enabled()
Is the engine call currently allowed?
extra_loaded()
[(filename, path, reached_engine, engine_arg)] for every call so far, so a
report can say what was loaded, what exact file the ENGINE was pointed at, and
whether it heard about it. engine_arg is None when no file was found.
extra_replay()
Tell the engine again about every extra ship data file it has been given.
create_new_sim() REBUILDS the engine's ship data table - it reads the mission's
extraShipData.json inside that call - and everything add_extra_ship_data registered
beforehand is gone. Nothing reports it. The library keeps its own merged copy, so the
ships still have stats everywhere sbs_utils can see, and the loss surfaces later as
MemoryError: bad allocation from a spawn, against whichever mission line asked for
one of those hulls.
Missions register at story load, which is BEFORE the first map calls sim_create(), so
this is the ordinary case rather than an edge one. LegendaryMissions declares its
monsters with a top-level shared, which by design runs once and then becomes a no-op,
so nothing ever re-issued them: every monster in the game was unspawnable from the first
map start onward, and had been for as long as anyone could remember (measured
2026-08-14 - inside LM every hull fails and re-issuing this exact call fixes all five).
Replayed from the record rather than from the files: the library merge already happened and only the engine forgot.
extra_reset()
Forget the record. Called by the per-mission reset, not by missions.
filter_ship_data_by_side(test_ship_key, sides, role=None, ret_key_only=False)
Return ship data entries matching a key substring, side filter, and optional role.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
test_ship_key
|
str | None
|
Substring that must appear in the ship key,
or |
required |
sides
|
str
|
Comma-separated side names to include (case-insensitive). |
required |
role
|
str
|
Single role that must be in the ship's role list. Defaults to None (no role filter). |
None
|
ret_key_only
|
bool
|
Return a list of key strings instead of full data dicts. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
|
list[str | dict]: Matching ship keys or data entries. |
get_mod(key_or_entry)
Return the source mod of a ship data entry, or None if it is engine-known.
Exposed to MAST as ship_data_get_mod (the ship_data_ prelude prefix); named
get_mod here so it doesn't double-prefix to ship_data_ship_data_get_mod.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key_or_entry
|
str | dict
|
A ship key, or a ship data entry dict. |
required |
Returns:
| Type | Description |
|---|---|
|
str | None: The mod name stamped at merge time, or |
get_ship_data()
Load and cache the full ship data, merging extraShipData.json if present.
Results are cached after the first call. The mission-directory
extraShipData.json is prepended to the #ship-list so mission ships
take priority over built-in data.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
The merged ship data dictionary. |
get_ship_data_for(ship_key)
Return the full ship data entry for a given key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ship_key
|
str
|
The ship type key. |
required |
Returns:
| Type | Description |
|---|---|
|
dict | None: Ship data dict, or |
get_ship_index()
Return ship data indexed by ship key for fast O(1) lookup.
Returns:
| Type | Description |
|---|---|
|
dict[str, dict]: Mapping of ship key → ship data entry. |
get_ship_name(ship_key)
Return the display name of a ship type by key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ship_key
|
str
|
The ship type key. |
required |
Returns:
| Type | Description |
|---|---|
|
str | None: Ship display name, or |
kralien_ship_keys()
Return all Kralien ship keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Kralien ship type keys. |
kralien_starbase_keys()
Return all Kralien starbase keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Kralien starbase type keys. |
merge_mod_ship_data(mod, file=None)
Merge a mod folder's extra ship data (YAML or JSON) into the ship data cache.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mod
|
str
|
Mod directory name (resolved via |
required |
file
|
str
|
The data file within the mod folder. Defaults to
the base name |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
dict |
The updated ship data cache. |
merge_mod_ship_yaml(content, mod=None)
Merge ship data supplied as a YAML/JSON string into the ship data cache.
Companion to :func:sbs_utils.procedural.media.media_read_relative_file, which
returns a data file's CONTENTS relative to the current addon -- working whether
the addon is a loose folder (dev) or a packaged .mastlib zip, where a plain
filesystem path can't reach the file. So an addon can ship its own ship/terrain
data next to its prefabs and load it in one line:
merge_mod_ship_yaml(media_read_relative_file("shipData_monsters.yaml"), "MyMod")
The parsed #ship-list is prepended (addon data ahead of built-in). Each entry
is stamped with mod (the #mod key) so the low-level spawn can tell these
engine-unknown entries apart and post-process them (see :func:mod_ship_data_process).
The derived caches (ship_index and the *_keys caches) are cleared so the new
entries are visible on the next lookup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
str
|
YAML or JSON text (YAML is a JSON superset, so both parse). |
required |
mod
|
str
|
Name of the mod/addon these entries come from; stamped on
each entry as |
None
|
Returns:
| Type | Description |
|---|---|
|
dict | None: The updated ship data cache, or |
mod_ship_data_process(so, entry)
Apply a runtime-merged (mod) ship data entry to a freshly spawned object.
The engine's built-in shipData table doesn't contain entries merged at runtime
(:func:merge_mod_ship_yaml / :func:merge_mod_ship_data / :func:add_ship_data),
so create_space_object returns a bare object that never got the values the engine
normally derives from a KNOWN shipData entry. The low-level spawn (spawn_common)
calls this for such objects to reproduce that derivation.
The shipData-field -> object mapping mirrors cosmos_dev.mock.sbs's reverse-engineered
_apply_ship_data_to_object (the engine's data_set names are NOT the shipData spellings):
- Art via
set_ship_data_key(artfileroot)when the modded key differs from its art (the engine picks the mesh fromdata_tag, not a data_set field).meshscale/radarscaleare engine-internal render props with NO data_set key -- not applied. exclusionradius-> the physics attributeengine_object.exclusion_radius(not a data_set field).- 1-to-1 float scalars (
turn_rate,speed_coeff,interactionradius, ...). hullpoints->armor/armorMax(stations only; ships use another system).baycount->bay_count;tubecount->torpedo_tube_count.shieldsarray ->shield_count+shield_val/shield_max_valper facing.hull_port_setsbeams ->beamCount+beamRange/beamDamage(coeff * 6.0) /beamCycleTime/beamArcWidth/beamBarrelAngleper port.torpedostart->{Type}_NUM/_MAX/_VAL+torpedo_types_available.
Fields with no known engine mapping (and the meta key/name/side/roles/#mod) are skipped; a prefab may still set anything extra afterwards (it runs after spawn, so it wins).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
so
|
The spawned SpaceObject (exposes |
required | |
entry
|
dict
|
The ship data entry (as merged, carrying |
required |
pirate_ship_keys()
Return all pirate ship keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Pirate ship type keys. |
pirate_starbase_keys()
Return all pirate starbase keys (cached).
As of v1.2.2 no pirate starbases exist in shipData; this returns an
empty list.
Returns:
| Type | Description |
|---|---|
|
list[str]: Pirate starbase type keys. |
plain_asteroid_keys()
Return all plain asteroid keys, excluding crystal asteroids (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Plain asteroid type keys. |
reset_ship_data_caches()
Clear the DERIVED ship data caches (index and key lists), not the data itself.
Called by the merge/add functions after they change the #ship-list so the next
lookup sees the new entries. For the mission-boundary reset that also drops the
loaded data, use :func:ship_data_reset_for_mission.
ship_data_is_loaded()
Reset-ledger probe: 1 while ship data (possibly mod-merged) is held, else 0.
ship_data_reset_for_mission()
Drop the loaded ship data ENTIRELY, including any merged mod entries.
Deliberately NOT the same as :func:reset_ship_data_caches, which clears only the
DERIVED caches and is called by the merge functions themselves - clearing
ship_data_cache there would throw away the entries just merged.
This is the MISSION-BOUNDARY reset. The next mission has its own mission directory
(its own extraShipData) and its own set of mods, so a #ship-list carrying the
previous mission's merged entries must not survive into it. The engine forks a fresh
process per mission and hides this; cosmos_dev reuses one interpreter and does
not. Registered in the reset ledger as ship_data_cache.
skaraan_ship_keys()
Return all Skaraan ship keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Skaraan ship type keys. |
skaraan_starbase_keys()
Return all Skaraan starbase keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Skaraan starbase type keys. |
terran_ship_keys()
Return all TSN ship keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Terran ship type keys. |
terran_starbase_keys()
Return all USPF station (Terran starbase) keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Terran starbase type keys. |
torgoth_ship_keys()
Return all Torgoth ship keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Torgoth ship type keys. |
torgoth_starbase_keys()
Return all Torgoth starbase keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Torgoth starbase type keys. |
ximni_ship_keys()
Return all Ximni ship keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Ximni ship type keys. |
ximni_starbase_keys()
Return all Ximni starbase keys (cached).
Returns:
| Type | Description |
|---|---|
|
list[str]: Ximni starbase type keys. |