diff --git a/data/games/cah/cards.json b/data/games/cah/cards.json
new file mode 100644
index 0000000000000000000000000000000000000000..a034777317940394db9bac0fff4c7ee5baeb45a2
--- /dev/null
+++ b/data/games/cah/cards.json
@@ -0,0 +1,486 @@
+[{
+		"name": "Cards against humanity",
+		"cards": [{
+				"bw": false,
+				"text": "Silence.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "The illusion of shoice in a late-stage capitalist society.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Many bats.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Famine.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Flesh-eating bacteria.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Flying sex snakes.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Not giving a shit about the Third World.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Magnets.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Shapeshifters.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Seeing what happens when you lock people in a room with hungry seagulls.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A crucifixion.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Jennifer Lawrence.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "72 virgins.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A live studio audience.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A time travel paradox.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Authentic Mexican cuisine.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Doing crimes.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Synergistic management solutions.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Crippling debt.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Daddy issues.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Used panties.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A fart so powerful that it wakes the giants from their thousand-year slumber.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Former President George W. Bush.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Full frontal nudity.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Covering myself with Parmesan cheese and chili flakes becauseI am pizza.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Laying an egg.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Getting naked and watching Nickelodeon.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Pretendingto care.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Having big dreams but no realistic way to achieve them.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Seeing Grandma naked.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Boogers.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "The inevitable heat death ofthe universe.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "The miracleof childbirth.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "The Rapture.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Whipping it out.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "White privilege.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Emerging from the sea and rampaging through Tokyo.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "The Hamburglar.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "AXE Body Spray.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "The Blood of Christ.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Soft, kissy missionary sex.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "BATMAN!",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Agriculture.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Barely making $25,000 a year.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Natural selection.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Coat hanger abortions.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Sex withPatrick Stewart.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "My abusive boyfriend who really isn’t so bad once you get to know him.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Prescription pain killers.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Swooping.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Mansplaining.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A homoerotic volleyball montage.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Alexandria Ocasio-Cortez.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Putting things where they go.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Fragile masculinity.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "All-you-can-eat shrimp for $8.99.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "An old guy who’s almost dead.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Kanye West.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Kanye West.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Raptor attacks.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Seven dead and three in critical condition.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Smegma.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Alcoholism.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A middle-aged man on roller skates.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Looking in the mirror, applying lipstick, and whispering “tonight, you will have sex with Tom Cruise.”",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Bingeing and purging.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "An oversized lollipop.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Self-loathing.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Hey Reddit! I’m __________________. Ask me anything.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Introducing X-treme Baseball! It’s like baseball, but with __________!",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "What is Batman’s guilty pleasure.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "TSA guidelinesnow prohibit __________________ on airplanes",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Next from J.K. Rowling: Harry Potter and the Chamber of __________________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "That’s right, I killed _________________. How, you ask? _________________.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"pick": 2
+				}
+			},
+			{
+				"bw": true,
+				"text": "I’m sorry, Professor, but I couldn’t complete my homework because of __________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "And the Academy Award for __________________ goes to __________.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"pick": 2
+				}
+			},
+			{
+				"bw": true,
+				"text": "Dude, do not go in that bathroom. There’s __________ in there.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "How did I losemy virginity?",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "It’s a pity that kids these days are all getting involved with _____________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Step 1: __________. Step 2: __________. Step 3: Profit.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"pick": 2
+				}
+			},
+			{
+				"bw": true,
+				"text": "_________________. Betcha can’t have just one!",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Kids, I don’t need drugs to get high. I’m high on __________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "For my next trick, I will pull __________ out of ___________.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"pick": 2
+				}
+			},
+			{
+				"bw": true,
+				"text": "While the United States raced the Soviet Union to the moon, the Mexican government funneled millions of pesos into research on _____________________",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "In the Disney Channel Original Movie, Hannah Montana struggles with _____________ for the first time.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "What’s mysecret power?",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "I’m going on a cleanse this week. Nothing but kale juice and __________________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "________ + ________ = ________________.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"draw": 2,
+					"pick": 3
+				}
+			}
+
+		]
+	},
+	{
+		"name": "Covid-19 Pack",
+		"cards": [{
+				"bw": false,
+				"text": "Coronavirus.",
+				"pack_name": "Covid-19 Pack"
+			},
+			{
+				"bw": false,
+				"text": "Boris Johnson in an ICU.",
+				"pack_name": "Covid-19 Pack"
+			},
+			{
+				"bw": false,
+				"text": "A man ate a bat.",
+				"pack_name": "Covid-19 Pack"
+			}
+		]
+	}
+]
\ No newline at end of file
diff --git a/package.json b/package.json
index a8c60b428252b15bc002ec69c9c157dd44cd5a72..a31cf460b24a3a09d6c5cc941cf03157e1af9ef9 100644
--- a/package.json
+++ b/package.json
@@ -1,29 +1,29 @@
 {
-  "name": "web-games",
-  "version": "1.0.0",
-  "description": "node-based backend and frontend for web-games",
-  "main": "server.js",
-  "scripts": {
-    "run": "export PORT=80; node server.js",
-    "test": "export PORT=8080; export DEBUG=1; node server.js"
-  },
-  "author": "Alex Mansfield",
-  "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",
-    "ts": "^0.2.2"
-  },
-  "devDependencies": {
-    "typescript": "^3.8.3"
-  }
-}
+	"name": "web-games",
+	"version": "1.0.0",
+	"description": "node-based backend and frontend for web-games",
+	"main": "server.js",
+	"scripts": {
+		"run": "export PORT=80; node server.js",
+		"test": "export PORT=8080; export DEBUG=1; node server.js"
+	},
+	"author": "Alex Mansfield",
+	"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",
+		"ts": "^0.2.2"
+	},
+	"devDependencies": {
+		"typescript": "^3.8.3"
+	}
+}
\ No newline at end of file
diff --git a/public/css/main.css b/public/css/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..aa7cdec09b1daefc06d800045db3cfdbfcb7fa2b
--- /dev/null
+++ b/public/css/main.css
@@ -0,0 +1,67 @@
+html {
+	background-color: #303030;
+	color: white;
+}
+
+body {
+	margin: 0;
+	/* border: 0;
+	padding: 0; */
+	/* position: absolute; */
+	/* top: 0; */
+}
+
+#header {
+
+	margin: 0;
+	border: 0;
+	padding: 20px;
+	width: calc(100vw - 40px);
+	background-color: #181818;
+	font-size: 24px;
+
+}
+
+#subheader {
+	margin: 0;
+	border: 0;
+	padding: 10px;
+	padding-left: 30px;
+	background-color: #202020;
+
+}
+
+h1 {
+	margin: 0;
+}
+
+#footer {
+
+	margin: 0;
+	width: 100vw;
+}
+
+#container {
+	margin: 20px;
+}
+
+#home {
+	color: white;
+	text-decoration: none;
+}
+
+a {
+	color: white;
+}
+
+button {
+	margin: 10px;
+	padding: 10px;
+	border-color: whitesmoke;
+	border-style: solid;
+	border: 10px;
+	border-radius: 5px;
+	font-size: 20px;
+	color: white;
+	background-color: #101010;
+}
\ No newline at end of file
diff --git a/public/errors/404.html b/public/errors/404.html
index b15639f701e101e58b2c22bf96d363be548c229d..061915fad571fbe749e7017f37dcbe2b54326848 100644
--- a/public/errors/404.html
+++ b/public/errors/404.html
@@ -1,17 +1,43 @@
 <!DOCTYPE html>
 <html>
-    <head>
-
-    </head>
-    <body>
-        <h1>
-            Error 404
-        </h1><br>
-        <p>
-            oops
-        </p><br>
-        <h3>
-            Page not found!
-        </h3><br>
-    </body>
+
+<head>
+	<link rel="stylesheet" href="/css/main.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Error
+		</h1>
+
+	</div>
+	<div id="container" style="text-align: center;">
+
+		<h1>
+			404
+		</h1>
+
+		<p>
+			Oops
+		</p>
+
+		<h3>
+			Page Not Found!
+		</h3>
+
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
 </html>
\ No newline at end of file
diff --git a/public/games/backgammon/game.html b/public/games/backgammon/game.html
new file mode 100644
index 0000000000000000000000000000000000000000..4b2c30709e676da16aa4b1c8bde3f6b7796309ca
--- /dev/null
+++ b/public/games/backgammon/game.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+	<title>Cards against Humanity</title>
+	<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
+	<script src="/js/p5/p5.js"></script>
+	<script src="./game.js"></script>
+	<link rel="stylesheet" href="/css/main.css">
+	<link rel="stylesheet" href="./cah.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Backgammon game
+		</h1>
+
+		<a href="../">Games/</a><a href="./">Backgammon/</a>
+	</div>
+	<div id="container">
+		<div id="game">
+
+		</div>
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/public/games/backgammon/game.js b/public/games/backgammon/game.js
new file mode 100644
index 0000000000000000000000000000000000000000..a97d83dcbead2161662fa648fe5f7f99a2faf0bb
--- /dev/null
+++ b/public/games/backgammon/game.js
@@ -0,0 +1,36 @@
+/// <reference path="../../../p5.global-mode.d.ts" />
+/// <reference path="../../../socket.io.d.ts" />
+const 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
+        });
+    }
+}, {});
+const params = new window.URLSearchParams(window.location.search);
+//@ts-ignore
+let _socket = io();
+_socket.on("connect", () => {
+    _socket.emit("join", {
+        gameId: params.get("id"),
+        userName: cookies["username"],
+    });
+    _socket.removeAllListeners();
+    _socket.on("welcome", data => {
+        new backgammon(_socket);
+    });
+});
+class backgammon extends p5 {
+    constructor(socket) {
+        super(() => { }, document.getElementById("game"), false);
+    }
+}
+// new backgammon(_socket);
diff --git a/public/games/backgammon/game.ts b/public/games/backgammon/game.ts
new file mode 100644
index 0000000000000000000000000000000000000000..664de1f16b85bbead63b1e47f51e154e30de05f7
--- /dev/null
+++ b/public/games/backgammon/game.ts
@@ -0,0 +1,61 @@
+/// <reference path="../../../p5.global-mode.d.ts" />
+/// <reference path="../../../socket.io.d.ts" />
+
+
+interface GameDataPacket {
+	gameId: number,
+		gameType: number,
+		gameToken: string,
+		packetType: string,
+		userName ? : string,
+		userToken ? : string,
+		data ? : any
+}
+
+interface Game {
+	gameId: number,
+		gameType: number,
+		gameName: string,
+		gameToken: string,
+}
+
+const cookies: any = 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
+			})
+		}
+	}, {});
+const params: any = new window.URLSearchParams(window.location.search);
+
+//@ts-ignore
+let _socket = io();
+
+
+_socket.on("connect", () => {
+	_socket.emit("join", {
+		gameId: params.get("id"),
+		userName: cookies["username"],
+	});
+	_socket.removeAllListeners();
+	_socket.on("welcome", data => {
+		new backgammon(_socket);
+	})
+})
+
+class backgammon extends p5 {
+
+	constructor(socket: SocketIO.Socket) {
+		super(() => {}, document.getElementById("game"), false);
+	}
+
+}
+
+// new backgammon(_socket);
\ No newline at end of file
diff --git a/public/games/backgammon/index.html b/public/games/backgammon/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..17f13ec090a7b713ea9c0fb7a68f1f0fdd76a867
--- /dev/null
+++ b/public/games/backgammon/index.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+	<title>Battleships</title>
+	<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>
+	<link rel="stylesheet" href="/css/main.css">
+	<link rel="stylesheet" href="../loader.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Backgammon games.
+		</h1>
+
+		<a href="../">Games/</a>
+	</div>
+	<div id="container">
+
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/public/games/backgammon/tsconfig.json b/public/games/backgammon/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..a19a54328f993ca26d564eecdbc93cecbbf93b43
--- /dev/null
+++ b/public/games/backgammon/tsconfig.json
@@ -0,0 +1,7 @@
+{
+    "compilerOptions": {
+        "module": "CommonJS",
+        "target": "ES2020"
+
+    }
+}
\ No newline at end of file
diff --git a/public/games/battleships/game.html b/public/games/battleships/game.html
index 2dac2386b369cfb90276d9a24c83702c44f477e5..92316e60b573c220e418231f27401ba190fc82dc 100644
--- a/public/games/battleships/game.html
+++ b/public/games/battleships/game.html
@@ -1,22 +1,45 @@
 <!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>
+
+<head>
+	<title>Battleships</title>
+	<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>
+	<link rel="stylesheet" href="/css/main.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Battleships game
+		</h1>
+
+		<a href="../">Games/</a><a href="./">Battleships/</a>
+	</div>
+	<div id="container">
+		<div id="game">
+			<div id="me" style="float: left; padding: 10px;">
+
+			</div>
+
+			<div id="opponent" style="float: left;padding: 10px">
+
+			</div>
+
+		</div>
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
 </html>
\ No newline at end of file
diff --git a/public/games/battleships/game.js b/public/games/battleships/game.js
index d7d0f610460d920b08370e3107691f82047f71d6..39e5c3f47549163403c9feaa250b0e52fb32da37 100644
--- a/public/games/battleships/game.js
+++ b/public/games/battleships/game.js
@@ -113,11 +113,10 @@ function _setup() {
 	} else {
 		gameID = params.id;
 	}
-
 	let join_data = {
-		username: username,
-		password: game_password,
-		id: gameID,
+		gameId: gameID,
+		userName: username,
+		gamePassword: game_password,
 	}
 	socket.on("disconnect", console.log);
 	socket.emit("join", join_data);
diff --git a/public/games/battleships/index.html b/public/games/battleships/index.html
index 2c907ae5b1cf489f44d2845dfb71f74588a508cd..3752123a0621a246fab3940c3ac54588ecdd629b 100644
--- a/public/games/battleships/index.html
+++ b/public/games/battleships/index.html
@@ -1,17 +1,37 @@
 <!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 game
-        </h1>
-        <a href="../">&lt- Back to Games!</a><br><br>
-        <!-- <p>
-            Current running games: <br>
-        </!--> 
-    </body>
+
+<head>
+	<title>Battleships</title>
+	<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>
+	<link rel="stylesheet" href="/css/main.css">
+	<link rel="stylesheet" href="../loader.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Battleships games
+		</h1>
+
+		<a href="../">Games/</a>
+	</div>
+	<div id="container">
+
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
 </html>
\ No newline at end of file
diff --git a/public/games/battleships_v2/game.html b/public/games/battleships_v2/game.html
index ea1429777edf9737325690a750e04463f55bac4a..587d50a40bf91dddd7bb6686a5588c7c9ea842b9 100644
--- a/public/games/battleships_v2/game.html
+++ b/public/games/battleships_v2/game.html
@@ -2,26 +2,47 @@
 <html>
 
 <head>
+	<title>Battleships-V2.0</title>
 	<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
 	<script src="/js/p5/p5.js"></script>
-	<!-- <script src="https://cdn.jsdelivr.net/npm/p5@1.0.0/lib/p5.js"></script> -->
-	<script src="/js/p5_build.js"></script>
 	<script src="./game.js"></script>
+	<link rel="stylesheet" href="/css/main.css">
 </head>
 
 <body>
-	<a href="./index.html">&lt_Back to games</a><br>
-	<div id="game">
-		<div id="defend" style="float: left; padding: 10px;">
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
 
-		</div>
+		<h1>
+			Battleships-V2.0 game
+		</h1>
 
-		<div id="attack" style="float: left;padding: 10px">
+		<a href="../">Games/</a><a href="./">Battleships-V2/</a>
+	</div>
+	<div id="container">
+		<div id="game">
+			<span style="display: inline-block;">
+				<div id="defend" style="padding: 10px;">
 
-		</div>
+				</div>
+			</span>
+			<span style="display: inline-block;">
+				<div id="attack" style="padding: 10px">
 
+				</div>
+			</span>
+
+		</div>
 	</div>
+	<div id="footer">
 
+	</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
index c1e919117d0b234480d892779bb0f3e105dc2cbf..f6843911538226a09e5a66e9cd28c23c2d3e8ad0 100644
--- a/public/games/battleships_v2/game.js
+++ b/public/games/battleships_v2/game.js
@@ -18,11 +18,12 @@ const cookies = document.cookie
 const URLParams = new window.URLSearchParams(window.location.search);
 //@ts-ignore
 const _socket = io();
-_socket.emit("join", {
-    username: cookies.username,
-    password: cookies.game_password,
-    id: URLParams.get("id"),
-});
+let joinParams = {
+    gameId: URLParams.get("id"),
+    userName: cookies.username,
+    gamePassword: cookies.game_password,
+};
+_socket.emit("join", joinParams);
 _socket.on("kick", (reason) => {
     console.log(reason);
     window.location.href = "./";
@@ -294,7 +295,7 @@ class attack extends base {
         this.createCanvas(500, 500);
         this.createElement("br");
         this.fireButton = this.createButton("Fire!");
-        this.fireButton.size(100, 30);
+        // this.fireButton.size(100, 30);
         this.fireButton.mouseClicked(() => {
             // this.shots.push(this.firing);
             this.socket.emit("fire", this.firing);
diff --git a/public/games/battleships_v2/game.ts b/public/games/battleships_v2/game.ts
index 15227a93a75c41667d73839715a025b0fcc6343c..d8851a8718e523850959d2b61c7ab7ecf559827e 100644
--- a/public/games/battleships_v2/game.ts
+++ b/public/games/battleships_v2/game.ts
@@ -18,11 +18,12 @@ const URLParams: any = new window.URLSearchParams(window.location.search);
 
 //@ts-ignore
 const _socket = io();
-_socket.emit("join", {
-	username: cookies.username,
-	password: cookies.game_password,
-	id: URLParams.get("id"),
-});
+let joinParams: GameJoinParams = {
+	gameId: URLParams.get("id"),
+	userName: cookies.username,
+	gamePassword: cookies.game_password,
+}
+_socket.emit("join", joinParams);
 _socket.on("kick", (reason) => {
 	console.log(reason)
 	window.location.href = "./"
@@ -53,6 +54,11 @@ interface shot {
 		y: number,
 		hit: boolean,
 }
+interface GameJoinParams {
+	gameId: number,
+		userName: string,
+		gamePassword: string,
+}
 
 class base extends p5 {
 	placing: ship;
@@ -367,7 +373,7 @@ class attack extends base {
 		this.createCanvas(500, 500);
 		this.createElement("br");
 		this.fireButton = this.createButton("Fire!");
-		this.fireButton.size(100, 30);
+		// this.fireButton.size(100, 30);
 		this.fireButton.mouseClicked(() => {
 			// this.shots.push(this.firing);
 			this.socket.emit("fire", this.firing);
diff --git a/public/games/battleships_v2/index.html b/public/games/battleships_v2/index.html
index d99bf12b1af524a6bb5f8e68af2b6b1b1a69a73f..0763f123c0e33c0252fd6aa8a86c1684e22beb04 100644
--- a/public/games/battleships_v2/index.html
+++ b/public/games/battleships_v2/index.html
@@ -1,17 +1,37 @@
 <!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>
+
+<head>
+	<title>Battleships</title>
+	<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>
+	<link rel="stylesheet" href="/css/main.css">
+	<link rel="stylesheet" href="../loader.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Battleships-V2 games
+		</h1>
+
+		<a href="../">Games/</a>
+	</div>
+	<div id="container">
+
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
 </html>
\ No newline at end of file
diff --git a/public/games/cah/cah.css b/public/games/cah/cah.css
new file mode 100644
index 0000000000000000000000000000000000000000..e9c4eb3cb7b7eb97ef2ae0b7df45a7da5107b39c
--- /dev/null
+++ b/public/games/cah/cah.css
@@ -0,0 +1,34 @@
+.card {
+	background-color: white;
+	color: black;
+	font-size: 22px;
+	padding: 10px;
+	position: relative;
+	border-width: 2px;
+	border-style: solid;
+	border-color: black;
+	border-radius: 15px;
+	margin: 5px;
+	cursor: pointer;
+	width: 240px;
+	height: 300px;
+	scale: 1;
+	display: inline-block;
+}
+
+.card p {
+	position: absolute;
+}
+
+.card div p {
+	margin-top: 10px;
+	margin-bottom: 10px;
+	left: 50px;
+	bottom: 0;
+	width: max-content;
+}
+
+.card img {
+	position: absolute;
+	bottom: 0;
+}
\ No newline at end of file
diff --git a/public/games/cah/game.html b/public/games/cah/game.html
new file mode 100644
index 0000000000000000000000000000000000000000..8222ebb39c6b19ee38acbcafdb5b464526c1e64f
--- /dev/null
+++ b/public/games/cah/game.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+	<title>Cards against Humanity</title>
+	<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
+	<script src="/js/p5/p5.js"></script>
+	<script src="./game.js"></script>
+	<link rel="stylesheet" href="/css/main.css">
+	<link rel="stylesheet" href="./cah.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Cards against Humanity game
+		</h1>
+
+		<a href="../">Games/</a><a href="./">Cards against Humanity/</a>
+	</div>
+	<div id="container">
+		<div id="game">
+
+		</div>
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/public/games/cah/game.js b/public/games/cah/game.js
new file mode 100644
index 0000000000000000000000000000000000000000..fd9c3eb3b5cd18563111f7bad3527926a52e26cf
--- /dev/null
+++ b/public/games/cah/game.js
@@ -0,0 +1,143 @@
+/// <reference path="../../../p5.global-mode.d.ts" />
+const 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
+        });
+    }
+}, {});
+const params = new window.URLSearchParams(window.location.search);
+const icon = "data:image/svg+xml;base64,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";
+// @ts-ignore
+let _socket = io();
+_socket.on("connect", () => {
+    _socket.emit("join", {
+        gameId: params.get("id"),
+        userName: cookies["username"],
+    });
+    _socket.removeAllListeners();
+    _socket.on("welcome", data => {
+        new cah(_socket);
+    });
+});
+class cah extends p5 {
+    constructor(socket) {
+        super(() => { }, document.getElementById("game"), false);
+        this.socket = socket;
+        this.socket.on("game_data", this.onData.bind(this));
+    }
+    onData(data) {
+        // console.log(data);
+        switch (data.packetType) {
+            case "black":
+                this.black = data.data;
+                break;
+            case "hand":
+                this.hand = data.data;
+                break;
+            default:
+                console.log(`Unknown data packet of type : ${data.packetType}`);
+                console.log(data.data);
+                break;
+        }
+        this.updateCards();
+    }
+    setup() {
+        // console.log(this.socket);
+        this.noCanvas();
+        this.game = {
+            gameId: parseInt(params.get("id")),
+            gameName: "",
+            gameType: 2,
+            gameToken: "",
+        };
+        //@ts-ignore
+        let b = this.createButton("rand");
+        b.mousePressed(() => {
+            this.socket.emit("game_data", {
+                gameId: this.game.gameId,
+                gameToken: this.game.gameToken,
+                packetType: "rand",
+                gameType: this.game.gameType,
+            });
+        });
+        this.createElement("br");
+        //@ts-ignore
+        this.createDiv().id("black");
+        //@ts-ignore
+        this.createDiv().id("hand");
+    }
+    updateCards() {
+        let cards = document.getElementsByClassName("card");
+        while (cards.length)
+            cards[0].remove();
+        // for (let card of cards) {
+        // 	card.remove();
+        // }
+        if (this.black)
+            this.drawCard(this.black, "black");
+        if (this.hand)
+            for (let card of this.hand) {
+                this.drawCard(card, "hand");
+            }
+    }
+    drawCard(c, node) {
+        // @ts-ignore
+        // let d: p5.Element = this.createSpan();
+        // if (node) d.parent(node);
+        // d.style("display", "inline-block")
+        let d;
+        //@ts-ignore
+        // d = this.createDiv().parent(d);
+        //@ts-ignore
+        d = this.createDiv().parent(node);
+        d.addClass("card");
+        if (c.bw)
+            d.addClass("black");
+        //@ts-ignore
+        let t = this.createP();
+        t.parent(d);
+        t.html(c.text);
+        t.style("margin", "0");
+        d.style("background-color", c.bw ? "black" : "white");
+        d.style("color", c.bw ? "white" : "black");
+        d.size(240, 300);
+        //@ts-ignore
+        let foot = this.createDiv();
+        foot.style("position", "absolute");
+        foot.style("bottom", "0");
+        foot.parent(d);
+        //@ts-ignore
+        let simg = this.createSpan();
+        simg.parent(foot);
+        simg.style("display", "inline-block");
+        //@ts-ignore
+        let img = this.createImg(icon, "cah icon");
+        img.parent(simg);
+        img.size(50, 50);
+        // img.style("position", "absolute");
+        // img.style("bottom", "0");
+        //@ts-ignore
+        let sp = this.createSpan();
+        sp.parent(foot);
+        sp.style("display", "inline-block");
+        // @ts-ignore
+        let p = this.createP();
+        p.parent(sp);
+        p.html(c.pack_name);
+        // p.style("position", "absolute");
+        // p.style("bottom", "0");
+        p.style("left", "60px");
+        p.style("font-size", "14px");
+        // if (node) d.parent(node);
+    }
+}
+// new cah(_socket)
diff --git a/public/games/cah/game.ts b/public/games/cah/game.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cdc5099f6ed4b9f8e407ea23facf4db07e2f3552
--- /dev/null
+++ b/public/games/cah/game.ts
@@ -0,0 +1,196 @@
+/// <reference path="../../../p5.global-mode.d.ts" />
+
+interface GameDataPacket {
+	gameId: number,
+		gameType: number,
+		gameToken: string,
+		packetType: string,
+		userName ? : string,
+		userToken ? : string,
+		data ? : any
+}
+
+interface Game {
+	gameId: number,
+		gameType: number,
+		gameName: string,
+		gameToken: string,
+}
+
+interface card {
+	bw: boolean,
+		text: string,
+		pack_name: string,
+		extra ? : {
+			draw ? : number,
+			pick ? : number
+		}
+}
+
+const cookies: any = 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
+			})
+		}
+	}, {});
+const params: any = new window.URLSearchParams(window.location.search);
+
+const icon = "data:image/svg+xml;base64,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"
+
+
+// @ts-ignore
+let _socket = io();
+_socket.on("connect", () => {
+	_socket.emit("join", {
+		gameId: params.get("id"),
+		userName: cookies["username"],
+	});
+	_socket.removeAllListeners();
+	_socket.on("welcome", data => {
+		new cah(_socket);
+	})
+})
+
+class cah extends p5 {
+	socket;
+
+	black: card;
+	hand: card[];
+	game: Game;
+
+	constructor(socket) {
+		super(() => {}, document.getElementById("game"), false);
+		this.socket = socket;
+		this.socket.on("game_data", this.onData.bind(this));
+	}
+
+	onData(data: GameDataPacket) {
+		// console.log(data);
+		switch (data.packetType) {
+			case "black":
+				this.black = data.data;
+				break;
+			case "hand":
+				this.hand = data.data;
+				break;
+			default:
+				console.log(`Unknown data packet of type : ${data.packetType}`);
+				console.log(data.data);
+				break;
+		}
+		this.updateCards();
+	}
+
+	setup() {
+		// console.log(this.socket);
+		this.noCanvas();
+
+		this.game = {
+			gameId: parseInt(params.get("id")),
+			gameName: "",
+			gameType: 2,
+			gameToken: "",
+		}
+		//@ts-ignore
+		let b: p5.Element = this.createButton("rand");
+		b.mousePressed(() => {
+			this.socket.emit("game_data", {
+				gameId: this.game.gameId,
+				gameToken: this.game.gameToken,
+				packetType: "rand",
+				gameType: this.game.gameType,
+			});
+		});
+		this.createElement("br");
+		//@ts-ignore
+		this.createDiv().id("black");
+		//@ts-ignore
+		this.createDiv().id("hand");
+	}
+
+	updateCards() {
+		let cards = document.getElementsByClassName("card");
+		while (cards.length) cards[0].remove();
+		// for (let card of cards) {
+		// 	card.remove();
+		// }
+		if (this.black)
+			this.drawCard(this.black, "black");
+		if (this.hand)
+			for (let card of this.hand) {
+				this.drawCard(card, "hand");
+			}
+	}
+
+	drawCard(c: card, node ? : string | HTMLElement | p5.Element) {
+		// @ts-ignore
+		// let d: p5.Element = this.createSpan();
+		// if (node) d.parent(node);
+		// d.style("display", "inline-block")
+		let d: p5.Element;
+		//@ts-ignore
+		// d = this.createDiv().parent(d);
+		//@ts-ignore
+		d = this.createDiv().parent(node);
+		d.addClass("card");
+		if (c.bw) d.addClass("black");
+		//@ts-ignore
+		let t: p5.Element = this.createP();
+		t.parent(d);
+		t.html(c.text);
+		t.style("margin", "0");
+		d.style("background-color", c.bw ? "black" : "white");
+		d.style("color", c.bw ? "white" : "black");
+		d.size(240, 300);
+
+
+		//@ts-ignore
+		let foot: p5.Element = this.createDiv();
+		foot.style("position", "absolute");
+		foot.style("bottom", "0");
+		foot.parent(d);
+
+
+		//@ts-ignore
+		let simg: p5.Element = this.createSpan();
+		simg.parent(foot);
+		simg.style("display", "inline-block");
+
+		//@ts-ignore
+		let img: p5.Element = this.createImg(icon, "cah icon");
+		img.parent(simg);
+		img.size(50, 50);
+		// img.style("position", "absolute");
+		// img.style("bottom", "0");
+
+		//@ts-ignore
+		let sp: p5.Element = this.createSpan();
+		sp.parent(foot);
+		sp.style("display", "inline-block");
+
+		// @ts-ignore
+		let p: p5.Element = this.createP();
+		p.parent(sp);
+		p.html(c.pack_name);
+		// p.style("position", "absolute");
+		// p.style("bottom", "0");
+		p.style("left", "60px");
+		p.style("font-size", "14px");
+
+
+
+		// if (node) d.parent(node);
+	}
+
+
+}
+
+// new cah(_socket)
\ No newline at end of file
diff --git a/public/games/cah/index.html b/public/games/cah/index.html
index acecd4ff8fbdce830934331ee96175d26116f859..572563cb014379c54db8c543155ef2b5b15caacf 100644
--- a/public/games/cah/index.html
+++ b/public/games/cah/index.html
@@ -1,17 +1,39 @@
 <!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>
+
+<head>
+	<title>Battleships</title>
+	<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>
+	<script src="./loader.js"></script>
+	<link rel="stylesheet" href="/css/main.css">
+	<link rel="stylesheet" href="../loader.css">
+	<link rel="stylesheet" href="./loader.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Cards Against Humanity.
+		</h1>
+
+		<a href="../">Games/</a>
+	</div>
+	<div id="container">
+
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
 </html>
\ No newline at end of file
diff --git a/public/games/cah/loader.css b/public/games/cah/loader.css
new file mode 100644
index 0000000000000000000000000000000000000000..b7ffd888dc96de96f8777db7f964cb3412b962d9
--- /dev/null
+++ b/public/games/cah/loader.css
@@ -0,0 +1,34 @@
+.spanPack {
+	display: inline-block;
+	margin-right: 20px;
+}
+
+#packs {
+	height: auto;
+}
+
+#wCreate {
+
+	position: absolute;
+	bottom: 0;
+	left: 0;
+	width: calc(100vw - 20px);
+	padding: 10px;
+	background-color: cyan;
+	height: max-content;
+}
+
+/* 
+#wButtons {
+	position: absolute;
+	bottom: 0;
+} */
+
+#dButtons {
+	position: absolute;
+	bottom: 0;
+}
+
+.wB {
+	margin: 5px;
+}
\ No newline at end of file
diff --git a/public/games/cah/loader.js b/public/games/cah/loader.js
new file mode 100644
index 0000000000000000000000000000000000000000..7a1adf0957d68ef9a269c1d6a0ece0b58a102579
--- /dev/null
+++ b/public/games/cah/loader.js
@@ -0,0 +1,132 @@
+const c_styles = {};
+async function customCreate() {
+    let w = document.getElementById("wCreate");
+    if (w)
+        w.remove();
+    let d = document.createElement("div");
+    d.id = "wCreate";
+    document.getElementById("container").appendChild(d);
+    let tName = document.createElement("input");
+    tName.setAttribute("type", "text");
+    tName.value = "name";
+    tName.id = "tName";
+    d.appendChild(tName);
+    tName.focus();
+    d.appendChild(document.createElement("br"));
+    let _packs = document.createElement("p");
+    _packs.innerText = "Packs: ";
+    d.appendChild(_packs);
+    //@ts-ignore
+    let add_packs = document.createElement("div");
+    add_packs.id = "packs";
+    d.appendChild(add_packs);
+    getPacks()
+        .then(packs => {
+        for (let pack of packs) {
+            let s = document.createElement("span");
+            s.className = "spanPack";
+            add_packs.appendChild(s);
+            //@ts-ignore
+            let p = document.createElement("input");
+            p.innerText = pack;
+            p.setAttribute("type", "checkbox");
+            s.appendChild(p);
+            let p_id = crypto.getRandomValues(new Uint32Array(1)).toString();
+            p.id = p_id;
+            let l = document.createElement("label");
+            l.setAttribute("for", p_id);
+            l.innerText = pack;
+            s.appendChild(l);
+            p.className = "checkPack";
+            if (pack == "Cards against humanity") {
+                p.setAttribute("disabled", "disabled");
+                p.setAttribute("checked", "checked");
+            }
+        }
+    });
+    d.appendChild(document.createElement("br"));
+    let pwd = document.createElement("div");
+    d.appendChild(pwd);
+    let p = document.createElement("input");
+    p.setAttribute("type", "text");
+    p.id = "tPass";
+    pwd.appendChild(p);
+    p.setAttribute("placeholder", "password");
+    let c = document.createElement("input");
+    c.id = "cPass";
+    c.setAttribute("type", "checkbox");
+    pwd.appendChild(c);
+    c.onmouseup = () => {
+        setInterval(() => {
+            //@ts-ignore
+            if (c.checked)
+                p.setAttribute("type", "text");
+            else
+                p.setAttribute("type", "password");
+        }, 0);
+    };
+    let cpL = document.createElement("label");
+    cpL.setAttribute("for", "cPass");
+    cpL.innerText = "Show password?";
+    pwd.appendChild(cpL);
+    let b = document.createElement("div");
+    d.appendChild(b);
+    let s1 = document.createElement("span");
+    b.appendChild(s1);
+    let bCreate = document.createElement("button");
+    s1.appendChild(bCreate);
+    bCreate.id = "bCreate";
+    bCreate.className = "wB";
+    bCreate.innerText = "Create.";
+    let s2 = document.createElement("span");
+    b.appendChild(s2);
+    let bCancel = document.createElement("button");
+    s2.appendChild(bCancel);
+    bCancel.id = "bCancel";
+    bCancel.innerText = "Cancel.";
+    return new Promise((resolve, reject) => {
+        bCreate.onmouseup = () => {
+            //resolve
+            // console.log(add_packs.child());
+            let packs = [];
+            let ePacks = document.getElementById("packs");
+            // packs.forEach(console.log);
+            for (let p of ePacks.children) {
+                let label = p.getElementsByTagName("label").item(0).innerText;
+                let checked = p.getElementsByTagName("input").item(0).checked;
+                console.log({
+                    label,
+                    checked
+                });
+                if (checked) {
+                    packs.push(label);
+                }
+            }
+            let createData = {
+                //@ts-ignore
+                name: document.getElementById("tName").value,
+                packs: packs,
+                //@ts-ignore
+                password: document.getElementById("tPass").value,
+            };
+            d.remove();
+            resolve(createData);
+        };
+        bCancel.onmouseup = () => {
+            //resolve
+            d.remove();
+            resolve(null);
+        };
+    });
+}
+async function getPacks() {
+    let res = await fetch("/games/api/cah/packs", {
+        method: "GET",
+    });
+    if (res.status == 200) {
+        return res.json();
+    }
+    else {
+        return null;
+    }
+}
diff --git a/public/games/cah/loader.ts b/public/games/cah/loader.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29feaa2e4efe67bbc30db5941fc8b7e9d7c3af02
--- /dev/null
+++ b/public/games/cah/loader.ts
@@ -0,0 +1,171 @@
+interface cahCreateData {
+	name: string,
+		packs: string[],
+
+		max_players ? : number,
+		timeout ? : number,
+		password ? : string,
+}
+
+const c_styles = {}
+
+async function customCreate(): Promise < cahCreateData > {
+
+	let w = document.getElementById("wCreate");
+	if (w) w.remove();
+	let d = document.createElement("div");
+	d.id = "wCreate";
+	document.getElementById("container").appendChild(d);
+
+
+	let tName = document.createElement("input");
+	tName.setAttribute("type", "text");
+	tName.value = "name";
+	tName.id = "tName";
+	d.appendChild(tName);
+	tName.focus();
+
+	d.appendChild(document.createElement("br"));
+	let _packs = document.createElement("p");
+	_packs.innerText = "Packs: ";
+	d.appendChild(_packs);
+
+	//@ts-ignore
+	let add_packs = document.createElement("div");
+	add_packs.id = "packs";
+	d.appendChild(add_packs);
+	getPacks()
+	.then(packs => {
+		for (let pack of packs) {
+			let s = document.createElement("span");
+			s.className = "spanPack";
+			add_packs.appendChild(s);
+			//@ts-ignore
+			let p = document.createElement("input");
+			p.innerText = pack;
+			p.setAttribute("type", "checkbox");
+			s.appendChild(p);
+
+			let p_id = crypto.getRandomValues(new Uint32Array(1)).toString();
+			p.id = p_id;
+
+			let l = document.createElement("label");
+			l.setAttribute("for", p_id);
+			l.innerText = pack;
+			s.appendChild(l);
+			p.className = "checkPack";
+			if (pack == "Cards against humanity") {
+				p.setAttribute("disabled", "disabled");
+				p.setAttribute("checked", "checked");
+			}
+		}
+	});
+
+
+	d.appendChild(document.createElement("br"));
+
+	let pwd = document.createElement("div");
+	d.appendChild(pwd);
+
+	let p = document.createElement("input");
+	p.setAttribute("type", "text");
+	p.id = "tPass";
+	pwd.appendChild(p);
+	p.setAttribute("placeholder", "password");
+
+	let c = document.createElement("input");
+	c.id = "cPass";
+	c.setAttribute("type", "checkbox");
+	pwd.appendChild(c);
+	c.onmouseup = () => {
+		setInterval(() => {
+			//@ts-ignore
+			if (c.checked)
+				p.setAttribute("type", "text");
+			else
+				p.setAttribute("type", "password");
+
+		}, 0)
+	};
+
+	let cpL = document.createElement("label");
+	cpL.setAttribute("for", "cPass");
+	cpL.innerText = "Show password?";
+	pwd.appendChild(cpL);
+
+	let b = document.createElement("div");
+	d.appendChild(b);
+
+
+	let s1 = document.createElement("span");
+	b.appendChild(s1);
+
+	let bCreate = document.createElement("button");
+	s1.appendChild(bCreate);
+	bCreate.id = "bCreate";
+	bCreate.className = "wB";
+	bCreate.innerText = "Create.";
+
+	let s2 = document.createElement("span");
+	b.appendChild(s2);
+
+	let bCancel = document.createElement("button");
+	s2.appendChild(bCancel);
+	bCancel.id = "bCancel";
+	bCancel.innerText = "Cancel.";
+	return new Promise < cahCreateData > ((resolve, reject) => {
+		bCreate.onmouseup = () => {
+
+			//resolve
+
+			// console.log(add_packs.child());
+
+			let packs = [];
+
+			let ePacks = document.getElementById("packs");
+			// packs.forEach(console.log);
+			for (let p of ePacks.children) {
+				let label = p.getElementsByTagName("label").item(0).innerText;
+				let checked = p.getElementsByTagName("input").item(0).checked;
+				console.log({
+					label,
+					checked
+				});
+				if (checked) {
+					packs.push(label);
+				}
+			}
+
+			let createData: cahCreateData = {
+				//@ts-ignore
+				name: document.getElementById("tName").value,
+				packs: packs,
+				//@ts-ignore
+				password: document.getElementById("tPass").value,
+			}
+			d.remove();
+
+			resolve(createData);
+		};
+		bCancel.onmouseup = () => {
+			//resolve
+
+			d.remove();
+			resolve(null);
+		};
+
+	})
+}
+
+async function getPacks(): Promise < string[] > {
+	let res = await fetch("/games/api/cah/packs", {
+		method: "GET",
+	});
+
+	if (res.status == 200) {
+		return res.json();
+	} else {
+		return null;
+	}
+
+}
\ No newline at end of file
diff --git a/public/games/cah/tsconfig.json b/public/games/cah/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..a19a54328f993ca26d564eecdbc93cecbbf93b43
--- /dev/null
+++ b/public/games/cah/tsconfig.json
@@ -0,0 +1,7 @@
+{
+    "compilerOptions": {
+        "module": "CommonJS",
+        "target": "ES2020"
+
+    }
+}
\ No newline at end of file
diff --git a/public/games/game_loader.js b/public/games/game_loader.js
index 05937dfa8569e8e957f8b3b06686d5e2b8d0c4fc..e6a253f062c67ae379258150539784232ead588e 100644
--- a/public/games/game_loader.js
+++ b/public/games/game_loader.js
@@ -43,17 +43,13 @@ function refreshList() {
 			while (children.length > 0) children[0].remove();
 
 			for (let _game of json) {
+				let s = createElement("span");
+				s.parent(GamesList);
+				s.addClass("game");
 				let child = createElement("div");
-				child.parent(GamesList);
+				child.parent(s);
 				child.html(`Join game:<br/>${_game.name}<br/>Players:${_game.players}`);
-				// let join_button = createButton("Join");
-				// join_button.parent(child);
 				child.mouseClicked(joinGame.bind(_game));
-				child.style("text-align", "center")
-				child.style("float", "left")
-				child.style("padding", "7px")
-				child.style("background-color", "lightblue")
-				child.style("margin", "2px")
 
 				// child.html(`<a href='/games/${game}/game.html?id=${_game.id}'>Join</a>`, true);
 				// createElement("li", `${JSON.stringify(game)}`).parent(GamesList);
@@ -81,15 +77,28 @@ function joinGame() {
 
 }
 
-function createGame() {
+async function createGame() {
+	let data = {};
+	if (window.customCreate) {
+		let custom_data = window.customCreate();
+		if (custom_data instanceof Promise) {
+			data = await custom_data;
+		}
+		if (data == null) return;
+	} else {
+		let name = prompt("Please enter game name", "name");
+		if (name == null) return;
+		let password = prompt("Enter password, \nor leave blank for open game.");
+		// let data = {
+		// 	name: name,
+		// 	password: password,
+		// };
+
+		data.name = name;
+		data.password = password;
+
+	}
 
-	let name = prompt("Please enter game name", "name");
-	if (name == null) return;
-	let password = prompt("Enter password, \nor leave blank for open game.");
-	let data = {
-		name: name,
-		password: password,
-	};
 
 	fetch(`/games/api/create/${game}`, {
 		method: "POST",
@@ -104,4 +113,6 @@ function createGame() {
 
 function draw() {
 
-}
\ No newline at end of file
+}
+
+new p5(null, "container");
\ No newline at end of file
diff --git a/public/games/index.html b/public/games/index.html
index a5aa81d3778e13d8e351de6d8fecdca7573d45f1..d8f422a530a85c1aeda5d1713a715d86c642d234 100644
--- a/public/games/index.html
+++ b/public/games/index.html
@@ -1,32 +1,57 @@
 <!DOCTYPE html>
 <html>
-    <head>
-
-    </head>
-    <body>
-        <h1>
-            Game listing
-        </h1>
-
-        <p>
-            <li>
-                <a href="./cah/">
-                    Cards against humanity
-                </a>
-            </li>
-            <li>
-                <a href="./battleships/">
-                    Battleships
-                </a>
-            </li>
-            <li>
-                <a href="./battleships_v2/">
-                    Battleships v2
-                </a>
-            </li>
-            
-        </p>
-
-
-    </body>
+
+<head>
+	<link rel="stylesheet" href="/css/main.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Game listing
+		</h1>
+
+	</div>
+	<div id="container">
+
+		<ul>
+			<li>
+				<a href="./battleships/">
+					Battleships
+				</a>
+			</li>
+			<li>
+				<a href="./battleships_v2/">
+					Battleships v2
+				</a>
+			</li>
+			<li>
+				<a href="./cah/">
+					Cards against humanity
+				</a>
+				(WIP)
+			</li>
+			<li>
+				<a href="./backgammon/">
+					Backgammon
+				</a>
+				(WIP)
+			</li>
+
+		</ul>
+
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
 </html>
\ No newline at end of file
diff --git a/public/games/loader.css b/public/games/loader.css
new file mode 100644
index 0000000000000000000000000000000000000000..491befe647444de251b4dfd6d47ad2d7f990a196
--- /dev/null
+++ b/public/games/loader.css
@@ -0,0 +1,18 @@
+.game {
+	text-align: center;
+	/* float: left; */
+	padding: 7px;
+	background-color: darkblue;
+	margin: 2px;
+	scale: 1.5;
+	margin: 20px;
+	border: 5px;
+	border-style: solid;
+	border-radius: 5px;
+	margin-right: 50px;
+	border-color: #101010;
+}
+
+span {
+	display: inline-block;
+}
\ No newline at end of file
diff --git a/public/games/template/index.html b/public/games/template/index.html
index ec95983991c2d1d940b26add6773cd70fef8f9ca..e755b3abdda7e400f1c71ef1aa6c42f0bb475f82 100644
--- a/public/games/template/index.html
+++ b/public/games/template/index.html
@@ -1,17 +1,37 @@
 <!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>
+
+<head>
+	<title>Battleships</title>
+	<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>
+	<link rel="stylesheet" href="/css/main.css">
+	<link rel="stylesheet" href="../loader.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="subheader">
+
+		<h1>
+			Game Loader Template!
+		</h1>
+
+		<a href="../">Games/</a>
+	</div>
+	<div id="container">
+
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
 </html>
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 805522aa32fe15f0134dcde2a1c6000b6dae0316..ac9dca2e104e0185ff5377caa8d1f73d6e8d1f48 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,16 +1,29 @@
 <!DOCTYPE html>
 <html>
-    <head>
 
-    </head>
-    <body>
-        <h1>
-            Test web-server
-        </h1>
-        <p>
-            <li>
-                <a href="games/">Games</a>
-            </li>
-        </p>
-    </body>
+<head>
+	<title>AM Web!</title>
+	<link rel="stylesheet" href="/css/main.css">
+</head>
+
+<body>
+	<div id="header">
+		<h1>
+			<a href="/" id="home">
+				Alex Mansfield's web server!
+			</a>
+		</h1>
+	</div>
+	<div id="container">
+		<ul>
+			<li>
+				<a href="games/">Games</a>
+			</li>
+		</ul>
+	</div>
+	<div id="footer">
+
+	</div>
+</body>
+
 </html>
\ No newline at end of file
diff --git a/public/js/p5/p5.js b/public/js/p5/p5.js
index 10e98d7954883a47330e69b363b6d1f7a404a9ba..1da9b6bc38a991dd6c837148f55fe5549611ff10 100644
--- a/public/js/p5/p5.js
+++ b/public/js/p5/p5.js
@@ -69872,8 +69872,8 @@
 						}
 
 						var context = this._isGlobal ? window : this;
-						var userSetup = context.setup.bind(this);
-						var userDraw = context.draw.bind(this);
+						var userSetup = context.setup; //.bind(this);
+						var userDraw = context.draw; //.bind(this);
 						if (typeof userDraw === 'function') {
 							if (typeof userSetup === 'undefined') {
 								context.scale(context._pixelDensity, context._pixelDensity);
@@ -69890,7 +69890,7 @@
 								context._registeredMethods.pre.forEach(callMethod);
 								this._inUserDraw = true;
 								try {
-									userDraw();
+									userDraw.call(this);
 								} finally {
 									this._inUserDraw = false;
 								}
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ceb9ba7c0857102b75d5c7e71bc8d67c013433f2
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,3 @@
+#google
+User-agent: Googlebot
+Allow: /
\ No newline at end of file
diff --git a/public/sitemap.xml b/public/sitemap.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c5fa78456dbde5c0ffa05377925131501c0c794d
--- /dev/null
+++ b/public/sitemap.xml
@@ -0,0 +1 @@
+<
\ No newline at end of file
diff --git a/src/gamedb.ts b/src/gamedb.ts
index 69053e3b161b0dc9495028616fb46bad60705233..e3b88e1baae687558dc1daf86c73013de7d0d472 100644
--- a/src/gamedb.ts
+++ b/src/gamedb.ts
@@ -26,6 +26,10 @@ export interface QueryResults {
 	fields ? : mysql.FieldInfo[];
 }
 
+export interface GameType {
+	name: string,
+		id: number,
+}
 
 export class GameDB {
 	pool: mysql.Pool;
@@ -83,13 +87,13 @@ export class GameDB {
 	}
 
 	async getGameData(gameId: number, key ? : number): Promise < game > {
-		let query: string = "SELECT game_id,active_game_type_id,data_key,data_value FROM game_data, active_games WHERE game_id=active_game_id AND game_id=?";
+		let query: string = "SELECT game_id,active_game_type_id,data_key,data_value FROM game_data, active_games WHERE game_id=active_game_id AND game_id=?  AND (data_key = 0 OR ";
 		let args: any[] = [gameId];
 		if (key) {
-			query += " AND data_key=?;";
+			query += " data_key=?);";
 			args.push(key);
 		} else {
-			query += ";";
+			query += " 1 );";
 		}
 		let results = await this.query(query, args);
 		let json = {};
@@ -98,11 +102,69 @@ export class GameDB {
 			json[row.data_key] = JSON.parse(row.data_value);
 		}
 		let game: game = {
-			id: results.results.reduce((acc: any, cur: any) => cur.data_key == 0 ? cur : acc, null).game_id,
+			id: results.results[0].game_id,
 			type_id: results.results[0].active_game_type_id,
 			data: JSON.parse(results.results.reduce((acc: any, cur: any) => cur.data_key == 0 ? cur : acc, null).data_value),
 			json: json
 		}
 		return game;
 	}
+
+	async getConnection(): Promise < mysql.PoolConnection > {
+		return new Promise((resolve, reject) => {
+			this.pool.getConnection((err, conn) => {
+				if (err) reject(err);
+				else resolve(conn);
+			})
+		})
+	}
+
+	async setGameData(gameId: number, json: any, key: number | Array < number > ) {
+		if (key instanceof Array && json instanceof Array) {
+			let conn = await this.getConnection();
+			for (let _k in key) {
+				let k = key[_k];
+				try {
+					await new Promise((resolve, reject) => {
+						conn.query("UPDATE game_data SET data_value=? WHERE game_id=? AND data_key=?;", [json[_k], gameId, k], (err, res, fields) => {
+							console.log(res);
+							if (err) reject(err);
+							else resolve(res);
+						})
+					});
+				} catch (err) {
+					console.log(err);
+					//insert instead of update
+				};
+			}
+			conn.release();
+		} else {
+			try {
+				this.query("UPDATE game_data SET data_value=? WHERE game_id=? AND data_key=?;", [json, gameId, key]);
+			} catch (err) {
+
+				//insert instead of update
+			}
+		}
+	}
+
+	async getGameType(game: string | number): Promise < GameType > {
+		let query: string;
+		if (typeof(game) == "string") {
+			query = "SELECT * FROM games WHERE game_name=?";
+		} else {
+			query = "SELECT * FROM games WHERE game_id=?";
+		}
+		let {
+			results
+		} = await this.query(query, game);
+		if (results[0]) {
+			return {
+				id: results[0].game_id,
+				name: results[0].game_name,
+			}
+		} else {
+			throw null as GameType;
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/games.ts b/src/games.ts
index 64395f2859bbfcd5f0cf36461bcce6c2f18bb785..ee96b1a83f6c85ebd7b8258ed68802bb26563257 100644
--- a/src/games.ts
+++ b/src/games.ts
@@ -9,7 +9,9 @@ import {
 } from "./debug";
 import {
 	Game,
-	GameConstructor
+	GameConstructor,
+	GameDataPacket,
+	GameJoinParams
 } from "./games/game";
 import {
 	resolve
@@ -21,6 +23,7 @@ import {
 	readFileSync
 } from "fs";
 
+
 const DB = {
 	active_games: {
 		table: "active_games",
@@ -72,13 +75,17 @@ const Games: GameDB = new GameDB(MYSQL_CONFIG);
 let socket;
 
 
-function joinGame(socket: io.Socket, args: any) {
+function joinGame(socket: io.Socket, args: GameJoinParams) {
 	console.log(`Join game request : `);
 	console.log(args);
+	if (!args) {
+		socket.emit("kick", "no data");
+		return;
+	}
 
 	//check game id exists
 
-	Games.query(`SELECT active_games.active_game_id, active_games.active_game_type_id, active_games.active_game_idle, game_data.data_value FROM active_games, game_data WHERE active_game_id = ? AND game_data.data_key = 0;`, [args.id])
+	Games.query(`SELECT active_games.active_game_id, active_games.active_game_type_id, active_games.active_game_idle, game_data.data_value FROM active_games, game_data WHERE active_game_id = ? AND game_data.data_key = 0;`, [args.gameId])
 		.then(res => {
 			if (res.err) {
 				console.log(res.err);
@@ -125,10 +132,14 @@ async function API(req: express.Request, res: express.Response, next: express.Ne
 			return;
 			break;
 		default:
+			let g = await getGame(paths[0]);
+			console.log(paths);
+			if (g) g.apiHandler(req, res, next);
+			else next(404);
 			break;
 	}
 
-	next();
+	// next();
 }
 
 function game_api(app: Server) {
@@ -138,12 +149,19 @@ function game_api(app: Server) {
 	socket = io(app);
 	socket.sockets.on("connection", (socket: io.Socket) => {
 		console.log(`New connection : ${socket.id}`);
-		socket.on("join", (data: any) => joinGame(socket, data));
+		socket.on("join", (data: GameJoinParams) => joinGame(socket, data));
 
 		socket.on("disconnect", (args) => {
 			console.log(`Socket disconnected : ${socket.id}`);
 		});
-
+		socket.on("game_data", async (data: GameDataPacket) => {
+			// console.log(data);
+			let game = await getGame(data.gameType);
+			if (game)
+				game.data(socket, data);
+			else
+				console.log("Unable to find game");
+		});
 	});
 
 	return {
@@ -180,8 +198,6 @@ function listGames(game: string, response: express.Response, next: express.NextF
 		.catch(console.log)
 		.catch(next)
 	return;
-
-
 }
 
 let loadedGames: Game[] = [];
@@ -195,7 +211,7 @@ function createGame(game: string, req: express.Request, res: express.Response, n
 	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 => {
+		game.create(req.body).then(active_game_id => {
 			res.send(`Created new ${game.name} game by ${"Alex"} with id ${active_game_id}`);
 			res.end();
 		});
@@ -209,10 +225,12 @@ function getGame(game: string | number): Promise < 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)};`;
 	}
-
+	// console.log(query)
 	return Games.query(query)
 
 		.then(results => {
+			// console.log(results);
+			if (!results.results[0]) return null;
 			// console.log(query);
 			// console.log(results);
 			let game_id: number = results.results[0].game_id;
diff --git a/src/games/backgammon.ts b/src/games/backgammon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8eb525676d5b5c746d352c0fd439f0a98767c7db
--- /dev/null
+++ b/src/games/backgammon.ts
@@ -0,0 +1,31 @@
+import {
+	Game,
+	GameJoinParams,
+	GameDataPacket
+} from "./game";
+import {
+	GameDB
+} from "../gamedb";
+import * as io from "socket.io"
+
+
+class backgammon extends Game {
+	constructor(db: GameDB) {
+		super("backgammon", db);
+	}
+
+	async join(socket: io.Socket, d: GameJoinParams): Promise < boolean > {
+
+		return true;
+	}
+
+	async data(socket: io.Socket, d: GameDataPacket) {
+
+	}
+
+
+}
+
+export {
+	backgammon as game
+};
\ No newline at end of file
diff --git a/src/games/battleships.ts b/src/games/battleships.ts
index b486c83a44276296858d4e454a79f43a8240447a..12b8c2005c83e2894ffa4d2fb8cad82cfffa1191 100644
--- a/src/games/battleships.ts
+++ b/src/games/battleships.ts
@@ -4,7 +4,10 @@ import {
 	GameInitFunction,
 	GameCreateFunction,
 	GameDestroyFunction,
-	GameConstructor
+	GameConstructor,
+	GameDataPacket,
+	GameJoinParams,
+	GameCreateData
 } from "./game";
 import {
 	debug
@@ -42,33 +45,30 @@ interface shot {
 		hit: boolean,
 }
 
-const battleships: GameConstructor = class battleships implements Game {
-
-	public readonly id: number = 1;
-	public readonly name: string = "battleships";
-
-	protected db: GameDB;
+const battleships: GameConstructor = class battleships extends Game {
 	protected players: player[] = [];
 
 	constructor(db_server: GameDB) {
+		super("battleships", db_server)
 		this.db = db_server;
+	}
+	async data(socket: io.Socket, game_data: GameDataPacket) {
+
 	}
 	// init(){}
-	async create(creator: string, password ? : string): Promise < number > {
-		debug(`Battleships creating game by ${creator}`);
+	async create(d: GameCreateData): Promise < number > {
+
+		return super.create(d);
 		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);
 					resolve(results.results.insertId || -1);
 					let json_data: any = {
-						name: creator,
+						name: d.name,
 						players: [],
-						password: password
+						password: d.password
 					};
-					if (password) {
-						json_data.password = password;
-					}
 					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 => {
@@ -85,7 +85,7 @@ const battleships: GameConstructor = class battleships implements Game {
 
 	}
 
-	async join(socket: io.Socket, args: any): Promise < boolean > {
+	async join(socket: io.Socket, args: GameJoinParams): Promise < boolean > {
 
 		console.log(`Joining battleships game`);
 		console.log(args);
@@ -94,13 +94,13 @@ const battleships: GameConstructor = class battleships implements Game {
 
 		//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;`)
+		return this.db.query(`SELECT data_key, data_value FROM game_data WHERE game_id = ${args.gameId} 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) {
+				if (json.password !== args.gamePassword) {
 					socket.emit("disconnect", "Incorrect password");
 					socket.disconnect(true);
 					return false;
@@ -112,18 +112,18 @@ const battleships: GameConstructor = class battleships implements Game {
 				}
 				// socket.emit("welcome", "Successfully joined game!");
 				console.log("Successfully joined game!");
-				json.players.push(args.username);
+				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;`)
+				this.db.query(`UPDATE game_data SET data_value = ${escape(JSON.stringify(json))} WHERE game_id = ${args.gameId} AND data_key = 0;`)
 					.then(() => {
-						return this.db.query(`SELECT data_key, data_value FROM game_data WHERE game_id = ${args.id};`)
+						return this.db.query(`SELECT data_key, data_value FROM game_data WHERE game_id = ${args.gameId};`)
 					})
 					.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);
+						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);
 						if (player_data) {
@@ -147,8 +147,8 @@ const battleships: GameConstructor = class battleships implements Game {
 
 				let player: player = {
 					socket: socket,
-					game_id: args.id,
-					username: args.username,
+					game_id: args.gameId,
+					username: args.userName,
 				}
 
 				this.players.push(player);
diff --git a/src/games/battleships_v2.ts b/src/games/battleships_v2.ts
index 452d792884bd10364842d8ee6267adefef8069b9..74037f9adde3728921a51c894a9e016c6e8b14be 100644
--- a/src/games/battleships_v2.ts
+++ b/src/games/battleships_v2.ts
@@ -6,7 +6,10 @@ import {
 } from "../gamedb";
 import {
 	Game,
-	GameConstructor
+	GameConstructor,
+	GameDataPacket,
+	GameCreateData,
+	GameJoinParams
 } from "./game"
 
 interface player {
@@ -36,10 +39,7 @@ interface shot {
 		hit: boolean,
 }
 
-const battleships_v2: GameConstructor = class battleships_v2 implements Game {
-	db: GameDB;
-	public readonly name: string = "battleships_v2";
-	public readonly id: number = 3;
+const battleships_v2: GameConstructor = class battleships_v2 extends Game {
 	players: player[] = [];
 	ships: ship[] = [{
 			size: 5,
@@ -77,39 +77,37 @@ const battleships_v2: GameConstructor = class battleships_v2 implements Game {
 	y: number = 10;
 
 	constructor(db_server: GameDB) {
-		this.db = db_server;
-		// this.db.query("")
+		super("battleships_v2", db_server);
 	}
 
-	async create(creator: string, password ? : string): Promise < number > {
-		let game_data: game = {
-			id: -1,
-			type_id: this.id,
-			data: {
-				name: creator,
-				players: [],
-				password: password
-			}
-		}
+	async data(socket: io.Socket, game_data: GameDataPacket) {
+
+	}
+
+	async create(d: GameCreateData): Promise < number > {
+		await this.ready;
+
+		let id: number;
 		try {
-			game_data = await this.db.createGame(game_data);
+			id = await super.create(d);
 			this.db.query("INSERT INTO game_data (game_id, data_key, data_value) VALUES\
 			(?, 1, '[]'),(?, 2, '[]');",
-				[game_data.id, game_data.id])
+				[id, id]);
 		} catch (e) {
 			console.log("ERROR: creating game");
 			console.log(e);
 		}
-		return game_data.id;
+		return id;
 	}
 
 	async destroy() {}
 
-	async join(socket: io.Socket, args: any): Promise < boolean > {
+	async join(socket: io.Socket, args: GameJoinParams): Promise < boolean > {
+		await this.ready;
 
 		let p: player = {
-			game_id: args.id,
-			username: args.username,
+			game_id: args.gameId,
+			username: args.userName,
 			socket: socket,
 			player_no: -1,
 		}
@@ -118,7 +116,7 @@ const battleships_v2: GameConstructor = class battleships_v2 implements Game {
 		let game_data: game_data = JSON.parse(game.results[0].data_value);
 		console.log(game_data);
 		if (game_data.password) {
-			if (args.password != game_data.password) {
+			if (args.gamePassword != game_data.password) {
 				return false;
 			}
 		}
@@ -132,10 +130,11 @@ const battleships_v2: GameConstructor = class battleships_v2 implements Game {
 		if (p.player_no >= 2) return false;
 
 		game_data.players[p.player_no] = p.username;
-		this.db.query(`UPDATE game_data SET data_value=? WHERE game_id=? AND data_key=0;`, [JSON.stringify(game_data), p.game_id]);
+		await this.db.query(`UPDATE game_data SET data_value=? WHERE game_id=? AND data_key=0;`, [JSON.stringify(game_data), p.game_id]);
 		let player_data = await this.db.getGameData(p.game_id);
 		let ships: ship[] = player_data.json[p.player_no + 1];
 		//JSON.parse(player_data.results.reduce((acc: any, row: any) => row.data_key == p.player_no + 1 ? row : acc, null).data_value)
+		// if (ships) {
 		setTimeout(() => {
 			socket.emit("placed", ships);
 			socket.emit("place", this.ships[ships.length]);
@@ -157,6 +156,7 @@ const battleships_v2: GameConstructor = class battleships_v2 implements Game {
 		console.log("sent placed ships");
 		console.log(ships);
 		console.log(this.ships[ships.length]);
+		// }
 
 
 		socket.on("place", (ship: ship) => this.place(p, ship));
diff --git a/src/games/cah.ts b/src/games/cah.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea8eebc08107aa1be03646f41c3be75b368e12ff
--- /dev/null
+++ b/src/games/cah.ts
@@ -0,0 +1,210 @@
+import {
+	GameConstructor,
+	Game,
+	GameDataPacket,
+	GameJoinParams,
+	GameCreateData
+} from "./game";
+import {
+	GameDB,
+	game,
+	GameType
+} from "../gamedb";
+import * as io from "socket.io"
+import {
+	readFileSync,
+	readFile
+} from "fs";
+import {
+	threadId
+} from "worker_threads";
+import {
+	Socket
+} from "dgram";
+import {
+	NextFunction,
+	Response,
+	Request
+} from "express";
+import {
+	randomBytes
+} from "crypto";
+
+interface CahCreateData extends GameCreateData {
+
+	packs: string[],
+
+	max_players ? : number,
+	timeout ? : number,
+}
+
+interface player {
+	userName: string,
+		gameId: number,
+		gameToken: string,
+		lastSocket: io.Socket,
+}
+
+interface card {
+	bw: boolean,
+		text: string,
+		pack_name: string,
+		extra ? : {
+			draw ? : number,
+			pick ? : number
+		}
+}
+
+interface pack {
+	name: string,
+		cards: card[],
+}
+
+const cah: GameConstructor = class cah extends Game {
+
+	packs: pack[];
+	cards_ready: Promise < void > ;
+	constructor(db_server: GameDB) {
+		super("cah", db_server);
+		let self = this;
+		this.cards_ready = new Promise < void > ((resolve, reject) => {
+			readFile("./data/games/cah/cards.json", (err, data) => {
+				if (err) {
+					reject(err);
+					return;
+				}
+				// console.log(data.toString());
+				self.packs = JSON.parse(data.toString());
+				for (let pack of this.packs) {
+					console.log(pack.name + ":");
+					for (let card of pack.cards) {
+						console.log("	" + card.text.substr(0, 40));
+					}
+				}
+				resolve();
+			})
+		})
+	}
+
+	async data(socket: io.Socket, data: GameDataPacket) {
+		await this.ready;
+		console.log(data);
+		switch (data.packetType) {
+			case "rand":
+				this.randCard(socket, data);
+				break;
+			case "hand":
+				this.sendHand(socket, data);
+				break;
+		}
+	}
+
+	async create(d: CahCreateData): Promise < number > {
+		await this.ready;
+		let id: number;
+		try {
+			id = await super.create(d);
+			await this.db.query("INSERT INTO game_data (game_id, data_key, data_value) VALUES (?, 1, ?);", [id, JSON.stringify(d.packs)]);
+		} catch (e) {
+			console.log(e);
+		}
+		return id;
+	}
+
+	async join(socket: io.Socket, args: GameJoinParams): Promise < boolean > {
+		await this.ready;
+		await this.cards_ready;
+
+		let p: player = {
+			gameId: args.gameId,
+			gameToken: randomBytes(16).toString("base64"),
+			userName: args.userName,
+			lastSocket: socket
+		}
+		console.log(p);
+
+		return true;
+	}
+
+	async randCard(socket: io.Socket, args: any) {
+		await this.cards_ready;
+
+		let cards = this.packs.reduce((arr: card[], cur) => {
+			return arr.concat(cur.cards)
+		}, []);
+		let d: GameDataPacket = {
+			gameId: -1,
+			gameToken: "",
+			// userName: "",
+			// userToken: "",
+			packetType: "black",
+			gameType: this.id,
+			data: cards[Math.floor(Math.random() * cards.length)],
+		}
+
+		if (!d.data) debugger;
+
+
+		socket.emit("game_data", d);
+
+	}
+
+	async sendHand(socket: io.Socket, data: GameDataPacket) {
+		await this.cards_ready
+		let packs = await this.db.getGameData(data.gameId, 1);
+
+		let game_cards = this.packs.reduce(
+			(arr, cur) => packs.json[1].indexOf(cur.name) != -1 ? arr.concat(cur.cards.reduce(
+				(_arr, _cur) => _cur.bw ? _arr : _arr.concat(_cur), [])) : arr, []);
+		// [])
+		console.log(game_cards);
+		console.trace("WIP");
+		let hand: card[] = [];
+		while (hand.length < 7) {
+			hand.push(game_cards[Math.floor(Math.random() * game_cards.length)]);
+		}
+		let d: GameDataPacket = {
+			gameId: data.gameId,
+			gameToken: data.gameToken,
+			gameType: data.gameType,
+			packetType: "hand",
+			data: hand,
+		}
+		socket.emit("game_data", d);
+	}
+
+	async getPacks(req: Request, res: Response, next: NextFunction) {
+		await this.cards_ready;
+		let packs: string[] = this.packs.reduce((p, c) => {
+			p.push(c.name);
+			return p;
+		}, []);
+		res.type("json");
+		res.send(packs);
+		res.end();
+
+	}
+
+	async apiHandler(req: Request, res: Response, next: NextFunction) {
+		if (req.method == "GET") {
+			let action = req.path.split("/");
+			action = action.slice(action.lastIndexOf("api") + 2);
+			switch (action[0]) {
+				case "packs":
+					this.getPacks(req, res, next);
+					break;
+				default:
+					next(404);
+			}
+		} else if (req.method == "POST") {
+			next(404);
+		}
+	}
+
+
+
+}
+
+export {
+	cah as game
+};
\ No newline at end of file
diff --git a/src/games/cah_cards.json b/src/games/cah_cards.json
new file mode 100644
index 0000000000000000000000000000000000000000..e825b944017e9efe47f0474b96e6ed9b49246ffe
--- /dev/null
+++ b/src/games/cah_cards.json
@@ -0,0 +1,316 @@
+[{
+		"name": "Cards against humanity",
+		"cards": [{
+				"bw": false,
+				"text": "Silence.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "The illusion of shoice in a late-stage capitalist society.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Many bats.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Famine.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Flesh-eating bacteria.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Flying sex snakes.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Not giving a shit about the Third World.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Magnets.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Shapeshifters.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Seeing what happens when you lock people in a room with hungry seagulls.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A crucifixion.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Jennifer Lawrence.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "72 virgins.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A live studio audience.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "A time travel paradox.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Authentic Mexican cuisine.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Doing crimes.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Synergistic management solutions.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Crippling debt.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "Daddy issues.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": false,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Hey Reddit! I’m __________________. Ask me anything.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Introducing X-treme Baseball! It’s like baseball, but with __________!",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "What is Batman’s guilty pleasure.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "TSA guidelinesnow prohibit __________________ on airplanes",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Next from J.K. Rowling: Harry Potter and the Chamber of __________________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "That’s right, I killed _________________. How, you ask? _________________.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"pick": 2
+				}
+			},
+			{
+				"bw": true,
+				"text": "I’m sorry, Professor, but I couldn’t complete my homework because of __________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "And the Academy Award for __________________ goes to __________.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"pick": 2
+				}
+			},
+			{
+				"bw": true,
+				"text": "Dude, do not go in that bathroom. There’s __________ in there.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "How did I losemy virginity?",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "It’s a pity that kids these days are all getting involved with _____________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Step 1: __________. Step 2: __________. Step 3: Profit.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"pick": 2
+				}
+			},
+			{
+				"bw": true,
+				"text": "_________________. Betcha can’t have just one!",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "Kids, I don’t need drugs to get high. I’m high on __________.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "For my next trick, I will pull __________ out of ___________.",
+				"pack_name": "Cards against humanity",
+				"extra": {
+					"pick": 2
+				}
+			},
+			{
+				"bw": true,
+				"text": "While the United States raced the Soviet Union to the moon, the Mexican government funneled millions of pesos into research on _____________________",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "In the Disney Channel Original Movie, Hannah Montana struggles with _____________ for the first time.",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "What’s mysecret power?",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			},
+			{
+				"bw": true,
+				"text": "",
+				"pack_name": "Cards against humanity"
+			}
+		]
+	},
+	{
+		"name": "Covid-19 Pack",
+		"cards": [{
+				"bw": false,
+				"text": "Coronavirus.",
+				"pack_name": "Covid-19 Pack"
+			},
+			{
+				"bw": false,
+				"text": "Boris Johnson in an ICU.",
+				"pack_name": "Covid-19 Pack"
+			}
+		]
+	}
+]
\ No newline at end of file
diff --git a/src/games/game.ts b/src/games/game.ts
index 902a89649e099bf37dc618c17b075475ed38e49e..40bdd982bc542d3662ec038a2d9ed77bd2e26c79 100644
--- a/src/games/game.ts
+++ b/src/games/game.ts
@@ -1,23 +1,107 @@
 import * as mysql from "mysql"
 import {
-	GameDB
+	GameDB,
+	GameType,
+	game
 } from "../gamedb";
 import * as io from "socket.io"
+import {
+	Request,
+	Response,
+	NextFunction
+} from "express";
 
 
 export type GameInitFunction = (db_server: GameDB) => void;
-export type GameCreateFunction = (creator: string, password ? : string) => Promise < number > ;
+export type GameCreateFunction = (d: GameCreateData) => 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;
 }
+export type GameDataFunction = (socket: io.Socket, game_data: GameDataPacket) => void;
+export interface GameCreateData {
+	name: string,
+		password ? : string,
+}
 
-export interface Game {
+export interface GameDataPacket {
+	gameId: number,
+		gameType: number,
+		gameToken: string,
+		packetType: string,
+		data ? : any,
+		userName ? : string,
+		userToken ? : string,
+}
+
+export interface GameJoinParams {
+	gameId: number,
+		userName: string,
+		gamePassword: string,
+}
+
+export type GameAPIHandlerFunction = (req: Request, res: Response, next: NextFunction) => void;
+
+interface _Game {
 	create: GameCreateFunction;
+	data: GameDataFunction;
 	destroy: GameDestroyFunction;
 	join: GameJoinFunction;
+	apiHandler: GameAPIHandlerFunction;
 	name: string;
 	id: number;
 	[key: string]: any;
+}
+
+let getGameType: Function = async function(name: string, db: GameDB): Promise < GameType > {
+	return db.getGameType(name)
+		.catch(() => {
+			return db.query("INSERT INTO games (game_name) VALUES (?);", name)
+				.then(getGameType(name, db));
+		})
+
+};
+
+export abstract class Game implements _Game {
+	public name: string;
+	public id: number;
+	protected db: GameDB;
+	protected ready: Promise < void > ;
+	constructor(name: string, db: GameDB) {
+		this.name = name;
+		this.db = db;
+		this.ready = getGameType(this.name, this.db)
+			.then((g: GameType) => {
+				this.id = g.id;
+			});
+	}
+	abstract async data(socket: io.Socket, data: GameDataPacket): Promise < void > ;
+
+	async create(d: GameCreateData): Promise < number > {
+		await this.ready;
+		let game: game = {
+			id: -1,
+			type_id: this.id,
+			data: {
+				name: d.name,
+				players: [],
+				password: d.password,
+			},
+		}
+		game = await this.db.createGame(game);
+
+		return game.id;
+	}
+
+	async destroy() {
+
+	}
+
+	abstract async join(socket: io.Socket, args: GameJoinParams): Promise < boolean > ;
+
+	apiHandler(req: Request, res: Response, next: NextFunction) {
+		next(404);
+	}
+
 }
\ No newline at end of file
diff --git a/src/sendFile.ts b/src/sendFile.ts
index 023bbd0f105bbf43f63456330b3305488f5a203d..700ff6753fe5481a0808f0de36f2829a73847cca 100644
--- a/src/sendFile.ts
+++ b/src/sendFile.ts
@@ -1,29 +1,40 @@
 import * as express from "express";
 import * as fs from "fs";
 
-function staticServe(root : string){
-    return function (req : express.Request, res : express.Response, next : express.NextFunction){
-        let fullpath = root+req.path;
-        sendFile(fullpath, req, res, next);
-    }
+function staticServe(root: string) {
+	return function(req: express.Request, res: express.Response, next: express.NextFunction) {
+		let fullpath = root + req.path;
+		sendFile(fullpath, req, res, next);
+	}
 }
 
-function sendFile(path : string, req : express.Request, res : express.Response, next : express.NextFunction){
-    fs.exists(path, (exists : boolean) => {
-        if(!exists){
-            next(404);
-            return;
-        }
-        let rs = fs.createReadStream(path);
-        res.type(path.substr(path.lastIndexOf(".")));
-        rs.on("data", (data : Buffer) => {
-            res.write(data);
-        });
-        rs.on("end", () => {
-            res.end();
-            rs.close();
-        });        
-    })
+function sendFile(path: string, req: express.Request, res: express.Response, next: express.NextFunction) {
+	fs.exists(path, (exists: boolean) => {
+		if (!exists) {
+			next(404);
+			return;
+		}
+		fs.stat(path, (err, stat) => {
+			if (stat.isDirectory()) {
+				res.redirect(req.path + "/");
+				res.end();
+			} else {
+				let rs = fs.createReadStream(path);
+				res.type(path.substr(path.lastIndexOf(".")));
+				rs.on("data", (data: Buffer) => {
+					res.write(data);
+				});
+				rs.on("end", () => {
+					res.end();
+					rs.close();
+				});
+			}
+
+		})
+	})
 }
 
-export {sendFile, staticServe};
\ No newline at end of file
+export {
+	sendFile,
+	staticServe
+};
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index e74bbca1a14ba0c74fe6e34f782b4e4b710be4c0..a8a22c014106b522d560a647785fdcb0b2ce71bc 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,17 +1,16 @@
 {
-    "compilerOptions": {
-        "module": "CommonJS",
-        "target": "ES2020",
-        "sourceMap": true,
-        "removeComments": false,
-        "outDir": "dst/",
-        "noImplicitAny": true,
-        
-    },
-    "include": [
-        "src/**/*"
-    ],
-    "exclude": [
-        "node_modules"
-    ]
+	"compilerOptions": {
+		"module": "CommonJS",
+		"target": "ES2020",
+		"sourceMap": true,
+		"removeComments": false,
+		"outDir": "dst/",
+		"noImplicitAny": true,
+	},
+	"include": [
+		"src/**/*"
+	],
+	"exclude": [
+		"node_modules"
+	]
 }
\ No newline at end of file