From bbf7d3ad025982e89b2d177efce2d8f7c7398709 Mon Sep 17 00:00:00 2001 From: Alex <atm2g19@soton.ac.uk> Date: Sat, 4 Apr 2020 20:29:42 +0100 Subject: [PATCH] Battleships v1 functional --- .gitignore | 6 +- package-lock.json | 132 +++++++- package.json | 3 + public/games/battleships/battleships.js | 73 ----- public/games/battleships/game.html | 22 ++ public/games/battleships/game.js | 397 ++++++++++++++++++++++++ public/games/battleships/index.html | 3 +- public/games/battleships_v2/game.html | 22 ++ public/games/battleships_v2/game.js | 52 ++++ public/games/battleships_v2/index.html | 17 + public/games/cah/index.html | 17 + public/games/game_loader.js | 97 ++++++ public/games/index.html | 5 + public/games/template/index.html | 17 + src/error.ts | 3 +- src/gamedb.ts | 39 ++- src/games.ts | 91 ++++-- src/games/battleships.ts | 318 ++++++++++++++++++- src/games/game.ts | 3 + 19 files changed, 1209 insertions(+), 108 deletions(-) delete mode 100644 public/games/battleships/battleships.js create mode 100644 public/games/battleships/game.html create mode 100644 public/games/battleships/game.js create mode 100644 public/games/battleships_v2/game.html create mode 100644 public/games/battleships_v2/game.js create mode 100644 public/games/battleships_v2/index.html create mode 100644 public/games/cah/index.html create mode 100644 public/games/game_loader.js create mode 100644 public/games/template/index.html diff --git a/.gitignore b/.gitignore index 6efbb4f..53c770f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ dbconf.json dst/ node_modules/ +launch.json log/ -p5* \ No newline at end of file +**/*.d.ts +**/*.zip +public/**/*.ts +public/**/tsconfig.json \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0026219..77acf64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,6 +71,14 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==" }, + "@types/multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-pVcPwuC0FbVcLhopJHx8Ro3WSXjvVvEpJMfy+DFAL/3DwNYAQH+hf/Vq+PqoS5kM4mng7L/4upzXhP/12yWh4w==", + "requires": { + "@types/express": "4.17.3" + } + }, "@types/mysql": { "version": "2.15.9", "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.9.tgz", @@ -128,6 +136,11 @@ "color-convert": "1.9.3" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -245,12 +258,49 @@ "concat-map": "0.0.1" } }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "optional": true }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.14" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -325,6 +375,17 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "1.1.1", + "inherits": "2.0.4", + "readable-stream": "2.3.7", + "typedarray": "0.0.6" + } + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -400,6 +461,38 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.14", + "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -890,8 +983,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "optional": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "minimist-options": { "version": "3.0.2", @@ -906,7 +998,6 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", - "optional": true, "requires": { "minimist": "1.2.5" } @@ -916,6 +1007,21 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "requires": { + "append-field": "1.0.0", + "busboy": "0.2.14", + "concat-stream": "1.6.2", + "mkdirp": "0.5.4", + "object-assign": "4.1.1", + "on-finished": "2.3.0", + "type-is": "1.6.18", + "xtend": "4.0.2" + } + }, "mysql": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", @@ -943,6 +1049,11 @@ "validate-npm-package-license": "3.0.4" } }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, "object-component": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", @@ -1403,6 +1514,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -1502,6 +1618,11 @@ "mime-types": "2.1.26" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "typescript": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", @@ -1567,6 +1688,11 @@ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "yargs-parser": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", diff --git a/package.json b/package.json index d00c554..a8c60b4 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,13 @@ "license": "ISC", "dependencies": { "@types/express": "^4.17.3", + "@types/multer": "^1.4.2", "@types/mysql": "^2.15.9", "@types/node": "^13.9.8", "@types/socket.io": "^2.1.4", + "body-parser": "^1.19.0", "express": "^4.17.1", + "multer": "^1.4.2", "mysql": "^2.18.1", "path": "^0.12.7", "socket.io": "^2.3.0", diff --git a/public/games/battleships/battleships.js b/public/games/battleships/battleships.js deleted file mode 100644 index 3e2a470..0000000 --- a/public/games/battleships/battleships.js +++ /dev/null @@ -1,73 +0,0 @@ -/// <reference path="../../../p5.global-mode.d.ts"/> - - -let socket; -let GamesList; - -function setup(){ - noCanvas(); - socket = io.connect("http://localhost:8080"); - - let newGame = createButton("New Game"); - newGame.mouseClicked(createGame); - let refresh = createButton("Refresh"); - refresh.mouseClicked(refreshList); - - - - GamesList = createDiv(); - createP("Current running games: ").parent(GamesList); - GamesList = createElement("ul").parent(GamesList); - - refreshList(); - -} - -function refreshList(){ - - let children = GamesList.child(); - while(children.length > 0){ - children[0].remove(); - } - - fetch("/games/api/list/battleships") - .then(response => { - console.log(response); - return response.json(); - }) - .then(json => { - console.log(json); - - - for(let game of json){ - createElement("li", `${JSON.stringify(game)}`).parent(GamesList); - } - }) - .catch(err =>{ - console.log(err); - }); -} - -function createGame(){ - - let name = prompt("Please enter game name", "name"); - let password = prompt("Enter password, \nor leave blank for open game."); - let data = { - name : name, - password : password, - }; - - fetch("/games/api/create/battleships", { - method : "POST", - body : JSON.stringify(data), - headers : { - 'Content-Type' : 'application/json' - } - }).then(() => { - refreshList(); - }) -} - -function draw(){ - -} \ No newline at end of file diff --git a/public/games/battleships/game.html b/public/games/battleships/game.html new file mode 100644 index 0000000..2dac238 --- /dev/null +++ b/public/games/battleships/game.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> + <head> + <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script> + <script src="https://cdn.jsdelivr.net/npm/p5@1.0.0/lib/p5.min.js"></script> + <script src="./game.js"></script> + </head> + <body> + <a href="./index.html"><_Back to games</a><br> + <div id="game"> + <div id="me" style="float: left; padding: 10px;"> + + </div> + + <div id="opponent" style="float: left;padding: 10px"> + + </div> + + </div> + + </body> +</html> \ No newline at end of file diff --git a/public/games/battleships/game.js b/public/games/battleships/game.js new file mode 100644 index 0000000..1f8a8be --- /dev/null +++ b/public/games/battleships/game.js @@ -0,0 +1,397 @@ +/// <reference path="../../../socket.io.d.ts"/> +/// <reference path="../../../p5.global-mode.d.ts"/> + +let socket; +let gameID; +let username; +let game_password; +let cookies; + +let canvas, battlecanvas; +let canvasContainer; + +let ships = [{ + x : 0, + y : 0, + rotation : "north", + size : 5, + }, + { + x : 0, + y : 0, + rotation : "north", + size : 4, + }, + { + x : 0, + y : 0, + rotation : "north", + size : 3, + }, + { + x : 0, + y : 0, + rotation : "north", + size : 3, + }, + { + x : 0, + y : 0, + rotation : "north", + size : 2, + } +]; +const me = (sketch) => { + sketch.ships = ships; + sketch.placing_ship; + sketch.placed_ships = []; + sketch.shots = []; + + sketch.setup = _setup.bind(sketch); + sketch.draw = _draw.bind(sketch); + sketch.drawGrid = _drawGrid.bind(sketch); + sketch.drawShip = _drawShip.bind(sketch); + sketch.checkCollisions = _checkCollisions.bind(sketch); + sketch.keyPressed = function (){ + if(this.keyCode == 0x20){ + this.placeShip(); + }else if(this.keyCode == 0x52){ + rotateShip(this.placing_ship); + } + }.bind(sketch); + sketch.placeShip = _placeShip.bind(sketch); + sketch.drawShot = _drawShot.bind(sketch); +} + +const opponent = (sketch) => { + sketch.placed_ships = []; + sketch.shots = []; + + sketch.setup = _battle_setup.bind(sketch); + sketch.draw = _draw.bind(sketch); + sketch.drawGrid = _drawGrid.bind(sketch); + sketch.drawShip = _drawShip.bind(sketch); + sketch.drawShot = _drawShot.bind(sketch); + // sketch.checkCollisions = _checkCollisions.bind(sketch); + sketch.fire = _fire.bind(sketch); + sketch.keyPressed = function(){ + if(this.keyCode == 0x20){ + this.fire(); + } + }.bind(sketch); + +} + +new p5(me, "me"); + + +function _setup(){ + cookies = document.cookie + .split(';') + .reduce((res, c) => { + const [key, val] = c.trim().split('=').map(decodeURIComponent) + try { + return Object.assign(res, { [key]: JSON.parse(val) }) + } catch (e) { + return Object.assign(res, { [key]: val }) + } + }, {}); + + username = cookies.username; + game_password = cookies.game_password; + + socket = io(); + let params = this.getURLParams(); + console.log(params); + if(!("id" in params)){ + history.back(); + return; + }else{ + gameID = params.id; + } + + let join_data = { + username : username, + password : game_password, + id : gameID, + } + socket.on("disconnect", console.log); + socket.emit('join', join_data); + socket.on("welcome", console.log); + socket.on("place", (data) => { + this.placed_ships = data; + this.ships = []; + this.placing_ship = null; + }); + socket.on("battle", ()=>{ + new p5(opponent, "opponent"); + }); + socket.on("shot", shot => { + console.log(shot); + this.shots.push(shot); + }); + + socket.onpacket(console.log); + let exit = ()=>{ + socket.emit("exit"); + }; + let isIOS = navigator.userAgent.match(/iPad/i)|| navigator.userAgent.match(/iPhone/i); + let evtName = isIOS? "pagehide" : "beforeunload"; + window.addEventListener(evtName, exit); + window.onbeforeunload = exit; + window.onunload = exit; + + this.createCanvas(500, 500); + this.createElement("br"); + let place = this.createButton("Place"); + let rotate = this.createButton("Rotate"); + place.mouseClicked(this.placeShip); + place.size(100, 30); + rotate.mouseClicked(()=>rotateShip(this.placing_ship)); + rotate.size(100, 30); + this.createElement("br"); + + document.oncontextmenu = () => false; + + + this.placing_ship = this.ships.splice(0, 1)[0]; + +} + +function _battle_setup(){ + + this.createCanvas(500, 500); + this.createElement("br"); + let fire = this.createButton("Fire!"); + fire.mouseClicked(this.fire); + fire.size(100, 30); + this.firing = { + x : 2, + y : 2, + hit : false, + }; + + socket.on("fire", ()=>{ + this.firing = { + x : 0, y : 0, hit : false + } + }); + socket.on("sunk", ship => { + this.placed_ships.push(ship); + }) + + socket.on("fired", (shot) => { + console.log(shot); + this.shots.pop(); + if(shot){ + if(shot.hit){ + console.log("hit"); + }else{ + console.log("miss"); + } + this.shots.push(shot); + } + }) + + // this.shots.push() + +} + + +function _draw(){ + let w = this.width/10; + let h = this.height/10; + + this.background(100); + this.fill(0, 255, 0); + for(let ship of this.placed_ships){ + this.drawShip(ship); + } + this.fill(255); + if(this.placing_ship){ + if(this.checkCollisions(this.placing_ship))this.fill(255, 0, 0); + if(this.mouseX < this.width && this.mouseY < this.height){ + this.placing_ship.x = this.floor(this.mouseX/w); + this.placing_ship.y = this.floor(this.mouseY/h); + } + this.placing_ship.hit = false; + this.drawShip(this.placing_ship); + } + for(let shot of this.shots){ + this.stroke(255); + this.drawShot(shot); + } + this.stroke(0, 255, 0); + if(this.firing){ + if(this.mouseX < this.width && this.mouseY < this.height){ + this.firing.x = this.floor(this.mouseX/w); + this.firing.y = this.floor(this.mouseY/h); + } + this.firing.hit = false; + this.drawShot(this.firing) + } + + this.drawGrid(10, 10); + +} + +function _drawGrid(x = 10, y = 10){ + this.stroke(0); + this.strokeWeight(5); + for(let i = 0; i <= x; i++){ + this.line(i*this.width/x, 0, i*this.width/x, this.height); + } + + for(let j = 0; j <= y; j++){ + this.line(0, j*this.height/y, this.width, j*this.height/y); + } + +} + +function _drawShip(ship, x = 10, y = 10){ + let xCentreStart = (ship.x+0.5)*this.width/x; + let yCentreStart = (ship.y+0.5)*this.height/y; + let xCentreEnd, yCentreEnd; + xCentreEnd = xCentreStart; + yCentreEnd = yCentreStart; + let w = this.width/x; + let h = this.height/y; + switch(ship.rotation){ + case "north": + yCentreEnd -= (ship.size-1)*h; + break; + case "east": + xCentreEnd += (ship.size-1)*w; + break; + case "south": + yCentreEnd += (ship.size-1)*h; + break; + case "west": + xCentreEnd -= (ship.size-1)*w; + break; + } + // fill(255); + this.noStroke(); + this.ellipse(xCentreStart, yCentreStart, this.width/x-5); + this.ellipse(xCentreEnd, yCentreEnd, this.width/x-5); + this.rectMode(this.CENTER); + this.rect((xCentreStart+xCentreEnd)/2, (yCentreStart+yCentreEnd)/2, this.abs(xCentreEnd-xCentreStart) + (ship.rotation=="north" || ship.rotation=="south" ? w : 0), this.abs(yCentreEnd-yCentreStart) + (ship.rotation=="east" || ship.rotation=="west" ? w : 0)); + + +} + +function _drawShot(shot, x = 10, y = 10){ + let w = this.width/x; + let h = this.height/y; + let xCentre = (shot.x+0.5)*w; + let yCentre = (shot.y+0.5)*h; + + this.strokeWeight(5); + if(shot.hit == true){ + this.stroke(255, 0, 0); + } + + this.line(xCentre-w/2, yCentre-h/2, xCentre+w/2, yCentre+h/2); + this.line(xCentre+w/2, yCentre-h/2, xCentre-w/2, yCentre+h/2); + + +} + +function _checkCollisions(ship){ + let inx = 0, iny = 0; + switch(ship.rotation){ + case "north": + iny = -1; + break; + case "east": + inx = +1; + break; + case "south": + iny = +1; + break; + case "west": + inx = -1; + break; + } + for(let i = 0; i < ship.size; i++){ + let shipX = ship.x + inx*i; + let shipY = ship.y + iny*i; + if(shipX < 0 || shipX >= 10) return true; + if(shipY < 0 || shipY >= 10) return true; + for(let other_ship of this.placed_ships){ + let oinx = 0, oiny = 0; + switch(other_ship.rotation){ + case "north": + oiny = -1; + break; + case "east": + oinx = +1; + break; + case "south": + oiny = +1; + break; + case "west": + oinx = -1; + break; + } + for(let j = 0; j < other_ship.size; j++){ + let oX = other_ship.x + oinx*j; + let oY = other_ship.y + oiny*j; + if(oX == shipX && oY == shipY) return true; + } + } + } + + return false; + +} + +function _placeShip(){ + if(this.placing_ship == null || this.checkCollisions(this.placing_ship) ) return; + this.placed_ships.push(Object.assign({}, this.placing_ship)); + this.placing_ship = this.ships.splice(0, 1)[0]; + if(this.placing_ship == null){ + socket.emit("place", this.placed_ships); + console.log("All ships placed!"); + } +} + +function rotateShip(ship){ + if(!ship)return; + switch(ship.rotation){ + case "north": + ship.rotation = "east"; + break; + case "east": + ship.rotation = "south"; + break; + case "south": + ship.rotation = "west"; + break; + case "west": + ship.rotation = "north"; + break; + } +} + +function _fire(){ + if(!this.firing) return; + this.shots.push(this.firing); + socket.emit("fire", this.firing); + this.firing = null; +} + +function mousePressed(e){ + return; + if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { + // some code.. + return; + } + e.preventDefault(); + if(mouseButton == RIGHT){ + rotateShip(); + }else{ + _placeShip(); + } + console.log("mouse pressed"); +} diff --git a/public/games/battleships/index.html b/public/games/battleships/index.html index 1a1d18f..2c907ae 100644 --- a/public/games/battleships/index.html +++ b/public/games/battleships/index.html @@ -3,12 +3,13 @@ <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.0.0/lib/p5.min.js"></script> - <script src="./battleships.js"></script> + <script src="../game_loader.js"></script> </head> <body> <h1> Battleships game </h1> + <a href="../"><- Back to Games!</a><br><br> <!-- <p> Current running games: <br> </!--> diff --git a/public/games/battleships_v2/game.html b/public/games/battleships_v2/game.html new file mode 100644 index 0000000..df5c601 --- /dev/null +++ b/public/games/battleships_v2/game.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> + <head> + <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script> + <script src="https://cdn.jsdelivr.net/npm/p5@1.0.0/lib/p5.min.js"></script> + <script src="./game.js"></script> + </head> + <body> + <a href="./index.html"><_Back to games</a><br> + <div id="game"> + <div id="defend" style="float: left; padding: 10px;"> + + </div> + + <div id="attack" style="float: left;padding: 10px"> + + </div> + + </div> + + </body> +</html> \ No newline at end of file diff --git a/public/games/battleships_v2/game.js b/public/games/battleships_v2/game.js new file mode 100644 index 0000000..817505e --- /dev/null +++ b/public/games/battleships_v2/game.js @@ -0,0 +1,52 @@ +/// <reference path="../../../p5.global-mode.d.ts" /> +let builder = (sketch) => { return (p) => { for (let key in sketch) + if (typeof (sketch[key]) == "function") + p[key] = sketch[key].bind(p); + else + p[key] = sketch[key]; }; }; +var rotation; +(function (rotation) { + rotation["NORTH"] = "north"; + rotation["EAST"] = "east"; + rotation["SOUTH"] = "south"; + rotation["WEST"] = "west"; +})(rotation || (rotation = {})); +const base = { + draw: function () { + this.background(100); + //draw ships + //draw shots + //draw grid + this.drawGrid(); + }, + drawGrid: function () { + this.strokeWeight(5); + this.stroke(0); + for (let i = 0; i <= this.x; i++) { + this.line(i * this.width / this.x, 0, i * this.width / this.x, this.height); + } + for (let i = 0; i <= this.y; i++) { + this.line(0, i * this.height / this.y, this.width, i * this.height / this.y); + } + }, + drawShip: function (ship) { + } +}; +const defend = { + ...base, + x: 10, + y: 10, + setup: function () { + this.createCanvas(500, 500); + }, +}; +const attack = { + ...base, + x: 10, + y: 10, + setup: function () { + this.createCanvas(500, 500); + } +}; +const defender = new p5(builder(defend), "defend"); +const attacker = new p5(builder(attack), "attack"); diff --git a/public/games/battleships_v2/index.html b/public/games/battleships_v2/index.html new file mode 100644 index 0000000..d99bf12 --- /dev/null +++ b/public/games/battleships_v2/index.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script> + <script src="https://cdn.jsdelivr.net/npm/p5@1.0.0/lib/p5.min.js"></script> + <script src="../game_loader.js"></script> + </head> + <body> + <h1> + Battleships 2.0 game + </h1> + <a href="../"><- Back to Games!</a><br><br> + <!-- <p> + Current running games: <br> + </!--> + </body> +</html> \ No newline at end of file diff --git a/public/games/cah/index.html b/public/games/cah/index.html new file mode 100644 index 0000000..acecd4f --- /dev/null +++ b/public/games/cah/index.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script> + <script src="https://cdn.jsdelivr.net/npm/p5@1.0.0/lib/p5.min.js"></script> + <script src="../game_loader.js"></script> + </head> + <body> + <h1> + Cards against humanity Game! + </h1> + <a href="../"><- Back to Games!</a><br><br> + <!-- <p> + Current running games: <br> + </!--> + </body> +</html> \ No newline at end of file diff --git a/public/games/game_loader.js b/public/games/game_loader.js new file mode 100644 index 0000000..9b6ed84 --- /dev/null +++ b/public/games/game_loader.js @@ -0,0 +1,97 @@ +/// <reference path="../../p5.global-mode.d.ts"/> + +let GamesList; +let game; + +function setup(){ + noCanvas(); + + let newGame = createButton("New Game"); + newGame.mouseClicked(createGame); + let refresh = createButton("Refresh"); + refresh.mouseClicked(refreshList); + + + let path = getURLPath(); + game = path[path.lastIndexOf("games")+1]; + + + GamesList = createDiv(); + createP("Current running games: ").parent(GamesList); + GamesList = createElement("ul").parent(GamesList); + + refreshList(); + +} + +function refreshList(){ + + let path = getURLPath(); + let game = path[path.lastIndexOf("games")+1]; + + fetch(`/games/api/list/${game}`) + .then(response => { + // console.log(response); + return response.json(); + }) + .then(json => { + // console.log(json); + let children = GamesList.child(); + while(children.length > 0) children[0].remove(); + + for(let _game of json){ + let child = createElement("li"); + child.parent(GamesList); + child.html(JSON.stringify(_game)); + let join_button = createButton("Join"); + join_button.parent(child); + join_button.mouseClicked(joinGame.bind(_game)); + // child.html(`<a href='/games/${game}/game.html?id=${_game.id}'>Join</a>`, true); + // createElement("li", `${JSON.stringify(game)}`).parent(GamesList); + } + }) + .catch(err =>{ + console.log(err); + }); +} + +function joinGame(){ + + if(document.cookie.indexOf("username") == -1){ + let uname = prompt("You need to choose a username first!", "username"); + document.cookie = `username=${uname}`; + return; + } + + let password = ""; + if(this.password_protected){ + password = prompt("Please enter the password for this game."); + } + document.cookie = `game_password=${password}`; + location.assign(`/games/${game}/game.html?id=${this.id}`); + +} + +function createGame(){ + + let name = prompt("Please enter game name", "name"); + let password = prompt("Enter password, \nor leave blank for open game."); + let data = { + name : name, + password : password, + }; + + fetch(`/games/api/create/${game}`, { + method : "POST", + body : JSON.stringify(data), + headers : { + 'Content-Type' : 'application/json' + } + }).then(() => { + refreshList(); + }) +} + +function draw(){ + +} \ No newline at end of file diff --git a/public/games/index.html b/public/games/index.html index dfc1cfc..4592763 100644 --- a/public/games/index.html +++ b/public/games/index.html @@ -19,6 +19,11 @@ Battleships </a> </li> + <li> + <a href="./battleships_v2//"> + Battleships v2 + </a> + </li> </p> diff --git a/public/games/template/index.html b/public/games/template/index.html new file mode 100644 index 0000000..ec95983 --- /dev/null +++ b/public/games/template/index.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script> + <script src="https://cdn.jsdelivr.net/npm/p5@1.0.0/lib/p5.min.js"></script> + <script src="../game_loader.js"></script> + </head> + <body> + <h1> + #{TEMPLATE} Game! + </h1> + <a href="../"><- Back to Games!</a><br><br> + <!-- <p> + Current running games: <br> + </!--> + </body> +</html> \ No newline at end of file diff --git a/src/error.ts b/src/error.ts index f2060ca..3871b61 100644 --- a/src/error.ts +++ b/src/error.ts @@ -36,7 +36,8 @@ function handled_error(err : express.Errback, req : express.Request, res : expre res.status(500); sendFile(`public/errors/500.html`, req, res, next); - + console.log("SERVER ERROR") + console.log(err); // next(); } diff --git a/src/gamedb.ts b/src/gamedb.ts index 6cf5fc0..0182611 100644 --- a/src/gamedb.ts +++ b/src/gamedb.ts @@ -4,6 +4,18 @@ interface DB { }; +interface game_data { + name : string, + players : string[], + [key : string] : any, +} + +interface game { + id : number, + type_id : number, + data : game_data, +} + export interface QueryResults { err? : mysql.MysqlError; results? : any; @@ -19,12 +31,37 @@ export class GameDB{ }); } - query(sql : string, values? : any) : Promise<QueryResults>{ + async query(sql : string, values? : any) : Promise<QueryResults>{ return new Promise((resolve, reject) => { this.pool.query(sql, values, (err : mysql.MysqlError, results : any[], fields : mysql.FieldInfo[]) => { + if(err) reject(err); resolve({err, results, fields} as QueryResults); }) }) } + + async getActiveGames(game : string | number) : Promise<game[]>{ + + let query : string + if(typeof(game) == 'number' || /^[0-9]+$/.test(game)){ + query = `SELECT games.game_id, games.game_name FROM games WHERE games.game_id = ${game};`; + }else{ + query = `SELECT games.game_id, games.game_name FROM games WHERE games.game_name = ${mysql.escape(game)};`; + } + let game_id = (await this.query(query)).results[0].game_id; + let query_results = await this.query(`SELECT active_game_id, active_game_type_id, game_id, data_value FROM active_games, game_data WHERE game_id = active_game_id AND active_game_type_id = ${game_id} AND data_key = 0;`); + let games : game[] = []; + for(let row of query_results.results){ + let game : game = { + id : row.game_id, + type_id : row.active_game_type_id, + data : JSON.parse(row.data_value), + } + games.push(game); + } + + return games; + + } } \ No newline at end of file diff --git a/src/games.ts b/src/games.ts index a33261a..cdb9860 100644 --- a/src/games.ts +++ b/src/games.ts @@ -60,7 +60,8 @@ let socket; function joinGame(socket : io.Socket, args : any){ - console.log(`Join game request : ${args}`); + console.log(`Join game request : `); + console.log(args); //check game id exists @@ -70,15 +71,21 @@ function joinGame(socket : io.Socket, args : any){ console.log(res.err); return; } - console.log(res.results); + // console.log(res.results); if(res.results.length == 0){ + socket.emit('disconnect', "Game does not exist"); + console.log("disconnecting socket"); socket.disconnect(true); + return; } - }); + return getGame(res.results[0].active_game_type_id); + }).then(game => { + return game.join(socket, args); + }).catch(()=>{}); } -function API(req : express.Request, res : express.Response, next : express.NextFunction){ - Games.query("delete from active_games where active_game_idle < (now()- INTERVAL 30 MINUTE) and active_game_id > 0;"); +async function API(req : express.Request, res : express.Response, next : express.NextFunction){ + await Games.query("delete from active_games where active_game_idle < (now()- INTERVAL 30 MINUTE) and active_game_id > 0;"); if(!/^\/games\/api\/.+/.test(req.path)) { next(); return; @@ -129,12 +136,33 @@ function listGames(game : string, response : express.Response, next : express.Ne //get all active games by game name/id + Games.getActiveGames(game) + .then(games => { + let data = []; + for(let game of games){ + data.push({ + name : game.data.name, + id : game.id, + players : game.data.players.length, + password : game.data.password, + }) + } + + response.type('application/json'); + response.send(data); + response.end(); + }) + .catch(console.log) + .catch(next) + return; + + console.log(`Listing games for : ${game}`); let query : string if(/^[0-9]+$/.test(game)){ - query = `SELECT ${DB.active_games.active_game_id.column},${DB.game_data.data_value.column} FROM ${DB.active_games.table}, ${DB.game_data.table}, ${DB.games.table} WHERE ${DB.games.game_id.column} = ${game} AND ${DB.active_games.active_game_id.column} = ${DB.game_data.game_id.column};` + query = `SELECT ${DB.active_games.active_game_id.column},${DB.game_data.data_value.column} FROM ${DB.active_games.table}, ${DB.game_data.table}, ${DB.games.table} WHERE ${DB.games.game_id.column} = ${game} AND ${DB.active_games.active_game_id.column} = ${DB.game_data.game_id.column} AND ${DB.game_data.data_key.column} = 0;` }else{ - query = `SELECT ${DB.active_games.active_game_id.column},${DB.game_data.data_value.column} FROM ${DB.active_games.table}, ${DB.game_data.table}, ${DB.games.table} WHERE ${DB.games.game_name.column} = ${mysql.escape(game)} AND ${DB.active_games.active_game_id.column} = ${DB.game_data.game_id.column};` + query = `SELECT ${DB.active_games.active_game_id.column},${DB.game_data.data_value.column} FROM ${DB.active_games.table}, ${DB.game_data.table}, ${DB.games.table} WHERE ${DB.games.game_name.column} = ${mysql.escape(game)} AND ${DB.active_games.active_game_id.column} = ${DB.game_data.game_id.column} AND ${DB.game_data.data_key.column} = 0;` } Games.query(query) @@ -153,8 +181,14 @@ function listGames(game : string, response : express.Response, next : express.Ne let row = results.results[key]; let data_value = JSON.parse(row.data_value); data_value.id = row.active_game_id; + data.push({ + name : data_value.name, + id : data_value.id, + players : data_value.players.length, + password_protected : data_value.password?true:false + }) debug(data_value); - data.push(data_value); + // data.push(data_value); // data[row.active_game_id] = data_value; } // console.log(JSON.parse(res)); @@ -176,15 +210,30 @@ function createGame(game : string ,req : express.Request, res : express.Response next(); return; } + + return getGame(game).then((game : Game) => { + debug(`Creating game by Alex. CHANGE`); + console.log(req.body); + game.create(req.body.name, req.body.password).then(active_game_id => { + res.send(`Created new ${game.name} game by ${"Alex"} with id ${active_game_id}`); + res.end(); + }); + }).catch(next) +} + +function getGame(game : string | number) : Promise<Game>{ let query : string - if(/^[0-9]+$/.test(game)){ + if(typeof(game) == 'number' || /^[0-9]+$/.test(game)){ query = `SELECT ${DB.games.game_id.column}, ${DB.games.game_name.column} FROM ${DB.games.table} WHERE ${DB.games.game_id.column} = ${game};`; }else{ query = `SELECT ${DB.games.game_id.column}, ${DB.games.game_name.column} FROM ${DB.games.table} WHERE ${DB.games.game_name.column} = ${mysql.escape(game)};`; } - Games.query(query) + return Games.query(query) + .then(results => { + // console.log(query); + // console.log(results); let game_id : number = results.results[0].game_id; let game_name : string = results.results[0].game_name; let game_prom : Promise<Game>; @@ -195,7 +244,7 @@ function createGame(game : string ,req : express.Request, res : express.Response .then((_game : any) => { let game : GameConstructor = _game.game; loadedGames[game_id] = new game(Games); - console.log(loadedGames[game_id]); + // console.log(loadedGames[game_id]); // game.init(Games); resolve(loadedGames[game_id]); }).catch(reject); @@ -205,20 +254,14 @@ function createGame(game : string ,req : express.Request, res : express.Response resolve(loadedGames[game_id]); }) } - game_prom.then((game : Game) => { - debug(`Creating game by Alex. CHANGE`); - console.log(req.body); - game.create(req.body.name, req.body.password).then(active_game_id => { - res.send(`Created new ${game_name} game by ${"Alex"} with id ${active_game_id}`); - res.end(); - }); - }).catch(err => { - //game does not exist - console.log(`Game ${game_name}:${game_id} does not exist!`); - next(500); - }); - }).catch(next) + return game_prom; + }).catch((err)=>{ + console.log("Server Error! Game not found!"); + console.log(err); + return Promise.reject(500); + }) + } export {game_api}; \ No newline at end of file diff --git a/src/games/battleships.ts b/src/games/battleships.ts index 89d4dbf..7b7c2b6 100644 --- a/src/games/battleships.ts +++ b/src/games/battleships.ts @@ -1,14 +1,43 @@ +import * as io from "socket.io"; import {Game, GameInitFunction, GameCreateFunction, GameDestroyFunction, GameConstructor} from "./game"; import { debug } from "../debug"; import { GameDB } from "../gamedb"; import { rejects } from "assert"; +import { escape } from "mysql"; + +interface player { + socket : io.Socket; + game_id : number; + username : string; +}; + +enum rotation { + NORTH = "north", + EAST = "east", + SOUTH = "south", + WEST = "west", +} + +interface ship { + x : number, + y : number, + rotation : rotation, + size : number, +} + +interface shot { + x : number, + y : number, + hit : boolean, +} const battleships : GameConstructor = class battleships implements Game{ - public id : number = 1; - public name : string = "battleships"; + public readonly id : number = 1; + public readonly name : string = "battleships"; protected db : GameDB; + protected players : player[] = []; constructor(db_server : GameDB){ this.db = db_server; @@ -19,7 +48,7 @@ const battleships : GameConstructor = class battleships implements Game{ return new Promise<number>((resolve, reject) => { this.db.query(`INSERT INTO active_games(active_game_type_id, active_game_idle) VALUES (${this.id}, now());`) // .then(results => { - console.log(results.results); + // console.log(results.results); resolve(results.results.insertId || -1); let json_data : any = {name: creator, players : [], password : password}; if(password){ @@ -28,7 +57,7 @@ const battleships : GameConstructor = class battleships implements Game{ return this.db.query(`INSERT INTO game_data(game_id, data_key, data_value) VALUES (${results.results.insertId}, 0, ?);`, JSON.stringify(json_data)); }) .then(results => { - console.log(results); + // console.log(results); }).catch(err => { console.log(err); reject(err); @@ -40,6 +69,287 @@ const battleships : GameConstructor = class battleships implements Game{ destroy(){ } + + join(socket : io.Socket, args : any) : Promise<boolean>{ + + console.log(`Joining battleships game`); + console.log(args); + + socket.removeAllListeners(); + + //query to check if password correct etc, max number of players not reached etc + + return this.db.query(`SELECT data_key, data_value FROM game_data WHERE game_id = ${args.id} AND data_key = 0;`) + .then(results => { + let row = results.results[0]; + // console.log(row.data_value); + let json = JSON.parse(row.data_value); + // console.log(json); + if(json.password !== args.password){ + socket.emit("disconnect", "Incorrect password"); + socket.disconnect(true); + return false; + } + if(json.players.length >= 2){ + socket.emit("disconnect", "Too many players"); + socket.disconnect(true); + return false; + } + socket.emit("welcome", "Successfully joined game!"); + console.log("Successfully joined game!"); + json.players.push(args.username); + // console.log(json); + console.trace("JOINING USERS"); + this.db.query(`UPDATE game_data SET data_value = ${escape(JSON.stringify(json))} WHERE game_id = ${args.id} AND data_key = 0;`) + .then(() => { + return this.db.query(`SELECT data_key, data_value FROM game_data WHERE game_id = ${args.id};`) + }) + .then(results => { + //player number + console.log(results.results); + let json = JSON.parse(results.results.reduce((acc : any, cur : any) => cur.data_key == 0 ? cur : acc, null).data_value); + let player_number = json.players.indexOf(args.username); + debug(player_number) + let player_data = results.results.reduce((acc : any, cur : any) => cur.data_key == player_number+1 ? cur : acc, null); + debug(player_data); + let player_json = JSON.parse(player_data.data_value); + if(player_json){ + debug(player_json); + socket.emit("place", player_json); + } + if(results.results.length >= 3){ + socket.emit("battle"); + } + }).catch(console.log); + + socket.on("exit", () => { + this.leave(socket.id); + }); + + socket.on("place", (data : ship[]) => this.place(socket.id, data)); + + socket.on("fire", (data : shot) => this.fire(socket.id, data)); + + let player : player = { + socket : socket, + game_id : args.id, + username : args.username, + } + + this.players.push(player); + + return true; + }) + + // return Promise.resolve(true); + } + + place(sock_id : string, ships : ship[]){ + //check that all positions are valid and not intersecting + interface pos {x : number, y : number}; + let taken : pos[] = []; + let valid : boolean = true; + for(let ship of ships){ + let inx = 0, iny = 0; + switch(ship.rotation){ + case "north": + iny = -1; + break; + case "east": + inx = +1; + break; + case "south": + iny = +1; + break; + case "west": + inx = -1; + break; + } + for(let i = 0; i < ship.size; i++){ + let X = ship.x + i*inx; + let Y = ship.y + i*iny; + if(X < 0 || X >= 10 || Y < 0 || Y >= 10) valid = false; + let position : pos = {x : X, y : Y}; + if(taken.reduce((taken, cur)=> cur == position? cur : taken, null)) valid = false; + taken.push(position); + } + } + if(valid){ + let player = this.getPlayer(sock_id); + this.db.query(`SELECT data_value FROM game_data WHERE data_key = 0 AND game_id = ${player.game_id};`) + .then(results => { + let game_json = JSON.parse(results.results[0].data_value); + let player_no = game_json.players.indexOf(player.username); + + return this.db.query(`INSERT INTO game_data (game_id, data_key, data_value) VALUES (${player.game_id}, ${player_no+1}, ${escape(JSON.stringify(ships))})`); + }) + .then(results => { + if(results.err){ + console.log(results.err); + } + //check if altered + // console.log(results); + //if both entries exist, BATTLE + return this.db.query(`SELECT data_key, data_value FROM game_data WHERE game_id = ${player.game_id};`) + }) + .then(results => { + if(results.results.length >= 3){ + let game_data = results.results[0]; + let game_json = JSON.parse(game_data.data_value); + game_json.turn = 0; + //battle + //get both socket for game + let sockets : io.Socket[] = []; + for(let p of this.players){ + if(p.game_id == player.game_id) + sockets.push(p.socket); + } + sockets.map(socket => {socket.emit("battle");}); + this.db.query(`UPDATE game_data SET data_value=${escape(JSON.stringify(game_json))} WHERE data_key = 0 AND game_id = ${player.game_id};`) + this.db.query(`INSERT INTO game_data (game_id, data_key, data_value) VALUES (${player.game_id}, 3, "[]"), (${player.game_id}, 4, "[]")`) + } + }) + }else{ + + } + } + + fire(sock_id : string, shot : shot){ + let player = this.getPlayer(sock_id); + shot.hit = false; + console.log(shot); + this.db.query(`SELECT * FROM game_data WHERE game_id = ${player.game_id};`) + .then(results => { + //check if player's move + let game_data = results.results.reduce((acc : any, cur : any) => { + if(cur.data_key == 0) return cur; + return acc; + }); + let game_json = JSON.parse(game_data.data_value); + let player_number = game_json.players.indexOf(player.username); + if(game_json.turn != player_number) throw new Error("Not your turn!"); + game_json.turn ++; + game_json.turn %= 2; + console.log(`Player ${game_json.turn}'s turn`); + console.log(game_json); + this.db.query(`UPDATE game_data SET data_value=${escape(JSON.stringify(game_json))} WHERE game_id = ${player.game_id} AND data_key = 0;`) + // .then(console.log); + //check if hit any of ships + let opponent_ships : ship[] = JSON.parse(results.results.reduce((acc : any, cur : any) => { + if(cur.data_key == game_json.turn+1) + return cur; + return acc; + }, null).data_value); + + let idx = (game_json.turn+1)%2+3 + let shots : shot[] = JSON.parse(results.results.reduce((acc : any, cur : any) => { + if(cur.data_key == idx){ + return cur; + } + return acc; + }, null).data_value); + + for(let ship of opponent_ships){ + let inx = 0, iny = 0; + switch(ship.rotation){ + case "north": + iny = -1; + break; + case "east": + inx = +1; + break; + case "south": + iny = +1; + break; + case "west": + inx = -1; + break; + } + let hits = ship.size-1; + for(let i = 0; i < ship.size; i++){ + let X = ship.x + i*inx; + let Y = ship.y + i*iny; + if(shot.x == X && shot.y == Y){ + //check if ship sunk + shot.hit = true; + console.log(shots); + + } + for(let shot of shots){ + if(shot.x == X && shot.y == Y && shot.hit) + hits--; + } + } + if(hits == 0){ + player.socket.emit("sunk", ship); + console.log("SUNK"); + } + + } + + if(shots.reduce((dup, s) => { + if(s.x == shot.x && s.y == shot.y) return true; + return dup; + }, false)){ + + }else{ + shots.push(shot); + this.db.query(`UPDATE game_data SET data_value=${escape(JSON.stringify(shots))} WHERE game_id = ${player.game_id} AND data_key = ${idx};`); + } + + return shot.hit; + + }) + .then(hit => { + shot.hit = hit; + console.log(`Hit? : ${hit}`); + player.socket.emit("fired", shot); + //get other player + let opponent = this.players.reduce((acc, cur) => { + if(cur != player && cur.game_id == player.game_id){ + return cur; + } + return acc; + }, null); + if(opponent){ + opponent.socket.emit("shot", shot); + opponent.socket.emit("fire"); + }else{ + debug(`MISSING OPPONENT`); + } + + }).catch(e => { + console.log(e); + player.socket.emit("fired", null); + }) + } + + getPlayer(sock_id : string){ + let player = this.players.reduce((correct, current) => { + if(current.socket.id == sock_id) + return current; + return correct; + }, null); + return player; + } + + leave(sock_id : string){ + let player = this.getPlayer(sock_id); + this.players = this.players.filter(p => p==player?0:1); + let game_id = player.game_id; + this.db.query(`SELECT data_key, data_value FROM game_data WHERE game_id = ${game_id} AND data_key = 0;`) + .then(results => { + let row = results.results[0]; + let json = JSON.parse(row.data_value); + console.log(json); + let new_players = json.players.filter((p : any) => p==player.username?0:1); + json.players = new_players; + console.trace("REMOVING USERS"); + console.log(json) + this.db.query(`UPDATE game_data SET data_value = ${escape(JSON.stringify(json))} WHERE game_id = ${game_id} AND data_key = 0;`) + }); + + } } export {battleships as game}; \ No newline at end of file diff --git a/src/games/game.ts b/src/games/game.ts index 1fa975f..3fa6721 100644 --- a/src/games/game.ts +++ b/src/games/game.ts @@ -1,10 +1,12 @@ import * as mysql from "mysql" import { GameDB } from "../gamedb"; +import * as io from "socket.io" export type GameInitFunction = (db_server : GameDB) => void; export type GameCreateFunction = (creator : string, password? : string) => Promise<number>; export type GameDestroyFunction = (game_id : number) => void; +export type GameJoinFunction = (socket : io.Socket, args : any) => Promise<boolean>; export interface GameConstructor { new (db_server : GameDB) : Game; } @@ -12,6 +14,7 @@ export interface GameConstructor { export interface Game{ create : GameCreateFunction; destroy : GameDestroyFunction; + join : GameJoinFunction; name : string; id : number; [key : string] : any; -- GitLab