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 session from "express-session";
import { engine } from 'express-handlebars';
import { createClient } from 'redis';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@ -21,6 +22,11 @@ app.set('views', './views');
const server = createServer(app);
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);
const sessionMiddleware = session({
@ -34,7 +40,7 @@ app.use(express.static(path.join(__dirname, 'public')));
io.engine.use(sessionMiddleware);
app.get("/", (req, res) => {
app.get("/", async (req, res) => {
if (req.session.nickname == null) {
res.redirect("/setup");
} 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) {
req.session.nickname = req.body.nickname;
req.session.playerID = uuidv4();
}
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) => {
res.redirect("/?path=" + req.originalUrl);
});
@ -69,59 +87,113 @@ io.on('connection', (socket) => {
return;
}
socket.on('create lobby', (callback) => {
if (socket.rooms.size === 1) {
let id = genID();
callback({
status: "ok",
gameCode: id
});
console.log(isPlayerInGame(socket));
if (!isPlayerInGame(socket)) {
socket.on('whats my nick', (callback) => {
callback(session.nickname);
});
socket.join(id);
} 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 {
socket.on('create lobby', (callback) => {
if (socket.rooms.size === 1) {
io.to(msg).emit("joined", session.nickname);
let opp = io.sockets.sockets.get(io.sockets.adapter.rooms.get(msg).values().next().value);
let oppNickname = opp.request.session.nickname;
socket.join(msg);
let id = genID();
callback({
status: "ok",
oppNickname: oppNickname,
gameCode: id
});
socket.join(id);
} else {
callback({
status: "alreadyInLobby",
gameCode: id,
gameCode: socket.rooms[1]
});
}
}
});
});
socket.on('leave lobby', (callback) => {
if (socket.rooms.size === 2) {
socket.leave(socket.rooms[1]);
callback({
status: "ok"
});
} else {
callback({
status: "youreNotInLobby"
});
}
});
socket.on('join lobby', (msg, callback) => {
if (io.sockets.adapter.rooms.get(msg) == null || io.sockets.adapter.rooms.get(msg).size > 1) {
callback({
status: "bad_id"
});
} else {
if (socket.rooms.size === 1) {
io.to(msg).emit("joined", session.nickname); // Wyślij hostowi powiadomienie o dołączającym graczu
// 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, () => {
@ -130,4 +202,37 @@ server.listen(7777, () => {
function genID() {
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-handlebars": "^7.1.2",
"express-session": "^1.17.3",
"redis": "^4.6.12",
"socket.io": "^4.7.2",
"uuid": "^9.0.1",
"uuid4": "^2.0.3"
@ -42,6 +43,59 @@
"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": {
"version": "3.1.0",
"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"
}
},
"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": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -543,6 +605,14 @@
"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": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
@ -961,6 +1031,19 @@
"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": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -1454,6 +1537,11 @@
"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-handlebars": "^7.1.2",
"express-session": "^1.17.3",
"redis": "^4.6.12",
"socket.io": "^4.7.2",
"uuid": "^9.0.1",
"uuid4": "^2.0.3"

View File

@ -16,6 +16,10 @@
color: rgb(136, 136, 136)
}
#gameView {
display: flex;
}
body {
background: black;
color: white;
@ -163,4 +167,20 @@ h1,h2,h3,h4,h5,h6 {
#selectedShip {
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);
});
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 () {
lockUI(true);
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 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/spa_lib.js"></script>