From a3ab5fdb6e9d614f55bb7ecef5887ddb7875df4b Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 15 Jan 2019 17:52:35 +0100
Subject: [PATCH] Start thinking on room.js: TODO, implement and test challenge
 / new game HH / abort,resign,draw

---
 public/javascripts/components/board.js    |   2 +
 public/javascripts/components/game.js     | 218 +++++++++++++---------
 public/javascripts/components/gameList.js |   5 +-
 public/javascripts/components/moveList.js |   7 +-
 public/javascripts/components/room.js     |  34 ++--
 public/javascripts/settings.js            |  23 ---
 public/javascripts/utils/misc.js          |   4 +-
 public/javascripts/utils/squareId.js      |   5 +-
 public/javascripts/utils/storage.js       |   1 +
 public/javascripts/variant.js             |  31 ++-
 views/variant.pug                         |   9 +-
 11 files changed, 173 insertions(+), 166 deletions(-)
 delete mode 100644 public/javascripts/settings.js

diff --git a/public/javascripts/components/board.js b/public/javascripts/components/board.js
index 5a75f298..a59e41f7 100644
--- a/public/javascripts/components/board.js
+++ b/public/javascripts/components/board.js
@@ -1,3 +1,5 @@
+// This can work for squared boards (2 or 4 players), with some adaptations (TODO)
+// TODO: for 3 players, write a "board3.js"
 Vue.component('my-board', {
 	// Last move cannot be guessed from here, and is required to highlight squares
 	// vr: object to check moves, print board...
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index 37cdec19..d370d66e 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -1,14 +1,15 @@
-// TODO: envoyer juste "light move", sans FEN ni notation ...etc
-// TODO: also "observers" prop (human mode only), we should send moves to them too (in a web worker ? webRTC ?)
 // Game logic on a variant page: 3 modes, analyze, computer or human
+// TODO: envoyer juste "light move", sans FEN ni notation ...etc
+// TODO: if I'm an observer and player(s) disconnect/reconnect, how to find me ?
 Vue.component('my-game', {
 	// gameId: to find the game in storage (assumption: it exists)
 	// fen: to start from a FEN without identifiers (analyze mode)
-	// subMode: "auto" (game comp vs comp) or "corr" (correspondance game)
-	props: ["conn","gameId","fen","mode","subMode","allowChat","allowMovelist","queryHash","settings"],
+	// subMode: "auto" (game comp vs comp) or "corr" (correspondance game),
+	// or "examine" (after human game: TODO)
+	props: ["conn","gameId","fen","mode","subMode","allowChat","allowMovelist",
+		"queryHash","settings"],
 	data: function() {
 		return {
-			oppConnected: false, //TODO?
 			// Web worker to play computer moves without freezing interface:
 			compWorker: new Worker('/javascripts/playCompMove.js'),
 			timeStart: undefined, //time when computer starts thinking
@@ -16,8 +17,10 @@ Vue.component('my-game', {
 			endgameMessage: "",
 			orientation: "w",
 			lockCompThink: false, //used to avoid some ghost moves
-
-			oppid: "", //opponent ID in case of HH game
+			myname: user.name, //may be anonymous (thus no name)
+			opponents: {}, //filled later (potentially 2 or 3 opponents)
+			drawOfferSent: false, //did I just ask for draw?
+			people: {}, //observers
 			score: "*", //'*' means 'unfinished'
 			// userColor: given by gameId, or fen in problems mode (if no game Id)...
 			mycolor: "w",
@@ -32,24 +35,7 @@ Vue.component('my-game', {
 			// (Security) No effect if a computer move is in progress:
 			if (this.mode == "computer" && this.lockCompThink)
 				return this.$emit("computer-think");
-			this.vr = new VariantRules(newFen);
-			this.moves = [];
-			this.cursor = -1;
-			this.fenStart = newFen;
-			this.score = "*";
-			if (this.mode == "analyze")
-			{
-				this.mycolor = V.ParseFen(newFen).turn;
-				this.orientation = "w"; //convention (TODO?!)
-			}
-			else if (this.mode == "computer") //only other alternative (HH with gameId)
-			{
-				this.mycolor = (Math.random() < 0.5 ? "w" : "b");
-				this.orientation = this.mycolor;
-				this.compWorker.postMessage(["init",newFen]);
-				if (this.mycolor != "w" || this.subMode == "auto")
-					this.playComputerMove();
-			}
+			this.newGameFromFen(newFen);
 		},
 		gameId: function() {
 			this.loadGame();
@@ -73,9 +59,6 @@ Vue.component('my-game', {
 		},
 	},
 	// Modal end of game, and then sub-components
-	// TODO: provide chat parameters (connection, players ID...)
-	// TODO: controls: abort, clear, resign, draw (avec confirm box)
-	// TODO: add corrMsg to sent move in case of corr game
 	template: `
 		<div class="col-sm-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
 			<input id="modal-eog" type="checkbox" class="modal"/>
@@ -88,7 +71,8 @@ Vue.component('my-game', {
 					</h3>
 				</div>
 			</div>
-			<my-chat v-if="showChat">
+			<my-chat v-if="showChat" :conn="conn" :myname="myname"
+				:opponents="opponents" :people="people">
 			</my-chat>
 			<my-board v-bind:vr="vr" :last-move="lastMove" :mode="mode"
 				:orientation="orientation" :user-color="mycolor" :settings="settings"
@@ -101,6 +85,11 @@ Vue.component('my-game', {
 				<button @click="gotoBegin">GotoBegin</button>
 				<button @click="gotoEnd">GotoEnd</button>
 			</div>
+			<div v-if="mode=='human'" class="button-group">
+				<button @click="offerDraw">Draw</button>
+				<button @click="abortGame">Abort</button>
+				<button @click="resign">Resign</button>
+			</div>
 			<div v-if="mode=='human' && subMode=='corr'">
 				<textarea v-show="score=='*' && vr.turn==mycolor" v-model="corrMsg">
 				</textarea>
@@ -132,30 +121,34 @@ Vue.component('my-game', {
 			this.vr = new VariantRules(this.fen);
 			this.fenStart = this.fen;
 		}
-		// TODO: after game, archive in indexedDB
-		// TODO: this events listener is central. Refactor ? How ?
+		// TODO: also handle "draw accepted" (use opponents array?)
+		// --> must give this info also when sending lastState...
+		// and, if all players agree then OK draw (end game ...etc)
 		const socketMessageListener = msg => {
 			const data = JSON.parse(msg.data);
 			let L = undefined;
 			switch (data.code)
 			{
 				case "newmove": //..he played!
-					this.play(data.move, (variant.name!="Dark" ? "animate" : null));
+					this.play(data.move, variant.name!="Dark" ? "animate" : null);
 					break;
 				case "pong": //received if we sent a ping (game still alive on our side)
 					if (this.gameId != data.gameId)
 						break; //games IDs don't match: definitely over...
 					this.oppConnected = true;
-					// Send our "last state" informations to opponent
+					// Send our "last state" informations to opponent(s)
 					L = this.vr.moves.length;
-					this.conn.send(JSON.stringify({
-						code: "lastate",
-						oppid: this.oppid,
-						gameId: this.gameId,
-						lastMove: (L>0?this.vr.moves[L-1]:undefined),
-						movesCount: L,
-					}));
+					Object.keys(this.opponents).forEach(oid => {
+						this.conn.send(JSON.stringify({
+							code: "lastate",
+							oppid: oid,
+							gameId: this.gameId,
+							lastMove: (L>0?this.vr.moves[L-1]:undefined),
+							movesCount: L,
+						}));
+					});
 					break;
+				// TODO: refactor this, because at 3 or 4 players we may have missed 2 or 3 moves (not just one)
 				case "lastate": //got opponent infos about last move
 					L = this.vr.moves.length;
 					if (this.gameId != data.gameId)
@@ -178,7 +171,7 @@ Vue.component('my-game', {
 						// We must tell last move to opponent
 						this.conn.send(JSON.stringify({
 							code: "lastate",
-							oppid: this.oppid,
+							oppid: this.opponent.id,
 							gameId: this.gameId,
 							lastMove: this.vr.moves[L-1],
 							movesCount: L,
@@ -193,18 +186,24 @@ Vue.component('my-game', {
 				// TODO: also use (dis)connect info to count online players?
 				case "connect":
 				case "disconnect":
-					if (this.mode=="human" && this.oppid == data.id)
-						this.oppConnected = (data.code == "connect");
-					if (this.oppConnected && this.score != "*")
+					if (this.mode=="human")
 					{
-						// Send our name to the opponent, in case of he hasn't it
-						this.conn.send(JSON.stringify({
-							code:"myname", name:this.myname, oppid: this.oppid}));
+						const online = (data.code == "connect");
+						// If this is an opponent ?
+						if (!!this.opponents[data.id])
+							this.opponents[data.id].online = online;
+						else
+						{
+							// Or an observer ?
+							if (!online)
+								delete this.people[data.id];
+							else
+								this.people[data.id] = data.name;
+						}
 					}
 					break;
 			}
 		};
-
 		const socketCloseListener = () => {
 			this.conn.addEventListener('message', socketMessageListener);
 			this.conn.addEventListener('close', socketCloseListener);
@@ -214,8 +213,7 @@ Vue.component('my-game', {
 			this.conn.onmessage = socketMessageListener;
 			this.conn.onclose = socketCloseListener;
 		}
-
-		// Computer moves web worker logic: (TODO: also for observers in HH games)
+		// Computer moves web worker logic: (TODO: also for observers in HH games ?)
 		this.compWorker.postMessage(["scripts",variant.name]);
 		this.compWorker.onmessage = e => {
 			this.lockCompThink = true; //to avoid some ghost moves
@@ -234,16 +232,84 @@ Vue.component('my-game', {
 			}, delay);
 		}
 	},
-	// this.conn est une prop, donnée depuis variant.js
-	//dans variant.js (plutôt room.js) conn gère aussi les challenges
-	// Puis en webRTC, repenser tout ça.
+	// dans variant.js (plutôt room.js) conn gère aussi les challenges
+	// et les chats dans chat.js. Puis en webRTC, repenser tout ça.
 	methods: {
+		offerDraw: function() {
+			if (!confirm("Offer draw?"))
+				return;
+			// Stay in "draw offer sent" state until next move is played
+			this.drawOfferSent = true;
+			if (this.subMode == "corr")
+			{
+				// TODO: set drawOffer on in game (how ?)
+			}
+			else //live game
+			{
+				this.opponents.forEach(o => {
+					if (!!o.online)
+					{
+						try {
+							this.conn.send(JSON.stringify({code: "draw", oppid: o.id}));
+						} catch (INVALID_STATE_ERR) {
+							return;
+						}
+					}
+				});
+			}
+		},
+		// + conn handling: "draw" message ==> agree for draw (if we have "drawOffered" at true)
+		receiveDrawOffer: function() {
+			//if (...)
+			// TODO: ignore if preventDrawOffer is set; otherwise show modal box with option "prevent future offers"
+			// if accept: send message "draw"
+		},
+		abortGame: function() {
+			if (!confirm("Abort the game?"))
+				return;
+			//+ bouton "abort" avec score == "?" + demander confirmation pour toutes ces actions,
+			//send message: "gameOver" avec score "?"
+		},
+		resign: function(e) {
+			if (!confirm("Resign the game?"))
+				return;
+			if (this.mode == "human" && this.oppConnected(this.oppid))
+			{
+				try {
+					this.conn.send(JSON.stringify({code: "resign", oppid: this.oppid}));
+				} catch (INVALID_STATE_ERR) {
+					return;
+				}
+			}
+			this.endGame(this.mycolor=="w"?"0-1":"1-0");
+		},
 		translate: translate,
+		newGameFromFen: function(fen) {
+			this.vr = new VariantRules(fen);
+			this.moves = [];
+			this.cursor = -1;
+			this.fenStart = newFen;
+			this.score = "*";
+			if (this.mode == "analyze")
+			{
+				this.mycolor = V.ParseFen(newFen).turn;
+				this.orientation = this.mycolor;
+			}
+			else if (this.mode == "computer") //only other alternative (HH with gameId)
+			{
+				this.mycolor = (Math.random() < 0.5 ? "w" : "b");
+				this.orientation = this.mycolor;
+				this.compWorker.postMessage(["init",newFen]);
+				if (this.mycolor != "w" || this.subMode == "auto")
+					this.playComputerMove();
+			}
+		},
 		loadGame: function() {
 			const game = getGameFromStorage(this.gameId);
-			this.oppid = game.oppid; //opponent ID in case of running HH game
+			this.opponent.id = game.oppid; //opponent ID in case of running HH game
+			this.opponent.name = game.oppname; //maye be blank (if anonymous)
 			this.score = game.score;
-			this.mycolor = game.mycolor || "w";
+			this.mycolor = game.mycolor;
 			this.fenStart = game.fenStart;
 			this.moves = game.moves;
 			this.cursor = game.moves.length-1;
@@ -317,29 +383,12 @@ Vue.component('my-game', {
 		endGame: function(score) {
 			this.score = score;
 			this.showScoreMsg(score);
-			this.$emit("game-over", score);
 			if (this.mode == "human")
-			{
 				localStorage["score"] = score;
-				if (this.oppConnected)
-				{
-					// Send our nickname to opponent
-					this.conn.send(JSON.stringify({
-						code:"myname", name:this.myname, oppid:this.oppid}));
-				}
-			}
+			this.$emit("game-over");
 		},
-		resign: function(e) {
-			this.getRidOfTooltip(e.currentTarget);
-			if (this.mode == "human" && this.oppConnected)
-			{
-				try {
-					this.conn.send(JSON.stringify({code: "resign", oppid: this.oppid}));
-				} catch (INVALID_STATE_ERR) {
-					return; //socket is not ready (and not yet reconnected)
-				}
-			}
-			this.endGame(this.mycolor=="w"?"0-1":"1-0");
+		oppConnected: function(uid) {
+			return this.opponents.any(o => o.id == uidi && o.online);
 		},
 		playComputerMove: function() {
 			this.timeStart = Date.now();
@@ -370,7 +419,7 @@ Vue.component('my-game', {
 				for (let i=0; i<squares.length; i++)
 					squares.item(i).style.zIndex = "auto";
 				movingPiece.style = {}; //required e.g. for 0-0 with KR swap
-				this.play(move); //TODO: plutôt envoyer message "please play"
+				this.play(move);
 			}, 250);
 		},
 		play: function(move, programmatic) {
@@ -395,6 +444,10 @@ Vue.component('my-game', {
 				return this.animateMove(move);
 			}
 			// Not programmatic, or animation is over
+			if (this.mode == "human" && this.subMode == "corr" && this.mycolor == this.vr.turn)
+			{
+				// TODO: show confirm box "validate move ?"
+			}
 			if (!move.notation)
 				move.notation = this.vr.getNotation(move);
 			if (!move.color)
@@ -434,7 +487,6 @@ Vue.component('my-game', {
 					this.endGame(score);
 				else //just show score on screen (allow undo)
 					this.showScoreMsg(score);
-				// TODO: notify end of game (give score)
 			}
 			// subTurn condition for Marseille (and Avalanche) rules
 			else if ((this.mode == "computer" && (!this.vr.subTurn || this.vr.subTurn <= 1))
@@ -457,15 +509,13 @@ Vue.component('my-game', {
 			this.vr.undo(move);
 			this.cursor--;
 			this.lastMove = (this.cursor >= 0 ? this.moves[this.cursor] : undefined);
-			if (navigate)
-				this.$children[0].$forceUpdate(); //TODO!?
 			if (this.settings.sound == 2)
 				new Audio("/sounds/undo.mp3").play().catch(err => {});
 			this.incheck = this.vr.getCheckSquares(this.vr.turn);
-			if (!navigate && this.mode == "analyze")
-				this.moves.pop();
 			if (navigate)
-				this.$forceUpdate(); //TODO!?
+				this.$children[0].$forceUpdate(); //TODO!?
+			else if (this.mode == "analyze") //TODO: can this happen?
+				this.moves.pop();
 		},
 		gotoMove: function(index) {
 			this.vr = new VariantRules(this.moves[index].fen);
@@ -485,7 +535,3 @@ Vue.component('my-game', {
 		},
 	},
 })
-//TODO: confirm dialog with "opponent offers draw", avec possible bouton "prevent future offers" + bouton "proposer nulle"
-//+ bouton "abort" avec score == "?" + demander confirmation pour toutes ces actions,
-//comme sur lichess
-//TODO: quand partie terminée (ci-dessus) passer partie dans indexedDB
diff --git a/public/javascripts/components/gameList.js b/public/javascripts/components/gameList.js
index 3fcb40e5..371c89b9 100644
--- a/public/javascripts/components/gameList.js
+++ b/public/javascripts/components/gameList.js
@@ -1,4 +1,5 @@
 // TODO
 //My games : (tabs)
-//mes parties en cours --> démarrer là-dessus si y en a et c'est à moi de jouer ?
-//mes parties terminées (possibilité de supprimer)
+//mes parties corr en cours
+//mes parties (toutes) terminées (possibilité de supprimer)
+//parties importées
diff --git a/public/javascripts/components/moveList.js b/public/javascripts/components/moveList.js
index ded7d86d..48a8e05c 100644
--- a/public/javascripts/components/moveList.js
+++ b/public/javascripts/components/moveList.js
@@ -1,15 +1,14 @@
-//TODO: component for moves list on the right
-// TODO: generic "getPGN" in the same way (following move.color)
+// Component for moves list on the right
 Vue.component('my-move-list', {
 	props: ["moves","cursor"], //TODO: other props for e.g. players names + connected indicator
 	// --> we could also add turn indicator here
-	// + missing "cursor" prop
 	data: function() {
 		return {
-			something: "", //TODO
+			something: "", //TODO?
 		};
 	},
 	// TODO: extend rendering for more than 2 colors: would be a parameter
+	// in that case some moves for some colors could be just skipped (if a player lost)
 	render(h) {
 		if (this.moves.length == 0)
 			return;
diff --git a/public/javascripts/components/room.js b/public/javascripts/components/room.js
index f5daee28..c8071094 100644
--- a/public/javascripts/components/room.js
+++ b/public/javascripts/components/room.js
@@ -7,18 +7,8 @@ div(role="dialog" aria-labelledby="newGameTxt")
 		h3#newGameTxt= translations["New game"]
 		p= translations["Waiting for opponent..."]
 */
-
-
-
-
-// TODO: my-challenge-list, gérant clicks sur challenges, affichage, réception/émission des infos sur challenges
-// de même, my-player-list
-
-
-
-
+// TODO: my-challenge-list, gérant clicks sur challenges, affichage, réception/émission des infos sur challenges ; de même, my-player-list
 // TODO: si on est en train de jouer une partie, le notifier aux nouveaux connectés
-
 /*
 Players + challenges : == "room" home of variant (surligner si nouveau défi perso et pas affichage courant)
 joueurs en ligne (dte),
@@ -29,14 +19,20 @@ chat général (gauche, activé ou non (bool global storage)).
 quand je poste un lastMove corr, supprimer mon ancien lastMove le cas échéant (tlm l'a eu)
 fin de partie corr: garder maxi nbPlayers lastMove sur serveur, pendant 7 jours (arbitraire)
 */
-				case "newgame": //opponent found
-					// oppid: opponent socket ID
+Vue.component('my-room', {
+	props: ["conn","settings"],
+	data: {
+		something: "", //TODO
+	},
+
+});
+
+				case "newgame": //challenge accepted
+					// oppid: opponent socket ID (or DB id if registered)
 					this.newGame("human", data.fen, data.color, data.oppid, data.gameid);
 					break;
 
-		// TODO: elsewhere, probably (new game button)
 		clickGameSeek: function(e) {
-			this.getRidOfTooltip(e.currentTarget);
 			if (this.mode == "human" && this.score == "*")
 				return; //no newgame while playing
 			if (this.seek)
@@ -48,7 +44,6 @@ fin de partie corr: garder maxi nbPlayers lastMove sur serveur, pendant 7 jours
 				this.newGame("human");
 		},
 		clickComputerGame: function(e) {
-			this.getRidOfTooltip(e.currentTarget);
 			if (this.mode == "computer" && this.score == "*"
 				&& this.vr.turn != this.mycolor)
 			{
@@ -57,11 +52,6 @@ fin de partie corr: garder maxi nbPlayers lastMove sur serveur, pendant 7 jours
 			}
 			this.newGame("computer");
 		},
-		clickFriendGame: function(e) {
-			this.getRidOfTooltip(e.currentTarget);
-			document.getElementById("modal-fenedit").checked = true;
-		},
-		// In main hall :
 		newGame: function(mode, fenInit, color, oppId, gameId) {
 			const fen = fenInit || VariantRules.GenRandInitFen();
 			console.log(fen); //DEBUG
@@ -183,7 +173,7 @@ fin de partie corr: garder maxi nbPlayers lastMove sur serveur, pendant 7 jours
 		},
 		
 	
-	// TODO: option du bouton "new game"
+	// TODO: option du bouton "new game" :: seulement pour challenge indiv
 	const modalFenEdit = [
 			h('input',
 				{
diff --git a/public/javascripts/settings.js b/public/javascripts/settings.js
deleted file mode 100644
index f9d7649c..00000000
--- a/public/javascripts/settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// TODO:
-//à chaque onChange, envoyer matching event settings update
-//(par exemple si mise à jour du nom, juste envoyer cet update aux autres connectés ...etc)
-//		setMyname: function(e) {
-//			this.myname = e.target.value;
-//			localStorage["username"] = this.myname;
-//		},
-//		showSettings: function(e) {
-//			this.getRidOfTooltip(e.currentTarget);
-//			document.getElementById("modal-settings").checked = true;
-//		},
-//		toggleHints: function() {
-//			this.hints = !this.hints;
-//			localStorage["hints"] = (this.hints ? "1" : "0");
-//		},
-//		setBoardColor: function(e) {
-//			this.bcolor = e.target.options[e.target.selectedIndex].value;
-//			localStorage["bcolor"] = this.bcolor;
-//		},
-//		setSound: function(e) {
-//			this.sound = parseInt(e.target.options[e.target.selectedIndex].value);
-//			localStorage["sound"] = this.sound;
-//		},
diff --git a/public/javascripts/utils/misc.js b/public/javascripts/utils/misc.js
index 3643fa7f..287c0ea6 100644
--- a/public/javascripts/utils/misc.js
+++ b/public/javascripts/utils/misc.js
@@ -31,7 +31,6 @@ function getRandString()
 // Used both on index and variant page, to switch language
 function setLanguage(e)
 {
-	console.log(e);
 	setCookie("lang", e.target.value);
 	location.reload(); //to include the right .pug file
 }
@@ -42,6 +41,7 @@ function doClick(elemId)
 	document.getElementById(elemId).click(); //or ".checked = true"
 }
 
-function translate(msg) {
+function translate(msg)
+{
 	return translations[msg];
 }
diff --git a/public/javascripts/utils/squareId.js b/public/javascripts/utils/squareId.js
index a3423f7b..8fec48eb 100644
--- a/public/javascripts/utils/squareId.js
+++ b/public/javascripts/utils/squareId.js
@@ -6,7 +6,8 @@ function getSquareId(o)
 }
 
 // Inverse function
-function getSquareFromId(id) {
-	let idParts = id.split('-');
+function getSquareFromId(id)
+{
+	const idParts = id.split('-');
 	return [parseInt(idParts[1]), parseInt(idParts[2])];
 }
diff --git a/public/javascripts/utils/storage.js b/public/javascripts/utils/storage.js
index 9217c134..80087774 100644
--- a/public/javascripts/utils/storage.js
+++ b/public/javascripts/utils/storage.js
@@ -40,6 +40,7 @@ function getGameFromStorage(gameId)
 		// Retrieve running game from localStorage
 		game.score = localStorage.getItem("score");
 		game.oppid = localStorage.getItem("oppid");
+		game.oppname = localStorage.getItem("oppname");
 		game.mycolor = localStorage.getItem("mycolor");
 		game.fenStart = localStorage.getItem("fenStart");
 		game.moves = localStorage.getItem("moves");
diff --git a/public/javascripts/variant.js b/public/javascripts/variant.js
index 606c1b9a..5a6c224a 100644
--- a/public/javascripts/variant.js
+++ b/public/javascripts/variant.js
@@ -1,11 +1,13 @@
 new Vue({
 	el: "#VueElement",
 	data: {
-		display: "undefined", //default to main hall; see "created()" function
+		display: "", //default to main hall; see "created()" function
 		gameid: undefined, //...yet
 		queryHash: "",
 		conn: null,
-
+		mode: "analyze",
+		allowChat: false,
+		allowMovelist: true,
 		// Settings initialized with values from localStorage
 		settings:	{
 			bcolor: localStorage["bcolor"] || "lichess",
@@ -15,14 +17,6 @@ new Vue({
 			highlight: !!eval(localStorage["highlight"]),
 			sqSize: parseInt(localStorage["sqSize"]),
 		},
-
-		// TEMPORARY: DEBUG
-		mode: "analyze",
-		orientation: "w",
-		userColor: "w",
-		allowChat: false,
-		allowMovelist: true,
-		fen: V.GenRandInitFen(),
 	},
 	created: function() {
 		if (!!localStorage["variant"])
@@ -33,17 +27,13 @@ new Vue({
 		else
 			this.setDisplay();
 		window.onhashchange = this.setDisplay;
-		this.myid = "abcdefghij";
-//console.log(this.myid + " " + variant);
-			//myid: localStorage.getItem("myid"), //our ID, always set
-
+		// Our ID, always set (DB id if registered, collision-free random string otherwise)
+		this.myid = user.id || localStorage["myid"] || "anon-" + getRandString();
 		this.conn = new WebSocket(socketUrl + "/?sid=" + this.myid + "&page=" + variant.id);
 		const socketCloseListener = () => {
 			this.conn = new WebSocket(socketUrl + "/?sid=" + this.myid + "&page=" + variant.id);
 		}
 		this.conn.onclose = socketCloseListener;
-
-		//this.vr = new VariantRules( V.GenRandInitFen() );
 	},
 	methods: {
 		updateSettings: function(event) {
@@ -53,6 +43,11 @@ new Vue({
 				? event.target.checked
 				: event.target.value;
 		},
+		// Game is over, clear storage and put it in indexedDB
+		archiveGame: function() {
+			// TODO: ...
+			//clearStorage();
+		},
 		setDisplay: function() {
 			// Prevent set display if there is a running game
 			if (!!localStorage["variant"])
@@ -69,7 +64,3 @@ new Vue({
 		},
 	},
 });
-		
-//const continuation = (localStorage.getItem("variant") === variant.name);
-//			if (continuation) //game VS human has priority
-//				this.continueGame("human");
diff --git a/views/variant.pug b/views/variant.pug
index 89872ffb..5ea6c463 100644
--- a/views/variant.pug
+++ b/views/variant.pug
@@ -28,15 +28,14 @@ block content
 						i.material-icons settings
 					include userMenu
 		.row
-			//my-room(v-show="display=='room'")
+			my-room(v-show="display=='room'")
 			//my-game-list(v-show="display=='gameList'")
 			my-rules(v-show="display=='rules'" :settings="settings")
 			//my-problems(v-show="display=='problems'" :query-hash="queryHash")
 			my-game(v-show="display=='game'" :game-id="gameid" :conn="conn"
 				:allow-chat="allowChat" :allow-movelist="allowMovelist"
-				:mode="mode" :fen="fen" :query-hash="queryHash")
-			//my-board(:vr="vr" :mode="mode" :orientation="orientation"
-				:user-color="userColor" v-on:play-move="play")
+				:mode="mode" :query-hash="queryHash"
+				@game-over="archiveGame")
 
 block javascripts
 	script(src="/javascripts/utils/array.js")
@@ -50,7 +49,7 @@ block javascripts
 	script.
 		const V = VariantRules; //because this variable is often used
 		const variant = !{JSON.stringify(variant)};
-	//script(src="/javascripts/components/room.js")
+	script(src="/javascripts/components/room.js")
 	//script(src="/javascripts/components/gameList.js")
 	script(src="/javascripts/components/rules.js")
 	script(src="/javascripts/components/board.js")
-- 
2.44.0