scaffold is the tooling and add-in companion to
Golaxy Distributed Service Development Framework.
It focuses on the project-scaffolding parts that sit around the main runtime:
Excel table compilation, protobuf/GDScript code generation, Go-script hotfix
support, and entity property synchronization.
The repository is organized around two layers:
addins: service/runtime extensions that plug intogit.golaxy.org/framework.tools: command-line generators and protobuf plugins used during build-time code and data generation.
addins/goscr: a service add-in built on Yaegi for loading Go scripts, declaring script-backed entities/components, and hot-reloading local or remote script projects.addins/propview: a runtime add-in for managed entity properties, including load/save/sync flows across services and gateway-facing clients.tools/propc: a property-sync generator that scans annotated Go declarations and emitspropviewwrappers for synchronized operations.tools/excelc: an Excel pipeline that turns.xlsxtables into proto schemas, generated access code, and exported binary/JSON data files.tools/protoc-gen-go-excel: a protobuf plugin that adds table lookup and index-based access helpers to generated Go code.tools/protoc-gen-go-structure: a protobuf plugin that adds deep-clone helpers for messages, slices, maps, bytes, and nested messages.tools/protoc-gen-go-variant: a protobuf plugin that makes generated messages usable as GAP variant values in the Golaxy RPC stack.tools/protoc-gen-gdscript: a protobuf plugin that emits GDScript message types and serialization logic for Godot-side integration.tools/protoc-gen-gdscript-excel: a protobuf plugin that emits GDScript table wrappers and lookup helpers for Excel-generated schemas.
- Author shared
.protoschemas for RPC, persistence, snapshots, config, or any other cross-process data contract. - Run
protocand generate the bindings you need for each runtime, such as Go server code, clone helpers, GAP variant integration, or GDScript client code. - Keep generated outputs in per-runtime directories, for example
./server/src/genfor Go and./client/script/genfor Godot.
- Author
.xlsxtables. - Run
excelc prototo generate table-oriented.protofiles. - Run the normal protobuf pipeline on those generated schemas.
- Run
excelc datato export binary and/or JSON table data.
The Excel pipeline is layered on top of protobuf. The generated table schemas are still ordinary protobuf messages, so they can also be used for transport, serialization, storage, snapshots, or other tooling beyond table lookup.
- Define property state types and synchronized methods in Go.
- Mark declarations for
tools/propcand generate*.sync.gen.go. - Declare properties through
addins/propviewso they can load, save, and replicate revisions across services or clients.
- Install
addins/goscron the service side. - Point it at one or more local or remote script projects through
goscr.With.Projects(...). - Build entity prototypes with
goscr.BuildEntityPT(...)and script metadata. - Let the add-in reload script solutions on local file changes or remote source changes.
tools/protoc-gen-gdscript/libsis the runtime required by every generated*.pb.gdfile. Copy this directory into your Godot project so global classes such asProtoMessage,ProtoUtils,ProtoInputFile, andProtoOutputBufferare available to generated protobuf code.
tools/protoc-gen-gdscript-excel/libsis the additional runtime directory used by generated*.excel.gdwrappers.- Excel wrappers still depend on the protobuf runtime above, so when using
protoc-gen-gdscript-excel, both runtime directories must be present in the Godot project.
- These runtime scripts do not need to live in a fixed directory. A common
pattern is to place them under
libsoraddons/<name>and let Godot register them throughclass_name. - Keep generated
*.pb.gdfiles in the same relative layout as the source.protofiles. Cross-file protobuf references are emitted as relativepreload(...)calls. - Keep application protobuf output and Excel-derived protobuf output in different root directories. In practice, communication/storage schemas and table schemas are usually generated and maintained separately.
- Keep each generated
*.excel.gdfile next to its matching*.pb.gdfile. Generated Excel wrappers preload./<name>.pb.gdfrom the same output directory, andexcelc code --gdscript_out=...typically emits an aggregate loader such astables.gdinto that directory as well.
One common layout for regular protobuf output inside a Godot project:
res://addons/proto/ # files copied from tools/protoc-gen-gdscript/libs
res://script/gen/proto/ # regular protobuf-generated client messages
res://script/gen/proto/login.pb.gd
One common layout for Excel table output inside a Godot project. This section only lists the Excel table side of the layout:
res://addons/proto/ # files copied from tools/protoc-gen-gdscript/libs
res://addons/excel/ # files copied from tools/protoc-gen-gdscript-excel/libs
res://script/gen/excel/ # excel protobuf + wrapper output
res://script/gen/excel/excelc.pb.gd
res://script/gen/excel/example.pb.gd
res://script/gen/excel/example.excel.gd
res://script/gen/excel/tables.gd
res://res/excel/ # exported excel data files
res://res/excel/ExampleTable.bin.idx
res://res/excel/ExampleTable.bin.chk_0
For a split client/server project, one practical directory layout is:
./config/excel/ # source .xlsx files
./excelc/server/proto/ # server-facing excel proto schema
./excelc/client/proto/ # client-facing excel proto schema
./server/src/gen/ # generated Go protobuf/plugin output
./server/res/excel/ # exported server table data
./client/script/gen/ # generated GDScript protobuf/plugin output
./client/res/excel/ # exported client table data
With a layout like that, the Excel pipeline usually looks like this:
# 1. Export server-facing proto schema with hash-based unique indexes.
excelc proto \
--excel_files=./config/excel/Consts.xlsx \
--excel_dir=./config/excel \
--pb_out=./excelc/server/proto \
--pb_package=excel \
--pb_options=[go_package=./excel] \
--pb_imports=Consts.proto \
--pb_unique_index_as=hash_unique_index \
--targets=s
# 2. Export client-facing proto schema with sorted unique indexes.
excelc proto \
--excel_files=./config/excel/Consts.xlsx \
--excel_dir=./config/excel \
--pb_out=./excelc/client/proto \
--pb_package=excel \
--pb_options=[go_package=./excel] \
--pb_imports=Consts.proto \
--pb_unique_index_as=sorted_unique_index \
--targets=c
# 3. Generate server Go protobuf + Excel lookup code.
protoc -I./excelc/server/proto -I./protobuf/include \
--include_imports \
--retain_options \
--go_out=./server/src/gen \
--go-structure_out=./server/src/gen \
--go-excel_out=./server/src/gen \
./excelc/server/proto/*.proto
excelc code --pb_dir=./excelc/server/proto --pb_package=excel --go_out=./server/src/gen/excel
# 4. Generate client GDScript protobuf + Excel wrapper code.
protoc -I./excelc/client/proto -I./protobuf/include \
--include_imports \
--retain_options \
--gdscript_out=./client/script/gen \
--gdscript_opt=string_as_string_name=true \
--gdscript-excel_out=./client/script/gen \
./excelc/client/proto/*.proto
excelc code --pb_dir=./excelc/client/proto --pb_package=excel --gdscript_out=./client/script/gen/excel
# 5. Export server table data.
excelc data \
--excel_files=./config/excel/Consts.xlsx \
--excel_dir=./config/excel \
--pb_dir=./excelc/server/proto \
--pb_package=excel \
--targets=s \
--json_out=./server/res/excel \
--binary_out=./server/res/excel
# 6. Export client table data as chunked binary files.
excelc data \
--excel_files=./config/excel/Consts.xlsx \
--excel_dir=./config/excel \
--pb_dir=./excelc/client/proto \
--pb_package=excel \
--targets=c \
--binary_out=./client/res/excel \
--binary_chunked=true--targets does more than choose server/client outputs. It also controls
column visibility during Excel export: only fields marked for the selected
target are kept in the generated .proto, lookup code, and exported table
data. In practice, server-only columns can be excluded from client schemas and
client data packages, and client-only columns can be omitted from server-side
artifacts the same way.
--pb_unique_index_as controls how unique indexes are represented in the
exported protobuf schema. Different values produce different index message
shapes, which then affects the generated lookup code and the runtime memory
profile of loaded table data. In this example, the split is intentional: the
server keeps hash_unique_index so generated Go lookup code can query
hash-based indexes, while the client uses sorted_unique_index to reduce
index memory usage on the Godot side. Generated GDScript wrappers then query
SortedUniqueIndex.Values with binary search and can still work with
*.bin.idx / *.bin.chk_* chunked table data when needed.
hash_unique_indexemits hash-based unique indexes. Generated lookup code can use these indexes for direct key-oriented queries, which is usually a good fit on the server where table data often stays resident in memory.sorted_unique_indexemits unique indexes as sorted arrays. Queries become binary search instead of direct hash lookup, but it usually has lower memory overhead than hash-based indexes, which makes it a better fit for memory-constrained clients.- In practice,
hash_unique_indexis a good default for server-side lookup code, whilesorted_unique_indexis a better fit for Godot clients that need to keep table index memory usage under control.
--binary_chunked controls how binary table data is written during
excelc data. When enabled, rows are split into *.bin.chk_* chunk files and
paired with a *.bin.idx index file, which helps large client tables avoid
loading one monolithic binary blob at once. It affects the exported binary
layout, but does not change the generated protobuf schema.
Excel workbook conventions:
- The optional
@typessheet is used to declare reusable struct and enum types for the workbook. Table sheets can then reference those custom types in field definitions, instead of being limited to built-in scalar types only. - In the
@typessheet,Metaeffectively supportsseparatorandscope. Index-related keys such asunique_index,hash_unique_index, andsorted_unique_indexare only meaningful on table columns, not on@typesstruct or enum declarations. - Any table column whose header name does not start with a letter is ignored by
excelc. In practice, columns starting with#are commonly used as comment columns for notes, examples, or editor-only annotations, and they will not participate in generated schema or exported data.
Table sheet data layout:
ExampleCN.xlsxandExampleEN.xlsxuse the same table-sheet structure: row 1 is field names, row 2 is field types, row 3 is field meta, row 4 is comments, and actual data starts from row 5.- Scalar cells are written directly, such as
1,3.14,true, orHelloWorld. bytescells use Base64 text, as shown by values likeSGVsbG9Xb3JsZA==.- Enum cells can use enum number, enum name, or enum alias. The examples show
all three forms such as
1,EnumB/A, and localized aliases like枚举值B. - Repeated scalar fields are separated by the field separator. By default this
is
,, so examples look like1,2,3,4,5. Ifseparator=|is configured in meta, the same field can be written likeHelloWorld|HAHAHAHAHA. - Object cells use YAML-style mapping syntax. Example values include
A: 1, B: HelloWorld, C: [1, 2, 3], and object fields may also be addressed by alias, such asFieldA/字段A. - Repeated object fields support both full YAML sequences like
[{A: 1, B: HelloWorld}, {A: 2, B: HAHAHAHAHA}]and separator-based forms such asA: 1, B: HelloWorld | A: 2, B: HAHAHAHAHAwhen a custom separator is configured. - Map cells use YAML-style mapping syntax, for example
0: HelloWorld, 1: HAHAHAHAHAor0: {A: 1, B: HelloWorld}, 1: {A: 3, B: HAHAHAHAHA}.
Excel column parameters:
- Field-level options are configured in the table header's
Metasetting row (also accepted as元数据/特性). Each field column fills its own meta cell in that header row, using query-string syntax such asscope=c&sorted_unique_index=1orseparator=|. scope: repeatable target visibility tag. It works together with--targets; columns withoutscopeare visible to all targets. To match multiple targets, repeat the key, for examplescope=c&scope=sorscope=client&scope=editor.separator: delimiter used when parsing repeated or map-like cell values. The default is,.unique_index: repeatable integer index tag. It defines unique-index groups, and the actual exported representation follows--pb_unique_index_as.hash_unique_index: repeatable integer index tag. It forces the tagged unique index groups to use hash-based representation.sorted_unique_index: repeatable integer index tag. It forces the tagged unique index groups to use sorted-array representation.- A single-column unique index is configured by assigning one tag on one field,
for example
unique_index=1orhash_unique_index=1. - A composite unique index is configured by reusing the same tag on multiple
fields. For example,
role_idwithhash_unique_index=1andlevelwithhash_unique_index=1together form one composite unique index on(role_id, level). - One table can define multiple unique indexes at the same time by using
different tags, for example
id -> hash_unique_index=1,name -> sorted_unique_index=2, andtype + sub_type -> sorted_unique_index=3. - One field can participate in multiple indexes by repeating tags in its meta
cell, for example
hash_unique_index=1&hash_unique_index=2. That field will be included in both index groups. - The same tag cannot appear in both
hash_unique_indexandsorted_unique_index.
| Path | Responsibility |
|---|---|
./addins/goscr |
Service-level Go scripting add-in, script-backed entity/component helpers, lifecycle bridges |
./addins/goscr/dynamic |
Dynamic script loading, project/solution management, and hotfix support |
./addins/goscr/fwlib |
Symbols exported into script runtimes for core, framework, and scaffold packages |
./addins/propview |
Runtime property view, property sync, marshaling, and replication helpers |
./tools/excelc |
Excel schema generation, access-code generation, and data export CLI |
./tools/excelc/excelutils |
Hash/index conversion, table loading, and lookup helpers used by generated code |
./tools/propc |
Property synchronization code generator |
./tools/protoc-gen-go-excel |
Go protobuf plugin for Excel table lookup APIs |
./tools/protoc-gen-go-structure |
Go protobuf plugin for clone helpers |
./tools/protoc-gen-go-variant |
Go protobuf plugin for GAP variant integration |
./tools/protoc-gen-gdscript |
GDScript protobuf plugin for message types and serialization |
./tools/protoc-gen-gdscript/libs |
Godot protobuf runtime scripts required by generated *.pb.gd files |
./tools/protoc-gen-gdscript-excel |
GDScript protobuf plugin for Excel table wrappers |
./tools/protoc-gen-gdscript-excel/libs |
Godot Excel runtime scripts required by generated *.excel.gd wrappers |
tools/excelcuses Cobra/Viper and is split into three subcommands:proto,code, anddata.tools/propcreads a Go declaration file and writes a sibling*.sync.gen.gofile.tools/excelc/examplescontains example workbooks for the Excel pipeline.
Install the module itself when importing the add-in packages:
go get git.golaxy.org/scaffold@latestInstall only the command-line tools you need:
go install git.golaxy.org/scaffold/tools/excelc@latest
go install git.golaxy.org/scaffold/tools/propc@latest
go install git.golaxy.org/scaffold/tools/protoc-gen-go-excel@latest
go install git.golaxy.org/scaffold/tools/protoc-gen-go-structure@latest
go install git.golaxy.org/scaffold/tools/protoc-gen-go-variant@latest
go install git.golaxy.org/scaffold/tools/protoc-gen-gdscript@latest
go install git.golaxy.org/scaffold/tools/protoc-gen-gdscript-excel@latest