299 lines
8.5 KiB
TypeScript
299 lines
8.5 KiB
TypeScript
import {
|
|
NextApiResponseWithSocket,
|
|
sServer,
|
|
} from "../../interfaces/NextApiSocket"
|
|
import {
|
|
composeBody,
|
|
gameSelects,
|
|
getAnyGame,
|
|
getAnyRunningGame,
|
|
} from "./game/running"
|
|
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"
|
|
|
|
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
|