mysticsymbolic.github.io/lib/pages/creature-page/serialization.ts

144 wiersze
4.2 KiB
TypeScript

import * as avro from "avro-js";
import { CreatureDesign } from "./core";
import {
AvroAttachedCreatureSymbol,
AvroCreatureDesign,
AvroCreatureSymbol,
AvroNestedCreatureSymbol,
} from "./creature-design.avsc";
import { fromBase64, toBase64 } from "../../base64";
import CreatureAvsc from "./creature-design.avsc.json";
import CreatureAvscV2 from "./creature-design.v2.avsc.json";
import { Packer, SvgCompositionContextPacker } from "../../serialization";
import {
AttachedCreatureSymbol,
CreatureSymbol,
NestedCreatureSymbol,
} from "../../creature-symbol";
import { SvgVocabulary } from "../../svg-vocabulary";
import { ATTACHMENT_POINT_TYPES } from "../../specs";
import {
creatureAnimatorIdToName,
creatureAnimatorNameToId,
} from "../../creature-animator";
const LATEST_VERSION = "v3";
const avroCreatureDesign = avro.parse<AvroCreatureDesign>(CreatureAvsc);
const ATTACHMENT_POINT_MAPPING = new Map(
ATTACHMENT_POINT_TYPES.map((name, i) => {
return [name, i];
})
);
const NestedCreatureSymbolPacker: Packer<
NestedCreatureSymbol,
AvroNestedCreatureSymbol
> = {
pack: (value) => {
return {
base: CreatureSymbolPacker.pack(value),
indices: Buffer.from(value.indices),
};
},
unpack: (value) => {
return {
...CreatureSymbolPacker.unpack(value.base),
indices: Array.from(value.indices),
};
},
};
const AttachedCreatureSymbolPacker: Packer<
AttachedCreatureSymbol,
AvroAttachedCreatureSymbol
> = {
pack: (value) => {
const attachTo = ATTACHMENT_POINT_MAPPING.get(value.attachTo);
if (attachTo === undefined) {
throw new Error(`Invalid attachment type "${value.attachTo}"`);
}
return {
base: CreatureSymbolPacker.pack(value),
attachTo,
indices: Buffer.from(value.indices),
};
},
unpack: (value) => {
const attachTo = ATTACHMENT_POINT_TYPES[value.attachTo];
if (attachTo === undefined) {
throw new Error(`Invalid attachment type "${value.attachTo}"`);
}
return {
...CreatureSymbolPacker.unpack(value.base),
attachTo,
indices: Array.from(value.indices),
};
},
};
const CreatureSymbolPacker: Packer<CreatureSymbol, AvroCreatureSymbol> = {
pack: (value) => {
return {
invertColors: value.invertColors,
symbol: value.data.name,
attachments: value.attachments.map(AttachedCreatureSymbolPacker.pack),
nests: value.nests.map(NestedCreatureSymbolPacker.pack),
};
},
unpack: (value) => {
return {
invertColors: value.invertColors,
data: SvgVocabulary.get(value.symbol),
attachments: value.attachments.map(AttachedCreatureSymbolPacker.unpack),
nests: value.nests.map(NestedCreatureSymbolPacker.unpack),
};
},
};
const DesignConfigPacker: Packer<CreatureDesign, AvroCreatureDesign> = {
pack: (value) => {
return {
animatorId: creatureAnimatorNameToId(value.animatorName),
creature: CreatureSymbolPacker.pack(value.creature),
compCtx: SvgCompositionContextPacker.pack(value.compCtx),
};
},
unpack: (value) => {
return {
animatorName: creatureAnimatorIdToName(value.animatorId) ?? "none",
creature: CreatureSymbolPacker.unpack(value.creature),
compCtx: SvgCompositionContextPacker.unpack(value.compCtx),
};
},
};
function loadSchemaVersion(version: string, buf: Buffer): AvroCreatureDesign {
switch (version) {
case "v1":
throw new Error(`Sorry, we no longer support loading v1 creatures!`);
case "v2":
const res = avroCreatureDesign.createResolver(avro.parse(CreatureAvscV2));
return avroCreatureDesign.fromBuffer(buf, res);
case LATEST_VERSION:
return avroCreatureDesign.fromBuffer(buf);
default:
throw new Error(`Don't know how to load schema version ${version}`);
}
}
export function serializeCreatureDesign(value: CreatureDesign): string {
const buf = avroCreatureDesign.toBuffer(DesignConfigPacker.pack(value));
return `${LATEST_VERSION}.${toBase64(buf)}`;
}
export function deserializeCreatureDesign(value: string): CreatureDesign {
const [version, serialized] = value.split(".", 2);
const buf = fromBase64(serialized);
return DesignConfigPacker.unpack(loadSchemaVersion(version, buf));
}