Packaging in the Ikosa Framework saves and restores game, character and world state. It is the “paper” of the virtual pencil & paper model of an automated RPG. I didn’t have great well-defined advance plans for logically structuring the serialization layer; the structure and coding were guided by a vision of desired usage.
Consequently, it “grew” from a few basic principles, with a few more discovered principles added later. From an API standpoint, it has proven itself robust enough (without being too limiting) to handle what I have needed so far. It is stable in that I haven’t changed anything substantive in it’s core for several years now.
I put this on CodePlex a few years ago and it can be found here uzipack.codeplex.com
Packaging in the Ikosa framework is based on an “.Ikosa” package file, a portable file that can hold multiple streams of related-data within an Open Packaging Conventions defined structure (the basic OPC used by many Microsoft Office products, and ultimately based on ZIP files).
The main purpose was saving persisted models of game objects. As a game master, I envisioned reducing or eliminating the need to juggle & file paper and scratch scribblings. I also wanted to enable sharing game resource data among a broad set of users. I pictured sharing files to be easier than sharing paper or grabbing things from print material to use in game sessions. Many information managing “tools” for RPG over the years do similar things or have had similar goals of managing data (although with less emphasis on sharing data).
As the purpose of the overall system progressed, and it grew from “mere” game-information management to more emulation/simulation (and ultimately gaming); map structures and visualization resources (textures, models) also needed to be kept track of.
When I was starting, I figured a folder structure of loose files that could be related by convention of names and relative containment and parentage would be flexible and understandable without having to see the data through an API or a unique visualization system. What I decided upon (and it was a bit of a learning experience for me) was the implementation of the Open Packaging Convention (OPC) in the .NET framework. This allows a “file system” internal structure, but has a single portable expression in the more general file system of a personal computer.
The OPC provides a fairly robust and simple API for separate, identifiable stream access from program code; the ability to open the file in Windows explorer (if the extension is changed to .zip); and single-file portability for large aggregates of content. I could code against it, but I could crack it open in Windows Explorer (or any other ZIP viewer).
My earliest experimental package files were self-contained in that all resources for a “dungeon-map”, characters and visual resources were contained within a single package. I knew I would be aiming to have common resources (such as stock textures and models), and shareable user maintained custom maps, objects and models that could be blended by game-masters, so getting an abstraction of resource resolution and package references into the system was a fairly high priority.
Base Parts and Core Parts
Core parts are simply a mechanism to provide a generic relationship graph among multiple objects that might be in a package. ICorePart defines the interface for a part that might have related parts.
A base part is an ICorePart that additionally defines mechanisms for saving state within a package structure. Base parts represent addressable streams within a package. Base parts can have relationships to other base parts, or to core parts that do not maintain their own persistence.
(Highlight areas above indicate .NET framework classes).
Provider Registration and Handlers
Design-wise, packaging services are a very “low-dependency” feature, but de-packaging services are a very high-dependency feature. Converting an object graph into a serial stream of bytes (regardless of how it is formatted) requires only that the objects be capable of serialization. Since packaging only deals with streams, any object that “saves itself” uses packaging (i.e., is dependent on packaging).
The process of “de-packaging” and converting any stream in a package back into a BasePart, requires the packaging assembly to be able to determine the type information for the stream and construct a class instance to handle the deserialization from the stream. Type-registration within BasePartFactory implementations assists with the re-mapping. OPC defines a concept of strongly typed package part relations, that I mapped into identifying the factory that could recreate the part from the stream.
Consequently, every assembly that provides de-packaging services registers a BasePartFactory that will be used when streams of its registered types are accessed for de-packaging.
There are four assembly sources of handlers currently defined:
- packaging assembly: general-purpose folder support
- visualization assembly: graphical resources
- core assembly: single object streams
- ikosa assembly: maps and user definition
Within the visualization part list is a “special” part called a resource manager, which provides resource resolution via in package and cross-package resource references. Icons, models, model fragments and brushes can all be kept in external shared packages.
The packaging assembly itself neither serializes nor deserializes data. The serialization, deserialization, and formatting of data in the streams is managed by base parts. Consequently there are a variety of storage formats contained within a package depending on the part managing the stream.
- images: various formats including PNG, JPEG
- xml: typically via DataContract serializers
- XAML: icons, brushes, brush collections, model fragments, models
- .NET serialization: objects, map structure, map context