Major changes

- Functioning lobbies with redirection to the board view
- Partly working game mechanics
This commit is contained in:
MaciejkaG 2024-03-01 22:24:30 +01:00
parent a6d6d6b570
commit 302fe3d328
8 changed files with 327 additions and 75 deletions

193
index.js
View File

@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import session from "express-session"; import session from "express-session";
import { engine } from 'express-handlebars'; import { engine } from 'express-handlebars';
import { createClient } from 'redis';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
@ -21,6 +22,11 @@ app.set('views', './views');
const server = createServer(app); const server = createServer(app);
const io = new Server(server); const io = new Server(server);
const redis = createClient();
redis.on('error', err => console.log('Redis Client Error', err));
await redis.connect();
var gameData = [];
app.set('trust proxy', 1); app.set('trust proxy', 1);
const sessionMiddleware = session({ const sessionMiddleware = session({
@ -34,7 +40,7 @@ app.use(express.static(path.join(__dirname, 'public')));
io.engine.use(sessionMiddleware); io.engine.use(sessionMiddleware);
app.get("/", (req, res) => { app.get("/", async (req, res) => {
if (req.session.nickname == null) { if (req.session.nickname == null) {
res.redirect("/setup"); res.redirect("/setup");
} else { } else {
@ -50,14 +56,26 @@ app.get("/setup", (req, res) => {
} }
}); });
app.post('/api/setup-profile', function (req, res) { app.post('/api/setup-profile', (req, res) => {
if (req.session.nickname == null && 4 < req.body.nickname.length && req.body.nickname.length < 16) { if (req.session.nickname == null && 4 < req.body.nickname.length && req.body.nickname.length < 16) {
req.session.nickname = req.body.nickname; req.session.nickname = req.body.nickname;
req.session.playerID = uuidv4();
} }
res.redirect("/") res.redirect("/")
}); });
app.get("/game", async (req, res) => {
const game = await redis.json.get(`game:${req.query.id}`);
if (req.session.nickname == null) {
res.redirect("/setup");
} else if (req.query.id == null || game == null || game.state == "expired") {
res.status(400).send('badGameId');
} else {
res.render('board');
}
});
app.get("/*", (req, res) => { app.get("/*", (req, res) => {
res.redirect("/?path=" + req.originalUrl); res.redirect("/?path=" + req.originalUrl);
}); });
@ -69,59 +87,113 @@ io.on('connection', (socket) => {
return; return;
} }
socket.on('create lobby', (callback) => { console.log(isPlayerInGame(socket));
if (socket.rooms.size === 1) { if (!isPlayerInGame(socket)) {
let id = genID(); socket.on('whats my nick', (callback) => {
callback({ callback(session.nickname);
status: "ok", });
gameCode: id
});
socket.join(id); socket.on('create lobby', (callback) => {
} else {
callback({
status: "alreadyInLobby",
gameCode: socket.rooms[1]
});
}
});
socket.on('join lobby', (msg, callback) => {
if (io.sockets.adapter.rooms.get(msg) == null) {
callback({
status: "bad_id"
});
} else {
if (socket.rooms.size === 1) { if (socket.rooms.size === 1) {
io.to(msg).emit("joined", session.nickname); let id = genID();
let opp = io.sockets.sockets.get(io.sockets.adapter.rooms.get(msg).values().next().value);
let oppNickname = opp.request.session.nickname;
socket.join(msg);
callback({ callback({
status: "ok", status: "ok",
oppNickname: oppNickname, gameCode: id
}); });
socket.join(id);
} else { } else {
callback({ callback({
status: "alreadyInLobby", status: "alreadyInLobby",
gameCode: id, gameCode: socket.rooms[1]
}); });
} }
} });
});
socket.on('leave lobby', (callback) => { socket.on('join lobby', (msg, callback) => {
if (socket.rooms.size === 2) { if (io.sockets.adapter.rooms.get(msg) == null || io.sockets.adapter.rooms.get(msg).size > 1) {
socket.leave(socket.rooms[1]); callback({
callback({ status: "bad_id"
status: "ok" });
}); } else {
} else { if (socket.rooms.size === 1) {
callback({ io.to(msg).emit("joined", session.nickname); // Wyślij hostowi powiadomienie o dołączającym graczu
status: "youreNotInLobby" // Zmienna opp zawiera socket hosta
}); let opp = io.sockets.sockets.get(io.sockets.adapter.rooms.get(msg).values().next().value);
} let oppNickname = opp.request.session.nickname;
});
socket.join(msg); // Dołącz gracza do grupy
callback({
status: "ok",
oppNickname: oppNickname,
}); // Wyślij dołączonemu graczowi odpowiedź
// Teraz utwórz objekt partii w trakcie w bazie Redis
const gameId = uuidv4();
redis.json.set(`game:${gameId}`, '$', {
state: "pregame",
boards: {
host: { // typ 2 to trójmasztowiec pozycja i obrót na planszy które pola zostały trafione
ships: [], // zawiera np. {type: 2, posX: 3, posY: 4, rot: 2, hits: [false, false, true]}
// pozycja na planszy czy strzał miał udział w zatopieniu statku?
shots: [], // zawiera np. {posX: 3, posY: 5, sunk: true}
},
guest: {
ships: [],
shots: [],
}
},
nextPlayer: 0,
});
io.to(msg).emit("gameReady", gameId);
io.sockets.clients(msg).forEach((s) => {
s.leave(msg);
});
} else {
callback({
status: "alreadyInLobby",
gameCode: id,
});
}
}
});
socket.on('leave lobby', (callback) => {
if (socket.rooms.size === 2) {
socket.leave(socket.rooms[1]);
io.to(socket.rooms[1]).emit("player left");
callback({
status: "ok"
});
} else {
callback({
status: "youreNotInLobby"
});
}
});
socket.on('disconnecting', () => {
if (isPlayerInRoom(socket)) {
io.to(socket.rooms[1]).emit("player left");
}
});
} else {
socket.on('shoot', () => {
const playerGame = getPlayerGameData(socket);
if (playerGame.state === "action") {
}
});
socket.on('disconnecting', () => {
if (isPlayerInRoom(socket)) {
io.to(socket.rooms).emit("player left");
}
});
}
}); });
server.listen(7777, () => { server.listen(7777, () => {
@ -130,4 +202,37 @@ server.listen(7777, () => {
function genID() { function genID() {
return Math.floor(100000 + Math.random() * 900000).toString(); return Math.floor(100000 + Math.random() * 900000).toString();
}
// async function emitToParty(partyuuid) {
// const party = gameData.find((element) => element.partyId===partyuuid);
// if (party!==null) {
// party.members.forEach(socketId => {
// io.to(socketId).emit();
// });
// }
// }
function isPlayerInRoom(socket) {
return !socket.rooms.size === 1;
}
async function isPlayerInGame(socket) {
const room = getPlayerRoom(socket);
const game = await redis.json.get(`game:${room}`);
return game != null;
}
function getPlayerGameData(socket) {
const room = getPlayerRoom(socket);
const game = redis.json.get(`game:${room}`);
return game;
}
function getPlayerRoom(socket) {
return socket.rooms.values().next().value;
} }

88
package-lock.json generated
View File

@ -12,6 +12,7 @@
"express": "^4.18.2", "express": "^4.18.2",
"express-handlebars": "^7.1.2", "express-handlebars": "^7.1.2",
"express-session": "^1.17.3", "express-session": "^1.17.3",
"redis": "^4.6.12",
"socket.io": "^4.7.2", "socket.io": "^4.7.2",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"uuid4": "^2.0.3" "uuid4": "^2.0.3"
@ -42,6 +43,59 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@redis/bloom": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/client": {
"version": "1.5.13",
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.13.tgz",
"integrity": "sha512-epkUM9D0Sdmt93/8Ozk43PNjLi36RZzG+d/T1Gdu5AI8jvghonTeLYV69WVWdilvFo+PYxbP0TZ0saMvr6nscQ==",
"dependencies": {
"cluster-key-slot": "1.1.2",
"generic-pool": "3.9.0",
"yallist": "4.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@redis/graph": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/json": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz",
"integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/search": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz",
"integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/time-series": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz",
"integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@socket.io/component-emitter": { "node_modules/@socket.io/component-emitter": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
@ -185,6 +239,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -543,6 +605,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
"engines": {
"node": ">= 4"
}
},
"node_modules/get-intrinsic": { "node_modules/get-intrinsic": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
@ -961,6 +1031,19 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/redis": {
"version": "4.6.12",
"resolved": "https://registry.npmjs.org/redis/-/redis-4.6.12.tgz",
"integrity": "sha512-41Xuuko6P4uH4VPe5nE3BqXHB7a9lkFL0J29AlxKaIfD6eWO8VO/5PDF9ad2oS+mswMsfFxaM5DlE3tnXT+P8Q==",
"dependencies": {
"@redis/bloom": "1.2.0",
"@redis/client": "1.5.13",
"@redis/graph": "1.1.1",
"@redis/json": "1.0.6",
"@redis/search": "1.1.6",
"@redis/time-series": "1.0.5"
}
},
"node_modules/safe-buffer": { "node_modules/safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -1454,6 +1537,11 @@
"optional": true "optional": true
} }
} }
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
} }
} }
} }

View File

@ -22,6 +22,7 @@
"express": "^4.18.2", "express": "^4.18.2",
"express-handlebars": "^7.1.2", "express-handlebars": "^7.1.2",
"express-session": "^1.17.3", "express-session": "^1.17.3",
"redis": "^4.6.12",
"socket.io": "^4.7.2", "socket.io": "^4.7.2",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"uuid4": "^2.0.3" "uuid4": "^2.0.3"

View File

@ -16,6 +16,10 @@
color: rgb(136, 136, 136) color: rgb(136, 136, 136)
} }
#gameView {
display: flex;
}
body { body {
background: black; background: black;
color: white; color: white;
@ -163,4 +167,20 @@ h1,h2,h3,h4,h5,h6 {
#selectedShip { #selectedShip {
animation: changingIn 1 0.2s ease; animation: changingIn 1 0.2s ease;
}
.cover {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
background: rgb(0, 0, 0, 0.4);
backdrop-filter: blur(20px);
transition: opacity 0.5s;
z-index: 999;
} }

View File

@ -0,0 +1,6 @@
const socket = io();
socket.on("players ready", () => {
$(".cover").css({opacity: 0, pointerEvents: "none"});
socket.emit("shoot");
});

View File

@ -7,6 +7,35 @@ socket.on("joined", (nick) => {
lockUI(false); lockUI(false);
}); });
socket.on("player left", () => {
lockUI(true);
switchView("mainMenuView");
lockUI(false);
});
socket.on("gameReady", (gameId) => {
setTimeout(() => {
window.location.replace("/game?id=" + gameId);
}, 2000);
});
var nickname;
socket.emit("whats my nick", (myNickname) => {
nickname = myNickname;
});
socket.on("game start", (gameInfo) => {
let opp;
if (gameInfo.players[0]!==nickname) {
opp = gameInfo.players[0];
} else {
opp = gameInfo.players[1];
}
alert(`Grasz przeciwko: ${opp}`);
});
$("#createGameButton").on("click", function () { $("#createGameButton").on("click", function () {
lockUI(true); lockUI(true);
socket.emit("create lobby", (response) => { socket.emit("create lobby", (response) => {

34
views/board.handlebars Normal file
View File

@ -0,0 +1,34 @@
<div class="cover">
<h1>Oczekiwanie na przeciwnika...</h1>
</div>
<div class="container" id="gameView">
<div>
<h1>Statki</h1>
<div class="panelContainer">
<div class="shapes">
<div class="ownBoardInfo">
<h3>Wybrany statek</h3>
<h2 class="dynamic" id="selectedShip">Jednomasztowiec</h2>
<h3>Dostępne: <span class="dynamic danger">1</span></h3>
</div>
<span class="break"></span>
<h2>Sterowanie</h2>
<h3 class="controlsOwnBoard"><span class="important">S</span> Zmiana statku</h3>
<h3 class="controlsOwnBoard"><span class="important">R</span> Obrót statku</h3>
<h3><span class="important">B</span> Zamiana planszy</h3>
<span class="break"></span>
<h3>Ruch: <span class="dynamic">Przeciwnik</span></h3>
<h2 class="important">∞</h2>
</div>
<div class="boardContainer">
<div id="board" oncontextmenu="return false;"></div>
<div id="secondaryBoard" class="secondary" oncontextmenu="return false;"></div>
</div>
<div class="spaceFiller"></div>
</div>
</div>
<script src="/assets/js/battleships_lib.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/key_handling.js"></script>
</div>

View File

@ -77,36 +77,5 @@
</div> </div>
</div> </div>
<div class="container" id="gameView" data-title="Statki / Gra" data-path="/game">
<div>
<h1>Statki</h1>
<div class="panelContainer">
<div class="shapes">
<div class="ownBoardInfo">
<h3>Wybrany statek</h3>
<h2 class="dynamic" id="selectedShip">Jednomasztowiec</h2>
<h3>Dostępne: <span class="dynamic danger">1</span></h3>
</div>
<span class="break"></span>
<h2>Sterowanie</h2>
<h3 class="controlsOwnBoard"><span class="important">S</span> Zmiana statku</h3>
<h3 class="controlsOwnBoard"><span class="important">R</span> Obrót statku</h3>
<h3><span class="important">B</span> Zamiana planszy</h3>
<span class="break"></span>
<h3>Ruch: <span class="dynamic">Przeciwnik</span></h3>
<h2 class="important">∞</h2>
</div>
<div class="boardContainer">
<div id="board" oncontextmenu="return false;"></div>
<div id="secondaryBoard" class="secondary" oncontextmenu="return false;"></div>
</div>
<div class="spaceFiller"></div>
</div>
</div>
<script src="/assets/js/battleships_lib.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/key_handling.js"></script>
</div>
<script src="/assets/js/socket.js"></script> <script src="/assets/js/socket.js"></script>
<script src="/assets/js/spa_lib.js"></script> <script src="/assets/js/spa_lib.js"></script>