Project Config
The project manifest can be used as a simple schema root or as a full build description. It can be written as SCON, TOML, YAML, JSON, or Lua; SCON is recommended and used below.
project { id = "game_config" }
groups { common { default = true } }
views {
default {
contract = "game_config/default"
groups = ["common"]
}
}
includes = ["schema/items.scon"]
parsers { scripts = ["tools/parsers.lua"] }
type_mappings { scripts = ["tools/type_mappings.lua"] }
build {
default_source_format = "xlsx"
data_root = "data"
view = "default"
schema_lock = "generated/schema.lock"
excel_templates = "generated/excel"
codegen = [
{ target = "rust", out = "rust/src/generated", format = "auto" },
]
exports = [
{ format = "binary", out = "generated/config.sora" },
]
}
Run every configured output:
sora build --project project.scon
data_root and excel_templates serve different purposes. data_root is the input directory used by export and build, so it contains edited table rows. excel_templates is an output directory for generated workbook templates, so it can be deleted and regenerated after schema changes. Do not point excel_templates at your edited data directory unless replacing those workbooks is intentional.
parsers.scripts lists custom Lua cell parser scripts used by CLI commands that read the project. Paths are relative to the project file. See Cell Parsers for the script API.
type_mappings.scripts lists Lua scripts that customize generated language types. Paths are relative to the project file. Type mappings are codegen-only: the schema still uses language-neutral Sora types such as struct<Vec3>, while the mapping script can map that named type to a target-specific type.
Localization is declared at the project root with localization. Its keyed sources are independent from normal tables; see Localization.
Run one configured codegen target:
sora build --project project.scon --target rust
Target Options
Language-specific options live under [codegen.<target>]:
[codegen.rust]
runtime_format = "sora"
[codegen.typescript]
runtime_format = "json"
enum_repr = "string"
[codegen.lua]
runtime_format = "cbor"
lua_version = "5.4"
These options are consumed by the selected generator. The normalized IR stays language-neutral.
Type mapping scripts return a table with type_mappings. Each mapping targets one language and one named schema type:
return {
type_mappings = {
{
target = "csharp",
schema_type = "Vec3",
type_name = "Vector3",
nullable_type_name = "Vector3?",
decode = "GameMappings.ToVector3({value})",
value_decode = "GameMappings.ToVector3({value})",
imports = { "UnityEngine" },
},
},
}
nullable_type_name is optional. Use it when optional<schema_type> needs a different target-language type expression from the backend’s default nullable wrapper.
decode wraps the normal binary runtime decode expression, and value_decode wraps JSON/CBOR/protobuf-style value decode. The {value} placeholder is replaced with the generated default expression.
The C target uses write-into decode functions, so C mappings should use decode_into instead of decode. The {target} placeholder is replaced with the output pointer expression. C mappings can also provide free, where {target} is replaced with the pointer that should be released:
{
target = "c",
schema_type = "Vec3",
type_name = "game_vector3",
decode_into = "game_vector3_decode(reader, {target})",
free = "game_vector3_free({target});",
imports = { "#include \"vector3.h\"" },
}
imports is target-specific and is only emitted by language generators that need it. C#, Java, Kotlin, and Scala expect an import namespace/path without the leading keyword. Go expects an import spec such as "example.com/game/vector". Python, TypeScript, JavaScript, Dart, Godot, C, C++, and Rust expect a complete import/include/use/preload line.
runtime_format can be sora, json, cbor, or sora-protobuf, but not every target supports every runtime format. See Runtime Formats for the support matrix.
Built-In Target Options
| Target | Options |
|---|---|
rust | runtime_format default sora; map_type = "std" or "fx_hash_map" default std; string_storage = "owned" or "arc" default owned; optional crate emits a standalone library crate. |
kotlin | runtime_format default sora. |
csharp | runtime_format default sora. |
java | runtime_format default sora; nullable_annotation defaults to SoraNullable, set an annotation class such as org.jetbrains.annotations.Nullable, or set "" to disable annotations. |
scala | runtime_format default sora; scala_version = "2.12", "2.13", or "3" default 3. |
go | runtime_format default sora. |
dart | runtime_format = "json", "cbor", or "sora-protobuf". Set this explicitly; sora is not supported for Dart. |
godot | runtime_format = "json". Set this explicitly; it is the only supported Godot runtime format. |
c | runtime_format = "sora"; c_standard = "c99", "c11", "c17", or "c23" default c11; prefix optional symbol prefix. |
cpp | runtime_format = "sora"; cpp_standard = "c++11", "c++14", "c++17", "c++20", or "c++23" default c++17; namespace optional C++ namespace. |
typescript | runtime_format default sora; enum_repr = "string" or "integer" default string. |
javascript | runtime_format default sora; enum_repr = "string" or "integer" default string; emit_dts boolean default true. |
erlang | runtime_format default sora; enum_repr = "atom" or "integer" default atom. |
lua | runtime_format default sora; module optional require/import prefix; lua_version = "5.1", "5.2", "5.3", "5.4", or "luajit" default 5.4; enum_repr = "string" or "integer" default string. |
python | runtime_format default sora. |
proto-schema | No target options. Generates .proto schema files instead of a runtime loader. |
Example with several language-specific options:
[codegen.rust]
runtime_format = "sora"
map_type = "fx_hash_map"
string_storage = "arc"
[codegen.cpp]
runtime_format = "sora"
cpp_standard = "c++20"
namespace = "game::config"
[codegen.javascript]
runtime_format = "json"
enum_repr = "integer"
emit_dts = true
Standalone Rust Crate
Rust normally writes a module tree directly into the configured out directory. Set
codegen.rust.crate to make out the root of a complete library crate instead:
build {
codegen = [{ target = "rust", out = "generated/game-config" }]
}
codegen {
rust {
runtime_format = "sora"
crate {
name = "game-config"
version = "0.1.0"
edition = "2024"
publish = false
}
}
}
Sora then writes Cargo.toml, src/lib.rs, the generated models, and the runtime under
generated/game-config. name is required. version, edition, and publish default to
0.1.0, 2024, and false. The manifest includes the dependencies required by the selected
runtime format and Rust options. Applications can consume it with a normal path dependency:
[dependencies]
game-config = { path = "../generated/game-config" }
Without crate, the existing module layout remains unchanged and out should point to the
module directory inside the consuming crate.