Spool Of Yarn

pysoy's data format

<!> This document is a draft and under active development

Concept & Purpose

PySoy's design as a cloud game engine requires a data format to save and distribute game objects.

This format should also be usable within Python in much the same way as a Python module, to the extent that data in .soy files can be brought into the Python namespace with a simple...

import soy
import MyGame

MyGame will be a module object containing zero or more PySoy objects. If a soy.Client object is among them it'll open a window and begin "running" the included content.

Another issue we attempt to address with .soy is starting a game without having to wait for the full file to be downloaded. Muxing different types of data together in a progressive manner would allow objects to be used before they're fully transferred. For example, textures can transfer lower resolution versions (mipmap layers) early in the stream and leave the full resolution to the end of the stream after everything else has been created. Players will be far less annoyed by low resolution textures the first time they start a game than having to wait for the full game to download before playing.

.soy Importer

The .soy format is intended to be used within Python with the standard import command. To achieve this PySoy adds its importer to sys.path_hooks on initialization. This hook applies to any path added after import soy.

We aim to support network protocols such as HTTP through this same importer, thus the following code could work:

import sys
import soy
import blocks

.soy Exporter

While Python offers a standard import mechanism it does not offer a mechanism to export a namespace from memory to a file (its easy enough to write .py files in a text editor). Therefore, we will offer a soy.Exporter class.

Objects of this class can be populated by PySoy objects and exports them to a file when called. Example usage:

export = soy.Exporter()
export.mainwindow = soy.widgets.Window('My Game')

An Exporter object could be created based on an existing namespace as well for editing existing .soy files, ie

import MyGame
MyGame.window.size = (640,480)

Its intended that Exporter objects can be added to other Exporter objects or other namespaces containing PySoy objects and other math functions. This can be used for development, to save game state, or even as part of PySoy-based game developer tools.


Each PySoy object which can be stored provides two methods in libsoy, "import" and "export" which are exposed in PySoy as "import" and "export".

The libsoy backend does not contain any facilities for reading or writing .soy files, this is the responsibility of PySoy or cloud game client.

On import, chapter 0 is used to construct objects on the frontend (which also initializes them on the backend) and store references to them. Each chapter thereafter is sent to that object's import method for processing. When the stream is fully processed the import command returns.

On export, chapter 0 provides the object construction parameters and each chapter thereafter is a page for the .soy file. The Exporter is responsible for combining these into a valid .soy file and writing it to disk.

Exportable objects are duck typed; any object with an export method can be stored in a .soy file, though its class must be available before the object is later imported since .soy files may not contain new classes or other instruction code.

Soy Format Overview

For progressive

Each object stored in a .soy archive consists of one or more "pages", each page placed in a sequential "chapter". Each .soy archive begins with chapter 0 (the "Table of Contents") which contains the first page of each object, followed by chapter 1 which contains the second page of every object which has a second page and so on.

Chapter 0 consists of the .soy signature, license, and pages for every object in the file. These pages each consist the object type name and the arguments used to construct the object. To simplify ordering, no object should require another object as a construction argument.

Each further chapter consists of pages whos formats are specific to the object they're intended for and as little overhead as is reasonable. Other objects in the same archive may be linked to here as they were already defined in chapter 0, for example a Window may list widgets within it on page 1.

Table of Contents

Also known as "chapter 0", the table of contents consists of the .soy signature, license, copyright, authors, date, the name of each object, its type, and the arguments needed to construct it.

The first 3 bytes of the stream should read "soy" signature followed by a byte identifying the major PySoy version that generated the file. This can be used as mime-magic to identify the file. The major version of this format version is 0. Decoding should not be attempted for an unsupported major version.

Following the signature is the metadata; authors, date, version, documentation, etc. These are made available in the resulting Python module as __author__, __date___, __version__ and __doc__ respectively.

The license field is required and must contain the string "GNU AGPLv3", any other license field will be rejected as incompatible. This field is included because future PySoy versions may be licensed under the AGPLv4 or other AGPLv3-compatible licenses which have not been written yet, and because the AGPLv3 requires that the license be stated. See [Licensing] for more details.

The __credits__ string will be generated from the copyright and the boilerplate for the stated license. The first line of __credits__ is stored as copyright on export, the license field is automatically the license of the version of PySoy used to export the archive (currently "GNU AGPLv3").

Metadata is stored in key,value pairs of null terminated strings. A pair of zero-length strings signals the end of metadata.

The chapter 0 header is sequenced as follows:

char[3] "s", "o", "y"
uint8   Major Version (0)
uint16  Authors Length
char*   Authors String
uint16  Copyright Length
char*   Copyright String
uint16  Date Length
char*   Date String
uint16  Docs Length
char*   Docs String
uint16  License Length
char*   License String
uint16  Version Length
char*   Version String
uint32  Number of Pages
loop    Pages

uint16  Object Name Length
char*   Object Name String
uint16  Type Name Length
char*   Type Name String
uint8   Number of Arguments
loop    Arguments 

uint8   Data Type (0 = int, 1 = float, 2 = string)
type    Data

Int Data:
int32   Stored

Float Data:
double  Stored as "binary64" as per IEEE 754

String Data:
uint16  Length of Data
char*   Data



  • TwoFaces-Hexdump2.txt Download (2.9 KB) - added by PalleRaabjerg 11 years ago. Annotated hexdump of a mesh-file with four tri-faces (had two before export). Vertex structure may not be entirely up to specs.