Join games via invitation URL, instead of anonymously
authorJoachim Breitner <mail@joachim-breitner.de>
Sat, 8 Feb 2014 15:26:58 +0000 (15:26 +0000)
committerJoachim Breitner <mail@joachim-breitner.de>
Sat, 8 Feb 2014 15:28:34 +0000 (15:28 +0000)
README.md
client/index.html
client/sumserum.js
server.js

index 44c7bff..743fb51 100644 (file)
--- a/README.md
+++ b/README.md
@@ -10,8 +10,7 @@ on [BoardGameGeek](http://boardgamegeek.com/boardgame/30046/sim-serim).
 This implementation lets you play locally, with two player on one
 machine (make sure you do not let your opponent see what button you
 press), or via the internet. In that case, just press “Play online” and
-wait for someone else to join (or better arrange for someone else to
-join).
+share the shown URL with your friend.
 
 Sum Serum was implemented by [Joachim
 Breitner](http://www.joachim-breitner.de/) and is Free Software. Feel
index c9fc2d2..88e520b 100644 (file)
@@ -23,6 +23,7 @@
 <div align="center">
 <button id="playlocal">Play local</button>
 <button id="playonline">Play online</button>
+<input id="shareurl" style="display:none; width:450px" />
 </div>
 <div align="center">
 <canvas tabindex="1" id="canvas" align="center" width="500" height="400"
@@ -44,7 +45,7 @@ This implementation lets you play locally, with two players, or online:
 </p>
 <ul>
 <li>If you play locally, you probably do not want to select your number of stones with the mouse, as your opponent will see your selection. Therefore, the left player (black) can use the keys <tt>1</tt> up to <tt>4</tt> to make a (hopefully unseen) selection. The right player uses <tt>U</tt>, <tt>I</tt>, <tt>O</tt> and <tt>P</tt>, which just happen to be convenient keys on the right. It does not matter in which order the players make their choice.</li>
-<li>If you want to play online, just press “Play online” and wait for someone else to join (or better arrange for someone else to join).</li>
+<li>If you want to play online, just press “Play online”. You will see an URL shown below that button; share that with your friend (e.g. via e-mail or instant messenger) and you can play.</li>
 </ul>
 
 <p>Sum Serum was implemented by <a href="http://www.joachim-breitner.de/">Joachim Breitner</a> and is Free Software. Feel free to look <a href="http://git.nomeata.de/?p=sumserum.git">at the code</a> or <a href="https://github.com/nomeata/sumserum/issues">report a problem or a pull-request</a>. It builds on HTML5, Canvas, node.js and SockJS and is currently hosted by <a href="https://www.openshift.com/">OpenShift</a>.
index d7e015c..f8f148f 100644 (file)
@@ -30,6 +30,7 @@ function no_game() {
 
        if (sockjs) sockjs.close();
        sockjs = undefined;
+       document.getElementById("shareurl").style.display = "none";
 
 }
 
@@ -41,6 +42,7 @@ function start_game(){
        state.restart_game();
        draw_game();
        c.focus();
+       document.getElementById("shareurl").style.display = "none";
 }
 
 // Buttons
@@ -57,6 +59,7 @@ document.getElementById("playlocal").addEventListener("click", function () {
        start_game();
 });
 
+// Play online
 document.getElementById("playonline").addEventListener("click", function () {
        if (state && state.phase != FINISHED) {
                if (!confirm("Do you really want to abort current game?")) {
@@ -71,6 +74,22 @@ document.getElementById("playonline").addEventListener("click", function () {
        sockjs.onopen = sockjs_onopen;
 });
 
+
+// Related: join a game
+{
+       var joingameid;
+       var match = document.location.href.match(/#(.*)/);
+       window.location.hash='';
+       if (match) {joingameid = match[1]};
+       if (joingameid) {
+               console.log("Trying to join existing game " + joingameid);
+               sockjs = new SockJS('/game');
+               draw_message("Connecting...");
+               sockjs.onmessage = sockjs_onmessage;
+               sockjs.onopen = sockjs_onopen_join(joingameid);
+       }
+}
+
 window.addEventListener('beforeunload', function (e){
        if (state && state.phase != FINISHED) {
                e.returnValue = "You have an unfinished game running."
@@ -79,6 +98,10 @@ window.addEventListener('beforeunload', function (e){
 });
 
 // Handle server message
+function sockjs_onopen_join(gameid){return function() {
+       sockjs.send(JSON.stringify({meta: "join", gameid: gameid}));
+       draw_message("Waiting for another player to join...");
+}}
 function sockjs_onopen(){
        sockjs.send(JSON.stringify({meta: "hookmeup"}));
        draw_message("Waiting for another player to join...");
@@ -88,8 +111,13 @@ function sockjs_onmessage(event) {
        var input = JSON.parse(event.data);
        var meta;
        if (meta = input.meta) {
-               // New game
-               if (meta == "newgame") {
+               if (meta == "gameid") {
+                       var url = document.location.href.match(/(^[^#]*)/)[0];
+                       var game_url = url + "#" + input.gameid;
+                       document.getElementById("shareurl").value = game_url;
+                       document.getElementById("shareurl").style.display = "block";
+               } else if (meta == "newgame") {
+                       // New game
                        local[input.you] = true;
                        local[other(input.you)] = false;
                        start_game();
index 7933892..ad05690 100644 (file)
--- a/server.js
+++ b/server.js
@@ -3,14 +3,18 @@ var ip   = process.env.OPENSHIFT_NODEJS_IP || "127.0.0.1";
 var http = require('http');
 var sockjs = require('sockjs');
 var node_static = require('node-static');
+var uuid = require('node-uuid');
 
 // 1. Echo sockjs server
 var sockjs_opts = {sockjs_url: "http://cdn.sockjs.org/sockjs-0.3.min.js"};
 
+// key to client connection
 var clients = {};
 
-// An id, or no idea
-var waiting;
+// game id to waiting player
+var waiting = {};
+// waiting player to game id
+var waiting_clients = {};
 
 // Mapping from id to opponent's id
 var opp = {};
@@ -21,16 +25,20 @@ function broadcast(from, message) {
         }
 }
 
+function send(to, dict) {
+       clients[to].write(JSON.stringify(dict));
+}
+
 function error(to, message) {
-       clients[to].write(JSON.stringify({meta: "error", error: message}));
+       send(to, {meta: "error", error: message});
 }
 
 function hookup(p1, p2) {
+       console.log("Trying to hook up " + p1 + " and " + p2);
        opp[p1] = p2;
        opp[p2] = p1;
-       clients[p1].write(JSON.stringify({meta: "newgame", you: 1}));
-       clients[p2].write(JSON.stringify({meta: "newgame", you: 2}));
-       waiting = undefined;
+       send(p1, {meta: "newgame", you: 1});
+       send(p2, {meta: "newgame", you: 2});
 }
 
 var sockjs = sockjs.createServer(sockjs_opts);
@@ -44,10 +52,34 @@ sockjs.on('connection', function(conn) {
                if (meta = msg.meta) {
                        console.log(conn.id, msg);
                        if (meta == "hookmeup") {
-                               if (waiting && waiting != conn.id) {
-                                       hookup(waiting, conn.id);
+                               if (waiting_clients.hasOwnProperty(conn.id)) {
+                                       var gameid = waiting_clients[conn.id];
+                                       delete waiting[gameid];
+                                       delete waiting_clients[conn.id];
+                               }
+                               var gameid = uuid().substr(0,8);
+
+                               console.log('New waiting game ' + gameid + ' for ' + conn.id);
+                               waiting[gameid] = conn.id;
+                               waiting_clients[conn.id] = gameid;
+
+                               send(conn.id, {meta: "gameid", gameid: gameid});
+                       } else if (meta == "join") {
+                               if (waiting_clients.hasOwnProperty(conn.id)) {
+                                       var gameid = waiting_clients[conn.id];
+                                       delete waiting[gameid];
+                                       delete waiting_clients[conn.id];
+                               }
+                               var gameid = msg.gameid;
+
+                               console.log('Player ' + conn.id + 'wants to join game ' + gameid);
+                               if (waiting.hasOwnProperty(gameid)) {
+                                       var oppid = waiting[gameid];
+                                       hookup(oppid, conn.id);
+                                       delete waiting[gameid];
+                                       delete waiting_clients[oppid];
                                } else {
-                                       waiting = conn.id;
+                                       error (conn.id, "No such game to join.");
                                }
                        } else {
                                error(conn.id, "Unknown command " + meta);
@@ -64,11 +96,15 @@ sockjs.on('connection', function(conn) {
        conn.on('close', function() {
                console.log('closed', conn.id);
                if (opp.hasOwnProperty(conn.id)){
-                       clients[opp[conn.id]].write(JSON.stringify({meta:"left" }));
+                       send(opp[conn.id], {meta:"left" });
                        delete opp[opp[conn.id]];
                        delete opp[conn.id];
                }
-               if (waiting == conn.id) waiting = undefined;
+               if (waiting_clients.hasOwnProperty[conn.id]) {
+                       var gameid = waiting_clients[conn.id];
+                       delete waiting_clients[conn.id];
+                       delete waiting[gameid];
+               }
                delete clients[conn.id];
        });
 });
@@ -87,5 +123,5 @@ server.addListener('upgrade', function(req,res){
 
 sockjs.installHandlers(server, {prefix:'/game'});
 
-console.log(' [*] Listening on ' + ip + ':' + port );
+console.log(' [*] Listening on http://' + ip + ':' + port + '/');
 server.listen(port, ip);