Solve serialization
This commit is contained in:
parent
ba8b1a29fd
commit
8e44dd3733
45 changed files with 242 additions and 201 deletions
|
|
@ -7,4 +7,4 @@
|
|||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"initialize": "npm install"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
import { Typed } from '../transport/typed';
|
||||
export abstract class Command {
|
||||
public static get type(): string {
|
||||
return (this as any).name;
|
||||
}
|
||||
|
||||
export abstract class Command extends Typed { }
|
||||
public get type(): string {
|
||||
return (this as any).constructor.name;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { GameObject } from '../../objects/game-object';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class CreateObjectsCommand extends Command {
|
||||
public constructor(public readonly serializedObjects: string) {
|
||||
public constructor(public readonly objects: Array<GameObject>) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.serializedObjects];
|
||||
public toArray(): Array<any> {
|
||||
return [this.objects];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { CharacterBase } from '../../objects/types/character-base';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class CreatePlayerCommand extends Command {
|
||||
public constructor(public readonly serializedPlayer: string) {
|
||||
public constructor(public readonly character: CharacterBase) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.serializedPlayer];
|
||||
public toArray(): Array<any> {
|
||||
return [this.character];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { Id } from '../../transport/identity';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class DeleteObjectsCommand extends Command {
|
||||
public constructor(public readonly ids: Array<Id>) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.ids];
|
||||
public toArray(): Array<any> {
|
||||
return [this.ids];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class MoveActionCommand extends Command {
|
||||
public constructor(public readonly delta: vec2) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.delta];
|
||||
public toArray(): Array<any> {
|
||||
return [this.delta];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class PrimaryActionCommand extends Command {
|
||||
public constructor(public readonly position: vec2) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.position];
|
||||
public toArray(): Array<any> {
|
||||
return [this.position];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class SecondaryActionCommand extends Command {
|
||||
public constructor(public readonly position: vec2) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.position];
|
||||
public toArray(): Array<any> {
|
||||
return [this.position];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { Rectangle } from '../../helper/rectangle';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class SetViewAreaActionCommand extends Command {
|
||||
public constructor(public readonly viewArea: Rectangle) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.viewArea];
|
||||
public toArray(): Array<any> {
|
||||
return [this.viewArea];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class TernaryActionCommand extends Command {
|
||||
public constructor(public readonly position: vec2) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.position];
|
||||
public toArray(): Array<any> {
|
||||
return [this.position];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { GameObject } from '../../objects/game-object';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { Command } from '../command';
|
||||
|
||||
@serializable()
|
||||
export class UpdateObjectsCommand extends Command {
|
||||
public constructor(public readonly serializedObjects: string) {
|
||||
public constructor(public readonly objects: Array<GameObject>) {
|
||||
super();
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return [this.type, this.serializedObjects];
|
||||
public toArray(): Array<any> {
|
||||
return [this.objects];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { serializable } from '../transport/serializable/serializable';
|
||||
|
||||
@serializable()
|
||||
export class Circle {
|
||||
constructor(public center: vec2, public radius: number) {}
|
||||
constructor(public center: vec2, public radius: number) { }
|
||||
|
||||
public toArray(): Array<any> {
|
||||
return [this.center, this.radius];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { serializable } from '../transport/serializable/serializable';
|
||||
|
||||
@serializable()
|
||||
export class Rectangle {
|
||||
constructor(public topLeft = vec2.create(), public size = vec2.create()) {}
|
||||
constructor(public topLeft = vec2.create(), public size = vec2.create()) { }
|
||||
|
||||
public toArray(): Array<any> {
|
||||
return [this.topLeft, this.size];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ export * from './helper/random';
|
|||
export * from './helper/unique';
|
||||
export * from './helper/rotate-90-deg';
|
||||
export * from './objects/game-object';
|
||||
export * from './objects/deserialize';
|
||||
export * from './transport/serializable/deserialize';
|
||||
export * from './transport/serializable/serialize';
|
||||
export * from './transport/serializable/serializable';
|
||||
export * from './objects/types/character-base';
|
||||
export * from './objects/types/lamp-base';
|
||||
export * from './objects/types/tunnel-base';
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import { CreatePlayerCommand } from '../commands/types/create-player';
|
||||
import { MoveActionCommand } from '../commands/types/move-action';
|
||||
import { PrimaryActionCommand } from '../commands/types/primary-action';
|
||||
import { SecondaryActionCommand } from '../commands/types/secondary-action';
|
||||
import { SetViewAreaActionCommand } from '../commands/types/set-view-area-action';
|
||||
import { TernaryActionCommand } from '../commands/types/ternary-action';
|
||||
import { UpdateObjectsCommand } from '../commands/types/update-objects';
|
||||
import { Command, CreateObjectsCommand, DeleteObjectsCommand } from '../main';
|
||||
|
||||
export const commandConstructors: {
|
||||
[type: string]: new (...values: Array<any>) => any;
|
||||
} = {
|
||||
[CreateObjectsCommand.type]: CreateObjectsCommand,
|
||||
[DeleteObjectsCommand.type]: DeleteObjectsCommand,
|
||||
[CreatePlayerCommand.type]: CreatePlayerCommand,
|
||||
[MoveActionCommand.type]: MoveActionCommand,
|
||||
[PrimaryActionCommand.type]: PrimaryActionCommand,
|
||||
[SecondaryActionCommand.type]: SecondaryActionCommand,
|
||||
[TernaryActionCommand.type]: TernaryActionCommand,
|
||||
[SetViewAreaActionCommand.type]: SetViewAreaActionCommand,
|
||||
[UpdateObjectsCommand.type]: UpdateObjectsCommand,
|
||||
};
|
||||
|
||||
export const deserializeCommand = ([type, ...values]: [string, Array<any>]): Command => {
|
||||
return new commandConstructors[type](...values);
|
||||
};
|
||||
|
|
@ -2,14 +2,6 @@ import { CommandReceiver } from '../commands/command-receiver';
|
|||
import { Id } from '../transport/identity';
|
||||
|
||||
export abstract class GameObject extends CommandReceiver {
|
||||
public static get type(): string {
|
||||
return (this as any).name;
|
||||
}
|
||||
|
||||
public get type(): string {
|
||||
return (this as any).constructor.name;
|
||||
}
|
||||
|
||||
constructor(public readonly id: Id) {
|
||||
super();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { Circle } from '../../helper/circle';
|
||||
import { Id } from '../../main';
|
||||
import { Id } from '../../transport/identity';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { GameObject } from '../game-object';
|
||||
|
||||
@serializable()
|
||||
export abstract class CharacterBase extends GameObject {
|
||||
constructor(
|
||||
id: Id,
|
||||
|
|
@ -11,4 +13,9 @@ export abstract class CharacterBase extends GameObject {
|
|||
) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public toArray(): Array<any> {
|
||||
const { id, head, leftFoot, rightFoot } = this as any;
|
||||
return [id, head, leftFoot, rightFoot];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
import { vec2, vec3 } from 'gl-matrix';
|
||||
import { Id } from '../../main';
|
||||
import { Id, serializable } from '../../main';
|
||||
import { GameObject } from '../game-object';
|
||||
|
||||
@serializable()
|
||||
export abstract class LampBase extends GameObject {
|
||||
constructor(id: Id, public center: vec2, public color: vec3, public lightness: number) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public toArray(): Array<any> {
|
||||
const { id, center, color, lightness } = this as any;
|
||||
return [id, center, color, lightness];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { GameObject, Id } from '../../main';
|
||||
import { Id } from '../../transport/identity';
|
||||
import { serializable } from '../../transport/serializable/serializable';
|
||||
import { GameObject } from '../game-object';
|
||||
|
||||
@serializable()
|
||||
export abstract class TunnelBase extends GameObject {
|
||||
constructor(
|
||||
id: Id,
|
||||
|
|
@ -11,4 +14,9 @@ export abstract class TunnelBase extends GameObject {
|
|||
) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public toArray(): Array<any> {
|
||||
const { id, from, to, fromRadius, toRadius } = this as any;
|
||||
return [id, from, to, fromRadius, toRadius];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
shared/src/transport/serializable/deserialize.ts
Normal file
13
shared/src/transport/serializable/deserialize.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
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;
|
||||
});
|
||||
};
|
||||
58
shared/src/transport/serializable/serializable.ts
Normal file
58
shared/src/transport/serializable/serializable.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
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;
|
||||
};
|
||||
};
|
||||
10
shared/src/transport/serializable/serialize.ts
Normal file
10
shared/src/transport/serializable/serialize.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
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;
|
||||
};
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
export abstract class Typed {
|
||||
public static get type(): string {
|
||||
return (this as any).name;
|
||||
}
|
||||
|
||||
public get type(): string {
|
||||
return (this as any).constructor.name;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,9 @@
|
|||
"allowSyntheticDefaultImports": true,
|
||||
"moduleResolution": "Node",
|
||||
"module": "es6",
|
||||
"lib": ["es2015", "dom"]
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue