File Formats

Field-by-field reference for every file the editor writes into a project, from project.vastopia and game_config.json to map binaries and data JSON.

21 min read

How a project is laid out#

A Vastopia game is a folder of plain files: JSON, INI, Godot .tscn text scenes, C# scripts and a handful of small binary blobs. There is no database. The editor reads and writes these files directly, and the MCP tools edit the same files, so anything you see here can also be inspected with a text editor or kept in version control.

The project splits into two halves that ship separately:

HalfFolderPacked intoHolds
Clientassets/the game PCK players downloadModels, icons, animations, interface scenes, terrain, names and descriptions
Serverserver/server_data.zip uploaded with each buildStats, drops, scripts, spawn coordinates, collision, prices

Two folders are build products rather than source: packs/ holds intermediate PCK output, and .export_cache/ (dot-prefixed, never packed) caches converted models so the editor and the publish step do not re-convert them. See Project structure for the full tree.

Conventions that apply everywhere#

  • IDs. Items, NPCs, objects, VFX and interfaces use sequential integers allocated by the editor. Skills use numeric skill_ids. Shops, prayers and tasks use string ids. Assets use 26-character ULIDs. Never invent an id: the Data Editor, the map tools and the MCP all allocate the next free one.
  • Floats for ints. Godot serialises many integers as floats in client JSON ("item_id": 1.0, "row": 0.0). Every loader tolerates this. Server JSON tends to use clean integers.
  • Paths. Client model and icon references are res://assets/... or assets/... project-relative paths. Server script references are project-relative, such as server/scripts/objects/Obstacle.cs. A machine-absolute path in client data breaks the game at runtime, so keep everything relative.
  • Versions. Files that carry a "version" key keep it; copy the value from an existing file of the same kind.
  • Coordinates. TILE_SIZE is 0.5 world units. Spawn files store world units; tile-based files store integer tile coordinates. See Coordinate system.

Project root files#

project.vastopia#

An INI file in Godot ConfigFile style: quoted string values, a blank line after each section header. Written by the project wizard and updated by the editor as you work.

INI
[project]

name="Default"
version="1.0.0"
created="2026-08-16T18:45:08"
modified="2026-08-16T18:45:08"
description=""
data_format=1

[paths]

maps_dir="assets/maps/"
ui_dir="assets/UI/"
data_dir="assets/data/"
interface_dir="assets/interface/screens/"

[game]

starting_map=""
title="Default"

[platform]

game_id=""

[server]

project_path=""

[client]

project_path=""
SectionKeyTypeDefaultMeaning
[project]namestringthe name you gave the projectDisplay name used by the wizard and the editor title
[project]versionstring"1.0.0"Project version string
[project]created, modifiedISO-8601 stringnowTimestamps; modified is bumped on save
[project]descriptionstring""Free text
[project]data_formatint1On-disk format version. The MCP server compares it against the range it was built for and refuses to modify a project from a newer editor.
[project]template_id, template_versionstring"default", ""Which starter template the project was created from; used to copy a system's files back when you re-enable it
[paths]maps_dir, ui_dir, data_dir, interface_dirstringas aboveRelative to the project root, trailing /
[game]starting_mapstring""Name of the map players spawn into; mirrored in game_config.json
[game]start_x, start_zint0Spawn tile; mirrored in game_config.json
[game]titlestringproject nameGame title
[platform]game_idstring""UUID of the game on the platform, set by the Project Wizard. Required to publish; the publish dialog has no game selector and reads this value.
[platform]deploy_host, deploy_portstring, int"", 7777Optional self-hosted deploy target
[server]project_pathstring""Local path to a Vastopia-Server checkout for Dev Test. Machine-specific; not shipped.
[client]project_pathstring""Local path to a Vastopia-Client checkout. Machine-specific; not shipped.
[systems]enabledarray of stringsall systemsGameplay systems switched on for this project. Projects without the key read as "everything on".
[branding]icon_path, icon_cdn_urlstring""Game icon chosen in the wizard
[export]pack_only_used_modelsbooltrueSkip packing models nothing references
[world]max_world_sizeint0Maximum map footprint per side in world units; 0 = unlimited. The Map Editor warns on save when a map exceeds it.
[map_defaults]terrain_type, lighting_themeint0Indices preselected in the Map Editor's create-map view

game_config.json#

The metadata file the launcher and client read. Uploaded as-is with every build.

JSON
{
	"description": "",
	"developer": "",
	"genre": "Adventure",
	"starting_map": "",
	"tags": [],
	"title": "Default",
	"version": "1.0.0",
	"website": ""
}
KeyTypeWritten by
titlestringProject wizard (set to the project name)
versionstringWizard; defaults to "1.0.0"
description, developer, websitestringWizard identity step; always present, "" when unset
genrestringWizard identity step
tagsarray of stringsWizard identity step
visibilitystringWizard identity step, when set
starting_mapstringGame Settings; a map directory name under assets/maps/
start_x, start_zintGame Settings; the player spawn tile

The Game Config editor's branding, cover, screenshot and loading-screen settings are stored in a separate INI, game_config.cfg, at the project root ([basic], [branding], [gameplay], [platform] sections). That file is editor state for the platform listing, not something the client reads.

Other root files#

FilePurpose
prop_library.jsonThe Map Editor's model Library: props[] with id, name, path, folder, is_permanent, added_date (and source_path for models added from a linked folder), plus folders[]
interface_library.jsonGUI editor folder assignments for interface scenes (assignments, folders)
recent_interfaces.cfgRecently opened interfaces; editor convenience only

Client data: assets/data/#

items.json (client)#

A JSON array, one object per item. Visual and inventory fields only.

FieldTypeDefaultNotes
item_idintallocatedJoin key with the server copy
name, descriptionstring""Shown in the inventory
is_equippableboolfalse
slotstring""One of right_hand, left_hand, head, body, hands, legs, boots, amulet, ring; empty when not equippable
equip_modestring""How the item attaches: attach_right_hand, attach_left_hand, replace_head, replace_head_jaw, overlay_head, overlay_head_jaw, attach_head, replace_body, replace_body_arms, overlay_body, overlay_body_arms, replace_hands, replace_legs, overlay_legs, and so on per slot. replace_ hides the body part, overlay_ draws over it, attach_ hangs the model off one bone.
attach_bonestring""Bone name for attach_ modes
model_asset_id, model_pathstring""Asset ULID and path of the equipped model
icon_mesh_asset_id, icon_mesh_pathstring""Model the inventory icon was rendered from
icon_path, icon_path_48, icon_path_32string""assets/icons/64/N.png and the 48 and 32 px variants
editor_propertiesobject{}editor_position, editor_rotation, editor_scale (composed equip transform as "(x, y, z)" strings), optional equip_base_*, and the icon camera (icon_orbit_yaw, icon_orbit_pitch, icon_orbit_distance, icon_orbit_target, icon_model_rotation)
interact_optionsarray of strings[]Right-click labels only, for example ["Equip", "Use", "Drop"]
weapon_animation_overridesobject{}Animation name to animation id
equipment_statsobject{}Same block as the server copy, so the equipment tab can sum bonuses

items.json (server)#

FieldTypeDefaultNotes
item_id, name, is_equippable, slot, equip_mode, attach_boneSame as client
stackableboolfalse for new itemsUnits merge into one slot. Rows that predate the key are treated as stackable.
tradeablebooltrueCan be offered in a trade
droppablebooltrueCan be dropped on the ground
interact_optionsarray of objects[]{id, label, script_path}; id is the slugified label. The drop option may carry drop_mode: "drop" spawns a floor item, "destroy" removes it outright.
script_pathstring""Item script
equipment_script_pathstring""Script run when equipped
propertiesobject{}Custom properties declared in property_manifests.json (for example value, weight)
requirementsarray[]{skill, level} pairs
equipment_statsobjectSee below
weapon_animation_overridesobject{}Animation name to animation id (integers)

equipment_stats keys: accuracy_melee, accuracy_ranged, accuracy_magic, strength_melee, strength_ranged, strength_magic, defence_melee, defence_ranged, defence_magic (all default 0), attack_speed (default 6), attack_distance (default 1), combat_type (0 melee, 1 ranged, 2 magic), attack_anim_id, hit_gfx_id, projectile_id (default 0). See Items.

npcs.json (client)#

FieldTypeDefaultNotes
npc_id, name, description
tilesint1Footprint size
model_name, model_asset_id, model_path, extracted_mesh_pathstring""Project-relative model paths
icon_pathstring""
optionsarray of strings[]Right-click labels only, for example ["Talk-to"]
animation_basestring""Animation base id from animations.json
idle_animation_id, walk_animation_id, run_animation_idint0
idle_animation_speed, walk_animation_speed, run_animation_speedfloat1.0
position_x/y/z, rotation_x/y/zfloat0.0Model offset and rotation
scale, scale_x/y/z, scalex/scaley/scalezfloat1.0Uniform and per-axis scale (the three spellings are kept in step for older clients)
clickboxobject{}shape_type (box, capsule or mesh), size{x,y,z}, offset{x,y,z}, capsule_radius, capsule_height
combat_levelint1Copied from the server combat block for display

npcs.json (server)#

FieldTypeDefaultNotes
npc_id, name, tiles
optionsarray of objects[]{id, label} plus dialogue_id when an option opens a dialogue
script_pathstring""NPC script
animationsobject{}Animation name to animation id
propertiesobject{}Declared custom properties, such as attackable
combatobjectSee below

combat fields and the server's defaults: attackable (false), aggressive (false), aggro_radius (5.0), aggro_leash (12.0), combat_level (1), hitpoints (100), max_hit (0), attack_speed (0), attack_distance (0.0), accuracy_melee/ranged/magic (75/70/65), strength_melee/ranged/magic (1), defence_melee/ranged/magic (1), respawn_ticks (0), combat_script_path (""). The editor also writes combat_type, max_hit_melee, max_hit_ranged and max_hit_magic for its own UI.

objects.json (client)#

FieldTypeDefaultNotes
object_id, name, description
model_asset_id, model_pathstring""
tile_width, tile_depthint1Footprint in tiles
interact_actionsarray of strings[]Right-click labels only
offset_x/y/z, rotation_x/y/zfloat0.0Model transform
scale, scale_x/y/zfloat1.0
clickboxobject{}Same shape as NPCs
idle_animationstring""Clip name the client loops on the model
render_on_minimapbooltrue

objects.json (server)#

FieldTypeDefaultNotes
object_id, name
collision_typestring"obstacle"obstacle (blocks movement and line of sight), wall (same), ground_deco (fully passable), door, low_fence (blocks movement, projectiles pass over), transparent (blocks movement, line of sight passes through). The Data Editor writes obstacle, ground_deco or low_fence from its two clip checkboxes.
tile_width, tile_depthint1Used for movement blocking and interaction range on every map that places the object
interact_distanceint2Tiles a player must be within
interact_actionsarray of objects[]{id, label}
script_pathstring""Object script
propertiesobject{}Declared custom properties, such as interactable

animations.json#

Client only. An object with version and bases[]. Each base is a shared skeleton:

JSON
{ "version": 1, "bases": [ {
  "id": "humanoid", "display_name": "humanoid",
  "skeleton_path": "assets/skeletons/humanoid_skeleton.tscn",
  "animations": [
    { "id": 1.0, "name": "idle", "length_sec": 2.0, "in_place": false,
      "resource_path": "assets/anims/humanoid/anim_1.tres", "retarget_hint": "", "tags": [] }
  ]
} ] }

Animation ids are what idle_animation_id, NPC animations and weapon_animation_overrides reference. See Animations.

vfx_database.json#

Client only: { "vfx": [ { "vfx_id": 1, "name": "fire", "scene_path": "res://assets/vfx/fire.tscn", "scalex": 1.0, "scaley": 1.0, "scalez": 1.0 } ] }. The effect itself is the self-contained scene at assets/vfx/<name>.tscn. See VFX.

tab_config.json#

The client's tab bar. Two rows of up to six tabs each, twelve tabs maximum.

JSON
{
	"version": 1,
	"layout": {
		"rows": [
			{ "tab_width": 48, "spacing": 2 },
			{ "tab_width": 48, "spacing": 2 }
		]
	},
	"tabs": [
		{ "index": 0, "name": "Combat", "icon": "1.png", "interface_name": "combat",
		  "interface_id": 13, "enabled": true, "row": 0 }
	]
}
FieldTypeNotes
layout.rows[n].tab_widthintDefault 48; allowed 16150, and capped per row so the row never exceeds the 300 px bar
layout.rows[n].spacingintDefault 2; allowed 032
tabs[].indexintPosition; the editor re-indexes on save
tabs[].namestringTooltip / display name
tabs[].iconstringFile name in assets/sprites/ (the numbered 1.png12.png ship with every project)
tabs[].interface_namestringThe tab's interface scene name. inventory, equipment and skills are drawn by the client itself and are protected from removal.
tabs[].interface_idintInjected from interface_database.json on save
tabs[].enabledbool
tabs[].rowint0 top row, 1 bottom row

A legacy flat layout: {tab_width, spacing} still loads and applies to both rows. See Tabs.

map_area_config.json#

The HUD stat orbs and the up to two map buttons next to the minimap.

JSON
{
	"version": 1,
	"progress_bars": [
		{ "name": "HealthBar", "stat_key": "health", "icon": "1.png",
		  "fill_color": [1.0, 0.0, 0.0, 1.0], "default_value": 50.0 }
	],
	"bottom_buttons": []
}
FieldTypeNotes
progress_bars[]array, max 4One orb per entry; stat_key is one of health, stamina, mana, energy
progress_bars[].namestringDefaults HealthBar, StaminaBar, ManaBar, EnergyBar
progress_bars[].fill_color[r, g, b, a] floats
progress_bars[].default_valuefloatHealth 50, stamina 20, mana 80, energy 60 by default
bottom_buttons[]array, max 2{name, action_key, icon}; the client draws two and ignores more

Position and size are computed by the client; only the fields above are read. See Game screen and HUD.

skills_config.json#

Present in both assets/data/ and server/data/ with slightly different rows.

FieldClientServerNotes
versionyesyes1
skills[].skill_idyesyesNumeric id
skills[].nameyesyes
skills[].categoryyesyesFor example combat
skills[].max_levelyesyes99 in the template, 120 for Defence
skills[].iconyesnoSprite file name
skills[].ordinalyesnoDisplay order in the skills tab
skills[].script_pathnoyesOptional skill script

The client builds its skills panel from this file, so the skills you define really appear in game. See Skills.

interface_database.json#

Both sides, ids must match: { "interfaces": [ { "id": 1, "name": "Shop", "path": "res://assets/interfaces/screens/Shop.tscn", "type": "gamescreen" } ] } with type one of gamescreen, tab, chatbox. The editor regenerates it by scanning assets/interface/screens/ on save and export; the server opens interfaces by these integer ids. See Custom interfaces.

locations.json, wisdom_config.json#

locations.json is the same on both sides: { "locations": [ { "name": "Wilderness", "script_path": "" } ] }. wisdom_config.json (prayers/buffs) is on both sides but with a different row on each. Server: { "version": 1, "drain_interval_ticks": 4, "prayers": [ { "prayer_id", "name", "category", "level_required", "drain_rate", "effect_type", "effect_value", "overhead_icon", "conflicts_with": [] } ] }. Client: { "version": 1, "categories": ["overheads", "combat", "utility", "misc"], "prayers": [ { "prayer_id", "name", "category", "level_required", "icon_asset_id", "ordinal" } ] } — display fields only, no drain or effect data. prayer_id is a string and joins the two.

Server-only data: server/data/#

systems.json#

Written whenever you create a project or change systems in Game Config. The server honours it at module-load time, which matters because some template modules also exist as pre-compiled platform copies that deleting a file cannot remove.

JSON
{
	"version": 1,
	"enabled_systems": ["dialogue", "quests", "tasks", "skills_xp", "wisdom", "crafting", "shops",
		"banking", "trading", "social", "combat", "aggro", "specials", "buffs", "bosses", "drops"],
	"disabled_systems": [],
	"disabled_modules": []
}

enabled_systems mirrors [systems] enabled in project.vastopia; disabled_modules lists the module ids the server must refuse to load. See Templates and gameplay systems.

property_manifests.json#

Declares the custom typed properties available under each entity's properties block. type is bool, int, float or string.

JSON
{ "items":   [ {"name": "value", "type": "int", "default": 0}, {"name": "weight", "type": "float", "default": 0} ],
  "npcs":    [ {"name": "attackable", "type": "bool", "default": false} ],
  "objects": [ {"name": "interactable", "type": "bool", "default": false},
               {"name": "render_on_minimap", "type": "bool", "default": true} ] }

script_bindings.json#

Entity id to script path, rebuilt from the script_path fields when a definition that binds a script is saved: { "items": {}, "equipment": {}, "npcs": {}, "npc_combat": {}, "objects": {"3": "server/scripts/objects/Obstacle.cs"} }.

world_settings.json#

Engine defaults for respawn and death rules; every key is the engine default, so deleting the file changes nothing. Keys: respawn_region, respawn_x, respawn_y, respawn_z, drop_items_on_death, drop_equipment_on_death. Keys starting with _ are comments.

Schema-backed content: server/schemas/#

Shops, recipes, tasks, loot tables, dialogue, bosses, buffs and combat formulas are not in server/data/. Each lives as a pair under server/schemas/<Category>/<name>/: <name>.json (the schema the Script Editor uses for autocomplete and table editing) and <name>_data.json (the rows). Drop tables, for example, are server/schemas/Drops/npc_drops/npc_drops_data.json, with npc_table_drops, npc_wheel_drops and the item_*, object_* and misc_* variants alongside. The Game Tools pages document each: Loot tables, Shops, Recipes, Tasks, Dialogue.

Map files: assets/maps/<map>/#

Saving a map writes the files below into its folder and re-derives the server exports in the same pass.

FileFormatPurpose
<map>_region.tscnGodot text sceneThe map scene: root node, the Terrain3D node, environment
data/Terrain3D binary .res filesHeightmap, control and color maps, one file per terrain region. Do not hand-edit; terrain can only be authored in the editor or with the MCP terrain tools.
regions.jsonJSONPlaced region elements: NPC spawners, interactable objects, trigger zones, location bounds
props.binbinary, version 2Every decorative prop and painted mesh instance
props_meta.jsonJSON, version 1Optional sidecar: per-instance surface-follow and align-to-slope records. Absent when no prop carries metadata.
height_layers.terrainbinary HTLR, version 3Height layers and their per-tile offsets
terrain_data.jsonJSONBounds and blocked tiles in world units, for tools that want a quick read
water.jsonJSON, version 1Water plane settings
dungeon.jsonJSON, version 1Only on dungeon-style maps: carved tiles, voided tiles, wall set and corner style
tile_flags.jsonJSON, version 1Legacy per-tile walkability ({tiles: [{x, z, layers: {"0": true}}]}). The editor still loads one if present; walkability is exported to the server binary below.

regions.json#

One object with version, next_id and four arrays. Every element has id, name and type.

ArraytypeElement fields
npc_spawnersnpc_spawnerposition [x, y, z], npc_id, rotation_y (degrees), spawn_radius, spawn_count, respawn_time, behavior (idle, wander, patrol), wander_intensity (0 low, 1 medium, 2 high), spawn_condition, height_layer_id (-1 terrain, 0+ layer), patrol_path [[x, y, z], ...]
interactable_objectsinteractableposition_2d [x, z] (height is derived on load), height_layer_id, object_id, model_rotation_y, interaction_type, interaction_radius, script_on_interact, is_persistent, custom_properties {}
trigger_zonestrigger_zoneposition [x, y, z], polygon [[x, z], ...], trigger_type, script_on_trigger, audio_to_play, one_shot, cooldown, enabled
location_boundslocation_boundsposition [x, y, z], tiles [[tx, tz], ...], color [r, g, b, a], audio_path, script_path, music_path, ambient_track, enter_sound, leave_sound, fog_enabled, fog_color [r, g, b], fog_density, fog_height, ambient_light_color, ambient_light_energy, sun_light_color, sun_light_energy, weather_type, difficulty_modifier

Model paths, scale and footprint are never stored here; they come from the NPC and object definitions, so changing a definition changes every map that uses it. See Regions and elements.

props.bin#

Little-endian binary. Version 2 is the only version the client and the MCP accept, which is why editor-only metadata lives in the sidecar instead.

Text
magic        "PROP" (4 bytes)
version      u8  = 2
model_count  u32
per model:
  flags        u8   bit0 = batched (multimesh), bit1 = has scale
  path_len     u16
  path         UTF-8, project-relative, e.g. assets/models/<ULID>_tree/tree.scn
  inst_count   u32
  per instance:
    position   3 x f32  (world units)
    rotation   3 x f32  (radians)
    scale      3 x f32  only when the model's has-scale flag is set

The has-scale flag is per model: if any instance of a model is scaled, every instance of that model writes three scale floats. The same path may appear twice, once batched and once as individual props.

props_meta.json#

{ "version": 1, "models": { "<path>": [ {...}, null, ... ] } }. Keys are the same model paths as props.bin, with #mm appended for the batched entry; each array runs parallel to that model's instances. A record may hold follow (re-seat on terrain edits; on a batched entry it only ever appears as false, the single opt-out, carried in by batching a Fixed prop), surface (terrain or layer), layer (1-based height layer id), offset (height above the surface), align (tilt to the surface normal) and normal. An empty record means "fixed" for an individual prop and "sit exactly on the surface" for a batched copy. If the sidecar is missing or the counts no longer match, that model's metadata is ignored and its instances are treated as fixed. See Props and model painting.

height_layers.terrain#

Text
magic            "HTLR" (4 bytes)
version          u8  = 3
layer_count      u8
active_layer     u8
tile_size        f32  (0.5)
tiles_per_region u16  (64 for a standard region)
region_count     u16
regions          region_count x (x: i16, z: i16)
per layer:
  layer_id       u8
  name_len       u8, then name bytes (UTF-8)
  color          4 bytes r, g, b, a (0-255)
  default_height f32
  per region:    tiles_per_region^2 x f32 height offsets above the terrain (0.0 = on the ground), row by row

Only active terrain regions are stored, so the file stays small on sparse maps. See Height layers.

water.json#

{ "version": 1, "enabled": false, "level": -0.01, "water_color": [r, g, b, a], "deep_color": [r, g, b, a], "highlight_color": [r, g, b, a], "fresnel_power": 3.0, "highlight_strength": 0.2, "roughness": 0.3, "metallic": 0.0 }. The default level sits just under zero so the surface never z-fights terrain standing at exactly height 0.

terrain_data.json#

{ "bounds": { "min_x", "min_z", "max_x", "max_z" }, "blocked_tiles": [[world_x, world_z], ...], "height_levels": [] } with bounds and blocked tiles in world units. It is a convenience export; the server reads the binary flag file instead.

Server map exports: server/data/maps/#

Saving a map writes four per-map files here. They are derived, so edit the map rather than these files.

npc_spawns/<map>.json#

JSON
{ "version": 1, "map_name": "home", "spawns": [
  { "spawn_id": 275, "npc_id": 1, "x": -0.5, "z": 7.0, "rotation_y": 0.0,
    "spawn_radius": 1.0, "spawn_count": 1, "respawn_time": 0, "behavior": "idle",
    "wander_intensity": 1, "patrol_path": [], "spawn_condition": "" } ] }

x and z are world units (tile times 0.5); the server derives height from the terrain. patrol_path entries are {x, y, z} objects.

object_spawns/<map>.json#

JSON
{ "version": 1, "map_name": "home", "spawns": [
  { "spawn_id": 300, "object_id": 3, "x": 6.0, "z": -1.0, "height_layer_id": 0,
    "rotation_y": 0.0, "scale": 1.0, "interaction_type": "use", "interaction_radius": 2.0,
    "is_persistent": true, "script_on_interact": "", "custom_properties": {} } ] }

Footprint is deliberately absent: the server reads tile_width and tile_depth from the object definition.

location_bounds/<map>.json#

{ "version": 2, "map_name", "locations": [ { "location_id", "name", "tiles": [[tx, tz], ...], "audio": { "ambient_track", "music_path", "enter_sound", "leave_sound" }, "environment": { "fog_enabled", "fog_color", "fog_density", "ambient_color", "ambient_energy", "sun_color", "sun_energy" }, "weather_type", "difficulty_modifier", "script_path" } ] }.

flag_data/<map>.terrain#

The walkability file the server loads. Little-endian, sparse version 2:

Text
magic            "REGN" (4 bytes)
version          u8  = 2
layer_count      u8
tile_size        f32  (0.5)
tiles_per_region u16
region_count     u16
regions          region_count x (x: i16, z: i16)   active region grid cells
blocked_count    u32
blocked tiles    blocked_count x (tile_x: i16, tile_z: i16, layer_mask: u8)

layer_mask is a bitfield: bit N set means the tile is blocked on height layer N. Only tiles inside an active region are walkable at all, so activating regions in the Map Editor is what defines the playable area. Version 1 files (bounds-based: tile_size, min_x, min_z, max_x, max_z instead of the region list) still load. On dungeon maps every un-carved tile is written as blocked on the ground layer. See Map files and export.

Other project files#

assets/data/building_prefabs.json#

{ "version": 1, "prefabs": [ { "name", "width", "depth", "floors", "pieces": [...] } ] }. width, depth and floors describe the plot; stamping reads only name and pieces. Each piece has dx, dz (tile offset from the plot's south-west corner), rotation_y, height_layer_id (the floor) and optional off_x, off_z for free-placed pieces, then either:

  • a model piece: kind: "prop", prop_id, model_path, name, tile_width, tile_depth, stamped as a plain prop; or
  • an interactable object: object_id, interaction_type: "use", interaction_radius: 2.0, script_on_interact, is_persistent: true, custom_properties: {}, stamped as a region element.

See Building prefabs.

assets/AssetLibrary.json#

The registry of every imported asset, keyed by ULID.

JSON
{ "version": 1, "assets": [
  { "id": "01KY39MGW5AJAXFWX4Q2836T4N", "name": "humanoid_skeleton", "type": 7,
    "path": "skeletons/humanoid_skeleton.tscn", "original_filename": "humanoid_skeleton.tscn",
    "import_date": "2026-07-21T17:34:44", "tags": [], "metadata": {},
    "cloud_id": 0, "sha256": "" } ] }
FieldTypeNotes
idstring26-character ULID, time-sortable
namestringDisplay name
typeint0 sprite, 1 3D model, 2 audio SFX, 3 audio music, 4 texture, 5 animation, 6 UI scene, 7 skeleton, 8 other
pathstringRelative to assets/
original_filenamestringName of the file that was imported
import_dateISO-8601 string
tagsarray of strings
metadataobjectType-specific; sprites carry width, height, format
cloud_idintPublic asset id on the cloud library, 0 when not uploaded
sha256stringHash of the uploaded source file, "" when not uploaded

Type folders under assets/: sprites in sprites/, models in models/<ULID>_<name>/ (converted to a self-contained .scn on import), SFX in audio/sfx/, music in audio/music/, textures in textures/, animations in anims/<base>/, UI scenes in UI/, skeletons in skeletons/, anything else in misc/. Recognised import extensions: .png .jpg .jpeg .webp .svg (sprites), .glb .gltf .obj .fbx (models), .wav .ogg (SFX), .mp3 (music). See Asset manager.

Quick index#

FileSideShape
project.vastopiarootINI
game_config.jsonroot, shippedobject
game_config.cfgroot, editor onlyINI
assets/data/items.json, npcs.json, objects.jsonclientarray
server/data/items.json, npcs.json, objects.jsonserverarray
assets/data/animations.json, vfx_database.jsonclientobject
assets/data/tab_config.json, map_area_config.jsonclientobject
skills_config.json, wisdom_config.json, locations.json, interface_database.jsonbothobject
server/data/systems.json, property_manifests.json, script_bindings.json, world_settings.jsonserverobject
server/schemas/<Category>/<name>/<name>_data.jsonserverarray
assets/maps/<map>/regions.json, water.json, terrain_data.json, props_meta.jsonclientobject
assets/maps/<map>/props.bin, height_layers.terrainclientbinary
server/data/maps/npc_spawns/, object_spawns/, location_bounds/serverobject per map
server/data/maps/flag_data/<map>.terrainserverbinary
assets/data/building_prefabs.jsoneditorobject
assets/AssetLibrary.jsoneditorobject
Spotted a mistake or something missing?Tell us on Discord