diff --git a/TODO b/TODO index 840f749..c49fc89 100644 --- a/TODO +++ b/TODO @@ -2,17 +2,22 @@ [ ] Add support for sharding / load balancing with Nginx [ ] Use a database lol +[ ] Lobby discovery system to find and join "public" lobbies + [ ] Matchmaking + + # Short term, coursework scope [ ] Translate - ✔ Portuagese @done(21-04-11 01:03) - ✔ Spanish @done(21-04-11 01:03) - → French - → Czech + ✔ Portuagese (Ines) @done(21-04-11 01:03) + ✔ Spanish (Ines) @done(21-04-11 01:03) + → French (Alexendro) + → Czech (Mikdore) + [ ] Sort out how intents work in game transitioning [ ] Game logic ✘ Singleplayer game logic - OUT OF SCOPE @cancelled(21-04-11 01:04) - + ✘ AI CPU player @cancelled(21-04-11 01:07) \ No newline at end of file diff --git a/client/public/game/lobbies.js b/client/public/game/lobbies.js index 8f323a5..b90bced 100644 --- a/client/public/game/lobbies.js +++ b/client/public/game/lobbies.js @@ -240,13 +240,11 @@ socket.on('request-intent-change', obj => { if (!obj.intent) return; if (!obj.lobby) return; - if (!obj.intent === 'GAMETRANSITION') return; + if (obj.intent !== 'GAMETRANSITION') return; // window.location.search = `?uid=${obj.lobby.uid}`; // window.location.pathname = '/scrabble'; window.location = `/scrabble?uid=${obj.lobby.uid}`; - - }); socket.on('lobby-update', obj => { diff --git a/client/public/scrabble/events.js b/client/public/scrabble/events.js index f2638f5..26ed5b0 100644 --- a/client/public/scrabble/events.js +++ b/client/public/scrabble/events.js @@ -3,7 +3,6 @@ const gameinfoLeft = document.querySelector('#game-info-left'); const gameinfoCompact = document.querySelector('#game-info-compact'); - function windowResize() { if (window.innerWidth < 1100) diff --git a/client/public/scrabble/index.html b/client/public/scrabble/index.html index 27b7b16..00836a3 100644 --- a/client/public/scrabble/index.html +++ b/client/public/scrabble/index.html @@ -5,6 +5,8 @@ + + @@ -21,6 +23,7 @@ SCRABBLE +
:
@@ -309,6 +312,7 @@ Score:
102
+
:
diff --git a/client/public/scrabble/network.js b/client/public/scrabble/network.js index 6ef4bcc..9f9d6e1 100644 --- a/client/public/scrabble/network.js +++ b/client/public/scrabble/network.js @@ -1,15 +1,109 @@ - -// is game singleplayer? const urlParser = new URLSearchParams(window.location.search); +const ConnectionState = document.querySelector('#connection-state'); + + +function initMultiplayer() +{ + // init socket + const socket = io(window.location.host); + + socket.on('connect', (...args) => { + console.log('Socket Connected'); + ConnectionState.innerHTML = `${localeString('status')}: Waiting for identify`; + }); + + socket.on('disconnect', (...args) => { + console.log('Socket Disconnected'); + ConnectionState.innerHTML = `${localeString('status')}: ${localeString('status-disconnected')}`; + onDisconnect(); + }); + + + socket.on('identify', (...args) => { + ConnectionState.innerHTML = 'Identify recived' + + if (!sessionStorage.user) + { + socket.disconnect(); + ConnectionState.innerHTML = 'Identify cannot proceed, no user'; + document.location.href += '../'; + return; + } + + let user = {}; + try + { + user = JSON.parse(sessionStorage.user); + } catch (e) + { + socket.disconnect(); + ConnectionState.innerHTML = 'Identify cannot proceed, corrupted user'; + document.location.href += '../'; + return; + } + + if (!user.uid) + { + socket.disconnect(); + ConnectionState.innerHTML = 'Identify cannot proceed, corrupted user'; + document.location.href += '../'; + return; + } + + const lobbyUID = urlParser.get('uid') + + if (!lobbyUID) + { + socket.disconnect(); + ConnectionState.innerHTML = 'Identify cannot proceed, corrupted lobby'; + document.location.href += '../'; + return; + } + + socket.emit('identify', { userid: user.uid, lobbyuid: lobbyUID, intent: 'GAME' }); + ConnectionState.innerHTML = 'Identify response'; + }); + + + socket.on('identify-success', (...args) => { + console.log(args[0]); + ConnectionState.innerHTML = localeString('status-connected-as') + ' ' + args[0].user.username; + onConnect(); + }); + + socket.on('identify-error', (...args) => { + console.log(args[0]); + ConnectionState.innerHTML = JSON.stringify(args[0]); + onDisconnect(); + }); + +} + + +function onConnect() +{ + +} + +function onDisconnect() +{ + +} + + + +// is game singleplayer? let isSingleplayer = false; if (urlParser.get('uid') === null) { isSingleplayer = true; } -if (!isSingleplayer) +if (isSingleplayer) { - // init socket + ConnectionState.innerHTML = localeString('no-connection-singleplayer'); +} else +{ + initMultiplayer(); } - diff --git a/data/en.lang b/data/en.lang index d0668fc..aa10f6c 100644 --- a/data/en.lang +++ b/data/en.lang @@ -1,6 +1,7 @@ language:en country:Britain ---- + button-back:back; (Action) to go back (a page) button-create:create button-join:join; (Action) to join @@ -14,6 +15,7 @@ error-bad-intent:Client has no intent error-bold:ERROR error-cannot-join-lobby:Cannot join lobby error-creating-lobby:An error occurred while creating the lobby +error-illegal-intent:Illegal intent error-illegal-lobby:Illegal lobby error-illegal-user:Illegal user error-invalid-username:Invalid username @@ -48,6 +50,7 @@ lobby-private:private lobby log-console:log console message:message; (noun) piece of text name:name +no-connection-singleplayer:No connection, playing singleplayer; there is no (connection) as the user is playing (on their own) players:Players ready:ready ; (Action) to be (ready) to play scrabble-name:Scrabble diff --git a/data/es.lang b/data/es.lang index 70ab713..2632e9d 100644 --- a/data/es.lang +++ b/data/es.lang @@ -14,6 +14,7 @@ error-bad-intent:Cliente no tiene intención error-bold:ERRO error-cannot-join-lobby:No se puede unir al lobby error-creating-lobby:Ocurrió un error al crear el lobby +error-illegal-intent: error-illegal-lobby:Lobby ilegal error-illegal-user:Usuario ilegal error-invalid-username:Nombre de usuario no es válido @@ -48,6 +49,7 @@ lobby-private:lobby privado log-console:consola de log message:mensaje name:nombre +no-connection-singleplayer: players:Jugadores ready: scrabble-name:Scrabble diff --git a/data/locale.json b/data/locale.json index 39882d5..149f805 100644 --- a/data/locale.json +++ b/data/locale.json @@ -64,6 +64,11 @@ "es": "Ocurrió un error al crear el lobby", "pt": "Ocorreu um erro ao criar o lobby" }, + "error-illegal-intent": { + "en": "Illegal intent", + "es": "", + "pt": "" + }, "error-illegal-lobby": { "en": "Illegal lobby", "es": "Lobby ilegal", @@ -234,6 +239,11 @@ "es": "nombre", "pt": "nome" }, + "no-connection-singleplayer": { + "en": "No connection, playing singleplayer", + "es": "", + "pt": "" + }, "players": { "en": "Players", "es": "Jugadores", diff --git a/data/pt.lang b/data/pt.lang index e65568a..c49fce7 100644 --- a/data/pt.lang +++ b/data/pt.lang @@ -14,6 +14,7 @@ error-bad-intent:Cliente não tem intenção error-bold:ERROR error-cannot-join-lobby:Não é possível entrar no lobby error-creating-lobby:Ocorreu um erro ao criar o lobby +error-illegal-intent: error-illegal-lobby:Lobby ilegal error-illegal-user:Usuário ilegal error-invalid-username:Nome de usuário Inválido @@ -48,6 +49,7 @@ lobby-private:lobby privado log-console:console de log message:mensagem name:nome +no-connection-singleplayer: players:Jogadores ready: scrabble-name:Scrabble diff --git a/server/src/game-registrar.js b/server/src/game-registrar.js index b8afce9..b3cfaab 100644 --- a/server/src/game-registrar.js +++ b/server/src/game-registrar.js @@ -158,14 +158,15 @@ function GetUserbyConnection(connectionid) } // TODO: User intent -function UserConnect(useruid, connectionid) +function UserConnect(useruid, connectionid, intent) { if (OnlineUsers[useruid].state === 'CONNECTED') return 'error-taken-user-connection'; OnlineUsers[useruid].connectionid = connectionid; OnlineUsers[useruid].state = 'CONNECTED'; + OnlineUsers[useruid].intent = intent; - Logger.game(`SOCKET ${connectionid} IDENTIFIED AS ${useruid} (${OnlineUsers[useruid].username})`); + Logger.game(`SOCKET ${connectionid} IDENTIFIED AS ${useruid} (${OnlineUsers[useruid].username}) INTENDS TO ${intent}`); return true; } diff --git a/server/src/lobbies.js b/server/src/lobbies.js index 1cffce6..2e06f56 100644 --- a/server/src/lobbies.js +++ b/server/src/lobbies.js @@ -8,7 +8,7 @@ LOBBY OBJECT uid: uid, name: string owneruid: useruid, - players: [{uid, name, ready}], + players: [{uid, name, ready, ingame}], spectators: [{uid, name}], // PUBLIC, PRIVATE visibility: 'PUBLIC', @@ -74,13 +74,18 @@ function IsLobbyReady(lobbyuid) if (!Lobbies[lobbyuid]) return false; // only support 2-4 players // https://en.wikipedia.org/wiki/Scrabble - // TODO: ADD THIS BACK - REMOVED FOR TESTING + // TODO: URGENT ADD THIS BACK AFTER TESTING // if (Lobbies[lobbyuid].players.length <= 1) return false; if (Lobbies[lobbyuid].players.length > 4) return false; return Lobbies[lobbyuid].players.every(e => e.ready); } +function IsLobbyReadyForGame(lobbyuid) +{ + if (!Lobbies[lobbyuid]) return false; + return Lobbies[lobbyuid].players.every(e => e.ingame); +} function GetLobbyByUID(lobbyuid) @@ -95,14 +100,14 @@ function GetLobbyByOwnerUID(owneruid) return false; } -function GetLobbyByUserUID(playeruid) +function GetLobbyByUserUID(useruid) { for (const lobby in Lobbies) { for (const player of Lobbies[lobby].players) - if (player.uid === playeruid) return Lobbies[lobby]; + if (player.uid === useruid) return Lobbies[lobby]; for (const player of Lobbies[lobby].spectators) - if (player.uid === playeruid) return Lobbies[lobby]; + if (player.uid === useruid) return Lobbies[lobby]; } return false; @@ -155,7 +160,12 @@ function UserJoinLobby(lobbyuid, useruid, callback) // TODO: check users and change lobby status const user = Registrar.GetUserByUID(useruid); - Lobbies[lobbyuid].players.push({uid: useruid, name: user.username, ready: false}); + Lobbies[lobbyuid].players.push({ + uid: useruid, + name: user.username, + ready: false, + ingame: false + }); Logger.game(`LOBBY ${lobbyuid} USER ${useruid} (${user.username}) JOINING`); @@ -238,6 +248,24 @@ function SpectatorJoinLobby(lobbyuid, useruid, callback) } +function UserConnectGame(useruid) +{ + if (!IsUserInLobby(useruid)) return false; + + const lobby = GetLobbyByUserUID(useruid); + + for (const player in Lobbies[lobby.uid].players) + if (Lobbies[lobby.uid].players[player].uid === useruid) + Lobbies[lobby.uid].players[player].ingame = true; + + + for (const spectator in Lobbies[lobby.uid].spectators) + if (Lobbies[lobby.uid].spectators[player].uid === useruid) + Lobbies[lobby.uid].spectators[spectator].ingame = true; + + return true; +} + module.exports = { // Lobby validation exports @@ -245,6 +273,7 @@ module.exports = { DoesUserOwnLobby: DoesUserOwnLobby, IsUserInLobby: IsUserInLobby, IsLobbyReady: IsLobbyReady, + IsLobbyReadyForGame: IsLobbyReadyForGame, // Get lobby exports GetLobbyByUID: GetLobbyByUID, @@ -259,5 +288,6 @@ module.exports = { UserReady: UserReady, UserUnReady: UserUnReady, UserLeaveLobby: UserLeaveLobby, - SpectatorJoinLobby: SpectatorJoinLobby + SpectatorJoinLobby: SpectatorJoinLobby, + UserConnectGame: UserConnectGame } diff --git a/server/src/socketserver.js b/server/src/socketserver.js index a35569f..55fa4af 100644 --- a/server/src/socketserver.js +++ b/server/src/socketserver.js @@ -76,13 +76,6 @@ function ClientIdentify(socket, args) const user = Game.Registrar.GetUserByUID(args.userid); const intent = args.intent; - if (!intent) - { - err.addError(400, 'Bad Request', 'error-bad-intent'); - socket.emit('identify-error', err.toError); - return; - } - if (!user) { err.addError(400, 'Bad Request', 'error-unknown-uid'); @@ -90,14 +83,47 @@ function ClientIdentify(socket, args) return; } - // TODO: Sort out client intent - - const status = Game.Registrar.UserConnect(user.uid, socket.id); + if (!intent) + { + err.addError(400, 'Bad Request', 'error-bad-intent'); + socket.emit('identify-error', err.toError); + return; + } + + const oldIntent = user.intent; + + Game.Registrar.ChangeUserIntent(user.uid, intent); + const status = Game.Registrar.UserConnect(user.uid, socket.id, intent); + + // If the user enters a game without transitioning, no bueno + if (intent === 'GAME' && oldIntent !== 'GAMETRANSITION') + { + err.addError(500, 'Internal Server Error', 'error-illegal-intent'); + socket.emit('identify-error', err.toError); + return; + } + + // User intends to enter a game + if (intent === 'GAME' && oldIntent === 'GAMETRANSITION') + { + const lobbyUID = args.lobbyuid; + + // Make sure the user is actually in this game + const lobby = Game.Lobbies.GetLobbyByUserUID(user.uid); + if (lobby.uid !== lobbyUID) + { + err.addError(500, 'Internal Server Error', 'error-illegal-intent'); + socket.emit('identify-error', err.toError); + return; + } + + Game.Lobbies.UserConnectGame(user.uid); + } + if (status === true) { socket.emit('identify-success', {connected: true, user: user}); - Game.Registrar.ChangeUserIntent(user.uid, intent); return; } else if (status === 'error-taken-user-connection') @@ -319,7 +345,11 @@ function HandleDisconnect(socket, args) // if user is in a lobby, leave and if user own's a lobby, destruct // leave lobby before user is disconnected - LobbyLeave(socket); + + if (user.intent !== 'GAMETRANSITION') + { + LobbyLeave(socket); + } Game.Registrar.UserDisconnect(user.uid);