diff --git a/.gitignore b/.gitignore index 76add87..b10e71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -dist \ No newline at end of file +dist +repo.lgr diff --git a/.gitignore b/.gitignore index 76add87..b10e71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -dist \ No newline at end of file +dist +repo.lgr diff --git a/package-lock.json b/package-lock.json index 41c55c4..f7ad28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -393,6 +393,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", diff --git a/.gitignore b/.gitignore index 76add87..b10e71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -dist \ No newline at end of file +dist +repo.lgr diff --git a/package-lock.json b/package-lock.json index 41c55c4..f7ad28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -393,6 +393,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", diff --git a/package.json b/package.json index e113d7a..3db30fb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@types/express-graphql": "^0.6.1", "express": "^4.16.4", "express-graphql": "^0.6.12", + "fs": "0.0.1-security", "graphql": "^0.13.2" }, "devDependencies": { diff --git a/.gitignore b/.gitignore index 76add87..b10e71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -dist \ No newline at end of file +dist +repo.lgr diff --git a/package-lock.json b/package-lock.json index 41c55c4..f7ad28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -393,6 +393,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", diff --git a/package.json b/package.json index e113d7a..3db30fb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@types/express-graphql": "^0.6.1", "express": "^4.16.4", "express-graphql": "^0.6.12", + "fs": "0.0.1-security", "graphql": "^0.13.2" }, "devDependencies": { diff --git a/src/FileStateRepository.spec.ts b/src/FileStateRepository.spec.ts new file mode 100644 index 0000000..03cb6a0 --- /dev/null +++ b/src/FileStateRepository.spec.ts @@ -0,0 +1,105 @@ +import {expect} from "chai"; +import * as fs from "fs"; +import { FileStateRepository } from "./FileStateRepository"; +import { GameState } from "./GameServer"; + +const file = "./TestStateRepository.lgr"; + +after(function(){ + // clean up after all tests + if(fs.existsSync(file)){ + fs.unlinkSync(file); + } +}); + +describe("Scenario: Loading content", function(){ + describe("Given no file exists", function(){ + if(fs.existsSync(file)){ + fs.unlinkSync(file); + } + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then an empty list should be returned", function(){ + expect(states).to.be.not.null; + expect(states).to.deep.equal([]); + }); + + it("then the file should have been created", function(){ + const fileExists = fs.existsSync(file); + expect(fileExists).to.be.true; + }); + }); + }); + + describe("Given a file with one entry exists", function(){ + const originialStates = [new GameState("12345", "lvl1", "0")]; + fs.writeFileSync(file, JSON.stringify(originialStates)); + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then one entry should be returned.", function(){ + expect(states).to.have.lengthOf(originialStates.length); + }); + + it("then the entry should be returned correctly.", function(){ + expect(states).to.deep.equal(originialStates); + }); + }); + }); + + describe("Given a file with two entries exists", function(){ + const originialStates = [new GameState("12345", "lvl1", "0"), new GameState("6879", "lvl1", "3")]; + fs.writeFileSync(file, JSON.stringify(originialStates)); + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then one entry should be returned.", function(){ + expect(states).to.have.lengthOf(originialStates.length); + }); + + it("then the entries should be returned correctly.", function(){ + expect(states).to.deep.equal(originialStates); + }); + }); + }); +}); + +describe("Scenario: Saving content", function(){ + describe("Given a list of two entries ", function(){ + const originialStates = [new GameState("12345", "lvl1", "0"), new GameState("6879", "lvl1", "3")]; + + describe("When states are saved", function(){ + const repo = new FileStateRepository(file); + const result = repo.saveAllGameStates(originialStates); + + it("then it should return true.", function(){ + expect(result).to.be.true; + }); + + it("then the file should exist.", function(){ + const fileExists = fs.existsSync(file); + expect(fileExists).to.be.true; + }); + + it("then file should contain exactly the entries.", function(){ + if(fs.existsSync(file)) + { + const fileContent = fs.readFileSync(file).toString(); + const entries = JSON.parse(fileContent); + + expect(entries).to.deep.equal(originialStates); + } + else { + expect(false, "No fiel to validate content of.").to.be.true; + } + }); + }); + }); +}); diff --git a/.gitignore b/.gitignore index 76add87..b10e71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -dist \ No newline at end of file +dist +repo.lgr diff --git a/package-lock.json b/package-lock.json index 41c55c4..f7ad28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -393,6 +393,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", diff --git a/package.json b/package.json index e113d7a..3db30fb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@types/express-graphql": "^0.6.1", "express": "^4.16.4", "express-graphql": "^0.6.12", + "fs": "0.0.1-security", "graphql": "^0.13.2" }, "devDependencies": { diff --git a/src/FileStateRepository.spec.ts b/src/FileStateRepository.spec.ts new file mode 100644 index 0000000..03cb6a0 --- /dev/null +++ b/src/FileStateRepository.spec.ts @@ -0,0 +1,105 @@ +import {expect} from "chai"; +import * as fs from "fs"; +import { FileStateRepository } from "./FileStateRepository"; +import { GameState } from "./GameServer"; + +const file = "./TestStateRepository.lgr"; + +after(function(){ + // clean up after all tests + if(fs.existsSync(file)){ + fs.unlinkSync(file); + } +}); + +describe("Scenario: Loading content", function(){ + describe("Given no file exists", function(){ + if(fs.existsSync(file)){ + fs.unlinkSync(file); + } + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then an empty list should be returned", function(){ + expect(states).to.be.not.null; + expect(states).to.deep.equal([]); + }); + + it("then the file should have been created", function(){ + const fileExists = fs.existsSync(file); + expect(fileExists).to.be.true; + }); + }); + }); + + describe("Given a file with one entry exists", function(){ + const originialStates = [new GameState("12345", "lvl1", "0")]; + fs.writeFileSync(file, JSON.stringify(originialStates)); + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then one entry should be returned.", function(){ + expect(states).to.have.lengthOf(originialStates.length); + }); + + it("then the entry should be returned correctly.", function(){ + expect(states).to.deep.equal(originialStates); + }); + }); + }); + + describe("Given a file with two entries exists", function(){ + const originialStates = [new GameState("12345", "lvl1", "0"), new GameState("6879", "lvl1", "3")]; + fs.writeFileSync(file, JSON.stringify(originialStates)); + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then one entry should be returned.", function(){ + expect(states).to.have.lengthOf(originialStates.length); + }); + + it("then the entries should be returned correctly.", function(){ + expect(states).to.deep.equal(originialStates); + }); + }); + }); +}); + +describe("Scenario: Saving content", function(){ + describe("Given a list of two entries ", function(){ + const originialStates = [new GameState("12345", "lvl1", "0"), new GameState("6879", "lvl1", "3")]; + + describe("When states are saved", function(){ + const repo = new FileStateRepository(file); + const result = repo.saveAllGameStates(originialStates); + + it("then it should return true.", function(){ + expect(result).to.be.true; + }); + + it("then the file should exist.", function(){ + const fileExists = fs.existsSync(file); + expect(fileExists).to.be.true; + }); + + it("then file should contain exactly the entries.", function(){ + if(fs.existsSync(file)) + { + const fileContent = fs.readFileSync(file).toString(); + const entries = JSON.parse(fileContent); + + expect(entries).to.deep.equal(originialStates); + } + else { + expect(false, "No fiel to validate content of.").to.be.true; + } + }); + }); + }); +}); diff --git a/src/FileStateRepository.ts b/src/FileStateRepository.ts new file mode 100644 index 0000000..a3e6ab8 --- /dev/null +++ b/src/FileStateRepository.ts @@ -0,0 +1,30 @@ +import * as fs from "fs"; +import { GameState, IStateRepository } from "./GameServer"; + +export class FileStateRepository implements IStateRepository +{ + private file: string; + + constructor(file: string) + { + this.file = file; + } + + public loadAllGameStates(): GameState[] + { + if(!fs.existsSync(this.file)){ + fs.writeFileSync(this.file, JSON.stringify([])); + return []; + } + + const content = fs.readFileSync(this.file).toString(); + return JSON.parse(content); + } + + public saveAllGameStates(states: GameState[]): boolean + { + fs.writeFileSync(this.file, JSON.stringify(states)); + + return true; + } +} diff --git a/.gitignore b/.gitignore index 76add87..b10e71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -dist \ No newline at end of file +dist +repo.lgr diff --git a/package-lock.json b/package-lock.json index 41c55c4..f7ad28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -393,6 +393,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", diff --git a/package.json b/package.json index e113d7a..3db30fb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@types/express-graphql": "^0.6.1", "express": "^4.16.4", "express-graphql": "^0.6.12", + "fs": "0.0.1-security", "graphql": "^0.13.2" }, "devDependencies": { diff --git a/src/FileStateRepository.spec.ts b/src/FileStateRepository.spec.ts new file mode 100644 index 0000000..03cb6a0 --- /dev/null +++ b/src/FileStateRepository.spec.ts @@ -0,0 +1,105 @@ +import {expect} from "chai"; +import * as fs from "fs"; +import { FileStateRepository } from "./FileStateRepository"; +import { GameState } from "./GameServer"; + +const file = "./TestStateRepository.lgr"; + +after(function(){ + // clean up after all tests + if(fs.existsSync(file)){ + fs.unlinkSync(file); + } +}); + +describe("Scenario: Loading content", function(){ + describe("Given no file exists", function(){ + if(fs.existsSync(file)){ + fs.unlinkSync(file); + } + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then an empty list should be returned", function(){ + expect(states).to.be.not.null; + expect(states).to.deep.equal([]); + }); + + it("then the file should have been created", function(){ + const fileExists = fs.existsSync(file); + expect(fileExists).to.be.true; + }); + }); + }); + + describe("Given a file with one entry exists", function(){ + const originialStates = [new GameState("12345", "lvl1", "0")]; + fs.writeFileSync(file, JSON.stringify(originialStates)); + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then one entry should be returned.", function(){ + expect(states).to.have.lengthOf(originialStates.length); + }); + + it("then the entry should be returned correctly.", function(){ + expect(states).to.deep.equal(originialStates); + }); + }); + }); + + describe("Given a file with two entries exists", function(){ + const originialStates = [new GameState("12345", "lvl1", "0"), new GameState("6879", "lvl1", "3")]; + fs.writeFileSync(file, JSON.stringify(originialStates)); + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then one entry should be returned.", function(){ + expect(states).to.have.lengthOf(originialStates.length); + }); + + it("then the entries should be returned correctly.", function(){ + expect(states).to.deep.equal(originialStates); + }); + }); + }); +}); + +describe("Scenario: Saving content", function(){ + describe("Given a list of two entries ", function(){ + const originialStates = [new GameState("12345", "lvl1", "0"), new GameState("6879", "lvl1", "3")]; + + describe("When states are saved", function(){ + const repo = new FileStateRepository(file); + const result = repo.saveAllGameStates(originialStates); + + it("then it should return true.", function(){ + expect(result).to.be.true; + }); + + it("then the file should exist.", function(){ + const fileExists = fs.existsSync(file); + expect(fileExists).to.be.true; + }); + + it("then file should contain exactly the entries.", function(){ + if(fs.existsSync(file)) + { + const fileContent = fs.readFileSync(file).toString(); + const entries = JSON.parse(fileContent); + + expect(entries).to.deep.equal(originialStates); + } + else { + expect(false, "No fiel to validate content of.").to.be.true; + } + }); + }); + }); +}); diff --git a/src/FileStateRepository.ts b/src/FileStateRepository.ts new file mode 100644 index 0000000..a3e6ab8 --- /dev/null +++ b/src/FileStateRepository.ts @@ -0,0 +1,30 @@ +import * as fs from "fs"; +import { GameState, IStateRepository } from "./GameServer"; + +export class FileStateRepository implements IStateRepository +{ + private file: string; + + constructor(file: string) + { + this.file = file; + } + + public loadAllGameStates(): GameState[] + { + if(!fs.existsSync(this.file)){ + fs.writeFileSync(this.file, JSON.stringify([])); + return []; + } + + const content = fs.readFileSync(this.file).toString(); + return JSON.parse(content); + } + + public saveAllGameStates(states: GameState[]): boolean + { + fs.writeFileSync(this.file, JSON.stringify(states)); + + return true; + } +} diff --git a/src/GameServer.ts b/src/GameServer.ts index dc4c177..d4bd6aa 100644 --- a/src/GameServer.ts +++ b/src/GameServer.ts @@ -5,7 +5,7 @@ MazeTile, } from "./MazeMap"; -class GameState +export class GameState { public userID: string; public mapID: string; @@ -19,6 +19,23 @@ } } +export interface IStateRepository +{ + loadAllGameStates(): GameState[]; + saveAllGameStates(states: GameState[]): boolean; +} + +export interface IMapRepository +{ + loadAllMaps(): MazeMap[]; +} + +export class GameServerOptions +{ + public stateRepo: IStateRepository; + public mapRepo: IMapRepository; +} + export class GameServer { // By keeping mapList private, we can ensure, @@ -30,11 +47,32 @@ * that no two consecutive generated characters are the same. */ private randomCharacter: string; + private options: GameServerOptions; - constructor() + constructor(options?: GameServerOptions) { + if(options != null) + { + this.options = options; + } + this.mapList = []; - this.states = []; + if(options != null && options.mapRepo != null) + { + this.mapList = options.mapRepo.loadAllMaps(); + for (const map of options.mapRepo.loadAllMaps()) { + this.addMap(map); + } + } + + if(options != null && options.stateRepo != null) + { + this.states = options.stateRepo.loadAllGameStates(); + } + else + { + this.states = []; + } } public addMap(newMap: MazeMap): boolean @@ -79,6 +117,8 @@ state.tileID = start.id; } + this.commitGameStates(); + return true; } @@ -89,6 +129,9 @@ { this.states.splice(stateIndex, 1); } + + this.commitGameStates(); + return true; } @@ -122,6 +165,9 @@ const nextTile = map.layout[i-1+vec[0]][j+vec[1]]; state.tileID = nextTile.id; + + this.commitGameStates(); + return nextTile; } @@ -186,4 +232,13 @@ return newChar; } + + private commitGameStates(): void + { + if(this.options == null || this.options.stateRepo == null || this.options.stateRepo.saveAllGameStates == null){ + return; + } + + this.options.stateRepo.saveAllGameStates(this.states); + } } diff --git a/.gitignore b/.gitignore index 76add87..b10e71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -dist \ No newline at end of file +dist +repo.lgr diff --git a/package-lock.json b/package-lock.json index 41c55c4..f7ad28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -393,6 +393,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", diff --git a/package.json b/package.json index e113d7a..3db30fb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@types/express-graphql": "^0.6.1", "express": "^4.16.4", "express-graphql": "^0.6.12", + "fs": "0.0.1-security", "graphql": "^0.13.2" }, "devDependencies": { diff --git a/src/FileStateRepository.spec.ts b/src/FileStateRepository.spec.ts new file mode 100644 index 0000000..03cb6a0 --- /dev/null +++ b/src/FileStateRepository.spec.ts @@ -0,0 +1,105 @@ +import {expect} from "chai"; +import * as fs from "fs"; +import { FileStateRepository } from "./FileStateRepository"; +import { GameState } from "./GameServer"; + +const file = "./TestStateRepository.lgr"; + +after(function(){ + // clean up after all tests + if(fs.existsSync(file)){ + fs.unlinkSync(file); + } +}); + +describe("Scenario: Loading content", function(){ + describe("Given no file exists", function(){ + if(fs.existsSync(file)){ + fs.unlinkSync(file); + } + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then an empty list should be returned", function(){ + expect(states).to.be.not.null; + expect(states).to.deep.equal([]); + }); + + it("then the file should have been created", function(){ + const fileExists = fs.existsSync(file); + expect(fileExists).to.be.true; + }); + }); + }); + + describe("Given a file with one entry exists", function(){ + const originialStates = [new GameState("12345", "lvl1", "0")]; + fs.writeFileSync(file, JSON.stringify(originialStates)); + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then one entry should be returned.", function(){ + expect(states).to.have.lengthOf(originialStates.length); + }); + + it("then the entry should be returned correctly.", function(){ + expect(states).to.deep.equal(originialStates); + }); + }); + }); + + describe("Given a file with two entries exists", function(){ + const originialStates = [new GameState("12345", "lvl1", "0"), new GameState("6879", "lvl1", "3")]; + fs.writeFileSync(file, JSON.stringify(originialStates)); + + describe("When states are loaded", function(){ + const loader = new FileStateRepository(file); + const states = loader.loadAllGameStates(); + + it("then one entry should be returned.", function(){ + expect(states).to.have.lengthOf(originialStates.length); + }); + + it("then the entries should be returned correctly.", function(){ + expect(states).to.deep.equal(originialStates); + }); + }); + }); +}); + +describe("Scenario: Saving content", function(){ + describe("Given a list of two entries ", function(){ + const originialStates = [new GameState("12345", "lvl1", "0"), new GameState("6879", "lvl1", "3")]; + + describe("When states are saved", function(){ + const repo = new FileStateRepository(file); + const result = repo.saveAllGameStates(originialStates); + + it("then it should return true.", function(){ + expect(result).to.be.true; + }); + + it("then the file should exist.", function(){ + const fileExists = fs.existsSync(file); + expect(fileExists).to.be.true; + }); + + it("then file should contain exactly the entries.", function(){ + if(fs.existsSync(file)) + { + const fileContent = fs.readFileSync(file).toString(); + const entries = JSON.parse(fileContent); + + expect(entries).to.deep.equal(originialStates); + } + else { + expect(false, "No fiel to validate content of.").to.be.true; + } + }); + }); + }); +}); diff --git a/src/FileStateRepository.ts b/src/FileStateRepository.ts new file mode 100644 index 0000000..a3e6ab8 --- /dev/null +++ b/src/FileStateRepository.ts @@ -0,0 +1,30 @@ +import * as fs from "fs"; +import { GameState, IStateRepository } from "./GameServer"; + +export class FileStateRepository implements IStateRepository +{ + private file: string; + + constructor(file: string) + { + this.file = file; + } + + public loadAllGameStates(): GameState[] + { + if(!fs.existsSync(this.file)){ + fs.writeFileSync(this.file, JSON.stringify([])); + return []; + } + + const content = fs.readFileSync(this.file).toString(); + return JSON.parse(content); + } + + public saveAllGameStates(states: GameState[]): boolean + { + fs.writeFileSync(this.file, JSON.stringify(states)); + + return true; + } +} diff --git a/src/GameServer.ts b/src/GameServer.ts index dc4c177..d4bd6aa 100644 --- a/src/GameServer.ts +++ b/src/GameServer.ts @@ -5,7 +5,7 @@ MazeTile, } from "./MazeMap"; -class GameState +export class GameState { public userID: string; public mapID: string; @@ -19,6 +19,23 @@ } } +export interface IStateRepository +{ + loadAllGameStates(): GameState[]; + saveAllGameStates(states: GameState[]): boolean; +} + +export interface IMapRepository +{ + loadAllMaps(): MazeMap[]; +} + +export class GameServerOptions +{ + public stateRepo: IStateRepository; + public mapRepo: IMapRepository; +} + export class GameServer { // By keeping mapList private, we can ensure, @@ -30,11 +47,32 @@ * that no two consecutive generated characters are the same. */ private randomCharacter: string; + private options: GameServerOptions; - constructor() + constructor(options?: GameServerOptions) { + if(options != null) + { + this.options = options; + } + this.mapList = []; - this.states = []; + if(options != null && options.mapRepo != null) + { + this.mapList = options.mapRepo.loadAllMaps(); + for (const map of options.mapRepo.loadAllMaps()) { + this.addMap(map); + } + } + + if(options != null && options.stateRepo != null) + { + this.states = options.stateRepo.loadAllGameStates(); + } + else + { + this.states = []; + } } public addMap(newMap: MazeMap): boolean @@ -79,6 +117,8 @@ state.tileID = start.id; } + this.commitGameStates(); + return true; } @@ -89,6 +129,9 @@ { this.states.splice(stateIndex, 1); } + + this.commitGameStates(); + return true; } @@ -122,6 +165,9 @@ const nextTile = map.layout[i-1+vec[0]][j+vec[1]]; state.tileID = nextTile.id; + + this.commitGameStates(); + return nextTile; } @@ -186,4 +232,13 @@ return newChar; } + + private commitGameStates(): void + { + if(this.options == null || this.options.stateRepo == null || this.options.stateRepo.saveAllGameStates == null){ + return; + } + + this.options.stateRepo.saveAllGameStates(this.states); + } } diff --git a/src/app.ts b/src/app.ts index a095451..0e54e57 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,15 +1,20 @@ import * as express from "express"; -import { GameServer } from "./GameServer"; +import { FileStateRepository } from "./FileStateRepository"; +import { GameServer, GameServerOptions } from "./GameServer"; import { CreateLabyrinthMiddleware } from "./LabyrinthMiddleware"; import { createSampleMaps } from "./SampleMaps"; const port = 8080; const apiRoot = "/graphql"; +const saveFile = "./repo.lgr"; const app = express(); -const games = new GameServer(); -const maps = createSampleMaps(); -maps.forEach(m => games.addMap(m)); +const gameOptions = { + mapRepo: { loadAllMaps: createSampleMaps }, + stateRepo: new FileStateRepository(saveFile), +} as GameServerOptions; + +const games = new GameServer(gameOptions); app.use(apiRoot, CreateLabyrinthMiddleware(true, games));