1
0
Fork 0

Removed Serializable base class

Serialization is then broken for now, will be restored later using tscommon
This commit is contained in:
Michaël Lemaire 2017-02-07 19:54:53 +01:00
parent 1167aedbdf
commit fcb45346e4
29 changed files with 27 additions and 442 deletions

1
TODO
View file

@ -1,3 +1,4 @@
* Restore serialization
* Refactor attribute effects (add/sub, value/max, temporary/permanent)
* Drones: add hooks on events
* Drones: add sprite, radius and tooltip

View file

@ -1,5 +1,3 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Code to identify
export enum AttributeCode {
@ -62,7 +60,7 @@ module TS.SpaceTac.Game {
// Value computed from equipment
// This value can be altered by effects
// Example attributes are health points, power recovery...
export class Attribute extends Serializable {
export class Attribute {
// Identifying code of this attribute
code: AttributeCode;
@ -74,8 +72,6 @@ module TS.SpaceTac.Game {
// Create an attribute
constructor(code: AttributeCode = AttributeCode.Misc, current: number = 0, maximal: number = null) {
super();
this.code = code;
this.maximal = maximal;
this.current = current;

View file

@ -1,15 +1,11 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Collection of several attributes
export class AttributeCollection extends Serializable {
export class AttributeCollection {
// Attributes
private attributes: Attribute[];
// Base constructor
constructor() {
super();
this.attributes = [];
}

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// A turn-based battle between fleets
export class Battle extends Serializable {
export class Battle {
// Flag indicating if the battle is ended
ended: boolean;
@ -34,8 +32,6 @@ module TS.SpaceTac.Game {
// Create a battle between two fleets
constructor(fleet1: Fleet = null, fleet2: Fleet = null) {
super();
this.log = new BattleLog();
this.fleets = [fleet1 || new Fleet(), fleet2 || new Fleet()];
this.play_order = [];

View file

@ -1,10 +1,8 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Log of a battle
// This keeps track of all events in a battle
// It also allows to register a callback to receive these events
export class BattleLog extends Serializable {
export class BattleLog {
// Full list of battle events
events: BaseLogEvent[];
@ -16,8 +14,6 @@ module TS.SpaceTac.Game {
// Create an initially empty log
constructor() {
super();
this.events = [];
this.subscribers = [];
this.filters = [];

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Result of an ended battle
export class BattleOutcome extends Serializable {
export class BattleOutcome {
// Indicates if the battle is a draw (no winner)
draw: boolean;
@ -13,8 +11,6 @@ module TS.SpaceTac.Game {
loot: Equipment[];
constructor(winner: Fleet) {
super();
this.winner = winner;
this.draw = winner ? false : true;
this.loot = [];

View file

@ -1,10 +1,8 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
/**
* Drones are static objects that apply effects in a circular zone around themselves.
*/
export class Drone extends Serializable {
export class Drone {
// Ship that deployed the drone
owner: Ship;
@ -26,8 +24,6 @@ module TS.SpaceTac.Game {
inside_at_start: Ship[] = [];
constructor(owner: Ship) {
super();
this.owner = owner;
}

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Piece of equipment to attach in slots
export class Equipment extends Serializable {
export class Equipment {
// Actual slot this equipment is attached to
attached_to: Slot;
@ -44,8 +42,6 @@ module TS.SpaceTac.Game {
// Basic constructor
constructor(slot: SlotType = null, code: string = null) {
super();
this.slot = slot;
this.code = code;
this.name = code;

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// A fleet of ships
export class Fleet extends Serializable {
export class Fleet {
// Fleet owner
player: Player;
@ -17,8 +15,6 @@ module TS.SpaceTac.Game {
// Create a fleet, bound to a player
constructor(player: Player = null) {
super();
this.player = player || new Player();
this.ships = [];
this.location = null;

View file

@ -6,7 +6,7 @@ module TS.SpaceTac.Game.Specs {
battle.endBattle(battle.fleets[0]);
}
describe("GameSession", () => {
/*describe("GameSession", () => {
it("serializes to a string", () => {
var session = new GameSession();
session.startQuickBattle(true);
@ -37,5 +37,5 @@ module TS.SpaceTac.Game.Specs {
// Check equality after game steps
expect(loaded_session).toEqual(session);
});
});
});*/
}

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// A game session, binding a universe and a player
export class GameSession extends Serializable {
export class GameSession {
// Game universe
universe: Universe;
@ -10,8 +8,6 @@ module TS.SpaceTac.Game {
player: Player;
constructor() {
super();
this.universe = new Universe();
this.player = new Player(this.universe);
}

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Template used to generate a loot equipment
export class LootTemplate extends Serializable {
export class LootTemplate {
// Type of slot this equipment will fit in
slot: SlotType;
@ -35,7 +33,6 @@ module TS.SpaceTac.Game {
// Create a loot template
constructor(slot: SlotType, name: string) {
super();
this.slot = slot;
this.name = name;
this.requirements = [];

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// One player (human or IA)
export class Player extends Serializable {
export class Player {
// Universe in which we are playing
universe: Universe;
@ -17,8 +15,6 @@ module TS.SpaceTac.Game {
// Create a player, with an empty fleet
constructor(universe: Universe = new Universe()) {
super();
this.universe = universe;
this.fleet = new Fleet(this);
this.ai = null;

View file

@ -1,15 +1,11 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Random generator, used in all throws
export class RandomGenerator extends Serializable {
export class RandomGenerator {
// Array of next values, empty for a correct generator
private fake_values: number[];
// Basic constructor (can specify fake values as arguments)
constructor(...values: number[]) {
super();
this.fake_values = [];
values.forEach((value: number) => {

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Range of number values
export class Range extends Serializable {
export class Range {
// Minimal value
min: number;
@ -11,7 +9,6 @@ module TS.SpaceTac.Game {
// Create a range of values
constructor(min: number, max: number = null) {
super();
this.set(min, max);
}

View file

@ -1,30 +0,0 @@
module TS.SpaceTac.Game {
// Base class for serializable objects
export class Serializable {
static _next_sid: number = 0;
private _sid: string;
constructor() {
this._sid = null;
}
// Reset the SID sequence, given a maximal known SID
static resetIdSequence(highest_known: number): void {
Serializable._next_sid = highest_known + 1;
}
// Get an ID that can be used for serialization
getSerializeId(): string {
if (this._sid === null) {
this._sid = (Serializable._next_sid++).toString();
}
return this._sid;
}
// Method called when fields have been serialized in this object
postSerialize(fields: any): void {
// Abstract method
}
}
}

View file

@ -1,138 +0,0 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game.Specs {
export class SerializableTestObj3 extends Serializable {
a: boolean;
b: SerializableTestObj1;
}
export class SerializableTestObj2 extends Serializable {
a: string;
b: SerializableTestObj3;
constructor(a: string = "test") {
super();
this.a = a;
this.b = null;
}
prepend(prefix: string): string {
return prefix + this.a;
}
}
export class SerializableTestObj1 extends Serializable {
a: number;
b: SerializableTestObj2;
c: SerializableTestObj2[];
constructor(a: number = 5, b: SerializableTestObj2 = null) {
super();
this.a = a;
this.b = b;
this.c = [];
}
}
describe("Serializer", () => {
it("collects serializable classes", () => {
var serializer = new Serializer();
var classes = serializer.collectSerializableClasses();
expect(classes["SpaceTac.Game.Specs.SerializableTestObj1"]).toBe(SerializableTestObj1);
expect(classes["SpaceTac.Game.Specs.SerializableTestObj2"]).toBe(SerializableTestObj2);
expect(classes["SpaceTac.Game.Specs.SerializableTestObj3"]).toBe(SerializableTestObj3);
expect(classes["SpaceTac.Game.Range"]).toBe(Range);
expect(classes["SpaceTac.Game.Equipments.GatlingGun"]).toBe(Equipments.GatlingGun);
});
it("gets an object's full path in namespace", () => {
var serializer = new Serializer();
expect(serializer.getClassPath(new SerializableTestObj1())).toBe("SpaceTac.Game.Specs.SerializableTestObj1");
expect(serializer.getClassPath(new Range(0, 1))).toBe("SpaceTac.Game.Range");
});
it("serializes and deserializes simple typescript objects", () => {
var serializer = new Serializer();
var obj = new SerializableTestObj2("a string");
var dumped = serializer.serialize(obj);
var loaded = serializer.unserialize(dumped);
expect(loaded).toEqual(obj);
expect((<SerializableTestObj2>loaded).prepend("this is ")).toEqual("this is a string");
});
it("serializes and deserializes nested typescript objects", () => {
var serializer = new Serializer();
var obj = new SerializableTestObj1(8, new SerializableTestObj2("test"));
obj.c.push(new SerializableTestObj2("second test"));
obj.c.push(new SerializableTestObj2("third test"));
var dumped = serializer.serialize(obj);
var loaded = serializer.unserialize(dumped);
expect(loaded).toEqual(obj);
expect((<SerializableTestObj1>loaded).b.prepend("this is a ")).toEqual("this is a test");
expect((<SerializableTestObj1>loaded).c[1].prepend("this is a ")).toEqual("this is a third test");
});
it("does not create copies of same object", () => {
var serializer = new Serializer();
var obj = new SerializableTestObj1(8, new SerializableTestObj2("test"));
obj.c.push(obj.b);
var dumped = serializer.serialize(obj);
var loaded = serializer.unserialize(dumped);
expect(loaded).toEqual(obj);
expect((<SerializableTestObj1>loaded).b).toBe((<SerializableTestObj1>loaded).c[0]);
});
it("handles reference cycles", () => {
var serializer = new Serializer();
var obj3 = new SerializableTestObj3();
obj3.a = true;
var obj2 = new SerializableTestObj2("test");
var obj1 = new SerializableTestObj1(8, obj2);
obj3.b = obj1;
obj2.b = obj3;
var dumped = serializer.serialize(obj1);
var loaded = serializer.unserialize(dumped);
expect(loaded).toEqual(obj1);
expect((<SerializableTestObj1>loaded).b.b.a).toBe(true);
expect((<SerializableTestObj1>loaded).b.b.b).toBe(loaded);
});
it("resets id sequence between sessions", () => {
// Start with a fresh ID sequence
Serializable._next_sid = 0;
var serializer = new Serializer();
// Serialize an object
var obj1 = new SerializableTestObj1(8);
var dumped = serializer.serialize(obj1);
// Simulate a page refresh
Serializable._next_sid = 0;
// Load dumped object
var loaded = <SerializableTestObj1>serializer.unserialize(dumped);
// Add a new object
loaded.b = new SerializableTestObj2("test");
// If the ID sequence is not propertly reset, the ID of both objects will clash
dumped = serializer.serialize(loaded);
var loaded_again = serializer.unserialize(dumped);
expect(loaded_again).toEqual(loaded);
});
});
}

View file

@ -1,154 +0,0 @@
module TS.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 = 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(TS.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;
}
}
}
}

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// A single ship in a Fleet
export class Ship extends Serializable {
export class Ship {
// Fleet this ship is a member of
fleet: Fleet;
@ -65,8 +63,6 @@ module TS.SpaceTac.Game {
// Create a new ship inside a fleet
constructor(fleet: Fleet = null, name: string = null) {
super();
this.attributes = new AttributeCollection();
this.fleet = fleet || new Fleet();
this.level = 1;

View file

@ -1,5 +1,3 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Types of slots
export enum SlotType {
@ -11,7 +9,7 @@ module TS.SpaceTac.Game {
}
// Slot to attach an equipment to a ship
export class Slot extends Serializable {
export class Slot {
// Link to the ship
ship: Ship;
@ -23,8 +21,6 @@ module TS.SpaceTac.Game {
// Create an empty slot for a ship
constructor(ship: Ship, type: SlotType) {
super();
this.ship = ship;
this.type = type;
this.attached = null;

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// A star system
export class Star extends Serializable {
export class Star {
// Available names for star systems
static NAMES_POOL = [
@ -78,8 +76,6 @@ module TS.SpaceTac.Game {
level: number;
constructor(universe: Universe = null, x = 0, y = 0, name = "") {
super();
this.universe = universe || new Universe();
this.x = x;
this.y = y;

View file

@ -1,15 +1,11 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// An hyperspace link between two star systems
export class StarLink extends Serializable {
export class StarLink {
// Stars
first: Star;
second: Star;
constructor(first: Star, second: Star) {
super();
this.first = first;
this.second = second;
}

View file

@ -1,5 +1,3 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
export enum StarLocationType {
STAR,
@ -10,7 +8,7 @@ module TS.SpaceTac.Game {
}
// Point of interest in a star system
export class StarLocation extends Serializable {
export class StarLocation {
// Parent star system
star: Star;
@ -29,8 +27,6 @@ module TS.SpaceTac.Game {
encounter_gen: boolean;
constructor(star: Star, type: StarLocationType = StarLocationType.PLANET, x: number = 0, y: number = 0) {
super();
this.star = star || new Star();
this.type = type;
this.x = x;

View file

@ -1,5 +1,3 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Find the nearest intersection between a line and a circle
// Circle is supposed to be centered at (0,0)
@ -19,7 +17,7 @@ module TS.SpaceTac.Game {
// Target for a capability
// This could be a location in space, or a ship
export class Target extends Serializable {
export class Target {
// Coordinates of the target
x: number;
y: number;
@ -29,8 +27,6 @@ module TS.SpaceTac.Game {
// Standard constructor
constructor(x: number, y: number, ship: Ship) {
super();
this.x = x;
this.y = y;
this.ship = ship;

View file

@ -1,8 +1,6 @@
/// <reference path="Serializable.ts"/>
module TS.SpaceTac.Game {
// Main game universe
export class Universe extends Serializable {
export class Universe {
// List of star systems
stars: Star[];
@ -13,8 +11,6 @@ module TS.SpaceTac.Game {
radius: number;
constructor() {
super();
this.stars = [];
this.starlinks = [];
this.radius = 5;

View file

@ -1,8 +1,6 @@
/// <reference path="../Serializable.ts"/>
module TS.SpaceTac.Game {
// Base class for action definitions
export class BaseAction extends Serializable {
export class BaseAction {
// Identifier code for the type of action
code: string;
@ -17,8 +15,6 @@ module TS.SpaceTac.Game {
// Create the action
constructor(code: string, name: string, needs_target: boolean, equipment: Equipment = null) {
super();
this.code = code;
this.name = name;
this.needs_target = needs_target;

View file

@ -1,8 +1,6 @@
/// <reference path="../Serializable.ts"/>
module TS.SpaceTac.Game.AI {
// Base class for all Artificial Intelligence interaction
export class AbstractAI extends Serializable {
export class AbstractAI {
// The fleet controlled by this AI
fleet: Fleet;
@ -25,8 +23,6 @@ module TS.SpaceTac.Game.AI {
private workqueue: Function[];
constructor(fleet: Fleet) {
super();
this.fleet = fleet;
this.async = true;
this.workqueue = [];

View file

@ -1,16 +1,12 @@
/// <reference path="../Serializable.ts"/>
module TS.SpaceTac.Game {
// Base class for effects of actions
// Effects are typically one shot, but sticky effects can be used to apply effects over a period
export class BaseEffect extends Serializable {
export class BaseEffect {
// Identifier code for the type of effect
code: string;
// Base constructor
constructor(code: string) {
super();
this.code = code;
}

View file

@ -1,8 +1,6 @@
/// <reference path="../Serializable.ts"/>
module TS.SpaceTac.Game {
// Base class for a BattleLog event
export class BaseLogEvent extends Serializable {
export class BaseLogEvent {
// Code of the event (its type)
code: string;
@ -13,8 +11,6 @@ module TS.SpaceTac.Game {
target: Target;
constructor(code: string, ship: Ship = null, target: Target = null) {
super();
this.code = code;
this.ship = ship;
this.target = target;