155 lines
5.9 KiB
TypeScript
155 lines
5.9 KiB
TypeScript
module SpaceTac.Game {
|
|
// Serializer to cascade through Serializable objects
|
|
export class Serializer {
|
|
// Mapping of IDs to objects
|
|
refs: { [index: string]: any };
|
|
|
|
// Serializable classes
|
|
classes: { [index: string]: typeof Serializable };
|
|
|
|
constructor() {
|
|
this.refs = {};
|
|
this.classes = this.collectSerializableClasses();
|
|
}
|
|
|
|
// List all classes that implement "Serializable", with their full path in SpaceTac.Game namespace
|
|
collectSerializableClasses(container: any = null, path: string = ""): { [index: string]: typeof Serializable } {
|
|
if (container) {
|
|
var result: { [index: string]: typeof Serializable } = {};
|
|
for (var obj_name in container) {
|
|
if (container.hasOwnProperty(obj_name)) {
|
|
var obj = container[obj_name];
|
|
var obj_path = path + "." + obj_name;
|
|
if (typeof obj === "object") {
|
|
result = Tools.merge(result, this.collectSerializableClasses(obj, obj_path));
|
|
} else if (typeof obj === "function" && obj.prototype instanceof Serializable) {
|
|
result[obj_path] = obj;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
} else {
|
|
return this.collectSerializableClasses(SpaceTac.Game, "SpaceTac.Game");
|
|
}
|
|
}
|
|
|
|
// Get the full path in SpaceTac namespace, of a serializable object
|
|
getClassPath(obj: Serializable): string {
|
|
for (var class_path in this.classes) {
|
|
if (this.classes.hasOwnProperty(class_path)) {
|
|
var class_obj = this.classes[class_path];
|
|
if (class_obj.prototype === obj.constructor.prototype) {
|
|
return class_path;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Compute the highest known SID
|
|
getMaxId(): number {
|
|
var result = -1;
|
|
for (var sid in this.refs) {
|
|
if (typeof sid === "string") {
|
|
result = Math.max(result, parseInt(sid, 10));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Serialize an object to a string
|
|
serialize(obj: Serializable): string {
|
|
this.refs = {};
|
|
var data = {
|
|
refs: this.refs,
|
|
root: this.toData(obj)
|
|
};
|
|
return JSON.stringify(data);
|
|
}
|
|
|
|
// Load an object from a serialized string
|
|
unserialize(sdata: string): Serializable {
|
|
var data = JSON.parse(sdata);
|
|
this.refs = data.refs;
|
|
var result = this.fromData(data.root);
|
|
Serializable.resetIdSequence(this.getMaxId());
|
|
return result;
|
|
}
|
|
|
|
private toData(obj: Serializable): any {
|
|
var sid = obj.getSerializeId();
|
|
var cached = this.refs[sid];
|
|
var data = {
|
|
_s: "r",
|
|
_i: sid
|
|
};
|
|
if (typeof cached !== "undefined") {
|
|
return data;
|
|
}
|
|
|
|
var fields = {};
|
|
this.refs[sid] = {
|
|
_s: "o",
|
|
path: this.getClassPath(obj),
|
|
fields: fields
|
|
};
|
|
|
|
for (var field_name in obj) {
|
|
if (obj.hasOwnProperty(field_name)) {
|
|
var field_value = obj[field_name];
|
|
if (field_value instanceof Serializable) {
|
|
fields[field_name] = this.toData(field_value);
|
|
} else if (Array.isArray(field_value) && field_value.length > 0 && field_value[0] instanceof Serializable) {
|
|
var items: Serializable[] = [];
|
|
field_value.forEach((item: any) => {
|
|
items.push(this.toData(item));
|
|
});
|
|
fields[field_name] = {
|
|
_s: "a",
|
|
items: items
|
|
};
|
|
} else {
|
|
if (Object.prototype.toString.call(field_value) === "[object Object]") {
|
|
console.error("Non Serializable object", field_value);
|
|
}
|
|
fields[field_name] = field_value;
|
|
}
|
|
}
|
|
}
|
|
obj.postSerialize(fields);
|
|
|
|
return data;
|
|
}
|
|
|
|
private fromData(data: any): Serializable {
|
|
var sid = data._i;
|
|
var cached = this.refs[sid];
|
|
|
|
if (cached._s === "o") {
|
|
var class_info = this.classes[cached.path];
|
|
var obj = Object.create(class_info.prototype);
|
|
this.refs[sid] = obj;
|
|
for (var field_name in cached.fields) {
|
|
if (cached.fields.hasOwnProperty(field_name)) {
|
|
var field_value = cached.fields[field_name];
|
|
if (field_value !== null && typeof field_value === "object" && field_value._s === "r") {
|
|
obj[field_name] = this.fromData(field_value);
|
|
} else if (field_value !== null && typeof field_value === "object" && field_value._s === "a") {
|
|
var items: Serializable[] = [];
|
|
field_value.items.forEach((item: any) => {
|
|
items.push(this.fromData(item));
|
|
});
|
|
obj[field_name] = items;
|
|
} else {
|
|
obj[field_name] = field_value;
|
|
}
|
|
}
|
|
}
|
|
return obj;
|
|
} else {
|
|
return cached;
|
|
}
|
|
}
|
|
}
|
|
}
|