arkserde

package module
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 22, 2025 License: MIT Imports: 10 Imported by: 2

README

Ark Serde

Test status Coverage Status Go Report Card Go Reference GitHub MIT license

Ark Serde provides JSON serialization and deserialization for the Ark Entity Component System (ECS).

Features

  • Serialize/deserialize an entire Ark world in one line.
  • Proper serialization of entity relations, as well as of entities stored in components.
  • Skip arbitrary components and resources when serializing or deserializing.
  • Optional in-memory GZIP compression for vast reduction of file sizes.

Installation

go get github.com/mlange-42/ark-serde

Usage

See the API docs for more details and examples.
Go Reference

Serialize a world:

jsonData, err := arkserde.Serialize(world)
if err != nil {
    // handle the error
}

Deserialize a world:

err = arkserde.Deserialize(jsonData, world)
if err != nil {
    // handle the error
}

License

This project is distributed under the MIT licence.

Documentation

Overview

Package arkserde provides JSON serialization and deserialization for the Ark ECS.

Example
package main

import (
	"fmt"
	"math/rand"

	arkserde "github.com/mlange-42/ark-serde"
	"github.com/mlange-42/ark/ecs"
)

const (
	width  = 40
	height = 12
)

type Coords struct {
	X int
	Y int
}

func main() {
	rng := rand.New(rand.NewSource(42))

	// Create a world.
	world := ecs.NewWorld(1024)

	// Populate the world with entities, components and resources.
	builder := ecs.NewMap1[Coords](world)
	builder.NewBatchFn(60, func(entity ecs.Entity, coord *Coords) {
		coord.X = rng.Intn(width)
		coord.Y = rng.Intn(height)
	})

	// Print the original world
	fmt.Println("====== Original world ========")
	printWorld(world)

	// Serialize the world.
	jsonData, err := arkserde.Serialize(world)
	if err != nil {
		fmt.Printf("could not serialize: %s\n", err)
		return
	}

	// Print the resulting JSON.
	//fmt.Println(string(jsonData))

	// Create a new, empty world.
	newWorld := ecs.NewWorld(1024)

	// Register required components and resources
	_ = ecs.ComponentID[Coords](newWorld)

	// Deserialize into the new world.
	err = arkserde.Deserialize(jsonData, newWorld)
	if err != nil {
		fmt.Printf("could not deserialize: %s\n", err)
		return
	}

	// Print the deserialized world
	fmt.Println("====== Deserialized world ========")
	printWorld(newWorld)
}

func printWorld(world *ecs.World) {
	grid := make([][]rune, height)
	for i := range grid {
		grid[i] = make([]rune, width)
		for j := range grid[i] {
			grid[i][j] = '-'
		}
	}

	filter := ecs.NewFilter1[Coords](world)
	query := filter.Query()

	for query.Next() {
		coords := query.Get()
		grid[coords.Y][coords.X] = 'O'
	}

	for i := range grid {
		fmt.Println(string(grid[i]))
	}
}
Output:

====== Original world ========
--------------------------------O-O---O-
-----------------------O----------------
-O-------------O------OO--------------O-
----O------------------------OOO--------
O--------------OO-O---------------------
------------O-----------O---------------
--O-------------O-------O---O------O----
O-O-----O----OOO-O--O--------------OO---
-----------O---OO----O--O------------O--
------------O-----O---------------------
---O---------------O------O--O----------
------O-OO--O---------OO-OOO-----------O
====== Deserialized world ========
--------------------------------O-O---O-
-----------------------O----------------
-O-------------O------OO--------------O-
----O------------------------OOO--------
O--------------OO-O---------------------
------------O-----------O---------------
--O-------------O-------O---O------O----
O-O-----O----OOO-O--O--------------OO---
-----------O---OO----O--O------------O--
------------O-----O---------------------
---O---------------O------O--O----------
------O-OO--O---------OO-OOO-----------O
Example (Options)
world := ecs.NewWorld(1024)
builder := ecs.NewMap2[Position, Velocity](world)
builder.NewBatch(10, &Position{}, &Velocity{})

// Serialize the world, skipping Velocity and compressing with gzip.
jsonData, err := arkserde.Serialize(
	world,
	arkserde.Opts.SkipComponents(ecs.C[Velocity]()),
	arkserde.Opts.Compress(arkserde.BestCompression),
)
if err != nil {
	fmt.Printf("could not serialize: %s\n", err)
	return
}

newWorld := ecs.NewWorld(1024)

// Register required components and resources
_ = ecs.ComponentID[Position](newWorld)

err = arkserde.Deserialize(jsonData, newWorld,
	arkserde.Opts.Compress())
if err != nil {
	fmt.Printf("could not deserialize: %s\n", err)
	return
}

Index

Examples

Constants

View Source
const (
	BestSpeed          = flate.BestSpeed
	BestCompression    = flate.BestCompression
	DefaultCompression = flate.DefaultCompression
)

GZip compression level, re-exported from the flate package

Variables

View Source
var Opts = Options{}

Opts is a helper to create Option instances.

Functions

func Deserialize

func Deserialize(jsonData []byte, world *ecs.World, options ...Option) error

Deserialize an Ark ecs.World from JSON.

The world must be prepared the following way:

  • The world must not contain any alive or dead entities (i.e. a new or ecs.World.Reset world)
  • All required component types must be registered using ecs.ComponentID
  • All required resources must be added as dummies using ecs.AddResource

The options can be used to skip some or all components, entities entirely, and/or some or all resources. It only some components or resources are skipped, they still need to be registered to the world.

Query iteration order

After deserialization, it is not guaranteed that entity iteration order in queries is the same as before. More precisely, it should at first be the same as before, but will likely deviate over time from what would happen when continuing the original, serialized run. Multiple worlds deserialized from the same source should, however, behave exactly the same.

func Serialize

func Serialize(world *ecs.World, options ...Option) ([]byte, error)

Serialize an Ark ecs.World to JSON.

Serializes the following:

  • Entities and the entity pool
  • All components of all entities
  • All resources

All components and resources must be "JSON-able" with encoding/json.

The options can be used to skip some or all components, entities entirely, and/or some or all resources.

Types

type Option

type Option func(o *serdeOptions)

Option is an option. Modifies o. Create them using Opts.

type Options

type Options struct{}

Options is a helper to create Option instances. Use it via the instance Opts.

func (Options) Compress added in v0.2.0

func (o Options) Compress(level ...int) Option

Compress data using gzip. For serialized data created with this option, the option must also be used for deserialization. The optional compression level argument has no effect when deserializing.

Ideally, save as <file>.json.gz instead of <file>.json.

func (Options) SkipAllComponents

func (o Options) SkipAllComponents() Option

SkipAllComponents skips serialization or de-serialization of all components.

func (Options) SkipAllResources

func (o Options) SkipAllResources() Option

SkipAllResources skips serialization or de-serialization of all resources.

func (Options) SkipComponents

func (o Options) SkipComponents(comps ...ecs.Comp) Option

SkipComponents skips serialization or de-serialization of certain components.

When deserializing, the skipped components must still be registered.

func (Options) SkipEntities

func (o Options) SkipEntities() Option

SkipEntities skips serialization or de-serialization of all entities and components.

func (Options) SkipResources

func (o Options) SkipResources(comps ...ecs.Comp) Option

SkipResources skips serialization or de-serialization of certain resources.

When deserializing, the skipped resources must still be registered.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL