Refactor serialization

This commit is contained in:
schmelczerandras 2020-10-07 10:04:05 +02:00
parent 8e44dd3733
commit b3da27e73b
35 changed files with 142 additions and 133 deletions

View file

@ -1,8 +1,8 @@
import { GameObject } from '../../objects/game-object';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class CreateObjectsCommand extends Command {
public constructor(public readonly objects: Array<GameObject>) {
super();

View file

@ -1,8 +1,8 @@
import { CharacterBase } from '../../objects/types/character-base';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class CreatePlayerCommand extends Command {
public constructor(public readonly character: CharacterBase) {
super();

View file

@ -1,8 +1,8 @@
import { Id } from '../../transport/identity';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class DeleteObjectsCommand extends Command {
public constructor(public readonly ids: Array<Id>) {
super();

View file

@ -1,8 +1,8 @@
import { vec2 } from 'gl-matrix';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class MoveActionCommand extends Command {
public constructor(public readonly delta: vec2) {
super();

View file

@ -1,8 +1,8 @@
import { vec2 } from 'gl-matrix';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class PrimaryActionCommand extends Command {
public constructor(public readonly position: vec2) {
super();

View file

@ -1,8 +1,8 @@
import { vec2 } from 'gl-matrix';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class SecondaryActionCommand extends Command {
public constructor(public readonly position: vec2) {
super();

View file

@ -1,8 +1,8 @@
import { Rectangle } from '../../helper/rectangle';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class SetViewAreaActionCommand extends Command {
public constructor(public readonly viewArea: Rectangle) {
super();

View file

@ -1,8 +1,8 @@
import { vec2 } from 'gl-matrix';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class TernaryActionCommand extends Command {
public constructor(public readonly position: vec2) {
super();

View file

@ -1,8 +1,8 @@
import { GameObject } from '../../objects/game-object';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { Command } from '../command';
@serializable()
@serializable
export class UpdateObjectsCommand extends Command {
public constructor(public readonly objects: Array<GameObject>) {
super();

View file

@ -1,7 +1,7 @@
import { vec2 } from 'gl-matrix';
import { serializable } from '../transport/serializable/serializable';
import { serializable } from '../transport/serialization/serializable';
@serializable()
@serializable
export class Circle {
constructor(public center: vec2, public radius: number) { }

View file

@ -1,7 +1,7 @@
import { vec2 } from 'gl-matrix';
import { serializable } from '../transport/serializable/serializable';
import { serializable } from '../transport/serialization/serializable';
@serializable()
@serializable
export class Rectangle {
constructor(public topLeft = vec2.create(), public size = vec2.create()) { }

View file

@ -22,9 +22,11 @@ export * from './helper/random';
export * from './helper/unique';
export * from './helper/rotate-90-deg';
export * from './objects/game-object';
export * from './transport/serializable/deserialize';
export * from './transport/serializable/serialize';
export * from './transport/serializable/serializable';
export * from './transport/serialization/deserialize';
export * from './transport/serialization/serialize';
export * from './transport/serialization/serializes-to';
export * from './transport/serialization/serializable';
export * from './transport/serialization/override-deserialization';
export * from './objects/types/character-base';
export * from './objects/types/lamp-base';
export * from './objects/types/tunnel-base';

View file

@ -1,10 +1,10 @@
import { Circle } from '../../helper/circle';
import { Id } from '../../transport/identity';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { GameObject } from '../game-object';
@serializable()
export abstract class CharacterBase extends GameObject {
@serializable
export class CharacterBase extends GameObject {
constructor(
id: Id,
public head: Circle,

View file

@ -2,8 +2,8 @@ import { vec2, vec3 } from 'gl-matrix';
import { Id, serializable } from '../../main';
import { GameObject } from '../game-object';
@serializable()
export abstract class LampBase extends GameObject {
@serializable
export class LampBase extends GameObject {
constructor(id: Id, public center: vec2, public color: vec3, public lightness: number) {
super(id);
}

View file

@ -1,10 +1,10 @@
import { vec2 } from 'gl-matrix';
import { Id } from '../../transport/identity';
import { serializable } from '../../transport/serializable/serializable';
import { serializable } from '../../transport/serialization/serializable';
import { GameObject } from '../game-object';
@serializable()
export abstract class TunnelBase extends GameObject {
@serializable
export class TunnelBase extends GameObject {
constructor(
id: Id,
public readonly from: vec2,

View file

@ -1,13 +0,0 @@
import { serializableClasses } from './serializable';
export const deserialize = (json: string): any => {
return JSON.parse(json, (k, v) => {
const possibleType = v[0];
const overridableConstructor = serializableClasses.get(possibleType);
if (overridableConstructor) {
const [_, ...values] = v;
return new overridableConstructor.constructor(...values);
}
return v;
});
};

View file

@ -1,58 +0,0 @@
export const serializableClasses = new Map<
string,
{
constructor: any;
overriden: boolean;
}
>();
export const serializable = (serializableType?) => {
return (type): any => {
const actualType = serializableType
? Object.getPrototypeOf((serializableType as any).prototype).constructor
: type;
const typeName = actualType.name;
const overridableConstructor = serializableClasses.get(typeName);
if (!overridableConstructor) {
serializableClasses.set(typeName, {
constructor: actualType,
overriden: false,
});
}
if (!Object.prototype.hasOwnProperty.call(actualType.prototype, 'toArray')) {
throw new Error(
`Class ${typeName} must define a toArray returning an array containing the arguments for its constructor.`
);
}
return class extends type {
public get type(): string {
return typeName;
}
public static get type(): string {
return typeName;
}
};
};
};
export const deserializable = (fromType) => {
return (type): any => {
const overridableConstructor = serializableClasses.get(fromType.name);
if (!overridableConstructor || !overridableConstructor.overriden) {
serializableClasses.set(fromType.name, {
constructor: type,
overriden: true,
});
} else {
throw new Error(
`Constructor ${fromType} already overriden, cannot override again with ${type}`
);
}
return type;
};
};

View file

@ -1,10 +0,0 @@
export const serialize = (object): string => {
const result = JSON.stringify(object, (key, value) => {
if (value?.type) {
return [value.type, ...value.toArray()];
}
return value?.toFixed ? Number(value.toFixed(3)) : value;
});
return result;
};

View file

@ -0,0 +1 @@
export type DeserializableClass = { new(...args: Array<any>): {}; name: string };

View file

@ -0,0 +1,13 @@
import { serializableMapping } from './serializable-mapping';
export const deserialize = (json: string): any => {
return JSON.parse(json, (k, v) => {
const possibleType = v[0];
const overridableConstructor = serializableMapping.get(possibleType);
if (overridableConstructor) {
v.shift();
return new overridableConstructor.constructor(...v);
}
return v;
});
};

View file

@ -0,0 +1,13 @@
import { DeserializableClass } from './deserializable-class';
import { SerializableClass } from './serializable-class';
import { serializableMapping } from './serializable-mapping';
export const overrideDeserialization = (
source: SerializableClass,
target: DeserializableClass
) => {
serializableMapping.set(source.name, {
constructor: target,
overriden: true,
});
};

View file

@ -0,0 +1,4 @@
export type SerializableClass = {
new(...args: Array<any>): { toArray(): Array<any> };
name: string;
};

View file

@ -0,0 +1,12 @@
import { DeserializableClass } from './deserializable-class';
/**
* @internal
*/
export const serializableMapping = new Map<
string,
{
constructor: DeserializableClass;
overriden: boolean;
}
>();

View file

@ -0,0 +1,16 @@
import { SerializableClass } from './serializable-class';
import { serializableMapping } from './serializable-mapping';
export const serializable = (type: SerializableClass): any => {
if (!serializableMapping.get(type.name)) {
serializableMapping.set(type.name, {
constructor: type,
overriden: false,
});
}
Object.defineProperty(type, '__serializable_type', { value: type.name });
Object.defineProperty(type.prototype, '__serializable_type', { value: type.name });
return type;
};

View file

@ -0,0 +1,8 @@
export const serialize = (object): string => {
return JSON.stringify(object, (_, value) => {
if (value?.__serializable_type) {
return [value.__serializable_type, ...value.toArray()];
}
return value?.toFixed ? Number(value.toFixed(3)) : value;
});
};

View file

@ -0,0 +1,20 @@
import { SerializableClass } from './serializable-class';
import { serializableMapping } from './serializable-mapping';
export const serializesTo = (target: SerializableClass) => {
return (actual: SerializableClass): any => {
if (!serializableMapping.get(target.name)) {
serializableMapping.set(target.name, {
constructor: target,
overriden: false,
});
}
Object.defineProperty(actual, '__serializable_type', { value: target.name });
Object.defineProperty(actual.prototype, '__serializable_type', {
value: target.name,
});
return actual;
};
};