0.1.3
This commit is contained in:
		
							
								
								
									
										130
									
								
								.hmrc
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								.hmrc
									
									
									
									
									
								
							@@ -1,34 +1,134 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "path": "G:\\Other\\Development\\Projects\\[ideas]\\domino",
 | 
					  "path": "G:\\Other\\Development\\Projects\\[ideas]\\domino",
 | 
				
			||||||
  "name": "domino",
 | 
					  "name": "domino-server",
 | 
				
			||||||
  "initialVersion": "1.0.0",
 | 
					  "initialVersion": "0.1.1",
 | 
				
			||||||
  "version": "1.0.0",
 | 
					  "version": "0.1.2",
 | 
				
			||||||
  "docker": {
 | 
					  "docker": {
 | 
				
			||||||
    "repository": "arhuako/domino"
 | 
					    "useRegistry": true,
 | 
				
			||||||
 | 
					    "registry": "192.168.1.115:5000",
 | 
				
			||||||
 | 
					    "repository": "arhuako/domino-server"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "repository": {
 | 
					  "repository": {
 | 
				
			||||||
    "type": "github",
 | 
					    "type": "other",
 | 
				
			||||||
    "user": "jmconde",
 | 
					    "url": "https://gitea.xintanalabs.net/arhuako/domino-server.git",
 | 
				
			||||||
    "name": "domino",
 | 
					    "manage": true
 | 
				
			||||||
    "manage": true,
 | 
					 | 
				
			||||||
    "createOnInit": true
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "changelog": {
 | 
					  "changelog": {
 | 
				
			||||||
    "create": true,
 | 
					 | 
				
			||||||
    "managed": true,
 | 
					    "managed": true,
 | 
				
			||||||
    "createHTML": true,
 | 
					    "createHTML": true,
 | 
				
			||||||
    "htmlPath": "public"
 | 
					    "htmlPath": "public"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "_backupInitial": {
 | 
					  "_backupInitial": {
 | 
				
			||||||
    "name": "domino",
 | 
					    "name": "domino-server",
 | 
				
			||||||
    "version": "1.0.0",
 | 
					    "version": "0.1.1",
 | 
				
			||||||
    "description": "",
 | 
					    "description": "",
 | 
				
			||||||
    "main": "index.js",
 | 
					    "main": "index.js",
 | 
				
			||||||
 | 
					    "engines": {
 | 
				
			||||||
 | 
					      "node": ">=20.6.0"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "scripts": {
 | 
					    "scripts": {
 | 
				
			||||||
      "test": "echo \"Error: no test specified\" && exit 1"
 | 
					      "build": "tsc",
 | 
				
			||||||
 | 
					      "dev": "node --env-file=.env --watch -r ts-node/register src/server/index.ts",
 | 
				
			||||||
 | 
					      "create": "node --env-file=.env -r ts-node/register ./create.ts",
 | 
				
			||||||
 | 
					      "test": "node --env-file=.env -r ts-node/register src/test.ts",
 | 
				
			||||||
 | 
					      "test:watch": "node --env-file=.env --watch -r ts-node/register src/test.ts",
 | 
				
			||||||
 | 
					      "docker-build": "docker build -t arhuako/domino:latest .",
 | 
				
			||||||
 | 
					      "docker-tag": "docker tag arhuako/domino:latest arhuako/domino:1.0.0",
 | 
				
			||||||
 | 
					      "docker-push": "docker push arhuako/domino:latest && docker push arhuako/domino:1.0.0",
 | 
				
			||||||
 | 
					      "publish": "npm run docker-build && npm run docker-tag && npm run docker-push"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "keywords": [],
 | 
					    "keywords": [],
 | 
				
			||||||
    "author": "",
 | 
					    "author": "arhuako",
 | 
				
			||||||
    "license": "ISC"
 | 
					    "license": "ISC",
 | 
				
			||||||
 | 
					    "type": "commonjs",
 | 
				
			||||||
 | 
					    "reposityory": "github:jmconde/domino",
 | 
				
			||||||
 | 
					    "dependencies": {
 | 
				
			||||||
 | 
					      "bcryptjs": "^2.4.3",
 | 
				
			||||||
 | 
					      "chalk": "^4.1.2",
 | 
				
			||||||
 | 
					      "cors": "^2.8.5",
 | 
				
			||||||
 | 
					      "express": "^4.19.2",
 | 
				
			||||||
 | 
					      "express-validator": "^7.1.0",
 | 
				
			||||||
 | 
					      "jsonwebtoken": "^9.0.2",
 | 
				
			||||||
 | 
					      "mongodb": "^6.8.0",
 | 
				
			||||||
 | 
					      "nodemailer": "^6.9.14",
 | 
				
			||||||
 | 
					      "nodemailer-express-handlebars": "^6.1.2",
 | 
				
			||||||
 | 
					      "pino": "^9.2.0",
 | 
				
			||||||
 | 
					      "pino-http": "^10.2.0",
 | 
				
			||||||
 | 
					      "pino-pretty": "^11.2.1",
 | 
				
			||||||
 | 
					      "pino-rotating-file-stream": "^0.0.2",
 | 
				
			||||||
 | 
					      "pubsub-js": "^1.9.4",
 | 
				
			||||||
 | 
					      "seedrandom": "^3.0.5",
 | 
				
			||||||
 | 
					      "socket.io": "^4.7.5"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "devDependencies": {
 | 
				
			||||||
 | 
					      "@types/bcryptjs": "^2.4.6",
 | 
				
			||||||
 | 
					      "@types/express": "^4.17.21",
 | 
				
			||||||
 | 
					      "@types/jsonwebtoken": "^9.0.6",
 | 
				
			||||||
 | 
					      "@types/node": "^20.14.8",
 | 
				
			||||||
 | 
					      "@types/nodemailer": "^6.4.15",
 | 
				
			||||||
 | 
					      "@types/nodemailer-express-handlebars": "^4.0.5",
 | 
				
			||||||
 | 
					      "@types/pubsub-js": "^1.8.6",
 | 
				
			||||||
 | 
					      "@types/seedrandom": "^3.0.8",
 | 
				
			||||||
 | 
					      "ts-node": "^10.9.2",
 | 
				
			||||||
 | 
					      "typescript": "^5.5.2"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "_backup": {
 | 
				
			||||||
 | 
					    "name": "domino-server",
 | 
				
			||||||
 | 
					    "version": "0.1.1",
 | 
				
			||||||
 | 
					    "description": "",
 | 
				
			||||||
 | 
					    "main": "index.js",
 | 
				
			||||||
 | 
					    "engines": {
 | 
				
			||||||
 | 
					      "node": ">=20.6.0"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "scripts": {
 | 
				
			||||||
 | 
					      "build": "tsc",
 | 
				
			||||||
 | 
					      "dev": "node --env-file=.env --watch -r ts-node/register src/server/index.ts",
 | 
				
			||||||
 | 
					      "create": "node --env-file=.env -r ts-node/register ./create.ts",
 | 
				
			||||||
 | 
					      "test": "node --env-file=.env -r ts-node/register src/test.ts",
 | 
				
			||||||
 | 
					      "test:watch": "node --env-file=.env --watch -r ts-node/register src/test.ts",
 | 
				
			||||||
 | 
					      "docker-build": "docker build -t 192.168.1.115:5000/arhuako/domino-server:latest .",
 | 
				
			||||||
 | 
					      "docker-tag": "docker tag 192.168.1.115:5000/arhuako/domino-server:latest 192.168.1.115:5000/arhuako/domino-server:0.1.1",
 | 
				
			||||||
 | 
					      "docker-push": "docker push 192.168.1.115:5000/arhuako/domino-server:latest && docker push 192.168.1.115:5000/arhuako/domino-server:0.1.1",
 | 
				
			||||||
 | 
					      "publish": "npm run docker-build && npm run docker-tag && npm run docker-push"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "keywords": [],
 | 
				
			||||||
 | 
					    "author": "arhuako",
 | 
				
			||||||
 | 
					    "license": "ISC",
 | 
				
			||||||
 | 
					    "type": "commonjs",
 | 
				
			||||||
 | 
					    "reposityory": {
 | 
				
			||||||
 | 
					      "type": "git",
 | 
				
			||||||
 | 
					      "url": "https://gitea.xintanalabs.net/arhuako/domino-server.git"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "dependencies": {
 | 
				
			||||||
 | 
					      "bcryptjs": "^2.4.3",
 | 
				
			||||||
 | 
					      "chalk": "^4.1.2",
 | 
				
			||||||
 | 
					      "cors": "^2.8.5",
 | 
				
			||||||
 | 
					      "express": "^4.19.2",
 | 
				
			||||||
 | 
					      "express-validator": "^7.1.0",
 | 
				
			||||||
 | 
					      "jsonwebtoken": "^9.0.2",
 | 
				
			||||||
 | 
					      "mongodb": "^6.8.0",
 | 
				
			||||||
 | 
					      "nodemailer": "^6.9.14",
 | 
				
			||||||
 | 
					      "nodemailer-express-handlebars": "^6.1.2",
 | 
				
			||||||
 | 
					      "pino": "^9.2.0",
 | 
				
			||||||
 | 
					      "pino-http": "^10.2.0",
 | 
				
			||||||
 | 
					      "pino-pretty": "^11.2.1",
 | 
				
			||||||
 | 
					      "pino-rotating-file-stream": "^0.0.2",
 | 
				
			||||||
 | 
					      "pubsub-js": "^1.9.4",
 | 
				
			||||||
 | 
					      "seedrandom": "^3.0.5",
 | 
				
			||||||
 | 
					      "socket.io": "^4.7.5"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "devDependencies": {
 | 
				
			||||||
 | 
					      "@types/bcryptjs": "^2.4.6",
 | 
				
			||||||
 | 
					      "@types/express": "^4.17.21",
 | 
				
			||||||
 | 
					      "@types/jsonwebtoken": "^9.0.6",
 | 
				
			||||||
 | 
					      "@types/node": "^20.14.8",
 | 
				
			||||||
 | 
					      "@types/nodemailer": "^6.4.15",
 | 
				
			||||||
 | 
					      "@types/nodemailer-express-handlebars": "^4.0.5",
 | 
				
			||||||
 | 
					      "@types/pubsub-js": "^1.8.6",
 | 
				
			||||||
 | 
					      "@types/seedrandom": "^3.0.8",
 | 
				
			||||||
 | 
					      "ts-node": "^10.9.2",
 | 
				
			||||||
 | 
					      "typescript": "^5.5.2"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,4 +2,7 @@
 | 
				
			|||||||
All notable changes to this project will be documented in this file.
 | 
					All notable changes to this project will be documented in this file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Unreleased
 | 
					## Unreleased
 | 
				
			||||||
Initial commit
 | 
					
 | 
				
			||||||
 | 
					## 0.1.2 - 2024-07-17
 | 
				
			||||||
 | 
					### Added
 | 
				
			||||||
 | 
					- This changelog
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "domino",
 | 
					  "name": "domino-server",
 | 
				
			||||||
  "version": "1.0.0",
 | 
					  "version": "0.1.2",
 | 
				
			||||||
  "description": "",
 | 
					  "description": "",
 | 
				
			||||||
  "main": "index.js",
 | 
					  "main": "index.js",
 | 
				
			||||||
  "engines": {
 | 
					  "engines": {
 | 
				
			||||||
@@ -12,16 +12,19 @@
 | 
				
			|||||||
    "create": "node --env-file=.env -r ts-node/register ./create.ts",
 | 
					    "create": "node --env-file=.env -r ts-node/register ./create.ts",
 | 
				
			||||||
    "test": "node --env-file=.env -r ts-node/register src/test.ts",
 | 
					    "test": "node --env-file=.env -r ts-node/register src/test.ts",
 | 
				
			||||||
    "test:watch": "node --env-file=.env --watch -r ts-node/register src/test.ts",
 | 
					    "test:watch": "node --env-file=.env --watch -r ts-node/register src/test.ts",
 | 
				
			||||||
    "docker-build": "docker build -t arhuako/domino:latest .",
 | 
					    "docker-build": "docker build -t 192.168.1.115:5000/arhuako/domino-server:latest .",
 | 
				
			||||||
    "docker-tag": "docker tag arhuako/domino:latest arhuako/domino:1.0.0",
 | 
					    "docker-tag": "docker tag 192.168.1.115:5000/arhuako/domino-server:latest 192.168.1.115:5000/arhuako/domino-server:0.1.2",
 | 
				
			||||||
    "docker-push": "docker push arhuako/domino:latest && docker push arhuako/domino:1.0.0",
 | 
					    "docker-push": "docker push 192.168.1.115:5000/arhuako/domino-server:latest && docker push 192.168.1.115:5000/arhuako/domino-server:0.1.2",
 | 
				
			||||||
    "publish": "npm run docker-build && npm run docker-tag && npm run docker-push"
 | 
					    "publish": "npm run docker-build && npm run docker-tag && npm run docker-push"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "keywords": [],
 | 
					  "keywords": [],
 | 
				
			||||||
  "author": "arhuako",
 | 
					  "author": "arhuako",
 | 
				
			||||||
  "license": "ISC",
 | 
					  "license": "ISC",
 | 
				
			||||||
  "type": "commonjs",
 | 
					  "type": "commonjs",
 | 
				
			||||||
  "reposityory": "github:jmconde/domino",
 | 
					  "reposityory": {
 | 
				
			||||||
 | 
					    "type": "git",
 | 
				
			||||||
 | 
					    "url": "https://gitea.xintanalabs.net/arhuako/domino-server.git"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "bcryptjs": "^2.4.3",
 | 
					    "bcryptjs": "^2.4.3",
 | 
				
			||||||
    "chalk": "^4.1.2",
 | 
					    "chalk": "^4.1.2",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								public/CHANGELOG.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								public/CHANGELOG.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					<h1>Changelog</h1>
 | 
				
			||||||
 | 
					<p>All notable changes to this project will be documented in this file.</p>
 | 
				
			||||||
 | 
					<h2>0.1.2 - 2024-07-17</h2>
 | 
				
			||||||
 | 
					<h3>Added</h3>
 | 
				
			||||||
 | 
					<ul>
 | 
				
			||||||
 | 
					<li>This changelog</li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
@@ -288,11 +288,18 @@ export class DominoesGame extends EventEmitter {
 | 
				
			|||||||
    if (this.winner !== null) {
 | 
					    if (this.winner !== null) {
 | 
				
			||||||
      const winner = this.winner;
 | 
					      const winner = this.winner;
 | 
				
			||||||
      winner.score = totalPips;
 | 
					      winner.score = totalPips;
 | 
				
			||||||
      if (winner.teamedWith !== null) {
 | 
					      if (winner.teamedWith !== undefined) {
 | 
				
			||||||
        winner.teamedWith.score = totalPips;
 | 
					        const p = this.getPlayer(winner.teamedWith)
 | 
				
			||||||
 | 
					        if (p !== undefined) {
 | 
				
			||||||
 | 
					          p.score = totalPips;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private getPlayer(userId: string) {
 | 
				
			||||||
 | 
					    return this.players.find(player => player.id === userId) || undefined;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private autoDealTiles() {
 | 
					  private autoDealTiles() {
 | 
				
			||||||
    for (let i = 0; i < this.handSize; i++) {
 | 
					    for (let i = 0; i < this.handSize; i++) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import { GameSummary } from "./dto/GameSummary";
 | 
				
			|||||||
import { PlayerMove } from "./entities/PlayerMove";
 | 
					import { PlayerMove } from "./entities/PlayerMove";
 | 
				
			||||||
import { SessionService } from "../server/services/SessionService";
 | 
					import { SessionService } from "../server/services/SessionService";
 | 
				
			||||||
import { Score } from "../server/db/interfaces";
 | 
					import { Score } from "../server/db/interfaces";
 | 
				
			||||||
 | 
					import { MatchSessionOptions } from "./dto/MatchSessionOptions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class MatchSession {
 | 
					export class MatchSession {
 | 
				
			||||||
@@ -33,26 +34,30 @@ export class MatchSession {
 | 
				
			|||||||
  maxPlayers: number = 4;
 | 
					  maxPlayers: number = 4;
 | 
				
			||||||
  mode: string = 'classic';
 | 
					  mode: string = 'classic';
 | 
				
			||||||
  players: PlayerInterface[] = [];
 | 
					  players: PlayerInterface[] = [];
 | 
				
			||||||
  pointsToWin: number = 50;
 | 
					  // pointsToWin: number = 50;
 | 
				
			||||||
  rng!: PRNG
 | 
					  rng!: PRNG
 | 
				
			||||||
  scoreboard: Map<string, number> = new Map();
 | 
					  scoreboard: Map<string, number> = new Map();
 | 
				
			||||||
  seed!: string
 | 
					  seed!: string
 | 
				
			||||||
  sessionInProgress: boolean = false;
 | 
					  sessionInProgress: boolean = false;
 | 
				
			||||||
  status: string = 'created'
 | 
					  status: string = 'created'
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(public creator: PlayerInterface, public name?: string, seed?: string) {
 | 
					  constructor(public creator: PlayerInterface, private options: MatchSessionOptions) {
 | 
				
			||||||
 | 
					    const { sessionName, seed, winType, winTarget } = options;
 | 
				
			||||||
    this.seed = seed || getRandomSeed();
 | 
					    this.seed = seed || getRandomSeed();
 | 
				
			||||||
    this.id = uuid();
 | 
					    this.id = uuid();
 | 
				
			||||||
    this.name = name || `Game ${this.id}`;
 | 
					    this.name = sessionName || `Match ${this.id}`;
 | 
				
			||||||
    this.addPlayerToSession(creator);    
 | 
					    this.addPlayerToSession(creator);    
 | 
				
			||||||
    this.creator = creator;
 | 
					    this.creator = creator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.logger.info(`Match session created by: ${creator.name}`);
 | 
					    this.logger.info(`Match session created by: ${creator.name}`);
 | 
				
			||||||
    this.logger.info(`Match session ID: ${this.id}`);
 | 
					    this.logger.info(`Match session ID: ${this.id}`);
 | 
				
			||||||
    this.logger.info(`Match session name: ${this.name}`);
 | 
					    this.logger.info(`Match session name: ${this.name}`);
 | 
				
			||||||
    this.logger.info(`Points to win: ${this.pointsToWin}`);
 | 
					    this.logger.info(`Win type: ${options.winType}`);
 | 
				
			||||||
    this.sessionInProgress = true;
 | 
					    this.logger.info(`Win target: ${options.winTarget}`);
 | 
				
			||||||
 | 
					    this.sessionInProgress = false;
 | 
				
			||||||
    this.waitingForPlayers = true;
 | 
					    this.waitingForPlayers = true;
 | 
				
			||||||
 | 
					    this.status = 'created';
 | 
				
			||||||
    this.logger.info('Waiting for players to be ready');
 | 
					    this.logger.info('Waiting for players to be ready');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@@ -103,7 +108,7 @@ export class MatchSession {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getPlayer(userId: string) {
 | 
					  getPlayer(userId: string) {
 | 
				
			||||||
    return this.players.find(player => player.id === userId);
 | 
					    return this.players.find(player => player.id === userId) || null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  playerMove(move: any) {
 | 
					  playerMove(move: any) {
 | 
				
			||||||
@@ -122,20 +127,43 @@ export class MatchSession {
 | 
				
			|||||||
      if (!tile) {
 | 
					      if (!tile) {
 | 
				
			||||||
        throw new Error("Tile not found");
 | 
					        throw new Error("Tile not found");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      const newMove = new PlayerMove(tile, move.type, move. playerId)
 | 
					      const newMove = new PlayerMove(tile, move.type, move.playerId, move.direction)
 | 
				
			||||||
      this.currentGame.finishTurn(newMove);
 | 
					      this.currentGame.finishTurn(newMove);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // This is the entry point for the game, method called by session host
 | 
					  // This is the entry point for the game, method called by session host
 | 
				
			||||||
  async start() {
 | 
					  async start(data: any) {
 | 
				
			||||||
    if (this.matchInProgress) {
 | 
					    if (this.matchInProgress) {
 | 
				
			||||||
      throw new Error("Game already in progress");
 | 
					      throw new Error("Game already in progress");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.waitingForPlayers = false;
 | 
					    this.waitingForPlayers = false;
 | 
				
			||||||
 | 
					    this.sessionInProgress = true;
 | 
				
			||||||
 | 
					    this.status = 'started'
 | 
				
			||||||
 | 
					    this.sessionService.updateSession(this);
 | 
				
			||||||
    await this.startMatch(this.seed);
 | 
					    await this.startMatch(this.seed);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setTeams(data: any) {
 | 
				
			||||||
 | 
					    if (data.teamedWith !== undefined) {
 | 
				
			||||||
 | 
					      const creatorTeam = this.getPlayer(data.teamedWith)
 | 
				
			||||||
 | 
					      if (!creatorTeam) {
 | 
				
			||||||
 | 
					        throw new Error("Teamed player not found");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.creator.teamedWith =  data.teamedWith;
 | 
				
			||||||
 | 
					      this.creator.team = 1
 | 
				
			||||||
 | 
					      creatorTeam.teamedWith = this.creator.id;
 | 
				
			||||||
 | 
					      creatorTeam.team = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const others = this.players.filter(player => player.team === 0);
 | 
				
			||||||
 | 
					      others[0].teamedWith = others[1].id;
 | 
				
			||||||
 | 
					      others[0].team = 2;
 | 
				
			||||||
 | 
					      others[1].teamedWith = others[0].id;
 | 
				
			||||||
 | 
					      others[1].team = 2;
 | 
				
			||||||
 | 
					      this.players = [this.creator, others[0], creatorTeam, others[1]];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  addPlayerToSession(player: PlayerInterface) {
 | 
					  addPlayerToSession(player: PlayerInterface) {
 | 
				
			||||||
    if (this.numPlayers >= this.maxPlayers) {
 | 
					    if (this.numPlayers >= this.maxPlayers) {
 | 
				
			||||||
      throw new Error("GameSession is full");
 | 
					      throw new Error("GameSession is full");
 | 
				
			||||||
@@ -225,7 +253,16 @@ export class MatchSession {
 | 
				
			|||||||
  checkMatchWinner() {
 | 
					  checkMatchWinner() {
 | 
				
			||||||
    const scores = Array.from(this.scoreboard.values());
 | 
					    const scores = Array.from(this.scoreboard.values());
 | 
				
			||||||
    const maxScore = Math.max(...scores);
 | 
					    const maxScore = Math.max(...scores);
 | 
				
			||||||
    if (maxScore >= this.pointsToWin) {
 | 
					
 | 
				
			||||||
 | 
					    if (this.options.winType === 'rounds') {
 | 
				
			||||||
 | 
					      this.checkRoundsWinner(maxScore);
 | 
				
			||||||
 | 
					    } else if (this.options.winType === 'points') {
 | 
				
			||||||
 | 
					      this.checkPointsWinner(maxScore);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  checkRoundsWinner(maxScore: number) {
 | 
				
			||||||
 | 
					    if (maxScore >= this.options.winTarget) {
 | 
				
			||||||
      this.matchWinner = this.players.find(player => this.scoreboard.get(player.name) === maxScore);
 | 
					      this.matchWinner = this.players.find(player => this.scoreboard.get(player.name) === maxScore);
 | 
				
			||||||
      if (!this.matchWinner) {
 | 
					      if (!this.matchWinner) {
 | 
				
			||||||
        throw new Error('Match winner not found');
 | 
					        throw new Error('Match winner not found');
 | 
				
			||||||
@@ -234,6 +271,20 @@ export class MatchSession {
 | 
				
			|||||||
      this.matchInProgress = false;
 | 
					      this.matchInProgress = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  checkPointsWinner(maxScore: number) {
 | 
				
			||||||
 | 
					    if (maxScore >= this.options.winTarget) {
 | 
				
			||||||
 | 
					      this.matchWinner = this.players.find(player => this.scoreboard.get(player.name) === maxScore);
 | 
				
			||||||
 | 
					      if (!this.matchWinner) {
 | 
				
			||||||
 | 
					        throw new Error('Match winner not found');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.logger.info(`Match winner: ${this.matchWinner.name} with ${maxScore} points`);
 | 
				
			||||||
 | 
					      this.matchInProgress = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  resetScoreboard() {
 | 
					  resetScoreboard() {
 | 
				
			||||||
    this.scoreboard = new Map();
 | 
					    this.scoreboard = new Map();
 | 
				
			||||||
    this.players.forEach(player => {
 | 
					    this.players.forEach(player => {
 | 
				
			||||||
@@ -245,6 +296,22 @@ export class MatchSession {
 | 
				
			|||||||
    if (!gameSummary) {
 | 
					    if (!gameSummary) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.options.winType === 'rounds') {
 | 
				
			||||||
 | 
					      this.setScoresRounds(gameSummary);
 | 
				
			||||||
 | 
					    } else if (this.options.winType === 'points') {
 | 
				
			||||||
 | 
					      this.setScoresPoints(gameSummary);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }	
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setScoresRounds(gameSummary: GameSummary) {
 | 
				
			||||||
 | 
					    const { winner } = gameSummary;
 | 
				
			||||||
 | 
					    if (winner !== undefined) {
 | 
				
			||||||
 | 
					      const currentScore = this.scoreboard.get(winner.name) ?? 0;
 | 
				
			||||||
 | 
					      this.scoreboard.set(winner.name, 1 + currentScore);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setScoresPoints(gameSummary: GameSummary) {
 | 
				
			||||||
    const { score } = gameSummary;
 | 
					    const { score } = gameSummary;
 | 
				
			||||||
    score.forEach(playerScore => {
 | 
					    score.forEach(playerScore => {
 | 
				
			||||||
      const currentScore = this.scoreboard.get(playerScore.name) ?? 0;
 | 
					      const currentScore = this.scoreboard.get(playerScore.name) ?? 0;
 | 
				
			||||||
@@ -324,12 +391,12 @@ export class MatchSession {
 | 
				
			|||||||
      waitingSeconds: this.waitingSeconds,
 | 
					      waitingSeconds: this.waitingSeconds,
 | 
				
			||||||
      seed: this.seed,
 | 
					      seed: this.seed,
 | 
				
			||||||
      mode: this.mode,
 | 
					      mode: this.mode,
 | 
				
			||||||
      pointsToWin: this.pointsToWin,
 | 
					      status: this.status,
 | 
				
			||||||
      status: this.sessionInProgress ? 'in progress' : 'waiting',
 | 
					 | 
				
			||||||
      scoreboard: this.getScoreBoardState(),
 | 
					      scoreboard: this.getScoreBoardState(),
 | 
				
			||||||
      matchWinner: this.matchWinner?.getState(true) || null,
 | 
					      matchWinner: this.matchWinner?.getState(true) || null,
 | 
				
			||||||
      matchInProgress: this.matchInProgress,
 | 
					      matchInProgress: this.matchInProgress,
 | 
				
			||||||
      gameSummaries: this.gameSummaries,
 | 
					      gameSummaries: this.gameSummaries,
 | 
				
			||||||
 | 
					      options: this.options,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								src/game/dto/MatchSessionOptions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/game/dto/MatchSessionOptions.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					export interface MatchSessionOptions {
 | 
				
			||||||
 | 
					  boardScale?: number
 | 
				
			||||||
 | 
					  handScale?: number
 | 
				
			||||||
 | 
					  width?: number
 | 
				
			||||||
 | 
					  height?: number
 | 
				
			||||||
 | 
					  background: string
 | 
				
			||||||
 | 
					  teamed: boolean
 | 
				
			||||||
 | 
					  winTarget: number
 | 
				
			||||||
 | 
					  winType: 'points' | 'rounds'
 | 
				
			||||||
 | 
					  seed: string
 | 
				
			||||||
 | 
					  sessionName: string
 | 
				
			||||||
 | 
					  numPlayers: 1 | 2 | 3 | 4
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import { Score } from "../../server/db/interfaces";
 | 
					import { Score } from "../../server/db/interfaces";
 | 
				
			||||||
import { GameSummary } from "./GameSummary";
 | 
					import { GameSummary } from "./GameSummary";
 | 
				
			||||||
 | 
					import { MatchSessionOptions } from "./MatchSessionOptions";
 | 
				
			||||||
import { PlayerDto } from "./PlayerDto";
 | 
					import { PlayerDto } from "./PlayerDto";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface MatchSessionState {
 | 
					export interface MatchSessionState {
 | 
				
			||||||
@@ -10,7 +11,6 @@ export interface MatchSessionState {
 | 
				
			|||||||
  seed: string;
 | 
					  seed: string;
 | 
				
			||||||
  waitingForPlayers: boolean;
 | 
					  waitingForPlayers: boolean;
 | 
				
			||||||
  mode: string;
 | 
					  mode: string;
 | 
				
			||||||
  pointsToWin: number;
 | 
					 | 
				
			||||||
  sessionInProgress: boolean;
 | 
					  sessionInProgress: boolean;
 | 
				
			||||||
  status: string;
 | 
					  status: string;
 | 
				
			||||||
  maxPlayers: number;
 | 
					  maxPlayers: number;
 | 
				
			||||||
@@ -21,4 +21,5 @@ export interface MatchSessionState {
 | 
				
			|||||||
  matchInProgress: boolean;
 | 
					  matchInProgress: boolean;
 | 
				
			||||||
  playersReady: number,
 | 
					  playersReady: number,
 | 
				
			||||||
  gameSummaries: GameSummary[];
 | 
					  gameSummaries: GameSummary[];
 | 
				
			||||||
 | 
					  options: MatchSessionOptions
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -11,7 +11,7 @@ export interface PlayerDto {
 | 
				
			|||||||
  name: string;
 | 
					  name: string;
 | 
				
			||||||
  score?: number;
 | 
					  score?: number;
 | 
				
			||||||
  hand?: TileDto[];
 | 
					  hand?: TileDto[];
 | 
				
			||||||
  teamedWith?: PlayerDto | null;
 | 
					  teamedWith?: string;
 | 
				
			||||||
  ready: boolean;
 | 
					  ready: boolean;
 | 
				
			||||||
  isHuman: boolean;
 | 
					  isHuman: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -4,7 +4,7 @@ import { Tile } from "./Tile";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export class PlayerMove {
 | 
					export class PlayerMove {
 | 
				
			||||||
  id: string = uuid();
 | 
					  id: string = uuid();
 | 
				
			||||||
  constructor(public tile: Tile, public type: PlayerMoveSideType | null, public playerId: string, direction?: string) {}
 | 
					  constructor(public tile: Tile, public type: PlayerMoveSideType | null, public playerId: string, public direction?: string) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  toString() {
 | 
					  toString() {
 | 
				
			||||||
    return `PlayerMove:([${this.tile.pips[0]}|${this.tile.pips[1]}] ${this.type})`;
 | 
					    return `PlayerMove:([${this.tile.pips[0]}|${this.tile.pips[1]}] ${this.type})`;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,11 @@ export abstract class AbstractPlayer extends EventEmitter implements PlayerInter
 | 
				
			|||||||
  hand: Tile[] = [];
 | 
					  hand: Tile[] = [];
 | 
				
			||||||
  score: number = 0;
 | 
					  score: number = 0;
 | 
				
			||||||
  logger: LoggingService = new LoggingService();
 | 
					  logger: LoggingService = new LoggingService();
 | 
				
			||||||
  teamedWith: PlayerInterface | null = null;
 | 
					  teamedWith?: string;
 | 
				
			||||||
  playerInteraction: PlayerInteractionInterface = undefined as any;
 | 
					  playerInteraction: PlayerInteractionInterface = undefined as any;
 | 
				
			||||||
  id: string = uuid();
 | 
					  id: string = uuid();
 | 
				
			||||||
  ready: boolean = false;
 | 
					  ready: boolean = false;
 | 
				
			||||||
 | 
					  team: number = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(public name: string) {
 | 
					  constructor(public name: string) {
 | 
				
			||||||
    super();
 | 
					    super();
 | 
				
			||||||
@@ -58,7 +59,7 @@ export abstract class AbstractPlayer extends EventEmitter implements PlayerInter
 | 
				
			|||||||
      name: this.name,
 | 
					      name: this.name,
 | 
				
			||||||
      score: this.score,
 | 
					      score: this.score,
 | 
				
			||||||
      hand: this.hand.map(tile => tile.getState(showPips)),
 | 
					      hand: this.hand.map(tile => tile.getState(showPips)),
 | 
				
			||||||
      teamedWith: this.teamedWith?.getState(showPips) ?? null,
 | 
					      teamedWith: this.teamedWith,
 | 
				
			||||||
      ready: this.ready,
 | 
					      ready: this.ready,
 | 
				
			||||||
      isHuman: this instanceof PlayerHuman
 | 
					      isHuman: this instanceof PlayerHuman
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,8 @@ export interface PlayerInterface {
 | 
				
			|||||||
  name: string;
 | 
					  name: string;
 | 
				
			||||||
  score: number;
 | 
					  score: number;
 | 
				
			||||||
  hand: Tile[];
 | 
					  hand: Tile[];
 | 
				
			||||||
  teamedWith: PlayerInterface | null;
 | 
					  teamedWith?: string;
 | 
				
			||||||
 | 
					  team: number;
 | 
				
			||||||
  playerInteraction: PlayerInteractionInterface;
 | 
					  playerInteraction: PlayerInteractionInterface;
 | 
				
			||||||
  ready: boolean;
 | 
					  ready: boolean;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,8 @@ export class GameController extends BaseController {
 | 
				
			|||||||
  public async createMatch(req: Request, res: Response) {
 | 
					  public async createMatch(req: Request, res: Response) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const { user, body } = req;
 | 
					      const { user, body } = req;
 | 
				
			||||||
      const { sessionName, seed, options } = body;
 | 
					      const { options } = body;
 | 
				
			||||||
      const sessionId = await this.sessionService.createSession(user, sessionName, seed, options);
 | 
					      const sessionId = await this.sessionService.createSession(user, options);
 | 
				
			||||||
      res.status(201).json({ sessionId });
 | 
					      res.status(201).json({ sessionId });
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      this.handleError(res, error);
 | 
					      this.handleError(res, error);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ export function matchSessionAdapter(session: MatchSession, showPips: boolean = f
 | 
				
			|||||||
        players: state.players,
 | 
					        players: state.players,
 | 
				
			||||||
        seed: state.seed,
 | 
					        seed: state.seed,
 | 
				
			||||||
        mode: state.mode,
 | 
					        mode: state.mode,
 | 
				
			||||||
        pointsToWin: state.pointsToWin,
 | 
					        options: state.options,
 | 
				
			||||||
        maxPlayers: state.maxPlayers,
 | 
					        maxPlayers: state.maxPlayers,
 | 
				
			||||||
        numPlayers: state.numPlayers,
 | 
					        numPlayers: state.numPlayers,
 | 
				
			||||||
        scoreboard: state.scoreboard,
 | 
					        scoreboard: state.scoreboard,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { GameSummary } from "../../game/dto/GameSummary";
 | 
					import { GameSummary } from "../../game/dto/GameSummary";
 | 
				
			||||||
 | 
					import { MatchSessionOptions } from "../../game/dto/MatchSessionOptions";
 | 
				
			||||||
import { PlayerDto } from "../../game/dto/PlayerDto";
 | 
					import { PlayerDto } from "../../game/dto/PlayerDto";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Entity {
 | 
					export interface Entity {
 | 
				
			||||||
@@ -52,7 +53,7 @@ export interface DbMatchSession extends EntityMongo {
 | 
				
			|||||||
  players: PlayerDto[];
 | 
					  players: PlayerDto[];
 | 
				
			||||||
  seed: string;
 | 
					  seed: string;
 | 
				
			||||||
  mode: string;
 | 
					  mode: string;
 | 
				
			||||||
  pointsToWin: number;
 | 
					  options: MatchSessionOptions;
 | 
				
			||||||
  maxPlayers: number;
 | 
					  maxPlayers: number;
 | 
				
			||||||
  numPlayers: number;
 | 
					  numPlayers: number;
 | 
				
			||||||
  scoreboard: Score[];
 | 
					  scoreboard: Score[];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,10 +64,11 @@ export class InteractionService extends ServiceBase{
 | 
				
			|||||||
      for (let i = 0; i < missingHumans; i++) {
 | 
					      for (let i = 0; i < missingHumans; i++) {
 | 
				
			||||||
        session.addPlayerToSession(session.createPlayerAI(i));
 | 
					        session.addPlayerToSession(session.createPlayerAI(i));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      session.setTeams(data);
 | 
				
			||||||
      this.notifyService.sendEventToPlayers('server:match-starting', session.players, { 
 | 
					      this.notifyService.sendEventToPlayers('server:match-starting', session.players, { 
 | 
				
			||||||
        sessionState: session.getState()
 | 
					        sessionState: session.getState()
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      session.start();      
 | 
					      session.start(data);
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        status: 'ok'
 | 
					        status: 'ok'
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import { MatchSessionMongoManager } from "../db/mongo/MatchSessionMongoManager";
 | 
				
			|||||||
import { SessionManager } from "../managers/SessionManager";
 | 
					import { SessionManager } from "../managers/SessionManager";
 | 
				
			||||||
import { ServiceBase } from "./ServiceBase";
 | 
					import { ServiceBase } from "./ServiceBase";
 | 
				
			||||||
import { SocketIoService } from "./SocketIoService";
 | 
					import { SocketIoService } from "./SocketIoService";
 | 
				
			||||||
 | 
					import { MatchSessionOptions } from "../../game/dto/MatchSessionOptions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SessionService extends ServiceBase{
 | 
					export class SessionService extends ServiceBase{
 | 
				
			||||||
  private dbManager: MatchSessionMongoManager = new MatchSessionMongoManager();
 | 
					  private dbManager: MatchSessionMongoManager = new MatchSessionMongoManager();
 | 
				
			||||||
@@ -19,7 +20,7 @@ export class SessionService extends ServiceBase{
 | 
				
			|||||||
    super()
 | 
					    super()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  public async createSession(user: any, sessionName: string, seed: string, options: any ): Promise<string> {
 | 
					  public async createSession(user: any, options: MatchSessionOptions ): Promise<string> {
 | 
				
			||||||
    let socketClient;
 | 
					    let socketClient;
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      socketClient = await whileNotUndefined(() => SocketIoService.getClient(user._id));
 | 
					      socketClient = await whileNotUndefined(() => SocketIoService.getClient(user._id));
 | 
				
			||||||
@@ -27,8 +28,7 @@ export class SessionService extends ServiceBase{
 | 
				
			|||||||
      throw new SessionCreationError();
 | 
					      throw new SessionCreationError();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const player =  new NetworkPlayer(user._id, user.username, socketClient.socketId);
 | 
					    const player =  new NetworkPlayer(user._id, user.username, socketClient.socketId);
 | 
				
			||||||
    const session = new MatchSession(player, sessionName, seed);
 | 
					    const session = new MatchSession(player, options);
 | 
				
			||||||
    session.pointsToWin = options.pointsToWin;
 | 
					 | 
				
			||||||
    const dbSessionId = await this.dbManager.create(matchSessionAdapter(session));
 | 
					    const dbSessionId = await this.dbManager.create(matchSessionAdapter(session));
 | 
				
			||||||
    if (dbSessionId === undefined) {
 | 
					    if (dbSessionId === undefined) {
 | 
				
			||||||
      throw new SessionCreationError();
 | 
					      throw new SessionCreationError();
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user