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">&lt_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="../">&lt- 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">&lt_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="../">&lt- 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="../">&lt- 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="../">&lt- 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