leaky-ships/leaky-ships/pages/api/ws.ts
2023-07-11 19:25:44 +02:00

299 lines
8.5 KiB
TypeScript

import logging from "@lib/backend/logging"
import prisma from "@lib/prisma"
import { GamePropsSchema } from "@lib/zodSchemas"
import colors from "colors"
import status from "http-status"
import { NextApiRequest } from "next"
import { getSession } from "next-auth/react"
import { Server } from "socket.io"
import {
NextApiResponseWithSocket,
sServer,
} from "../../interfaces/NextApiSocket"
import {
composeBody,
gameSelects,
getAnyGame,
getAnyRunningGame,
} from "./game/running"
colors.enable()
const SocketHandler = async (
req: NextApiRequest,
res: NextApiResponseWithSocket,
) => {
if (res.socket.server.io) {
logging("Socket is already running " + req.url, ["infoCyan"], req)
} else {
logging("Socket is initializing " + req.url, ["infoCyan"], req)
const io: sServer = new Server(res.socket.server, {
path: "/api/ws",
cors: {
origin: "https://leaky-ships.mal-noh.de",
},
})
res.socket.server.io = io
// io.use(authenticate)
io.use(async (socket, next) => {
try {
const session = await getSession({
req: socket.request,
})
if (!session) return next(new Error(status["401"]))
socket.data.user = session.user
const game = await getAnyRunningGame(socket.data.user?.id ?? "")
if (!game) {
logging(
"Forbidden, no game found: " +
JSON.stringify(Array.from(socket.rooms)),
["debug"],
socket.request,
)
return next(new Error(status["403"]))
}
const { payload, hash } = composeBody(game)
// let index: number | null = null
const index = payload.users.findIndex(
(user) => socket.data.user?.id === user?.id,
)
if (index < 0) return next(new Error(status["401"]))
socket.data.index = index
socket.data.gameId = game.id
socket.join(game.id)
socket.to(game.id).emit("playerEvent", {
type: "connect",
i: socket.data.index,
payload: { users: payload.users },
hash,
})
next()
} catch (err: any) {
logging("Unkonwn error - " + status["401"], ["warn"], socket.request)
next(new Error(status["401"]))
}
})
io.on("connection", async (socket) => {
logging(
`User connected <${socket.data.user?.email}>`.green +
", " +
socket.id.cyan,
["infoGreen"],
socket.request,
)
socket.on("update", async (cb) => {
const game = await getAnyGame(socket.data.gameId ?? "")
if (!game) return
const body = composeBody(game)
cb(body)
})
socket.on("gameSetting", async (payload, cb) => {
const game = await prisma.game.update({
where: { id: socket.data.gameId ?? "" },
data: payload,
...gameSelects,
})
const { hash } = composeBody(game)
if (!hash) return
cb(hash)
socket.to(game.id).emit("gameSetting", payload, hash)
})
socket.on("ping", (callback) => callback())
socket.on("leave", async (cb) => {
if (!socket.data.gameId || !socket.data.user?.id) return cb(false)
const user_Game = await prisma.user_Game.delete({
where: {
gameId_userId: {
gameId: socket.data.gameId,
userId: socket.data.user?.id,
},
},
})
const enemy = await prisma.user_Game.findFirst({
where: {
gameId: socket.data.gameId,
},
})
let body: GamePropsSchema
if (user_Game.index === 1 && enemy) {
const { game } = await prisma.user_Game.update({
where: {
gameId_index: {
gameId: socket.data.gameId,
index: 2,
},
},
data: {
index: 1,
},
select: {
game: { ...gameSelects },
},
})
body = composeBody(game)
} else {
const game = await prisma.game.findUnique({
where: {
id: socket.data.gameId,
},
...gameSelects,
})
if (!game) return cb(false)
body = composeBody(game)
}
const { payload, hash } = body
if (!payload || !hash || socket.data.index === undefined)
return cb(false)
socket.to(socket.data.gameId).emit("playerEvent", {
type: "leave",
i: socket.data.index,
payload: { users: payload.users },
hash,
})
cb(true)
if (!payload.users.length) {
await prisma.game.delete({
where: {
id: socket.data.gameId,
},
})
}
})
socket.on("isReady", async (isReady) => {
if (socket.data.index === undefined || !socket.data.gameId) return
socket
.to(socket.data.gameId)
.emit("isReady", { i: socket.data.index, isReady })
socket
.to(socket.data.gameId)
.emit("isConnected", { i: socket.data.index, isConnected: true })
})
socket.on("canvas-state", (state) => {
if (!socket.data.gameId || !socket.data.index) return
console.log("received canvas state")
socket
.to(socket.data.gameId)
.emit("canvas-state-from-server", state, socket.data.index)
})
socket.on("draw-line", ({ prevPoint, currentPoint, color }) => {
if (!socket.data.gameId || !socket.data.index) return
socket
.to(socket.data.gameId)
.emit(
"draw-line",
{ prevPoint, currentPoint, color },
socket.data.index,
)
})
socket.on("canvas-clear", () => {
if (!socket.data.gameId) return
socket.to(socket.data.gameId).emit("canvas-clear")
})
socket.on("gameState", async (newState) => {
if (socket.data.index !== 0 || !socket.data.gameId) return
await prisma.game.update({
where: { id: socket.data.gameId },
data: {
state: newState,
},
})
io.to(socket.data.gameId).emit("gameState", newState)
if (newState === "running")
io.to(socket.data.gameId).emit("activeIndex", 0)
})
socket.on("ships", async (ships) => {
if (
!socket.data.gameId ||
!socket.data.user?.id ||
typeof socket.data.index === "undefined"
)
return
await prisma.user_Game.update({
where: {
gameId_userId: {
gameId: socket.data.gameId,
userId: socket.data.user.id,
},
},
data: {
ships: {
deleteMany: {},
createMany: {
data: ships,
},
},
},
})
socket.to(socket.data.gameId).emit("ships", ships, socket.data.index)
})
socket.on("dispatchMove", async (props) => {
if (
!socket.data.gameId ||
!socket.data.user?.id ||
typeof socket.data.index === "undefined"
)
return
const user_Game = await prisma.user_Game
.update({
where: {
gameId_userId: {
gameId: socket.data.gameId,
userId: socket.data.user?.id,
},
},
data: {
moves: {
create: props,
},
},
select: { game: gameSelects },
})
.catch((e) => console.log(e, props))
if (!user_Game?.game) return
const game = user_Game.game
const l1 = game.users[0].moves.length
const l2 = game.users[1].moves.length
io.to(socket.data.gameId).emit("dispatchMove", props, socket.data.index)
io.to(socket.data.gameId).emit("activeIndex", l1 > l2 ? 1 : 0)
})
socket.on("disconnecting", async () => {
logging(
"Disconnecting: " + JSON.stringify(Array.from(socket.rooms)),
["debug"],
socket.request,
)
if (socket.data.index === undefined || !socket.data.gameId) return
socket.to(socket.data.gameId).emit("playerEvent", {
type: "disconnect",
i: socket.data.index,
})
})
socket.on("disconnect", () => {
logging("Disconnect: " + socket.id, ["debug"], socket.request)
})
})
}
res.end()
}
export default SocketHandler