-
Hello, thank you for a beautifully designed ECS system. I'm curious, what is the recommended way to serialize and deserialize ECS state wholesale? To avoid XY problem, the case I'm trying to cater for is hot reload. The main application holds ECS state and passes it to each new generation of a dynamically loaded library to work with. It's okay if the app crashes / requires restart when ECS compiled into the library changes in a non-compatible way, but it would be nice if it can continue to work with the current state when it is compatible. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hey @ul, To save/load entity state, you need to be able to load/save a You will need to typecast Here's a pared down example: import polymorph, streams, json_serialization
register defaultCompOpts:
type
Foo = object
value: int64
Bar = object
content: string
makeEcs()
# Tell serialization to treat distinct ids as int64.
type PolymorphIds* = EntityId | EntityInstance | ComponentTypeId | ComponentIndexTypeClass
PolymorphIds.borrowSerialization int64
proc writeValue*(w: var JsonWriter, c: Component) =
# Tell serialization how to write a 'Component' type.
w.beginRecord()
writeField(w, "typeId", c.typeId)
# Create a case statement that writes the appropriate container value.
caseComponent c.typeId:
writeField(w, "value", componentRefType()(c).value)
w.endRecord()
proc readValue*(r: var JsonReader, v: var Component) =
# Tell serialization how to read a 'Component' type.
var
typeId: ComponentTypeId
comp: Component
for fieldName in readObjectFields(r):
case fieldName
of "typeId":
var i: int
r.readValue(i)
typeId = ComponentTypeId(i)
of "value":
caseComponent typeId:
v = makeContainer( r.readValue(componentType()) )
proc writeBlueprint(entities: Entities, filename: string) =
var
stream = fileOutput(filename, fmWrite)
writer = JsonWriter[DefaultFlavor].init(stream, pretty = true)
writer.writeValue entities.toTemplate()
stream.close
proc readBlueprint(filename: string): ConstructionTemplate =
Json.loadFile(filename, ConstructionTemplate)
# Create some entities to test with.
let
state = @[
cl(Foo(value: 1)),
cl(Foo(value: 2), Bar(content: "Text1")),
cl(Bar(content: "Text2")),
].construct()
# Save a 'ConstructionTemplate' of 'state' to a JSON file.
state.writeBlueprint "data.json"
# Load the blueprint from JSON and check it matches 'state'.
let blueprint = readBlueprint "data.json"
assert $state.toTemplate == $blueprint
# Build entities from the loaded blueprint.
let newState = blueprint.construct()
for e in newState:
echo e I hope this helps! |
Beta Was this translation helpful? Give feedback.
Hey @ul,
I use nim-serialization/nim-json-serialization to persist entities to streams or files.
To save/load entity state, you need to be able to load/save a
ComponentList
. This is like a blueprint that you can pass toconstruct
to create live entities. You can usetoTemplate()
to create these blueprints from individual entities or sequences of entities.You will need to typecast
Component
to its 'real' value based on itstypeId
field, asComponent
itself is just an inheritable root type. The convenience templatecaseComponent
helps with this by creating a case statement that matchestypeId
to registered component types.Here's a pared down example:
import polymorph, streams, json_serial…